From fc84c788e2d8527050eccc4d7b4b31c2018c5dcd Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sat, 5 Oct 2024 21:25:28 -0400 Subject: [PATCH 001/194] v1 of boilerplate for printer classes --- server/Classes/Device.py | 130 ++++++++++++++++++ server/Classes/Ports.py | 26 ++++ server/Classes/Printer.py | 120 ++++++++++++++++ server/Classes/PrinterList.py | 70 ++++++++++ server/Classes/Printers/Ender/Ender3.py | 12 ++ server/Classes/Printers/Ender/Ender3Pro.py | 11 ++ server/Classes/Printers/Ender/EnderPrinter.py | 28 ++++ server/Classes/Printers/Prusa/PrusaMK3.py | 22 +++ server/Classes/Printers/Prusa/PrusaMK4.py | 21 +++ server/Classes/Printers/Prusa/PrusaMK4S.py | 11 ++ server/Classes/Printers/Prusa/PrusaPrinter.py | 62 +++++++++ server/Classes/Vector3.py | 52 +++++++ server/Interfaces/canPause.py | 17 +++ server/Interfaces/hasEndingSequence.py | 13 ++ server/Interfaces/hasResponseCodes.py | 22 +++ server/Interfaces/hasStartupSequence.py | 13 ++ 16 files changed, 630 insertions(+) create mode 100644 server/Classes/Device.py create mode 100644 server/Classes/Ports.py create mode 100644 server/Classes/Printer.py create mode 100644 server/Classes/PrinterList.py create mode 100644 server/Classes/Printers/Ender/Ender3.py create mode 100644 server/Classes/Printers/Ender/Ender3Pro.py create mode 100644 server/Classes/Printers/Ender/EnderPrinter.py create mode 100644 server/Classes/Printers/Prusa/PrusaMK3.py create mode 100644 server/Classes/Printers/Prusa/PrusaMK4.py create mode 100644 server/Classes/Printers/Prusa/PrusaMK4S.py create mode 100644 server/Classes/Printers/Prusa/PrusaPrinter.py create mode 100644 server/Classes/Vector3.py create mode 100644 server/Interfaces/canPause.py create mode 100644 server/Interfaces/hasEndingSequence.py create mode 100644 server/Interfaces/hasResponseCodes.py create mode 100644 server/Interfaces/hasStartupSequence.py diff --git a/server/Classes/Device.py b/server/Classes/Device.py new file mode 100644 index 00000000..dac1e479 --- /dev/null +++ b/server/Classes/Device.py @@ -0,0 +1,130 @@ +from abc import ABC, abstractmethod + +from flask import jsonify +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS + +from Classes.PrinterList import PrinterList +from Classes.Printers.Ender.Ender3Pro import Ender3Pro +from Classes.Printers.Ender.Ender3 import Ender3 +from Classes.Printers.Ender.EnderPrinter import EnderPrinter +from Classes.Printers.Prusa.PrusaMK3 import PrusaMK3 +from Classes.Printers.Prusa.PrusaMK4 import PrusaMK4 +from Classes.Printers.Prusa.PrusaMK4S import PrusaMK4S +from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Classes.Vector3 import Vector3 +import serial +import serial.tools.list_ports + +class Device(ABC): + # static variables + __MODEL: str= None + __VENDORID: int = None + __PRODUCTID: int = None + __DESCRIPTION: str = None + __serialID: str = None + __serialConnection: serial.Serial = None + __serialPort: ListPortInfo | SysFS = None + __homePosition: Vector3 = None + + def __init__(self, serialPort: ListPortInfo | SysFS): + self.__serialPort = serialPort + self.__serialID = serialPort.serial_number + + @staticmethod + def createDevice(serialPort: ListPortInfo | SysFS | None): + """creates the correct printer object based on the serial port info""" + if serialPort is None: + return None + if serialPort.vid == PrusaPrinter.__VENDORID: + if serialPort.pid == PrusaMK4.__PRODUCTID: + return PrusaMK4(serialPort) + elif serialPort.pid == PrusaMK4S.__PRODUCTID: + return PrusaMK4S(serialPort) + elif serialPort.pid == PrusaMK3.__PRODUCTID: + return PrusaMK3(serialPort) + else: + return None + elif serialPort.vid == EnderPrinter.__VENDORID: + if serialPort.pid == Ender3.__PRODUCTID: + return Ender3(serialPort) + elif serialPort.pid == Ender3Pro.__PRODUCTID: + return Ender3Pro(serialPort) + + + def connect(self): + try: + self.__serialConnection = serial.Serial(self.__serialPort.device, 115200, timeout=10) + return True + except Exception as e: + # let the printer parent class deal with the error + return e + + def disconnect(self): + if self.__serialConnection: + self.__serialConnection.close() + self.__serialConnection = None + + @abstractmethod + def home(self): + pass + + @abstractmethod + def diagnose(self): + try: + diagnoseString = "" + for port in serial.tools.list_ports.comports(): + if port.device == self.__serialPort.device: + diagnoseString += f"The system has found a matching port with the following details:

Device: {port.device},
Description: {port.description},
HWID: {port.hwid}" + hwid = self.getHWID().split(' LOCATION=')[0] + printerExists = PrinterList.getPrinterByHwid(hwid) + if printerExists: + printer = PrinterList.query.filter_by(hwid=hwid).first() + diagnoseString += f"

Device {port.device} is registered with the following details:

Name: {printer.name}
Device: {printer.device},
Description: {printer.description},
HWID: {printer.hwid}" + if diagnoseString == "": + diagnoseString = "The port this printer is registered under is not found. Please check the connection and try again." + # return diagnoseString + return { + "success": True, + "message": "Printer successfully diagnosed.", + "diagnoseString": diagnoseString, + } + + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 + + @abstractmethod + def parseGcode(self): + pass + + @abstractmethod + def sendGcode(self, gcode: str): + pass + + @abstractmethod + def repair(self): + pass + + @abstractmethod + def hardReset(self): + pass + + @abstractmethod + def changeColor(self): + pass + + def getModel(self): + return self.__MODEL + + def getHWID(self): + return self.__serialPort.hwid.split(' LOCATION=')[0] + + def getSerialConnection(self): + return self.__serialConnection + + def getSerialPort(self): + return self.__serialPort + + def getDescription(self): + return self.__DESCRIPTION \ No newline at end of file diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py new file mode 100644 index 00000000..3e105d28 --- /dev/null +++ b/server/Classes/Ports.py @@ -0,0 +1,26 @@ +import serial +import serial.tools.list_ports +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS + + +class Ports: + @staticmethod + def getPorts(): + return serial.tools.list_ports.comports() + + @staticmethod + def getPortByName(name: str) -> ListPortInfo | SysFS | None: + ports = Ports.getPorts() + for port in ports: + if port.device == name: + return port + return None + + @staticmethod + def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: + ports = Ports.getPorts() + for port in ports: + if port.hwid == hwid: + return port + return None \ No newline at end of file diff --git a/server/Classes/Printer.py b/server/Classes/Printer.py new file mode 100644 index 00000000..2e2f4c5b --- /dev/null +++ b/server/Classes/Printer.py @@ -0,0 +1,120 @@ +from flask import current_app +from Classes.Device import Device +from Classes.Queue import Queue +from Interfaces.canPause import canPause +from models.jobs import Job +from models.db import db +from datetime import datetime, timezone +from Interfaces.hasEndingSequence import hasEndingSequence + +class Printer(db.Model): + __dbID: int = db.Column(db.Integer, primary_key=True) + __description: str = db.Column(db.String(50), nullable=False) + __hwid: str = db.Column(db.String(150), nullable=False) + __name: str = db.Column(db.String(50), nullable=False) + __date: datetime = db.Column( + db.DateTime, + default=lambda: datetime.now(timezone.utc).astimezone(), + nullable=False, + ) + __devicePort: str = db.Column(db.String(50), nullable=False) + __device: Device = None + __verdict: str = None + __prevMsg: str = None + __job: Job = None + __queue: Queue = None + __status: str = None + + def __init__(self, device: Device, name): + self.__device = device + self.__name = name + self.__description = device.getDescription() + self.__hwid = device.getHWID() + self.__devicePort = device.getSerialPort().device + self.__device.connect() + db.session.add(self) + + @classmethod + def queryAll(cls) -> list["Printer"]: + return cls.query.all() + + def print(self): + # TODO: implement print method + # TODO: start print + # TODO: implement loop + # TODO: check for errors + # TODO: check for completion + # TODO: check for cancellation + # TODO: update progress + # TODO: update status + # TODO: end print + pass + + def pausePrint(self): + if not isinstance(self.__device, canPause): + return # TODO: return error message + self.__device.pause() + self.setStatus("paused") + self.__job.setStatus("paused") + + def resumePrint(self): + if not isinstance(self.__device, canPause): + return #TODO: return error message + self.__device.resume() + self.setStatus("printing") + self.__job.setStatus("printing") + + def cancelPrint(self): + pass + + def getStatus(self): + return self.__status + def setStatus(self, newStatus): + try: + print("SETTING STATUS TO:", newStatus) + if self.__status == "error" and newStatus!= "error": + Printer.hardReset(self.id, newStatus) + else: + self.__status = newStatus + + current_app.socketio.emit( + "status_update", {"printer_id": self.id, "status": newStatus} + ) + except Exception as e: + print("Error setting status:", e) + + + def handelVerdict(self, verdict: str, job): + if verdict == "complete": + self.__device.disconnect() + #self.sendStatusToJob(job, job.id, "complete") + self.setStatus("complete") + self.__job.setStatus("complete") + elif verdict == "error": + self.disconnect() + self.__queue.deleteJob(job.id, self.id) + self.setStatus("error") + self.sendStatusToJob(job, job.id, "error") + # self.setError("Error") + elif verdict == "cancelled": + if isinstance(self.__device, hasEndingSequence): + self.__device.endSequence() + else: + self.__device.home() + #self.sendStatusToJob(job, job.id, "cancelled") + self.setStatus("cancelled") + self.__job.setStatus("cancelled") + self.disconnect() + elif verdict== "misprint": + self.setStatus("misprint") + self.__job.setStatus("misprint") + #self.sendStatusToJob(job, job.id, "cancelled") + + def getName(self): + return self.__name + + def getHwid(self): + return self.__hwid + + def getDescription(self): + return self.__description \ No newline at end of file diff --git a/server/Classes/PrinterList.py b/server/Classes/PrinterList.py new file mode 100644 index 00000000..26940ec1 --- /dev/null +++ b/server/Classes/PrinterList.py @@ -0,0 +1,70 @@ +from Classes.Device import Device +from Classes.Ports import Ports +from Classes.Printer import Printer +from models.db import db + +# TODO: change https://github.com/prusa3d/Prusa-Firmware-Buddy/blob/master/src/gui/res/png/serial_printing_172x138.png to QView3D logo + +class PrinterList(db.Model): + printers = Printer.queryAll() + + @staticmethod + def addPrinter(serialPortName: str, name: str): + """add a printer to the list, and to the database""" + # TODO: test if the printer is already in the list + serialPort = Ports.getPortByName(serialPortName) + if PrinterList.getPrinterByHwid(serialPort.hwid): + return None # TODO: return error message + PrinterList.printers.append(Printer(Device.createDevice(serialPort), name)) + # TODO: check that printerlist and database are in sync + dbPrinters = Printer.queryAll() + if len(PrinterList.printers) != len(dbPrinters): + return None # TODO: return error message + for printer in dbPrinters: + if printer not in PrinterList.printers: + return None # TODO: return error message + + @staticmethod + def deletePrinter(printerid): + try: + ports = serial.tools.list_ports.comports() + for port in ports: + hwid = port["hwid"] # get hwid + if hwid == Printer.query.get(printerid).hwid: + ser = serial.Serial(port["device"], 115200, timeout=1) + ser.close() + break + + # ser.close() + + printer = cls.query.get(printerid) + db.session.delete(printer) + db.session.commit() + return {"success": True, "message": "Printer successfully deleted."} + except SQLAlchemyError as e: + print(f"Database error: {e}") + return ( + jsonify({"error": "Failed to delete printer. Database error"}), + 500, + ) + @staticmethod + def getPrinterCount(): + return len(PrinterList.printers) + + @staticmethod + def getPrinterByName(name) -> Printer | None: + """find the first printer with the given name""" + for printer in PrinterList.printers: + if printer.get_name() == name: + return printer + return None + + @staticmethod + def getPrinterByHwid(hwid) -> Printer | None: + """find the first printer with the given hwid""" + for printer in PrinterList.printers: + if printer.get_hwid() == hwid: + return printer + return None + + \ No newline at end of file diff --git a/server/Classes/Printers/Ender/Ender3.py b/server/Classes/Printers/Ender/Ender3.py new file mode 100644 index 00000000..6a76aafd --- /dev/null +++ b/server/Classes/Printers/Ender/Ender3.py @@ -0,0 +1,12 @@ +from abc import ABC + +from Classes.Printers.Ender.EnderPrinter import EnderPrinter + + +class Ender3(ABC, EnderPrinter): + __MODEL = "Ender 3" + __PRODUCTID = 0x7523 + __DESCRIPTION = "Ender 3 - CDC" + + def __init__(self, serialPort): + super().__init__(self, serialPort) \ No newline at end of file diff --git a/server/Classes/Printers/Ender/Ender3Pro.py b/server/Classes/Printers/Ender/Ender3Pro.py new file mode 100644 index 00000000..e78bd45a --- /dev/null +++ b/server/Classes/Printers/Ender/Ender3Pro.py @@ -0,0 +1,11 @@ +from abc import ABC + +from Classes.Printers.Ender.Ender3 import Ender3 + + +class Ender3Pro(ABC, Ender3): + __MODEL = "Ender 3 Pro" + __DESCRIPTION = "Ender 3 Pro - CDC" + + def __init__(self, serialPort): + super().__init__(self, serialPort) \ No newline at end of file diff --git a/server/Classes/Printers/Ender/EnderPrinter.py b/server/Classes/Printers/Ender/EnderPrinter.py new file mode 100644 index 00000000..55db138b --- /dev/null +++ b/server/Classes/Printers/Ender/EnderPrinter.py @@ -0,0 +1,28 @@ +from abc import ABC +from Classes.Device import Device +from Interfaces.hasEndingSequence import hasEndingSequence + + +class EnderPrinter(ABC, Device, hasEndingSequence): + __VENDORID = 0x1A86 + + def endSequence(self): + self.gcodeEnding("G91") # Relative positioning + self.gcodeEnding("G1 E-2 F2700") # Retract a bit + self.gcodeEnding("G1 E-2 Z0.2 F2400") # Retract and raise Z + self.gcodeEnding("G1 X5 Y5 F3000") # Wipe out + self.gcodeEnding("G1 Z10") # Raise Z more + self.gcodeEnding("G90") # Absolute positioning + self.gcodeEnding("G1 X0 Y220") # Present print + self.gcodeEnding("M106 S0") # Turn-off fan + self.gcodeEnding("M104 S0") # Turn-off hotend + self.gcodeEnding("M140 S0") # Turn-off bed + self.gcodeEnding("M84 X Y E") # Disable all steppers but Z + + + @classmethod + def __subclasshook__(cls, subclass): + if cls is EnderPrinter: + if any("Ender" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): + return True + return NotImplemented \ No newline at end of file diff --git a/server/Classes/Printers/Prusa/PrusaMK3.py b/server/Classes/Printers/Prusa/PrusaMK3.py new file mode 100644 index 00000000..1ec21391 --- /dev/null +++ b/server/Classes/Printers/Prusa/PrusaMK3.py @@ -0,0 +1,22 @@ +from abc import ABC +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS +from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Interfaces.hasEndingSequence import hasEndingSequence +from Interfaces.hasResponseCodes import hasResponsecodes + + +class PrusaMK3(ABC, PrusaPrinter, hasEndingSequence, hasResponsecodes): + __MODEL = "Prusa MK3" + __PRODUCTID = 0x0002 + __DESCRIPTION = "Original Prusa MK3 - CDC" + + def __init__(self, serialPort: ListPortInfo | SysFS): + super().__init__(self, serialPort) + + def endSequence(self): + self.gcodeEnding("M104 S0") # turn off extruder + self.gcodeEnding("M140 S0") # turn off heatbed + self.gcodeEnding("M107") # turn off fan + self.gcodeEnding("G1 X0 Y210") # home X axis and push Y forward + self.gcodeEnding("M84") # disable motors \ No newline at end of file diff --git a/server/Classes/Printers/Prusa/PrusaMK4.py b/server/Classes/Printers/Prusa/PrusaMK4.py new file mode 100644 index 00000000..fc5d85b8 --- /dev/null +++ b/server/Classes/Printers/Prusa/PrusaMK4.py @@ -0,0 +1,21 @@ +from abc import ABC +from serial.tools.list_ports_common import ListPortInfo +from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Interfaces.hasEndingSequence import hasEndingSequence +from Interfaces.hasResponseCodes import hasResponsecodes + + +class PrusaMK4(ABC, PrusaPrinter, hasEndingSequence, hasResponsecodes): + __MODEL = "Prusa MK4" + __PRODUCTID = 0x000D + __DESCRIPTION = "Original Prusa MK4 - CDC" + + def __init__(self, serialPort: ListPortInfo): + super().__init__(self, serialPort) + + def endSequence(self): + # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") + self.gcodeEnding("M104 S0") # ; turn off temperature + self.gcodeEnding("M140 S0") # ; turn off heatbed + self.gcodeEnding("M107") # ; turn off fan + diff --git a/server/Classes/Printers/Prusa/PrusaMK4S.py b/server/Classes/Printers/Prusa/PrusaMK4S.py new file mode 100644 index 00000000..d2b910ff --- /dev/null +++ b/server/Classes/Printers/Prusa/PrusaMK4S.py @@ -0,0 +1,11 @@ +from abc import ABC +from serial.tools.list_ports_common import ListPortInfo +from Classes.Printers.Prusa.PrusaMK4 import PrusaMK4 + +class PrusaMK4S(PrusaMK4, ABC): + __MODEL = "Prusa MK4S" + __PRODUCTID = 0x001A + __DESCRIPTION = "Original Prusa MK4S - CDC" + + def __init__(self, serialPort: ListPortInfo): + super().__init__(self, serialPort) diff --git a/server/Classes/Printers/Prusa/PrusaPrinter.py b/server/Classes/Printers/Prusa/PrusaPrinter.py new file mode 100644 index 00000000..12e1bfc6 --- /dev/null +++ b/server/Classes/Printers/Prusa/PrusaPrinter.py @@ -0,0 +1,62 @@ +from abc import ABC +import serial +from serial.tools.list_ports_common import ListPortInfo +from Classes.Vector3 import Vector3 +from Classes.Device import Device +from Interfaces.canPause import canPause +from Interfaces.hasEndingSequence import hasEndingSequence +from Interfaces.hasResponseCodes import hasResponsecodes + + +class PrusaPrinter(ABC, Device, hasEndingSequence, hasResponsecodes, canPause): + __VENDORID = 0x2C99 + + def __init__(self, serialPort: ListPortInfo): + super().__init__(self, serialPort) + + def home(self): + self.__serialConnection.write("G28\n".encode("utf-8")) + while self.getPrintHeadLocation() != self.__homeLocation: + pass + return True + + + def endSequence(self): + pass + + def getPrintHeadLocation(self) -> Vector3: + self.__serialConnection.write("M114\n".encode("utf-8")) + response = self.__serialConnection.readline().decode("utf-8") + x = float(response.split("X:")[1].split(" ")[0]) + y = float(response.split("Y:")[1].split(" ")[0]) + z = float(response.split("Z:")[1].split(" ")[0]) + return Vector3(x,y,z) + + def connect(self): + try: + super().connect() + if self.__serialConnection: + self.__serialConnection.write("M155 S5 C7\n".encode("utf-8")) + return True + except Exception as e: + return e + + def disconnect(self): + if self.__serialConnection: + self.__serialConnection.write(f"M155 S0\n".encode("utf-8")) + self.__serialConnection.close() + self.__serialConnection = None + + def pause(self): + self.__serialConnection.write("M601\n".encode("utf-8")) + self.__serialConnection.write("M113 S1\n".encode("utf-8")) + + def resume(self): + self.__serialConnection.write("M602\n".encode("utf-8")) + + @classmethod + def __subclasshook__(cls, subclass): + if cls is PrusaPrinter: + if any("Prusa" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): + return True + return NotImplemented diff --git a/server/Classes/Vector3.py b/server/Classes/Vector3.py new file mode 100644 index 00000000..892eba99 --- /dev/null +++ b/server/Classes/Vector3.py @@ -0,0 +1,52 @@ +import math + +class Vector3: + def __init__(self, x=0.0, y=0.0, z=0.0): + self.x = x + self.y = y + self.z = z + + # String representation for printing + def __repr__(self): + return f"Vector3({self.x}, {self.y}, {self.z})" + + # Adding two vectors + def __add__(self, other): + return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) + + # Subtracting two vectors + def __sub__(self, other): + return Vector3(self.x - other.x, self.y - other.y, self.z - other.z) + + # Scalar multiplication (dot product) + def __mul__(self, scalar): + if isinstance(scalar, (int, float)): # scalar multiplication + return Vector3(self.x * scalar, self.y * scalar, self.z * scalar) + raise TypeError(f"Multiplication with type {type(scalar)} not supported") + + # Vector multiplication (cross product) + def cross(self, other): + return Vector3( + self.y * other.z - self.z * other.y, + self.z * other.x - self.x * other.z, + self.x * other.y - self.y * other.x + ) + + # Dot product of two vectors + def dot(self, other): + return self.x * other.x + self.y * other.y + self.z * other.z + + # Magnitude (length) of the vector + def magnitude(self): + return math.sqrt(self.x**2 + self.y**2 + self.z**2) + + # Normalized vector (unit vector) + def normalize(self): + mag = self.magnitude() + if mag == 0: + raise ValueError("Cannot normalize a zero vector") + return Vector3(self.x / mag, self.y / mag, self.z / mag) + + # Comparison of vectors + def __eq__(self, other): + return self.x == other.x and self.y == other.y and self.z == other.z diff --git a/server/Interfaces/canPause.py b/server/Interfaces/canPause.py new file mode 100644 index 00000000..44ee1ba3 --- /dev/null +++ b/server/Interfaces/canPause.py @@ -0,0 +1,17 @@ +from abc import ABC, abstractmethod + +class canPause(ABC): + @abstractmethod + def pause(self): + pass + + @abstractmethod + def resume(self): + pass + + @classmethod + def __subclasshook__(cls, subclass): + if cls is canPause: + if any("pause" in B.__dict__ for B in subclass.__mro__): + return True + return NotImplemented \ No newline at end of file diff --git a/server/Interfaces/hasEndingSequence.py b/server/Interfaces/hasEndingSequence.py new file mode 100644 index 00000000..681b74e6 --- /dev/null +++ b/server/Interfaces/hasEndingSequence.py @@ -0,0 +1,13 @@ +from abc import ABC, abstractmethod + +class hasEndingSequence(ABC): + @abstractmethod + def endSequence(self): + pass + + @classmethod + def __subclasshook__(cls, subclass): + if cls is hasEndingSequence: + if any("endSequence" in B.__dict__ for B in subclass.__mro__): + return True + return NotImplemented \ No newline at end of file diff --git a/server/Interfaces/hasResponseCodes.py b/server/Interfaces/hasResponseCodes.py new file mode 100644 index 00000000..08a0366c --- /dev/null +++ b/server/Interfaces/hasResponseCodes.py @@ -0,0 +1,22 @@ +from abc import ABC, abstractmethod + +from Classes.Vector3 import Vector3 + + +class hasResponsecodes(ABC): + headPosition: Vector3 = None + + @abstractmethod + def getPrintTime(self): + pass + + @abstractmethod + def getPrintHeadLocation(self) -> Vector3: + pass + + @classmethod + def __subclasshook__(cls, subclass): + if cls is hasResponsecodes: + if any("getPrintTime" in B.__dict__ for B in subclass.__mro__): + return True + return NotImplemented \ No newline at end of file diff --git a/server/Interfaces/hasStartupSequence.py b/server/Interfaces/hasStartupSequence.py new file mode 100644 index 00000000..9c307eb6 --- /dev/null +++ b/server/Interfaces/hasStartupSequence.py @@ -0,0 +1,13 @@ +from abc import ABC, abstractmethod + +class hasStartupSequence(ABC): + @abstractmethod + def startupSequence(self): + pass + + @classmethod + def __subclasshook__(cls, subclass): + if cls is hasStartupSequence: + if any("startupSequence" in B.__dict__ for B in subclass.__mro__): + return True + return NotImplemented \ No newline at end of file From e7c6acd6c21b1cd9cf8257c1b1b51de4ff0ae795 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 7 Oct 2024 13:45:22 -0400 Subject: [PATCH 002/194] v1.0.1 of boilerplate for printer classes --- client/QView3D Setup.exe | 3 -- client/src/components/RegisterModal.vue | 2 +- client/src/model/ports.ts | 13 ++--- server/Classes/Device.py | 18 +++---- server/Classes/Printer.py | 1 + server/Classes/PrinterList.py | 53 ++++++++++--------- server/Classes/Printers/Ender/Ender3Pro.py | 6 ++- server/Classes/Printers/Prusa/PrusaPrinter.py | 1 - server/Interfaces/hasResponseCodes.py | 2 - server/Interfaces/usesMarlinGcode.py | 12 +++++ server/models/printers.py | 7 +-- 11 files changed, 62 insertions(+), 56 deletions(-) delete mode 100644 client/QView3D Setup.exe create mode 100644 server/Interfaces/usesMarlinGcode.py diff --git a/client/QView3D Setup.exe b/client/QView3D Setup.exe deleted file mode 100644 index 0325cf1c..00000000 --- a/client/QView3D Setup.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db2cec192721e629fccb5bb1b3ff2b4f9bc440e80218878da65d4467afe83170 -size 301482936 diff --git a/client/src/components/RegisterModal.vue b/client/src/components/RegisterModal.vue index 0b3a7a5f..2e6b1f19 100644 --- a/client/src/components/RegisterModal.vue +++ b/client/src/components/RegisterModal.vue @@ -8,7 +8,7 @@ import { useRegisterPrinter, useRetrievePrinters, useRetrievePrintersInfo -} from '../model/ports'; +} from '@/model/ports'; const { ports } = useGetPorts(); const { retrieve } = useRetrievePrinters(); diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index 65a22d4c..e93a24a3 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -1,9 +1,7 @@ -import { useRouter } from 'vue-router' -import { ref, computed, onUnmounted } from 'vue' +import {ref} from 'vue' import * as myFetch from './myFetch' -import { toast } from './toast' -import { type Job } from './jobs' -import { socket } from './myFetch' +import {toast} from './toast' +import {type Job} from './jobs' export function api(action: string, body?: unknown, method?: string, headers?: any) { headers = headers ?? {} @@ -28,14 +26,13 @@ export interface Device { colorChangeBuffer?: number } -export let printers = ref([]) +export const printers = ref([]) export function useGetPorts() { return { async ports() { try { - const response = await api('getports') - return response + return await api('getports') } catch (error) { console.error(error) } diff --git a/server/Classes/Device.py b/server/Classes/Device.py index dac1e479..7857c2bf 100644 --- a/server/Classes/Device.py +++ b/server/Classes/Device.py @@ -18,14 +18,14 @@ class Device(ABC): # static variables - __MODEL: str= None - __VENDORID: int = None - __PRODUCTID: int = None - __DESCRIPTION: str = None - __serialID: str = None - __serialConnection: serial.Serial = None - __serialPort: ListPortInfo | SysFS = None - __homePosition: Vector3 = None + __MODEL: str | None = None + __VENDORID: int | None = None + __PRODUCTID: int | None = None + __DESCRIPTION: str | None = None + __serialID: str | None = None + __serialConnection: serial.Serial | None = None + __serialPort: ListPortInfo | SysFS | None = None + __homePosition: Vector3| None = None def __init__(self, serialPort: ListPortInfo | SysFS): self.__serialPort = serialPort @@ -79,7 +79,7 @@ def diagnose(self): hwid = self.getHWID().split(' LOCATION=')[0] printerExists = PrinterList.getPrinterByHwid(hwid) if printerExists: - printer = PrinterList.query.filter_by(hwid=hwid).first() + printer = PrinterList.getPrinterByHwid(hwid) diagnoseString += f"

Device {port.device} is registered with the following details:

Name: {printer.name}
Device: {printer.device},
Description: {printer.description},
HWID: {printer.hwid}" if diagnoseString == "": diagnoseString = "The port this printer is registered under is not found. Please check the connection and try again." diff --git a/server/Classes/Printer.py b/server/Classes/Printer.py index 2e2f4c5b..28c2cc3e 100644 --- a/server/Classes/Printer.py +++ b/server/Classes/Printer.py @@ -65,6 +65,7 @@ def resumePrint(self): self.__job.setStatus("printing") def cancelPrint(self): + # TODO: implement cancel print pass def getStatus(self): diff --git a/server/Classes/PrinterList.py b/server/Classes/PrinterList.py index 26940ec1..335b731a 100644 --- a/server/Classes/PrinterList.py +++ b/server/Classes/PrinterList.py @@ -1,13 +1,14 @@ +from flask import jsonify +from sqlalchemy.exc import SQLAlchemyError +import serial +import serial.tools.list_ports from Classes.Device import Device from Classes.Ports import Ports from Classes.Printer import Printer -from models.db import db -# TODO: change https://github.com/prusa3d/Prusa-Firmware-Buddy/blob/master/src/gui/res/png/serial_printing_172x138.png to QView3D logo - -class PrinterList(db.Model): +class PrinterList(): printers = Printer.queryAll() - + @staticmethod def addPrinter(serialPortName: str, name: str): """add a printer to the list, and to the database""" @@ -26,27 +27,27 @@ def addPrinter(serialPortName: str, name: str): @staticmethod def deletePrinter(printerid): - try: - ports = serial.tools.list_ports.comports() - for port in ports: - hwid = port["hwid"] # get hwid - if hwid == Printer.query.get(printerid).hwid: - ser = serial.Serial(port["device"], 115200, timeout=1) - ser.close() - break - - # ser.close() - - printer = cls.query.get(printerid) - db.session.delete(printer) - db.session.commit() - return {"success": True, "message": "Printer successfully deleted."} - except SQLAlchemyError as e: - print(f"Database error: {e}") - return ( - jsonify({"error": "Failed to delete printer. Database error"}), - 500, - ) + """delete a printer from the list, and from the database""" + # TODO: Implement deletePrinter + pass + # try: + # ports = serial.tools.list_ports.comports() + # for port in ports: + # hwid = port["hwid"] # get hwid + # if hwid == Printer.query.get(printerid).hwid: + # ser = serial.Serial(port["device"], 115200, timeout=1) + # ser.close() + # break + # printer = PrinterList.query.get(printerid) + # db.session.delete(printer) + # db.session.commit() + # return {"success": True, "message": "Printer successfully deleted."} + # except SQLAlchemyError as e: + # print(f"Database error: {e}") + # return ( + # jsonify({"error": "Failed to delete printer. Database error"}), + # 500, + # ) @staticmethod def getPrinterCount(): return len(PrinterList.printers) diff --git a/server/Classes/Printers/Ender/Ender3Pro.py b/server/Classes/Printers/Ender/Ender3Pro.py index e78bd45a..b3d6d3a6 100644 --- a/server/Classes/Printers/Ender/Ender3Pro.py +++ b/server/Classes/Printers/Ender/Ender3Pro.py @@ -8,4 +8,8 @@ class Ender3Pro(ABC, Ender3): __DESCRIPTION = "Ender 3 Pro - CDC" def __init__(self, serialPort): - super().__init__(self, serialPort) \ No newline at end of file + super().__init__(self, serialPort) + + def home(self): + # TODO: make the home work without crashing the printer + pass \ No newline at end of file diff --git a/server/Classes/Printers/Prusa/PrusaPrinter.py b/server/Classes/Printers/Prusa/PrusaPrinter.py index 12e1bfc6..c85c3ebc 100644 --- a/server/Classes/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Printers/Prusa/PrusaPrinter.py @@ -1,5 +1,4 @@ from abc import ABC -import serial from serial.tools.list_ports_common import ListPortInfo from Classes.Vector3 import Vector3 from Classes.Device import Device diff --git a/server/Interfaces/hasResponseCodes.py b/server/Interfaces/hasResponseCodes.py index 08a0366c..11dc5a7d 100644 --- a/server/Interfaces/hasResponseCodes.py +++ b/server/Interfaces/hasResponseCodes.py @@ -1,8 +1,6 @@ from abc import ABC, abstractmethod - from Classes.Vector3 import Vector3 - class hasResponsecodes(ABC): headPosition: Vector3 = None diff --git a/server/Interfaces/usesMarlinGcode.py b/server/Interfaces/usesMarlinGcode.py new file mode 100644 index 00000000..4b9f3ad1 --- /dev/null +++ b/server/Interfaces/usesMarlinGcode.py @@ -0,0 +1,12 @@ +from abc import ABC + + +class usesMarlinGcode(ABC): + + @staticmethod + def home(): + return "G28\n" + + @staticmethod + def diagnose(): + return "M115\n" \ No newline at end of file diff --git a/server/models/printers.py b/server/models/printers.py index ecfdbcd8..75630a2e 100644 --- a/server/models/printers.py +++ b/server/models/printers.py @@ -1,18 +1,14 @@ import re from models.db import db -from datetime import datetime, timezone -from sqlalchemy import Column, String, LargeBinary, DateTime, ForeignKey -from sqlalchemy.orm import relationship from sqlalchemy.exc import SQLAlchemyError from flask import jsonify, current_app from Classes.Queue import Queue import serial import serial.tools.list_ports import time -from datetime import datetime, timezone, timedelta +from datetime import datetime, timezone from tzlocal import get_localzone import os -import json import requests from dotenv import load_dotenv @@ -163,6 +159,7 @@ def get_registered_printers(cls): @classmethod def getConnectedPorts(cls, retries=3, delay=2): """Detects all available printer ports with retries.""" + ports = [] for _ in range(retries): ports = serial.tools.list_ports.comports() if ports: From 5e373bd105c0495c7457b75013b9767c4c279415 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 8 Oct 2024 19:04:55 -0400 Subject: [PATCH 003/194] updated test.py --- server/test.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/server/test.py b/server/test.py index 535282eb..d940482a 100644 --- a/server/test.py +++ b/server/test.py @@ -1,7 +1,18 @@ -from Classes import Queue +import serial.tools.list_ports +from Classes.Vector3 import Vector3 -test = Queue() +homeLocation = Vector3(14.0,-4.0,2.0) -test.addToFront(5) - -print(test.queue) \ No newline at end of file +printer = serial.Serial("ttyACM0", 115200, timeout=10) +printer.write("G28\n".encode("utf-8")) +printer.write("M114\n".encode("utf-8")) +while True: + line = printer.readline() + try: + print(line) + if homeLocation.__repr__() in line.decode("utf-8"): + break + except KeyboardInterrupt: + break +printer.write("M84\n".encode("utf-8")) +printer.close() \ No newline at end of file From 68043a367fd9667096557e08c9a6c2fb2b21b417 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 8 Oct 2024 19:16:11 -0400 Subject: [PATCH 004/194] updated test.py --- server/Classes/Vector3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Classes/Vector3.py b/server/Classes/Vector3.py index 892eba99..c28a376b 100644 --- a/server/Classes/Vector3.py +++ b/server/Classes/Vector3.py @@ -8,7 +8,7 @@ def __init__(self, x=0.0, y=0.0, z=0.0): # String representation for printing def __repr__(self): - return f"Vector3({self.x}, {self.y}, {self.z})" + return f"X:{self.x:.2f} Y:{self.y:.2f} Z:{self.z:.2f}" # Adding two vectors def __add__(self, other): From 8da6d892bc3b1eb58fd3b9c19e7e1581bdb3d387 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 8 Oct 2024 19:22:27 -0400 Subject: [PATCH 005/194] FabricatorUML! --- server/FabricatorUML.png | Bin 0 -> 171562 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 server/FabricatorUML.png diff --git a/server/FabricatorUML.png b/server/FabricatorUML.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4011c9766ae4f75bbcd79f137395093d3c59b4 GIT binary patch literal 171562 zcmeEP2|SeR_m@&i(js;1CKcIbUq)HmtjRJ$XfVbwV=#jd(!xz?u~cG0S+a*L$X-~C$uzoF)v!Ph>nhq zS!MTjZ92MnL^?W#y^Qn07QW8+TfzV4;I(%t(Iu7q-bzPTdyKGS9|3#N5{*I9afvBU zz2XuTw!+~FTw>d~L`5we5YA|vpdH2$g|-Fn!G~C!1=e6pudI)&l2@2QM{s!53~P$Y{_3+R7gY!nht1hf{Pt;YBLgR1||k<6x+citO!1W{)s7D zpb;ns1QxtOQ>IM}t*xV{h0{hj*(y0%iCf^zt)>oUivX8AIrmy%<^)BiAB}QM?7>|Q z2s)MEO{Wp!2w#$$*U)CvczG?UzxnY4ty(`5zq!5a|Gt& zt@>ySf)#nJs4)4w(D2pwM0|L$tY&Ew+V=UAWE;vVMf$#*dD3o_fRyYT=D>y6y13rR|2|jip zkZn*@5^VTugbuVP7zP1?0}>DHR0~0#5NJ>8O&SQu`2QNHj+lfmXoEQp;Ef#~Z8rG@ zp&hXZ2LNSCI06BOCGX{Ig(e_%>=5SA{5S)z0>i8bSjt6Cn$#rTLHkcl8T8$eWS5&^ zy(TyPz0v$-I=henr3el#zragYGeI5)Ug@Ht5cSb9&7czl_aHcemHqUoCx=aijL!@L z7Hffq@cPr^Y@u@k$FjrO;*rW=t;nO%;Kj#RI62zR0qLZH2eI?hN1dEPB!~ljgiLlgFs+kbE1|?Cp#=OvSy)JdO9HSiW$+##2<1K41Pyl}D1&t& zI3S?q5ZjIfAm@yPfKKV3$1mWZUxptLa(r-2QDHJr04SgI@z24Rh$ML+1u3ZE>q{Ww zo7`ReBX|GEf-vaGP&j2l6gmP$f&blnu)q+YAVjejT_{>^2mJ2C7P3u={k4T+Q_!aA z_Jcu~Au(DwJen{?N*$)`Z<<|w&U9#f18r-CbO8JsIvYUh|1IWyqs@rOOddc~__Ibc zwMsw4WF%%V87N&bLjq30!#jmVPB_221NmCd;MLVFd`_I{}s010jXENNs z=iyTZZ{6(A9ym@!>*nHp_@#{aK7AW*irzgYcej&B9TzAwBK zJA?xsxyu}2J`Yr)`GWKU5Ab|y2f)p~V7vp;4uN*~!ppHjI9O;S@kqiKjz=L0yY*GJ zQ$~X;`pC^^Gm@{(NTxjU>m!8-1%*H7(jt_Y@?&Df7l<^#<`tpB6F@{zXv5-cEp)9w zu~JM4e6SP9d|*4IvVeD#N&}?PeMaC%T6|Qd`l}p6re}DZqr+s)0N6DJAwfX{*!>ip zo>U9IVu@y25(gv(K|nhJ0SO}Nr2@LTw=bW0-uQ(@J9sHnA{{7T^la?qllQQ&A=gwLVV6i~y zZ8FcH;@h*y^UqMUlM*Il71?(rDFr)fca|ax-qcyD2!**&ob2CaRd`J;KWJ1$RAv3Z$3d5sebHe0Q;Kx|+r!W$9>_Uy05RIE`SDl_i%<{Fb znvaUa=!MhMk7TIk<_7dCZqY0e2vluO2hG? zCK%A|YK61lfpCbdQO`osKiYo$&DQmW?J7SMlElciO=DjFG*MI51P#U0aBfy^`x)dm z5n+me@ppRLjF|XC85+fBXv`Ku#aV{-y}FsFkm5hi(8Ojce@{cMS%&uI4DDNu=9ge- zGgVh-8QQ;agT*&n*Dt})W|GpTK@wDF*CDs4BQa(;=N*4-P@3ET6e7y4I8#-8O0nM@ zgTUj_=CrkZb4LfK$@>-{Rf)FK`>zH7Y492Kt%uNiQ0a1T*$2r{;4U2UTLZE>HTl_g zs74MQb?P0&I-qwJ2)xy_8zuhd-U0GrQuhu_6_mdsW>IB?667vFYI%yX?ceDqkd`6q zQDlxLLFq4`h-3eJKY@(+v{O zNJvH+YP*5n|K&;08;T=ApJ;)GVxSH{w---3*HrHHPx#YUr^G*%KZyuSkjwQHhZ3Ep zHvjVuB`r+rP?93kZrb}L9O?`7%bz9`(KysU=K^2NIes!1pfGES3y4n>IlqGoh)!$m zp(OFYQcR)9B{agrKdqN(IXL1Gvwdnm^TQ=1py%d5T!QRB(voC+r*dSkZAn|-U%P@?CarZ=H~SZKvv0PpUqLq$pGlVer&HS8fFB5fs32*wsHmYvCWKTE8JMM1KSL+J7^PT9mZ?Bz{v`MRf=BsM zN|8S%_2pT&LrK-AH1pYeCBO4e9elHOkbsGu$-3D0Csh>k^<(Uc%;=~PY?f4g!>_eWMTD<6nqNYx z`jl!uORBy%srp7p`ckFphmtBvUO@#(6s1b%BW#+Ls(wnTN|dsQpQG2r2P8TUg-73FSRDg^roxMr2AFITF*)o6YNQYHB*)qIvzeP>ej z&DQlRl>aC$Km|z@rD~Q`{hW_V{Ya|%MM%}BB==cT_1#F-q}u#nCsng;>d@15sDE?& zn|@b#Dk6Nn(NH4F=a~49EdNPr9M@Inw4(9mvlSj+Fya}WDKR~ zA5=g;<3k21<0w%3ueM1`k-MR(@arRHJX;U{@_P8U8qKdD-BMc6Xl!QocG!R6cGz#W zt}m>IQ&#xLK+?z6q$x_J<$(JLn`WB~e}*Q*k66{OQ4g2;R1IpDRDCy6^^I-P6teVl zld3N$KFyM<|AAC}qtW~lq-v&O%`B<)}!|B0r)QIOAz4>L`j;(se7PskSp8&yLR6Thknx|W zic86mzuJeQ1(|nI%<-SE{!Mzpf2{f^O`gU7FQGw%Y$eoAGWGbTuh4pby3j!R9X+y7 zNKJ?6@8c8G&MHm$MM8=v{t|&`RPOUV`#GQ=JEi%NHOl*6$^&M)L+aDN zL!*H{h_>YtB@aM>&LN=KOnZlcwwgJB?USEdA@HD@fwo2IAnhHIw&uv6siXlKoMxYz zKGo|bO8zBjGLI6aB>q%{Z^mjqrBQ=o_5W%$UwWp3#xx2uD?ER>@GLzE2VW5k$W}vX z82p^k{Ky-zD8CLslm0=%Gm-|TczC(Q6rqO{t+Gvy&*lltX z4(w}*!8wDsR%iRtw>XpUqW&&d1SA%T>k2$hqt1NZ}97RYinn(pqMd)vx{~ zCU7eY6m7l%?Y_`#$e{Q`6b=#L$tDh(AIsqqg+i(w1W>bq^g9L8Z#Au7BaqG{pZ%kO z6sW)2NW2}87m&(03ncy{CFKY9v(JjZqQVCunMhQ0dXoCZ5tWkM{vd3e?K_?>LVnY8 zxI}~}0pY(MQ9sprj6xDrab~(`?1!+*CAO3NPgHVRU27@`gI#oSIE*^Z96>C9h4YoV!r}M$sRB*!Ih-g1$BZeL$(#_CzXBl zsIY&v8Y?0~b}}0CorZz4qVfNLdQ5a`WRYJf6aD;yTNOPMys z{X`hX>EgghP#HxKpem=R@bs6n)!gp_bKh*BGlb=t43zR{zwc?GV!&>8I^b}i*1m&F z7^?mM^>6Ys?7>|Q2spS!?`~Wh@qq7pi|kd zsC&?)qmG$mf^_d5F%5Npx_t*Gbs=U+$6+rECM@5TC@xa2z4i6x-#c$(8hKSZ1689B z|9(_-m)gQDBI)a{1Ru=_J&;nXTqbfuxFYS$a!ZQ=y1fg`tp?a6@2o~+d=<2J1YWyk zUefEi-*dRxA<->y#4-1IgPXU~Ww*rkCcH6AmPc-q&EsY3=PgyBoAcq17i^x8Yp0E= zTg?#$ruyN4u+i7ASD74q+3<)cJIGzSZSeUK*cxTx<5l~z>|WyKho77=>Cdjp?OJcz zAE)0bd+_axDv$T~*2`^fO%7>rHI(+F$494>_x<+Q8NjP+Y6tEdVOTZs)<=Hy>8*yb zuJv0?a^5qaT(#x=gkwiaVP%Ct8}`KQ@;&m2xo(4beN~xx{fXV~?6?=EB>(4`7 zc6pO*SjK!whKv4~rj96yu!8yo)~nt}y*eX5?gi~P(N!Kc-pAkY)?4anJ;xdnvzM~Z zl2(@S7c3sxHdRT5<1g2bzucXhg-Adtxf)b4O5us_)v1g zq29-Z6P36z_^VpAxwX7@4eo=jbJxGW?PY#i=FMZ4ZG9wsX9q)Kmp@&E&V`);c^rr7 z=o!g>6gDx`zrQafXFt-Jxn+Z$_3O8UxV@&MjjJ4+g7oDwoV)AGWh+Er<*IX(V&F^G z`t77%nGD8Iry6v(Q8uCVP5w|w{>!0w!K0NuPU9urqb29YMh5e{pX^$no7Q9;F(kO^ zg{iCHdWB18{yI4w{bRO^vd&#E=Pp>1b4e`p(l$+F>pFbtwwM4-SZl%hv6fxQt=jo5 zY9sT&BuH7_SWP_%HJj|7bdPs+*WYw(udK?5DY|ikiJo6SY4l~CbHmt(e^;xLXN$5d zG=DO-H`Y+^YRGZj?&aMxW6(tVKqiwBo-UrEM!$aO!LGgwoe!tHKfm+ja*G2UH!B*4jYpriU~5Wl|lU{~Fi_6O$$jR@}wR;_ZfEj+!> zFz?V!c8~W6qWeH~6yumlnEs2G_k7arU+pw@szzRt_w+y$jdNZAv$!FNdG_|W`^do9 z7UPcf?$$nF&1CPNfN!K;GbV=9+XkLZj8}P%RjCOQ&?AM)G4aWH*{BDB19;XA+WAiH zRmm4BZh?7;WhSy8daIb*ysdkvdyo8BU&wI(E7y5DxfEK&`BaJ(sg`rjyyae1Id3FN zyX$TZUNb+lEu&u&84bb!X8sLPK4Zj6eRYGZ*T|%ODQ1!u+1+^~4zvWG8>s-k}uEY$$8fZp~hAqa~gp7oWc>U7xZAk}Q8&7Z(s*If<*JpZ8j8{7d<-EOH zEzED+$~w`}nU?6`gk4)IgWq7KHjo-_yVnoS zZR)gf)I8Z+Lg=a3;bE)2zw>qciLHd5m8w2V)?H#gE6|ssr@_WnAn0Zm-bN}0tXtis z2+uAnQi>9Pwvk}{w42ed^*S&(p9L3^Hc%T}C%w)xSV7Q^-ZziSYtn6YEr%IwaKYuJ zWtJDfOb)#(xJxQ3{=+M9ESMBK(7?1tcxDV-cF~u_yX8-n%&MWV?%cup>Z5tDf;D&4 zwvVcF%X{)x;s@)T^ZUGE@>{p!+=$q*c!y}6t>HQ6gGIent4*Zl>-u)69VW$Y5fY+C zp*i!0mxBWrm7;6%+($Y>+JRy4Fl`m!Or4uwo>_&hPDMKP@2!9N`kgvAQ;2W9$tuq- z$IFB=l`XYps7*}>?fU9H1MY^VrdMm@u;$|FwYxZh4{hA-OY1`pz#1;Hr7v6xtJt}xAeb&=5=e1&wC}&+PYP_8aIlNrwy(uA%^Z`se zlNjSoA-2%xnQ=8*CU2~ zsL$lM=q<@KM$S=e;^N%&MPRmy_cYNgoc$d3VOhD`%-!t0$uH+V?FS3z=zgqqJ@uKM z^4?bVBo04)JZiq=DIZuke)*tH)ZSLeP|QBOrDQli-REAle@VcNydy!zZi$@y&asgd z{*|h*(C0Q)9RU{oLg#L1l%?7|wpHQh>WtMw)pPZ7)rs~N^eYF79H@BCHm~^2`hAS2 z^bV8I2|27ZbEYFiYL95Rds&Us_`-@sQ5d%6+c?m>PBe!{lt1=QT0G}m5WaosofQ!} zVw>f_pZf@FBgGc+bmcvT%c#iN0ebpe_Th4lq9d1m(j*fD2#5AXJ>?Jg))s+3 z4NW#|4Y^HH-?0*ytH`6rotzOm_(JdVG^;tn;LARY4{v0JKHTKcruJT(5eY;BogyBsFMYz@L$ zTu@js^(=yz* ztrJqkVqtRYHrH=cia8OWgJ9(#n8{I7Iy(ASE3C>?V^(YF8MLyN65=*m7}0eKa5G)F z{Gi%oSBvfr`x{kDVSbeXWy4_SwiTxyQeO?7m)yMj?$H4Yw&ghrb^~P2y47;vEr=*_ zdATKU8Dc20g0+-bEL(np6no}`*cG*f#GUh0M2-T)V}5hrnA#l)^_LYsq_wdN$R-*%@-0Emv#o1+#43QvL3ov zSSY{O7UH*oht zeOQ6$nBPGZC;zdI@pdkE9wV+Me&QRo)Rp`AIk7Cr`@>~nMl$-Z6H--`x!aEf;nN}k zY!!`njJJCk+E`ex)h|EQ2*UH=Pe5#-nvrJL@(E(rs zM{nA%qFzuAtsUVl5e@W2X8)uj`ZX*EE7fX(7zE+tH3Al?Z(q+>akiY}SJgRaz+*n= z9K#lNm>^?B$#{agkpUqj(kL~&=q@|cHO=!kMCNU+pU>hpy5?3@TZ)t6HS_)gyMzav zTL`|jLzi3jb}<-?BxtrLXVwij*S0rGuwOBF?`TlTajR;-ZO{!-kp$7x*j#T?Y~rS> zb51K@jusrP^QkXWTpL`ayz$Mz%X9TfN}JVS-1GEJ-t6LZS1(ic z?-FFZw8Sz(H`|)GF7HC6yj6?EU_sh7vzu!7oCiwT#fN+ayZ5-V=`HSQjEk`kHK=${ zB$Xz5niLzmNsOy=BjJ+2S9A!q8)}_|aBB@;wd$3e!w>XfEI zY#K5w8>-u)Zz*T7W~N4M&`K!Hq#G`E(%-i&Ftd!;TL})|dz0<(P+RoL)I`UOnqp?` z%78?=-U|Ktq^xbiq6x)ty$aC{oA>iZlpnhj+tmct=n*5e@hKcQMDLhPNIg(=_hR2G z{u6StX*I3-sWy#HnI` zKlfiqyd*2B^F&lBCU&(Y>;!dGmRNXdM4g+(7ZblfS&Z1x5q)sn-~QR0I;Y!Ct}PkI zrCNVe%UjNGtY;ygxwM!?Xp5PUo11a=N+H22mhFT0NP-uA;|n^L5=QooG|p+t5akBlrzAfo*@&QaoiL$jP5!g7<~R$I|=+g3L1L1vYnyIY-}K}+L; zz{1R`0;$IaXUY$NAebt4)-^Y(q(O95W@SIDfj7^fB)wC^&3+|3VM|@=1@?=!$UjNE z-c|#pJ8;P%(TR?40C?p!U{#Yi06Ca*LXB1qnZw|^t#5FVe_4>Ci_ae{&p2&Ez{SPz zuIbq96hw+WaaAdVdWUd+dL+wOM$H2`S+1eB4I}t$zmA+EX~Efz+vV?X(vqCGufrd; z@hm`Zk(Jc@(RsaF_?9FuXQV2$>cDu=TKj_4Ad|lThuRZr(-Q6nv}(^7>$R1Khx7rD zSpgGS@O_RFxUFwQvC+s){3S+3k7aqLO{a!4yeonoStBvR*&mmAp?I{Ter3^)hg_Z4 zZW}~-v_HJZfDA5FRdLo!NLC7fxu{f+wp+xs>f!-7UJ_%wF^`%CXLAEO`+t zJC!QINSAP>+oG)`w7XS6e_{P?XP4tn@P)*UCya6H{jui#}SzEQ0SOeDah@lX2w+0XSYRH@v-TW@^o-28pgjSHYUF!!@9IM}X z8K|dt>4OzKU|RHr476^*j;XhXC3XM`CGt8&EVN9%P_jDiYCsx^%UiRxEixy&JWp>S zu}mz=J2qc{&~u=O{^q_j>AmL#7>&*zv&&7$77bZQ%og<*>|79`qqg9*B@KV(p9jcc z`GFYKw*jkQQ7GvGj-~=kMcE~eCZoq?q@v6hLs3YhQeUN*XO}0x&`=v6Q?~1I&DJ}w z^O}T{*lh~_jC;0QDaKc8za%?Y;CETtd^8JufZj2Q0F!S%6m=y?bG$e1D!fTOhH2-% ziEBzRS(g&5O=}OWi_i(#l{^>AO=!E#QN}zcrofcVmuh-hEGqb2Zit3_uTz%6532j`kAh+~?{FYMdj0*Jl0R-2yY zva~pbAJn#E9#&!fOIE+=bks*S!>Z>r4Wt}R>twdgFz#|v>3&pvLvMFRlu&o4qrHuB zb!~e>$9n^zq9cMM2Zbfn$4mxzl(?CmHrBRh=cSi)FcRN8>&%=jg;yzix?VgOxm)>s zPH!kobJyTb09WGl+kIE>2ln*JV0l#HW%ETRBJY)4F;MY7lm|`Jj^Hv zswf;}-0qb+F;m-BWY6TqRS72$8X#3roS52KSWlh8FNx-l9%w+ES- zyryxn&1&NZH`yPQ|CTCISeay}6ypyu`1%v{I#p2OSV85fOUSKSR~< z$*b&!077u&EzZuff%CP|+uHF-Cp1FV)*$3fxmxo`r#lTib2=@N{P|@;CM(v&X~LCT zo)#gndtzo3ueAC|IIj|rU0G~6d0e%Yvu;z--QSr^OU~VpWC|9zeN(ZFSmY#A>zWC> zRYDkD!xVhTt!`Xk)P;xXP(o4&m$6J5aZ7i04hgNOxGguM%V51`$?%F18|2UcYy{Ol z*knMe$XjOX1oW0m+vJEi;4RHQ7njiF{eDnV)LI<1dY-}AfXq%o+)3lb#KuL}OlsdW zIv+iz*{T}9v3@nIz@~8k#zfpo*S_*Vh4it+q)VjOihEV#_W>w+E^eJe?IGg&5CdaA zS%yCilB`NRlXHf}yo}?IGoC~Z8o9G9*xZn(FF{CE;7PtLI+VqQbIG^_Fj!hQN8SEy z_d??Q%vUAL7y;p@JGYTG$kBT#g6x(*Ea83Xy+6kmbJemBZhvOd+$>X_k=vILZ4#kl zvNmB|3Oj#tF}L>tC->)*X_(!08%E-M{Hvs8Ao}_-`kT=n^mi|hQbIPbQLJvidQ8@F z!OJ78_aem!scuOnOZZBeh)a&)qeipE?hUE$XjY4=%RGxQ-RIJT+82d_Era=W>-&(< zP^O{?v={p27icq(xr-JL55U~gj1C1DJ*jQ)sb7i$GF&qX`O^RR&OC1X zZDsZx)V}Uo;D2{jBlLhD#;Mw`qh1CMQa+TkUE$zOJ`V`xSmr6&XmFM2H9K$Kapxph zzkNXxrvc1dg=*#eM9b7OiHv;1&o;bhMjF){vM4Rs%m?_(=9<-fA#wnwTi4=UPf_#2 zfE6HFbEtgUcar{*yycFWaqQnyeY6Hh-3v~1hoWEi`zM_x@SyGVmn_fB>|fcRQX1YIe&`;9V_pgb@7o-tdDK(Tk^pARg9#yYjzElDp++BU!v*OzC!-jNo8=E4(+rS` zxews?PXd)V18Bf~h}J+r@_+&)yy!iZH&$z{Tk^Pn}g}q=gSnV5svpOAJ4YVbH24wwC9NhIxkb78UqCecC$%tg> zSY3E3C-0r#pM>-3E#kyi^%qS*)@{jS#YnyA1T82>Qwo7{w7ct=&Sa=$?I3}~J z%ut0*)Zi@sXr!$2QsjP*1`UI2=8Bc?s@+k$Q{~;l%_lHnegRuv zr&)ySl&PtaA*3Od2dND-1HcMCuIj>nq9@~7)S!Vd;3s-1z3lT{T3+AL&hC8NR=o<& zZE(<{cK8rOxiOdG-o-?-u@rVDqM4`7y2vH^nk`RX-oYwY0Vow#y+Na|*6;x+4f9q; zzb)U>!phQoy6I9d_G)jU{XnxLA}YXV7pSmcPn=v|D}*v?a&;HT>gmMi)op=L=g?@{ zB@%k{{!@}NP>Nf)c4J?pNtU*OU546n+(oTsqcE{cbjkl z*i_vsQ#XLW*6ZolQ0b&}^y}DwFk#Bd*<}A3o7gVA)_CnXKs^88;I$M6cyQ-=787+i zUV3dKa6w#cOGkP80b8S7!e!3d#`_DrJb+AUDvz`mq<04bc)!MdW@^%KSOEz%U49+= zm#gPp^fXt9de8=a++he=mQ|}D9G{%q<@sZ*IFCi07kh7*w3Z*0S+w#FsfL0 zzx4b$ZyHu>@up@2IopvxiIyr2ujFxS85AQrm9QRizf=2sGf?BwTSG%LpEvc_)$i8t zW4px%zgKS>bsC))jTzTSV|EWa48Hihv$6El7ZnW;Z>w$B>{tf-t)8>Nq&cG8P{l`W zr|mP6)w7WpCo{bc=?I-2JVI;FfSnKS=UoX`s$rq@0#F}lsj!y`%b3V+L;vHNXLb)C zi>&j9tJVaF)C^vD6zFWce4fE?BX?`N1%zQTZso+fG>tM*m0GT9XCKb$;#fz2Hr3%c zPNoYbXZ!5n-D6@{X+Bg`0K@iy^SLVxTq@b)JvFSlcc{2E3S76rJ@Pov4>V!k=M!%J zLIDIq1!Kr;2jg1uarS7nfM*Z(mp*Up>f&U zgc9#nH;%Exwz0x(MecZmn#`J#Gq-$zI=g21*;?8!%RVU|$c zUe>8eQgaFvmLQTt6ZRPawaI>;A;WUM2S885P;o>bUDtcCNiru#PYYC9bNy`Xo&LyL z&uQ!9)11rPU4U_GFsQ!s8ptobl7`g-?>ZgF@wXZp07(i5J|BMuPUsi=5*ollUhvYBRL~Cx@UZmy`0r> zNjb(oNQN2aJ!!M7#R#A@PSln*it~X4$TZlOsgUFL=-Rn_JK$j5;%p0oXkjWJBsYWn z_xrE19vJu24oDq_3U?u=XfA-xs~C*Zk8Zpx!YDj(|FG)03W%Q>*>ll+EmATdT=FQ` z*vXdB4ov*Y>NApRhWxE|`7JAxGP7-iZ+5w=xVi4f`Q+A0=LJW@22r$wopPUxA|z z)RmxjPXk45i3je|=PJca@*N*e)4jbpkrD=LFQ6K#*LRLU)vJWjl(2?SW1OSG`xJ#T zNx)q|!O1e70HhPvV$&dotpiRu75*L^t!@5pIU2b&M!IJt%G+F}ZE*XG0#^yU$gpj{ zxz{8=XDpA8Z@z-%C`_gM!PbNlfOGraTsJCHRY9>O==$;#&s*|eP8jMw_j>SB<{;Ym z9$Yr141G8Mt?r-$&;~8%(aa#w1_2})p(k~h3_&I-0j-<7Oh*I+WaJWhQs~|QO?q?f zozc99X$S7>FHS5S|NY@jj(Pkk9!xp{;_sFgccq*OTGlPET$X6C+q_{_dge8DCX@ij zE$y;#0XkU@wD~|O%W*x;);}WoBg&i%tl+FL1D=)lq|;o@4tF|sS=^~jB6;A=qRQWU z`0(5;0Jb}Ws?W2CSQey~XI4c2o2)sHajy9~KmMyZuUrPJ9oddNYm3g(ZXP{Su@$ag zzioZ7M;E`}p{8@km1UI=2WCilYuXB(alOR_b2$uZo{B@gjS3uHKV=A=5>X{%N$gu6%|I}Whbo5Oum_do113$Kq+8t z+LlA{`}Z1k7|LR5^)+1Hrf*>)mbse04bVTsAZFz6mD%Z;fh8q%rg=nxha>g-~&3cuPEPhcdWV-eA3K47Y>W6ox6}Yd?|JRsoO2! zJMBcNsL(ngAE`7L96Hnl1uGaS6C(;dN~w?;7<|a-LKom19gut|&NW(RuN&6L#AF-j zM=*74YKA|42;US1peG|FJ`vv2eX`_Is$FqX+R?%>bv`9-k1fltsQ^^id@ybYHG7R+ z+A;_v9t)I66V`>_)y;UZ=i&;MRhfWwB|5V2PJHnC)c`EMN^St2@g9D&PlEt7rJkDh zIogJ2g4SnF6iBsKJEKR+y3O;;x(~^QjXfWC#j3QtUf>m00je9m8C5R%2i@v)M-9u1 z;(+<+MP)6a33n}~;rcCs?3?T3`*M{39KmUfuXAbM$jsRy;hfkDRWs-5~y4pp7DZ^Q5}B*J8G5EfHk z{TdEXf}cAd;Tv*$G^FtkPN@nA5yFdZ&~$2K^MOJ7yE4{|nT@BLhr()Am10Vk2(F_^ zBIYv!VOr`X$GeTuH$nCUQv-nEcK3K?m!Lm9==URX#T%|?ju~%hU8Lf()4Azz5+ zx-OWp%@IKSR_yeWq)y8f^dV{A=nsd<%9_xeEO^;me<$^&#mX^PxyJH0>`x7A;6MjQ zPJqn*$fhz+y zhqr)WWw!GZeGVW26FgPT(RxWBG=&2d5*d>rx!QF;o$G0>e z69!aGsXARsEgL`}rv3RGl}xTVXBniq)oLyp_wG>XUO49*Zs84^0i>7K; zbFli&mBCtU!l2=dIX7Dsk7EH1j8QgmO1zIGZhB48rTy1HB-pd@MYvWF3059QHTGoUsIg?*>)*2%Y`! zR_M!BTZaR^Nzgt6D>!yAytg{ILNnjr_!Wx3S~Z3{wexUBTuXZOV1{pTMwLMet9+g| zZ(T;!o|e~51#TTETh`2)cbjiDnu>vv-GshYF5NNB{$U+i>NrU-3J((_0_h!zf(;pH z*)tK>t+~v;lznd8LEAvTBiN|4d$-(?Al>c|e%I&5LUi&Len20NC?)tJ;GopEuT^*a z7MMQUKUbPVTzQkw4jSe7DN`RvC}lZokA&oOI$t;@^*{zQTzTvLo2;A)?H#z_v>pM0 z)U(B3&>p$>Eldr$tMT*-P$^@H>|d_qtkKyMHi;a zZW?3T`RRaNTYkc>fg8v8oEW_FKz94{q&=latVLrOOx_*n-JF@ukKIIQE8d-PV?Bt; z>!dIFQK$8ZO93{XF?!+)vq1Q*YpiP*h~Av^{#}rE2C`J{7FS~6pS5o`OF4ov|7$}f z4IN%UmxAtT#?a6uB}n9EykzabaM4r!lpz2I>!ui*?#Y%c0Fmp~zwmZGGgS3PndoN6X+?nx!hXiOMJEW5y+} z2Hm)l2C$#F&JgBJ{r#Ait{$vyZ|l0^qv`Zu=ydj4<*|H7wu-z4dX!>8uLO;zf0_x@ z(eeQZZb(SUe7Y^vB_V*26PfP6M9pwxtAB71e--H6zzoz)a=r@|@i9wOQhO3YkNf9c zGVXo(7R5huN~`%zf*ikFK;+f5%*TSI^_MgElvP^f6f1Gt)IP4>W{K#`7a6Ut$qqLg zsk`z@$1<{R6JZT6_wjyy&Ri~ZkB`LT57(0U|K5gx}+;uNNPXKr2PrLp|U#>vj%?;}aAFYQg6y!M` ziaQnMc1+V85k>k`h==6zLjvM3)NN2Vd+pLbx)jHRb!9L>6WRB<^Dwz#`uA4LXy^fE z6rnweUvtdBCDY=LLCyGh|IMSM6EdG@*Sq*9=uQ&YQIcAr1Jb?CP;04y3qR3!xbhuZ zEVT=$*QflC%wGmPdb^(+H89h8S<6*W2-}&hTYwWxWwM0_a2s<@Wnk+YcN@*%q?pa zTUFZ*f4%ydQTLLP(VVIQ*sa8>+CfI5_eej;aX*=s&ryznnvhN%-#YLvv#@F1#wA-p z#&b*)sdPvl4z4AoM_Za2`qXbiqBizuTB)W=EvMka@s9Y@OHjCLvRme9>1e7Bjx6N~ z@pNY`h+^#90&Fq=H)m?9Ku50!nv9Y`&h%<2NcWV3MEQYsr5D|(M9qa1!f`;m8rl5H zd)Xr435X+%K?)b{{yJK&pa(|9tJ>E|1BL6AR@9?naNR+%Sxaf>&Io8%c|}=>V?g0r zW~Qw>0Y(A-LDO5sp`{0!tK``^J#r)YIoGK!k~h+>1=V`Ft-lYapFa8|UNa&hi9f_s zd1c9C#z0W^0JXYp)YHNtoCVbexcsM-Zd4pdQW6Fxv%kU@8nK<4OqEIJZ)lgXtKhk? z#fE8c|J;Z(r3V2SzyC@#b|vs2J|+9#XpeXpI^t?h13F|xInW(9pq&}f2{o&=qBdyk z-*IDSndbwzx?s=^L;nS`Y~!J!iNZZE3X?KRdjv>5PVasB`DM>9Yp?meI3uqk;D&_A zrLZxK`|^tgVco?_*KVcSblfr$Wnqd<&;Y9#aKqTY{~r8k6!zp;n&rL_+ebBNd@i8wl2QXC3439Mh~@v&Z!)H7q_}4y2>Z1IVM1HmP(4M}yo_wI0w1;+3wMZ+HSa z86Ux!T(*>J6&_(@GVd5Si3l)8t!FygF>I7}_Mr!x{{Dgkg>dsafmT(Ok+kp@g(AtD z;(z*HeT9AYY!d-?@fh5x^j+}7-?k~Gx1c&RNNqjmlUUbU?py(5zFL^83=D!66__&m#Gc z3$S0rcAV79rFX(z{KjZxikk)x*g6BvfMqD_YnH9Aty;A&=INb>x$mZni*TI$}PVl zF79CVSm`*Xwz1=PZrO>Vj6qO8E69tvqn#kd+~u3mu7|Z*l?-Bba)3)$Vh$)XphQz_iyQ{qxq)3wkQo>G{`7;?EHU zgYt5P+~ZC6P9&TL3L5&kA!`0H&Cy%25TY3l#r{r>5HP5vIA8y4U@B1KA6{*D^wutn zZ8bR!THwpC%)A4n$H)72m>WItmCI0V$-x}g{%uu+PB4k!ZYntut#-6t6KW=Ce635< zim$_YTX}+&*=g>r;!&Fy;p#d8jKpa4jaGhYzgIT|c%^bu#}w#cBI`dvVYc=;A3D6Y ze%l+M=w9wnbg(Qn`M^ zVjaKjJ(^KEjPZ#H+0*vYJ(M35=0PET_1?mV^>?MNSZ{`_SC5A5Q@&;e0{|6`f8HuV zbEP{eI=z9~$$Aqht7~Gx^=d~m&XCaJo3^)zui2&gn--V`&{;#Bd(N51#|Q}08AbnO z9@p(<+?<^N82yt?QoFZ6Rl<7)xp`$MjVuq(jNw8L%dnh2h1}p*=Zwgzq>tlIOWpc5g;aE84O6N>OG+Ykfl^kV7X18z{Z12iEJjTC zj̇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%0DU%Y@x%SKQmbR9 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 K_6Mjk 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 Bby)xa literal 0 HcmV?d00001 From 2605550656822c04825c8351d469817264b6e509 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 9 Oct 2024 17:23:14 -0400 Subject: [PATCH 006/194] unmangled all the names, because it was making problems for the inheritance. did more testing, and the abstract classes are working as intended. --- server/Classes/Device.py | 130 ----------------- server/Classes/FabricatorList.py | 98 +++++++++++++ server/Classes/Fabricators/Device.py | 102 +++++++++++++ server/Classes/Fabricators/Fabricator.py | 136 ++++++++++++++++++ .../Fabricators/Printers/Ender/Ender3.py | 9 ++ .../Fabricators/Printers/Ender/Ender3Pro.py | 12 ++ .../Printers/Ender/EnderPrinter.py | 11 +- .../Classes/Fabricators/Printers/Printer.py | 12 ++ .../Fabricators/Printers/Prusa/PrusaMK3.py | 13 ++ .../Fabricators/Printers/Prusa/PrusaMK4.py | 16 +++ .../Fabricators/Printers/Prusa/PrusaMK4S.py | 6 + .../Printers/Prusa/PrusaPrinter.py | 85 +++++++++++ server/Classes/Ports.py | 2 +- server/Classes/Printer.py | 121 ---------------- server/Classes/PrinterList.py | 71 --------- server/Classes/Printers/Ender/Ender3.py | 12 -- server/Classes/Printers/Ender/Ender3Pro.py | 15 -- server/Classes/Printers/Prusa/PrusaMK3.py | 22 --- server/Classes/Printers/Prusa/PrusaMK4.py | 21 --- server/Classes/Printers/Prusa/PrusaMK4S.py | 11 -- server/Classes/Printers/Prusa/PrusaPrinter.py | 61 -------- server/FabricatorUML.png | Bin 171562 -> 178521 bytes server/Interfaces/usesMarlinGcode.py | 12 -- server/{Interfaces => Mixins}/canPause.py | 4 +- .../hasEndingSequence.py | 5 +- .../hasResponseCodes.py | 13 +- .../hasStartupSequence.py | 4 +- server/Mixins/usesMarlinGcode.py | 18 +++ 28 files changed, 530 insertions(+), 492 deletions(-) delete mode 100644 server/Classes/Device.py create mode 100644 server/Classes/FabricatorList.py create mode 100644 server/Classes/Fabricators/Device.py create mode 100644 server/Classes/Fabricators/Fabricator.py create mode 100644 server/Classes/Fabricators/Printers/Ender/Ender3.py create mode 100644 server/Classes/Fabricators/Printers/Ender/Ender3Pro.py rename server/Classes/{ => Fabricators}/Printers/Ender/EnderPrinter.py (80%) create mode 100644 server/Classes/Fabricators/Printers/Printer.py create mode 100644 server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py create mode 100644 server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py create mode 100644 server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py create mode 100644 server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py delete mode 100644 server/Classes/Printer.py delete mode 100644 server/Classes/PrinterList.py delete mode 100644 server/Classes/Printers/Ender/Ender3.py delete mode 100644 server/Classes/Printers/Ender/Ender3Pro.py delete mode 100644 server/Classes/Printers/Prusa/PrusaMK3.py delete mode 100644 server/Classes/Printers/Prusa/PrusaMK4.py delete mode 100644 server/Classes/Printers/Prusa/PrusaMK4S.py delete mode 100644 server/Classes/Printers/Prusa/PrusaPrinter.py delete mode 100644 server/Interfaces/usesMarlinGcode.py rename server/{Interfaces => Mixins}/canPause.py (81%) rename server/{Interfaces => Mixins}/hasEndingSequence.py (77%) rename server/{Interfaces => Mixins}/hasResponseCodes.py (62%) rename server/{Interfaces => Mixins}/hasStartupSequence.py (77%) create mode 100644 server/Mixins/usesMarlinGcode.py diff --git a/server/Classes/Device.py b/server/Classes/Device.py deleted file mode 100644 index 7857c2bf..00000000 --- a/server/Classes/Device.py +++ /dev/null @@ -1,130 +0,0 @@ -from abc import ABC, abstractmethod - -from flask import jsonify -from serial.tools.list_ports_common import ListPortInfo -from serial.tools.list_ports_linux import SysFS - -from Classes.PrinterList import PrinterList -from Classes.Printers.Ender.Ender3Pro import Ender3Pro -from Classes.Printers.Ender.Ender3 import Ender3 -from Classes.Printers.Ender.EnderPrinter import EnderPrinter -from Classes.Printers.Prusa.PrusaMK3 import PrusaMK3 -from Classes.Printers.Prusa.PrusaMK4 import PrusaMK4 -from Classes.Printers.Prusa.PrusaMK4S import PrusaMK4S -from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter -from Classes.Vector3 import Vector3 -import serial -import serial.tools.list_ports - -class Device(ABC): - # static variables - __MODEL: str | None = None - __VENDORID: int | None = None - __PRODUCTID: int | None = None - __DESCRIPTION: str | None = None - __serialID: str | None = None - __serialConnection: serial.Serial | None = None - __serialPort: ListPortInfo | SysFS | None = None - __homePosition: Vector3| None = None - - def __init__(self, serialPort: ListPortInfo | SysFS): - self.__serialPort = serialPort - self.__serialID = serialPort.serial_number - - @staticmethod - def createDevice(serialPort: ListPortInfo | SysFS | None): - """creates the correct printer object based on the serial port info""" - if serialPort is None: - return None - if serialPort.vid == PrusaPrinter.__VENDORID: - if serialPort.pid == PrusaMK4.__PRODUCTID: - return PrusaMK4(serialPort) - elif serialPort.pid == PrusaMK4S.__PRODUCTID: - return PrusaMK4S(serialPort) - elif serialPort.pid == PrusaMK3.__PRODUCTID: - return PrusaMK3(serialPort) - else: - return None - elif serialPort.vid == EnderPrinter.__VENDORID: - if serialPort.pid == Ender3.__PRODUCTID: - return Ender3(serialPort) - elif serialPort.pid == Ender3Pro.__PRODUCTID: - return Ender3Pro(serialPort) - - - def connect(self): - try: - self.__serialConnection = serial.Serial(self.__serialPort.device, 115200, timeout=10) - return True - except Exception as e: - # let the printer parent class deal with the error - return e - - def disconnect(self): - if self.__serialConnection: - self.__serialConnection.close() - self.__serialConnection = None - - @abstractmethod - def home(self): - pass - - @abstractmethod - def diagnose(self): - try: - diagnoseString = "" - for port in serial.tools.list_ports.comports(): - if port.device == self.__serialPort.device: - diagnoseString += f"The system has found a matching port with the following details:

Device: {port.device},
Description: {port.description},
HWID: {port.hwid}" - hwid = self.getHWID().split(' LOCATION=')[0] - printerExists = PrinterList.getPrinterByHwid(hwid) - if printerExists: - printer = PrinterList.getPrinterByHwid(hwid) - diagnoseString += f"

Device {port.device} is registered with the following details:

Name: {printer.name}
Device: {printer.device},
Description: {printer.description},
HWID: {printer.hwid}" - if diagnoseString == "": - diagnoseString = "The port this printer is registered under is not found. Please check the connection and try again." - # return diagnoseString - return { - "success": True, - "message": "Printer successfully diagnosed.", - "diagnoseString": diagnoseString, - } - - except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - - @abstractmethod - def parseGcode(self): - pass - - @abstractmethod - def sendGcode(self, gcode: str): - pass - - @abstractmethod - def repair(self): - pass - - @abstractmethod - def hardReset(self): - pass - - @abstractmethod - def changeColor(self): - pass - - def getModel(self): - return self.__MODEL - - def getHWID(self): - return self.__serialPort.hwid.split(' LOCATION=')[0] - - def getSerialConnection(self): - return self.__serialConnection - - def getSerialPort(self): - return self.__serialPort - - def getDescription(self): - return self.__DESCRIPTION \ No newline at end of file diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py new file mode 100644 index 00000000..56cffadd --- /dev/null +++ b/server/Classes/FabricatorList.py @@ -0,0 +1,98 @@ +import serial.tools.list_ports +from flask import jsonify + +from Classes.Fabricators.Device import Device +from Classes.Ports import Ports +from Classes.Fabricators.Fabricator import Fabricator + +class FabricatorList(): + fabricators = Fabricator.queryAll() + @staticmethod + def __iter__(): + return iter(FabricatorList.fabricators) + + @staticmethod + def addFabricator(serialPortName: str, name: str): + """add a printer to the list, and to the database""" + # TODO: test if the printer is already in the list + serialPort = Ports.getPortByName(serialPortName) + if FabricatorList.getPrinterByHwid(serialPort.hwid): + return None # TODO: return error message + FabricatorList.fabricators.append(Fabricator(Device.createDevice(serialPort), name)) + # TODO: check that printerlist and database are in sync + dbPrinters = Fabricator.queryAll() + if len(FabricatorList.fabricators) != len(dbPrinters): + return None # TODO: return error message + for printer in dbPrinters: + if printer not in FabricatorList.fabricators: + return None # TODO: return error message + + @staticmethod + def deleteFabricator(printerid): + """delete a printer from the list, and from the database""" + # TODO: Implement deletePrinter + pass + # try: + # ports = serial.tools.list_ports.comports() + # for port in ports: + # hwid = port["hwid"] # get hwid + # if hwid == Printer.query.get(printerid).hwid: + # ser = serial.Serial(port["device"], 115200, timeout=1) + # ser.close() + # break + # printer = PrinterList.query.get(printerid) + # db.session.delete(printer) + # db.session.commit() + # return {"success": True, "message": "Printer successfully deleted."} + # except SQLAlchemyError as e: + # print(f"Database error: {e}") + # return ( + # jsonify({"error": "Failed to delete printer. Database error"}), + # 500, + # ) + @staticmethod + def getFabricatorCount(): + return len(FabricatorList.fabricators) + + @staticmethod + def getFabricatorByName(name) -> Fabricator | None: + """find the first printer with the given name""" + for fabricator in FabricatorList.__iter__(): + if fabricator.getName() == name: + return fabricator + return None + + @staticmethod + def getPrinterByHwid(hwid) -> Fabricator | None: + """find the first printer with the given hwid""" + for fabricator in FabricatorList.__iter__(): + if fabricator.getHwid() == hwid: + return fabricator + return None + + @staticmethod + def diagnose(device: Device): + try: + diagnoseString = "" + for port in serial.tools.list_ports.comports(): + if port.device == device.getSerialPort().device: + diagnoseString += f"The system has found a matching port with the following details:

Device: {port.device},
Description: {port.description},
HWID: {port.hwid}" + hwid = device.getHWID() + printerExists = FabricatorList.getPrinterByHwid(hwid) + if printerExists: + printer = FabricatorList.getPrinterByHwid(hwid) + diagnoseString += f"

Device {port.device} is registered with the following details:

Name: {printer.name}
Device: {printer.device},
Description: {printer.description},
HWID: {printer.hwid}" + if diagnoseString == "": + diagnoseString = "The port this printer is registered under is not found. Please check the connection and try again." + # return diagnoseString + return { + "success": True, + "message": "Printer successfully diagnosed.", + "diagnoseString": diagnoseString, + } + + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 + + \ No newline at end of file diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py new file mode 100644 index 00000000..3a0bc089 --- /dev/null +++ b/server/Classes/Fabricators/Device.py @@ -0,0 +1,102 @@ +from abc import ABC, abstractmethod + +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS +from typing_extensions import Buffer + +# from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro +# from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 +# from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter +# from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 +# from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 +# from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S +# from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Classes.Vector3 import Vector3 +import serial +import serial.tools.list_ports + +class Device(ABC): + # static variables + MODEL: str | None = None + VENDORID: int | None = None + PRODUCTID: int | None = None + DESCRIPTION: str | None = None + serialID: str | None = None + serialConnection: serial.Serial | None = None + serialPort: ListPortInfo | SysFS | None = None + homePosition: Vector3 | None = None + + def __init__(self, serialPort: ListPortInfo | SysFS): + self.serialPort = serialPort + self.serialID = serialPort.serial_number + + def __repr__(self): + return f"{self.getModel()} on {self.getSerialPort().device}" + + # @staticmethod + # def createDevice(serialPort: ListPortInfo | SysFS | None): + # """creates the correct printer object based on the serial port info""" + # if serialPort is None: + # return None + # if serialPort.vid == PrusaPrinter.__VENDORID: + # if serialPort.pid == PrusaMK4.__PRODUCTID: + # return PrusaMK4(serialPort) + # elif serialPort.pid == PrusaMK4S.__PRODUCTID: + # return PrusaMK4S(serialPort) + # elif serialPort.pid == PrusaMK3.__PRODUCTID: + # return PrusaMK3(serialPort) + # else: + # return None + # elif serialPort.vid == EnderPrinter.__VENDORID: + # if serialPort.pid == Ender3.__PRODUCTID: + # return Ender3(serialPort) + # elif serialPort.pid == Ender3Pro.__PRODUCTID: + # return Ender3Pro(serialPort) + + + def connect(self): + try: + self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=10) + return True + except Exception as e: + # let the printer parent class deal with the error + return e + + def disconnect(self): + if self.serialConnection: + self.serialConnection.close() + self.serialConnection = None + + @abstractmethod + def home(self): + pass + + def parseGcode(self): + pass + + def sendGcode(self, gcode: Buffer, checkFunction): + pass + + def repair(self): + pass + + def hardReset(self, newStatus: str): + pass + + def getModel(self): + return self.MODEL + + def getHWID(self): + return self.serialPort.hwid.split(' LOCATION=')[0] + + def getSerialConnection(self): + return self.serialConnection + + def getSerialPort(self): + return self.serialPort + + def getDescription(self): + return self.DESCRIPTION + + def getHomePosition(self): + return self.homePosition diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py new file mode 100644 index 00000000..f269644f --- /dev/null +++ b/server/Classes/Fabricators/Fabricator.py @@ -0,0 +1,136 @@ +from flask import current_app +from Classes.Fabricators.Device import Device +from Classes.Queue import Queue +#from models.jobs import Job +from models.db import db +from datetime import datetime, timezone + +# class Fabricator(db.Model): +# dbID: int = db.Column(db.Integer, primary_key=True) +# description: str = db.Column(db.String(50), nullable=False) +# hwid: str = db.Column(db.String(150), nullable=False) +# name: str = db.Column(db.String(50), nullable=False) +# date: datetime = db.Column( +# db.DateTime, +# default=lambda: datetime.now(timezone.utc).astimezone(), +# nullable=False, +# ) +# devicePort: str = db.Column(db.String(50), nullable=False) +# device: Device = None +# verdict: str = None +# prevMsg: str = None +# #job: Job = None +# queue: Queue = None +# status: str = None + +class Fabricator: + dbID: int = None + description: str = None + hwid: str = None + name: str = None + date: datetime = None + devicePort: None + device: Device = None + verdict: str = None + prevMsg: str = None + # job: Job = None + queue: Queue = None + status: str = None + + + def __init__(self, device: Device, name): + self.device = device + self.name = name + self.description = device.getDescription() + self.hwid = device.getHWID() + self.devicePort = device.getSerialPort().device + self.device.connect() + self.date = datetime.now(timezone.utc).astimezone() + db.session.add(self) + + @classmethod + def queryAll(cls) -> list["Fabricator"]: + #return cls.query.all() + pass + + # def print(self): + # # TODO: implement print method + # # TODO: start print + # # TODO: implement loop + # # TODO: check for errors + # # TODO: check for completion + # # TODO: check for cancellation + # # TODO: update progress + # # TODO: update status + # # TODO: end print + # pass + # + # def pausePrint(self): + # if not isinstance(self.__device, canPause): + # return # TODO: return error message + # self.__device.pause() + # self.setStatus("paused") + # #self.__job.setStatus("paused") + # + # def resumePrint(self): + # if not isinstance(self.__device, canPause): + # return #TODO: return error message + # self.__device.resume() + # self.setStatus("printing") + # #self.__job.setStatus("printing") + # + # def cancelPrint(self): + # # TODO: implement cancel print + # pass + # + # def getStatus(self): + # return self.__status + # def setStatus(self, newStatus): + # try: + # print("SETTING STATUS TO:", newStatus) + # if self.__status == "error" and newStatus!= "error": + # self.__device.hardReset(newStatus) + # else: + # self.__status = newStatus + # + # current_app.socketio.emit( + # "status_update", {"printer_id": self.id, "status": newStatus} + # ) + # except Exception as e: + # print("Error setting status:", e) + # + # + # def handleVerdict(self, verdict: str, job): + # if verdict == "complete": + # self.__device.disconnect() + # #self.sendStatusToJob(job, job.id, "complete") + # self.setStatus("complete") + # #self.__job.setStatus("complete") + # elif verdict == "error": + # self.__device.disconnect() + # #self.__queue.deleteJob(job.id, self.id) + # self.setStatus("error") + # #self.sendStatusToJob(job, job.id, "error") + # # self.setError("Error") + # elif verdict == "cancelled": + # if isinstance(self.__device, hasEndingSequence): + # self.__device.endSequence() + # else: + # self.__device.home() + # #self.sendStatusToJob(job, job.id, "cancelled") + # self.setStatus("cancelled") + # #self.__job.setStatus("cancelled") + # self.__device.disconnect() + # elif verdict== "misprint": + # self.setStatus("misprint") + # #self.__job.setStatus("misprint") + # #self.sendStatusToJob(job, job.id, "cancelled") + + def getName(self): + return self.name + + def getHwid(self): + return self.hwid + + def getDescription(self): + return self.description \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3.py b/server/Classes/Fabricators/Printers/Ender/Ender3.py new file mode 100644 index 00000000..66886f57 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Ender/Ender3.py @@ -0,0 +1,9 @@ +from abc import ABC + +from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter + + +class Ender3(EnderPrinter): + MODEL = "Ender 3" + PRODUCTID = 0x7523 + DESCRIPTION = "Ender 3 - CDC" \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py new file mode 100644 index 00000000..bd723231 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py @@ -0,0 +1,12 @@ +from abc import ABC + +from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 + + +class Ender3Pro(Ender3): + MODEL = "Ender 3 Pro" + DESCRIPTION = "Ender 3 Pro - CDC" + + def home(self): + # TODO: make the home work without crashing the printer + pass \ No newline at end of file diff --git a/server/Classes/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py similarity index 80% rename from server/Classes/Printers/Ender/EnderPrinter.py rename to server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 55db138b..138c2074 100644 --- a/server/Classes/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,10 +1,9 @@ -from abc import ABC -from Classes.Device import Device -from Interfaces.hasEndingSequence import hasEndingSequence +from abc import ABCMeta +from Classes.Fabricators.Device import Device +from Mixins.hasEndingSequence import hasEndingSequence - -class EnderPrinter(ABC, Device, hasEndingSequence): - __VENDORID = 0x1A86 +class EnderPrinter(Device, hasEndingSequence, metaclass=ABCMeta): + VENDORID = 0x1A86 def endSequence(self): self.gcodeEnding("G91") # Relative positioning diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py new file mode 100644 index 00000000..7ac1faa1 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -0,0 +1,12 @@ +from abc import ABCMeta +from Classes.Fabricators.Device import Device + +class Printer(Device, metaclass=ABCMeta): + filamentType: str | None = None + filamentDiameter: float | None = None + nozzleDiameter: float | None = None + bedTemperature: int | None = None + nozzleTemperature: int | None = None + + def changeFilament(self): + pass \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py new file mode 100644 index 00000000..ac99c96b --- /dev/null +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -0,0 +1,13 @@ +from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter + +class PrusaMK3(PrusaPrinter): + MODEL = "Prusa MK3" + PRODUCTID = 0x0002 + DESCRIPTION = "Original Prusa MK3 - CDC" + + def endSequence(self): + self.gcodeEnding("M104 S0") # turn off extruder + self.gcodeEnding("M140 S0") # turn off heatbed + self.gcodeEnding("M107") # turn off fan + self.gcodeEnding("G1 X0 Y210") # home X axis and push Y forward + self.gcodeEnding("M84") # disable motors \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py new file mode 100644 index 00000000..1ca7dce7 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -0,0 +1,16 @@ +from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Classes.Vector3 import Vector3 + + +class PrusaMK4(PrusaPrinter): + MODEL = "Prusa MK4" + PRODUCTID = 0x000D + DESCRIPTION = "Original Prusa MK4 - CDC" + homePosition = Vector3(14.0, -4.0, 2.0) + + def endSequence(self): + # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") + self.gcodeEnding("M104 S0") # ; turn off temperature + self.gcodeEnding("M140 S0") # ; turn off heatbed + self.gcodeEnding("M107") # ; turn off fan + diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py new file mode 100644 index 00000000..dfb605a4 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py @@ -0,0 +1,6 @@ +from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 + +class PrusaMK4S(PrusaMK4): + MODEL = "Prusa MK4S" + PRODUCTID = 0x001A + DESCRIPTION = "Original Prusa MK4S - CDC" diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py new file mode 100644 index 00000000..c69523d4 --- /dev/null +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -0,0 +1,85 @@ +from abc import ABCMeta + +from typing_extensions import Buffer + +from Classes.Fabricators.Device import Device +from Classes.Vector3 import Vector3 +from Classes.Fabricators.Printers.Printer import Printer +from Mixins.canPause import canPause +from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import hasResponsecodes, checkXYZ, checkOK +from Mixins.usesMarlinGcode import usesMarlinGcode + + +class PrusaPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): + VENDORID = 0x2C99 + + def sendGcode(self, gcode: Buffer, checkFunction): + self.serialConnection.write(gcode) + while self.serialConnection.readline() == b'ok\n': + pass + while True: + try: + line = self.serialConnection.readline() + print(line) + if checkFunction(line): + break + except Exception as e: + print(e) + break + + + def connect(self): + Device.connect(self) + try: + if self.serialConnection: + self.serialConnection.write(usesMarlinGcode.connect) + return True + except Exception as e: + return e + + def disconnect(self): + if self.serialConnection: + self.serialConnection.write(usesMarlinGcode.disconnect) + self.serialConnection.close() + self.serialConnection = None + + def home(self): + self.sendGcode(usesMarlinGcode.home, checkOK) + return self.getHomePosition() == self.getPrintHeadLocation() + + def goTo(self, loc: Vector3): + self.sendGcode(usesMarlinGcode.goTo(loc), checkXYZ) + return loc == self.getPrintHeadLocation() + + def pause(self): + self.sendGcode(usesMarlinGcode.pause, checkOK) + + def resume(self): + self.sendGcode(usesMarlinGcode.resume, checkOK) + + def endSequence(self): + pass + + def getPrintTime(self): + pass + + def getPrintHeadLocation(self) -> Vector3: + self.serialConnection.write("M114\n".encode("utf-8")) + response = "" + while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): + response = self.serialConnection.readline() + print(response) + response = response.decode("utf-8") + x = float(response.split("X:")[1].split(" ")[0]) + y = float(response.split("Y:")[1].split(" ")[0]) + z = float(response.split("Z:")[1].split(" ")[0]) + return Vector3(x,y,z) + + + @classmethod + def __subclasshook__(cls, subclass): + if cls is PrusaPrinter: + if any("Prusa" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): + return True + return NotImplemented diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 3e105d28..8a3ec72f 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -21,6 +21,6 @@ def getPortByName(name: str) -> ListPortInfo | SysFS | None: def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: ports = Ports.getPorts() for port in ports: - if port.hwid == hwid: + if hwid in port.hwid: return port return None \ No newline at end of file diff --git a/server/Classes/Printer.py b/server/Classes/Printer.py deleted file mode 100644 index 28c2cc3e..00000000 --- a/server/Classes/Printer.py +++ /dev/null @@ -1,121 +0,0 @@ -from flask import current_app -from Classes.Device import Device -from Classes.Queue import Queue -from Interfaces.canPause import canPause -from models.jobs import Job -from models.db import db -from datetime import datetime, timezone -from Interfaces.hasEndingSequence import hasEndingSequence - -class Printer(db.Model): - __dbID: int = db.Column(db.Integer, primary_key=True) - __description: str = db.Column(db.String(50), nullable=False) - __hwid: str = db.Column(db.String(150), nullable=False) - __name: str = db.Column(db.String(50), nullable=False) - __date: datetime = db.Column( - db.DateTime, - default=lambda: datetime.now(timezone.utc).astimezone(), - nullable=False, - ) - __devicePort: str = db.Column(db.String(50), nullable=False) - __device: Device = None - __verdict: str = None - __prevMsg: str = None - __job: Job = None - __queue: Queue = None - __status: str = None - - def __init__(self, device: Device, name): - self.__device = device - self.__name = name - self.__description = device.getDescription() - self.__hwid = device.getHWID() - self.__devicePort = device.getSerialPort().device - self.__device.connect() - db.session.add(self) - - @classmethod - def queryAll(cls) -> list["Printer"]: - return cls.query.all() - - def print(self): - # TODO: implement print method - # TODO: start print - # TODO: implement loop - # TODO: check for errors - # TODO: check for completion - # TODO: check for cancellation - # TODO: update progress - # TODO: update status - # TODO: end print - pass - - def pausePrint(self): - if not isinstance(self.__device, canPause): - return # TODO: return error message - self.__device.pause() - self.setStatus("paused") - self.__job.setStatus("paused") - - def resumePrint(self): - if not isinstance(self.__device, canPause): - return #TODO: return error message - self.__device.resume() - self.setStatus("printing") - self.__job.setStatus("printing") - - def cancelPrint(self): - # TODO: implement cancel print - pass - - def getStatus(self): - return self.__status - def setStatus(self, newStatus): - try: - print("SETTING STATUS TO:", newStatus) - if self.__status == "error" and newStatus!= "error": - Printer.hardReset(self.id, newStatus) - else: - self.__status = newStatus - - current_app.socketio.emit( - "status_update", {"printer_id": self.id, "status": newStatus} - ) - except Exception as e: - print("Error setting status:", e) - - - def handelVerdict(self, verdict: str, job): - if verdict == "complete": - self.__device.disconnect() - #self.sendStatusToJob(job, job.id, "complete") - self.setStatus("complete") - self.__job.setStatus("complete") - elif verdict == "error": - self.disconnect() - self.__queue.deleteJob(job.id, self.id) - self.setStatus("error") - self.sendStatusToJob(job, job.id, "error") - # self.setError("Error") - elif verdict == "cancelled": - if isinstance(self.__device, hasEndingSequence): - self.__device.endSequence() - else: - self.__device.home() - #self.sendStatusToJob(job, job.id, "cancelled") - self.setStatus("cancelled") - self.__job.setStatus("cancelled") - self.disconnect() - elif verdict== "misprint": - self.setStatus("misprint") - self.__job.setStatus("misprint") - #self.sendStatusToJob(job, job.id, "cancelled") - - def getName(self): - return self.__name - - def getHwid(self): - return self.__hwid - - def getDescription(self): - return self.__description \ No newline at end of file diff --git a/server/Classes/PrinterList.py b/server/Classes/PrinterList.py deleted file mode 100644 index 335b731a..00000000 --- a/server/Classes/PrinterList.py +++ /dev/null @@ -1,71 +0,0 @@ -from flask import jsonify -from sqlalchemy.exc import SQLAlchemyError -import serial -import serial.tools.list_ports -from Classes.Device import Device -from Classes.Ports import Ports -from Classes.Printer import Printer - -class PrinterList(): - printers = Printer.queryAll() - - @staticmethod - def addPrinter(serialPortName: str, name: str): - """add a printer to the list, and to the database""" - # TODO: test if the printer is already in the list - serialPort = Ports.getPortByName(serialPortName) - if PrinterList.getPrinterByHwid(serialPort.hwid): - return None # TODO: return error message - PrinterList.printers.append(Printer(Device.createDevice(serialPort), name)) - # TODO: check that printerlist and database are in sync - dbPrinters = Printer.queryAll() - if len(PrinterList.printers) != len(dbPrinters): - return None # TODO: return error message - for printer in dbPrinters: - if printer not in PrinterList.printers: - return None # TODO: return error message - - @staticmethod - def deletePrinter(printerid): - """delete a printer from the list, and from the database""" - # TODO: Implement deletePrinter - pass - # try: - # ports = serial.tools.list_ports.comports() - # for port in ports: - # hwid = port["hwid"] # get hwid - # if hwid == Printer.query.get(printerid).hwid: - # ser = serial.Serial(port["device"], 115200, timeout=1) - # ser.close() - # break - # printer = PrinterList.query.get(printerid) - # db.session.delete(printer) - # db.session.commit() - # return {"success": True, "message": "Printer successfully deleted."} - # except SQLAlchemyError as e: - # print(f"Database error: {e}") - # return ( - # jsonify({"error": "Failed to delete printer. Database error"}), - # 500, - # ) - @staticmethod - def getPrinterCount(): - return len(PrinterList.printers) - - @staticmethod - def getPrinterByName(name) -> Printer | None: - """find the first printer with the given name""" - for printer in PrinterList.printers: - if printer.get_name() == name: - return printer - return None - - @staticmethod - def getPrinterByHwid(hwid) -> Printer | None: - """find the first printer with the given hwid""" - for printer in PrinterList.printers: - if printer.get_hwid() == hwid: - return printer - return None - - \ No newline at end of file diff --git a/server/Classes/Printers/Ender/Ender3.py b/server/Classes/Printers/Ender/Ender3.py deleted file mode 100644 index 6a76aafd..00000000 --- a/server/Classes/Printers/Ender/Ender3.py +++ /dev/null @@ -1,12 +0,0 @@ -from abc import ABC - -from Classes.Printers.Ender.EnderPrinter import EnderPrinter - - -class Ender3(ABC, EnderPrinter): - __MODEL = "Ender 3" - __PRODUCTID = 0x7523 - __DESCRIPTION = "Ender 3 - CDC" - - def __init__(self, serialPort): - super().__init__(self, serialPort) \ No newline at end of file diff --git a/server/Classes/Printers/Ender/Ender3Pro.py b/server/Classes/Printers/Ender/Ender3Pro.py deleted file mode 100644 index b3d6d3a6..00000000 --- a/server/Classes/Printers/Ender/Ender3Pro.py +++ /dev/null @@ -1,15 +0,0 @@ -from abc import ABC - -from Classes.Printers.Ender.Ender3 import Ender3 - - -class Ender3Pro(ABC, Ender3): - __MODEL = "Ender 3 Pro" - __DESCRIPTION = "Ender 3 Pro - CDC" - - def __init__(self, serialPort): - super().__init__(self, serialPort) - - def home(self): - # TODO: make the home work without crashing the printer - pass \ No newline at end of file diff --git a/server/Classes/Printers/Prusa/PrusaMK3.py b/server/Classes/Printers/Prusa/PrusaMK3.py deleted file mode 100644 index 1ec21391..00000000 --- a/server/Classes/Printers/Prusa/PrusaMK3.py +++ /dev/null @@ -1,22 +0,0 @@ -from abc import ABC -from serial.tools.list_ports_common import ListPortInfo -from serial.tools.list_ports_linux import SysFS -from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter -from Interfaces.hasEndingSequence import hasEndingSequence -from Interfaces.hasResponseCodes import hasResponsecodes - - -class PrusaMK3(ABC, PrusaPrinter, hasEndingSequence, hasResponsecodes): - __MODEL = "Prusa MK3" - __PRODUCTID = 0x0002 - __DESCRIPTION = "Original Prusa MK3 - CDC" - - def __init__(self, serialPort: ListPortInfo | SysFS): - super().__init__(self, serialPort) - - def endSequence(self): - self.gcodeEnding("M104 S0") # turn off extruder - self.gcodeEnding("M140 S0") # turn off heatbed - self.gcodeEnding("M107") # turn off fan - self.gcodeEnding("G1 X0 Y210") # home X axis and push Y forward - self.gcodeEnding("M84") # disable motors \ No newline at end of file diff --git a/server/Classes/Printers/Prusa/PrusaMK4.py b/server/Classes/Printers/Prusa/PrusaMK4.py deleted file mode 100644 index fc5d85b8..00000000 --- a/server/Classes/Printers/Prusa/PrusaMK4.py +++ /dev/null @@ -1,21 +0,0 @@ -from abc import ABC -from serial.tools.list_ports_common import ListPortInfo -from Classes.Printers.Prusa.PrusaPrinter import PrusaPrinter -from Interfaces.hasEndingSequence import hasEndingSequence -from Interfaces.hasResponseCodes import hasResponsecodes - - -class PrusaMK4(ABC, PrusaPrinter, hasEndingSequence, hasResponsecodes): - __MODEL = "Prusa MK4" - __PRODUCTID = 0x000D - __DESCRIPTION = "Original Prusa MK4 - CDC" - - def __init__(self, serialPort: ListPortInfo): - super().__init__(self, serialPort) - - def endSequence(self): - # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") - self.gcodeEnding("M104 S0") # ; turn off temperature - self.gcodeEnding("M140 S0") # ; turn off heatbed - self.gcodeEnding("M107") # ; turn off fan - diff --git a/server/Classes/Printers/Prusa/PrusaMK4S.py b/server/Classes/Printers/Prusa/PrusaMK4S.py deleted file mode 100644 index d2b910ff..00000000 --- a/server/Classes/Printers/Prusa/PrusaMK4S.py +++ /dev/null @@ -1,11 +0,0 @@ -from abc import ABC -from serial.tools.list_ports_common import ListPortInfo -from Classes.Printers.Prusa.PrusaMK4 import PrusaMK4 - -class PrusaMK4S(PrusaMK4, ABC): - __MODEL = "Prusa MK4S" - __PRODUCTID = 0x001A - __DESCRIPTION = "Original Prusa MK4S - CDC" - - def __init__(self, serialPort: ListPortInfo): - super().__init__(self, serialPort) diff --git a/server/Classes/Printers/Prusa/PrusaPrinter.py b/server/Classes/Printers/Prusa/PrusaPrinter.py deleted file mode 100644 index c85c3ebc..00000000 --- a/server/Classes/Printers/Prusa/PrusaPrinter.py +++ /dev/null @@ -1,61 +0,0 @@ -from abc import ABC -from serial.tools.list_ports_common import ListPortInfo -from Classes.Vector3 import Vector3 -from Classes.Device import Device -from Interfaces.canPause import canPause -from Interfaces.hasEndingSequence import hasEndingSequence -from Interfaces.hasResponseCodes import hasResponsecodes - - -class PrusaPrinter(ABC, Device, hasEndingSequence, hasResponsecodes, canPause): - __VENDORID = 0x2C99 - - def __init__(self, serialPort: ListPortInfo): - super().__init__(self, serialPort) - - def home(self): - self.__serialConnection.write("G28\n".encode("utf-8")) - while self.getPrintHeadLocation() != self.__homeLocation: - pass - return True - - - def endSequence(self): - pass - - def getPrintHeadLocation(self) -> Vector3: - self.__serialConnection.write("M114\n".encode("utf-8")) - response = self.__serialConnection.readline().decode("utf-8") - x = float(response.split("X:")[1].split(" ")[0]) - y = float(response.split("Y:")[1].split(" ")[0]) - z = float(response.split("Z:")[1].split(" ")[0]) - return Vector3(x,y,z) - - def connect(self): - try: - super().connect() - if self.__serialConnection: - self.__serialConnection.write("M155 S5 C7\n".encode("utf-8")) - return True - except Exception as e: - return e - - def disconnect(self): - if self.__serialConnection: - self.__serialConnection.write(f"M155 S0\n".encode("utf-8")) - self.__serialConnection.close() - self.__serialConnection = None - - def pause(self): - self.__serialConnection.write("M601\n".encode("utf-8")) - self.__serialConnection.write("M113 S1\n".encode("utf-8")) - - def resume(self): - self.__serialConnection.write("M602\n".encode("utf-8")) - - @classmethod - def __subclasshook__(cls, subclass): - if cls is PrusaPrinter: - if any("Prusa" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): - return True - return NotImplemented diff --git a/server/FabricatorUML.png b/server/FabricatorUML.png index 3d4011c9766ae4f75bbcd79f137395093d3c59b4..09f595a6dbd258ea367620842352ae41fa7d8592 100644 GIT binary patch delta 94813 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

Tcy88z`ka1P7 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 SGWs^bkDiXPcHyb3cm4}2sqMc2 delta 88835 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 Buffer: + return f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8") \ No newline at end of file From e0905550214df4c59c4132328bea9a6cbb6e5fe0 Mon Sep 17 00:00:00 2001 From: Ari Yeger Date: Thu, 10 Oct 2024 14:21:17 -0400 Subject: [PATCH 007/194] Delete QView3D Installer.exe --- QView3D Installer.exe | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 QView3D Installer.exe diff --git a/QView3D Installer.exe b/QView3D Installer.exe deleted file mode 100644 index 3067198c..00000000 --- a/QView3D Installer.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27557f843e9607398d4cf71de56a71e499982162fcd9c3d3cc664e0eb60b51a3 -size 201728 From 1774d0cbe70732dc3f35635ac51c409f174d91cd Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 10 Oct 2024 15:46:16 -0400 Subject: [PATCH 008/194] feat: clean install.sh and create install.bat for windows --- install.bat | 21 +++++++++++++++++++++ install.sh | 18 +++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 install.bat diff --git a/install.bat b/install.bat new file mode 100644 index 00000000..bcb36c4a --- /dev/null +++ b/install.bat @@ -0,0 +1,21 @@ +echo Installing dependencies for QView3D + +:pipSetup +pip install -r requirements.txt +goto flaskSetup + +:flaskSetup +cd server +flask db init +flask db migrate +flask db upgrade +goto clientSetup + +:clientSetup +cd ../client +npm install +npm run build-only +goto finish + +:finish +echo Finished installed dependencies! \ No newline at end of file diff --git a/install.sh b/install.sh index 251af405..01c0cf3e 100755 --- a/install.sh +++ b/install.sh @@ -1,16 +1,6 @@ #!/bin/bash -# Add the execute permission to the script. -chmod +x install.sh - -# Install the required packages. -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash - -# Source your bash profile to load changes made by nvm -source ~/.bashrc - -# Install Node.js. -nvm install node +echo "Installing dependencies for QView3D" # Install Python dependencies. Requirements was created with pipreqs. pip install -r requirements.txt @@ -33,5 +23,7 @@ cd ../client # Install Node.js dependencies. npm install -# Install npm-run-all as a dev dependency -npm install --save-dev npm-run-all \ No newline at end of file +# Build client so server can host it. +npm run build-only + +echo "Finished installed dependencies!" \ No newline at end of file From 6f284ab329d76df788a88e95a81f9cc236e85223 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 10 Oct 2024 16:21:10 -0400 Subject: [PATCH 009/194] fix: fix install scripts! --- client/package-lock.json | 651 +++++++++++++++------------------------ client/package.json | 1 + install.bat | 2 +- install.sh | 40 ++- 4 files changed, 280 insertions(+), 414 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 8816256c..baec755d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -19,7 +19,6 @@ "bootstrap-daterangepicker": "^3.1.0", "cors": "^2.8.5", "electron-squirrel-startup": "^1.0.1", - "electron-store": "^10.0.0", "file-saver": "^2.0.5", "gcode-preview": "^2.13.0", "node": "^21.6.2", @@ -54,13 +53,13 @@ "@vue/test-utils": "^2.4.3", "@vue/tsconfig": "^0.5.0", "electron": "^32.1.1", - "electron-wix-msi": "^5.1.3", "eslint": "^8.49.0", "eslint-plugin-vue": "^9.17.0", "jsdom": "^23.0.1", "npm-run-all": "^4.1.5", "npm-run-all2": "^6.1.1", "prettier": "^3.0.3", + "rollup": "^4.24.0", "typescript": "~5.3.0", "vite": "^5.2.9", "vitest": "^1.0.4", @@ -3185,8 +3184,220 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], @@ -3285,7 +3496,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, "license": "MIT" }, @@ -4253,39 +4466,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/ansi-colors": { "version": "4.1.3", "license": "MIT", @@ -4515,13 +4695,6 @@ "node": ">= 4.0.0" } }, - "node_modules/atomically": { - "version": "2.0.3", - "dependencies": { - "stubborn-fs": "^1.2.5", - "when-exit": "^2.1.1" - } - }, "node_modules/author-regex": { "version": "1.0.0", "dev": true, @@ -5209,55 +5382,6 @@ "version": "0.0.1", "license": "MIT" }, - "node_modules/conf": { - "version": "13.0.1", - "license": "MIT", - "dependencies": { - "ajv": "^8.16.0", - "ajv-formats": "^3.0.1", - "atomically": "^2.0.3", - "debounce-fn": "^6.0.0", - "dot-prop": "^9.0.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.6.2", - "uint8array-extras": "^1.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/ajv": { - "version": "8.17.1", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/conf/node_modules/env-paths": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/confbox": { "version": "0.1.7", "dev": true, @@ -5314,96 +5438,6 @@ "which": "^1.2.9" } }, - "node_modules/cross-spawn-windows-exe": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "@malept/cross-spawn-promise": "^1.1.0", - "is-wsl": "^2.2.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/cross-zip": { "version": "4.0.1", "dev": true, @@ -5559,19 +5593,6 @@ "dev": true, "license": "MIT" }, - "node_modules/debounce-fn": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/debug": { "version": "4.3.5", "license": "MIT", @@ -5814,29 +5835,6 @@ "node": ">=6.0.0" } }, - "node_modules/dot-prop": { - "version": "9.0.0", - "license": "MIT", - "dependencies": { - "type-fest": "^4.18.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/type-fest": { - "version": "4.26.1", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, @@ -6084,30 +6082,6 @@ "version": "2.0.0", "license": "MIT" }, - "node_modules/electron-store": { - "version": "10.0.0", - "license": "MIT", - "dependencies": { - "conf": "^13.0.0", - "type-fest": "^4.20.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-store/node_modules/type-fest": { - "version": "4.26.1", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/electron-to-chromium": { "version": "1.4.790", "dev": true, @@ -6133,59 +6107,6 @@ "@electron/windows-sign": "^1.1.2" } }, - "node_modules/electron-wix-msi": { - "version": "5.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/windows-sign": "^1.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.1.0", - "klaw": "^4.1.0", - "lodash": "^4.17.21", - "rcedit": "^4.0.1", - "rcinfo": "^0.1.3", - "semver": "^7.6.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@bitdisaster/exe-icon-extractor": "^1.0.10" - } - }, - "node_modules/electron-wix-msi/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-wix-msi/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-wix-msi/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/electron/node_modules/@electron/get": { "version": "2.0.3", "dev": true, @@ -7144,6 +7065,7 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "dev": true, "license": "MIT" }, "node_modules/fast-diff": { @@ -7175,10 +7097,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.1", - "license": "MIT" - }, "node_modules/fast-url-parser": { "version": "1.1.3", "license": "MIT", @@ -7488,6 +7406,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -8437,20 +8370,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "license": "MIT", @@ -8653,17 +8572,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "2.0.5", "license": "MIT" @@ -8849,10 +8757,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema-typed": { - "version": "8.0.1", - "license": "BSD-2-Clause" - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, @@ -8905,14 +8809,6 @@ "node": ">=0.10.0" } }, - "node_modules/klaw": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14.0" - } - }, "node_modules/kleur": { "version": "4.1.5", "license": "MIT", @@ -9565,16 +9461,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mimic-response": { "version": "1.0.1", "dev": true, @@ -11171,22 +11057,6 @@ "node": ">= 0.6" } }, - "node_modules/rcedit": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn-windows-exe": "^1.1.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/rcinfo": { - "version": "0.1.3", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/react": { "version": "18.3.1", "license": "MIT", @@ -11376,6 +11246,7 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11603,11 +11474,13 @@ "optional": true }, "node_modules/rollup": { - "version": "4.18.0", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -11617,22 +11490,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, @@ -12339,9 +12212,6 @@ "node": ">=0.10.0" } }, - "node_modules/stubborn-fs": { - "version": "1.2.5" - }, "node_modules/sudo-prompt": { "version": "9.2.1", "dev": true, @@ -12874,16 +12744,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uint8array-extras": { - "version": "1.4.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "license": "MIT", @@ -13429,9 +13289,6 @@ "node": ">=18" } }, - "node_modules/when-exit": { - "version": "2.1.3" - }, "node_modules/which": { "version": "1.3.1", "license": "ISC", diff --git a/client/package.json b/client/package.json index cc70aa87..1fcb396a 100644 --- a/client/package.json +++ b/client/package.json @@ -77,6 +77,7 @@ "npm-run-all": "^4.1.5", "npm-run-all2": "^6.1.1", "prettier": "^3.0.3", + "rollup": "^4.24.0", "typescript": "~5.3.0", "vite": "^5.2.9", "vitest": "^1.0.4", diff --git a/install.bat b/install.bat index bcb36c4a..09c936d4 100644 --- a/install.bat +++ b/install.bat @@ -13,7 +13,7 @@ goto clientSetup :clientSetup cd ../client -npm install +npm install --save-dev npm run build-only goto finish diff --git a/install.sh b/install.sh index 01c0cf3e..04b02a9a 100755 --- a/install.sh +++ b/install.sh @@ -1,29 +1,37 @@ #!/bin/bash -echo "Installing dependencies for QView3D" +setup_python() { + pip install -r requirements.txt +} -# Install Python dependencies. Requirements was created with pipreqs. -pip install -r requirements.txt +setup_flask() { + flask db init -# Change directory to the server. -cd server + flask db migrate + + flask db upgrade +} + +setup_client() { + npm install --save-dev +} -# Initialize the database. -flask db init +build_client() { + npm run build-only +} -# Generate a migration script. -flask db migrate +echo "Installing dependencies for QView3D" + +setup_python + +cd server -# Apply the migration. -flask db upgrade +setup_flask -# Change directory to the client. cd ../client -# Install Node.js dependencies. -npm install +setup_client -# Build client so server can host it. -npm run build-only +build_client echo "Finished installed dependencies!" \ No newline at end of file From 10b53003e3788011530b07c6cc31ec7e702cdb2e Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 10 Oct 2024 16:25:24 -0400 Subject: [PATCH 010/194] feat: make scripts delete migrations folder --- install.bat | 1 + install.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/install.bat b/install.bat index 09c936d4..7d89eed8 100644 --- a/install.bat +++ b/install.bat @@ -6,6 +6,7 @@ goto flaskSetup :flaskSetup cd server +rmdir /s /q "migrations" flask db init flask db migrate flask db upgrade diff --git a/install.sh b/install.sh index 04b02a9a..e0af56cd 100755 --- a/install.sh +++ b/install.sh @@ -5,6 +5,8 @@ setup_python() { } setup_flask() { + rm -rf migrations + flask db init flask db migrate From a73ae05f3d76c8cd97bd9a38a3c5d91f43b8dbd4 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 10 Oct 2024 16:29:37 -0400 Subject: [PATCH 011/194] feat: add windows run script --- run.bat | 2 ++ run.sh | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 run.bat diff --git a/run.bat b/run.bat new file mode 100644 index 00000000..f2d4f36a --- /dev/null +++ b/run.bat @@ -0,0 +1,2 @@ +cd client +npm run start \ No newline at end of file diff --git a/run.sh b/run.sh index 38651594..958c0b12 100755 --- a/run.sh +++ b/run.sh @@ -1,8 +1,5 @@ #!/bin/bash -# Go to the client directory cd client -# Run the npm run start -npm run start - +npm run start \ No newline at end of file From c5f89a9746a5788ec42f3dffb22a3bbf3525f5b4 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 10 Oct 2024 16:33:56 -0400 Subject: [PATCH 012/194] chore: cleanup server --- .DS_Store | Bin 0 -> 6148 bytes server/services/queueService.py | 1 - server/tasks/main.py | 62 -------------------------------- 3 files changed, 63 deletions(-) create mode 100644 .DS_Store delete mode 100644 server/services/queueService.py delete mode 100644 server/tasks/main.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ceb8eb0aee16a2431fbe81896f8bab23ecc33b18 GIT binary patch literal 6148 zcmeHK%}T>S5Z<-5O({YS3Oz1(Ef^aS#7n641&ruHr6wk5FlI}WnnNk%tS{t~_&m<+ zZVthMHxWAnyWi~m>}Ed5{xHV)co7{kW;4btXowt@HG<}~t}PRc$kiMfE5b6Lg{chJ zO!OB``0X9`z*3g5m^HuuBb=p4UO3Jt-)gnDJ6)@5^{qSqNtS*Q6pMTu%x`dXDPI~1lhypskB@l%{&tRnyJRn@B z0_s$5o)}!GgI$<7&tRodr!%frhI!1&_2Y%B)xj=QIOCp1>WKkjpvgc>4{bdE&*7I@ z`^aym(1;iy2L2fXygl(JV<^g;t>4PSvsOU6hlYZ26)GU0H!cA%z Date: Thu, 10 Oct 2024 16:34:22 -0400 Subject: [PATCH 013/194] fix: add mac files to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3f190c95..a27279a2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ migrations/ *.db server/qview3dserver.egg-info/ -server/dist \ No newline at end of file +server/dist + +.DS_Store \ No newline at end of file From 63f379aa132482218da1607ddc18168f9a45beab Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 10 Oct 2024 16:40:17 -0400 Subject: [PATCH 014/194] updated install scripts to delete the hvamc.db --- install.bat | 1 + install.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/install.bat b/install.bat index 7d89eed8..3793f02f 100644 --- a/install.bat +++ b/install.bat @@ -7,6 +7,7 @@ goto flaskSetup :flaskSetup cd server rmdir /s /q "migrations" +del "hvamc.db" flask db init flask db migrate flask db upgrade diff --git a/install.sh b/install.sh index e0af56cd..a856b516 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,8 @@ setup_python() { setup_flask() { rm -rf migrations + + rm -f hvamc.db flask db init From 281579faed3eb993698c73fba2ba82e50c22b39e Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 10 Oct 2024 21:37:18 -0400 Subject: [PATCH 015/194] refactored a bunch of the methods to usesMarlinGcode.py, because then it doesn't need to be copied in prusa and ender. --- server/Classes/FabricatorList.py | 8 +- server/Classes/Fabricators/Device.py | 32 +- server/Classes/Fabricators/Fabricator.py | 287 +++++++++++------- .../Fabricators/Printers/Ender/Ender3.py | 5 +- .../Fabricators/Printers/Ender/Ender3Pro.py | 8 +- .../Printers/Ender/EnderPrinter.py | 70 +++-- .../Printers/Prusa/PrusaPrinter.py | 58 +--- server/Classes/LocationResponse.py | 11 + server/Classes/Ports.py | 2 + server/Classes/Queue.py | 3 + server/Mixins/canPause.py | 9 +- server/Mixins/hasEndingSequence.py | 9 +- server/Mixins/hasResponseCodes.py | 13 +- server/Mixins/hasStartupSequence.py | 7 - server/Mixins/usesMarlinGcode.py | 65 +++- server/models/issues.py | 17 +- 16 files changed, 331 insertions(+), 273 deletions(-) create mode 100644 server/Classes/LocationResponse.py diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 56cffadd..91873231 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -11,6 +11,10 @@ class FabricatorList(): def __iter__(): return iter(FabricatorList.fabricators) + @staticmethod + def __len__(): + return len(FabricatorList.fabricators) + @staticmethod def addFabricator(serialPortName: str, name: str): """add a printer to the list, and to the database""" @@ -51,10 +55,6 @@ def deleteFabricator(printerid): # 500, # ) @staticmethod - def getFabricatorCount(): - return len(FabricatorList.fabricators) - - @staticmethod def getFabricatorByName(name) -> Fabricator | None: """find the first printer with the given name""" for fabricator in FabricatorList.__iter__(): diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 3a0bc089..21b396c6 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -33,30 +33,11 @@ def __init__(self, serialPort: ListPortInfo | SysFS): def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" - # @staticmethod - # def createDevice(serialPort: ListPortInfo | SysFS | None): - # """creates the correct printer object based on the serial port info""" - # if serialPort is None: - # return None - # if serialPort.vid == PrusaPrinter.__VENDORID: - # if serialPort.pid == PrusaMK4.__PRODUCTID: - # return PrusaMK4(serialPort) - # elif serialPort.pid == PrusaMK4S.__PRODUCTID: - # return PrusaMK4S(serialPort) - # elif serialPort.pid == PrusaMK3.__PRODUCTID: - # return PrusaMK3(serialPort) - # else: - # return None - # elif serialPort.vid == EnderPrinter.__VENDORID: - # if serialPort.pid == Ender3.__PRODUCTID: - # return Ender3(serialPort) - # elif serialPort.pid == Ender3Pro.__PRODUCTID: - # return Ender3Pro(serialPort) - def connect(self): try: self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=10) + self.serialConnection.reset_input_buffer() return True except Exception as e: # let the printer parent class deal with the error @@ -71,12 +52,21 @@ def disconnect(self): def home(self): pass - def parseGcode(self): + @abstractmethod + def goTo(self, loc: Vector3): + pass + + def parseGcode(self, file): pass + @abstractmethod def sendGcode(self, gcode: Buffer, checkFunction): pass + @abstractmethod + def getPrintHeadLocation(self) -> Vector3: + pass + def repair(self): pass diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index f269644f..d68525bb 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,130 +1,191 @@ +import serial from flask import current_app +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS + from Classes.Fabricators.Device import Device +from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 +from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro +from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter +from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 +from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 +from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S +from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Queue import Queue -#from models.jobs import Job +from Mixins.canPause import canPause +from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasStartupSequence import hasStartupSequence +from models.jobs import Job from models.db import db from datetime import datetime, timezone -# class Fabricator(db.Model): -# dbID: int = db.Column(db.Integer, primary_key=True) -# description: str = db.Column(db.String(50), nullable=False) -# hwid: str = db.Column(db.String(150), nullable=False) -# name: str = db.Column(db.String(50), nullable=False) -# date: datetime = db.Column( -# db.DateTime, -# default=lambda: datetime.now(timezone.utc).astimezone(), -# nullable=False, -# ) -# devicePort: str = db.Column(db.String(50), nullable=False) -# device: Device = None -# verdict: str = None -# prevMsg: str = None -# #job: Job = None -# queue: Queue = None -# status: str = None - -class Fabricator: - dbID: int = None - description: str = None - hwid: str = None - name: str = None - date: datetime = None - devicePort: None - device: Device = None - verdict: str = None - prevMsg: str = None - # job: Job = None - queue: Queue = None - status: str = None - - - def __init__(self, device: Device, name): - self.device = device - self.name = name - self.description = device.getDescription() - self.hwid = device.getHWID() - self.devicePort = device.getSerialPort().device - self.device.connect() +class Fabricator(db.Model): + dbID = db.Column(db.Integer, primary_key=True) + description = db.Column(db.String(50), nullable=False) + hwid = db.Column(db.String(150), nullable=False) + name = db.Column(db.String(50), nullable=False) + date = db.Column( + db.DateTime, + default=lambda: datetime.now(timezone.utc).astimezone(), + nullable=False, + ) + devicePort = db.Column(db.String(50), nullable=False) + + + def __init__(self, port: ListPortInfo | SysFS, name: str): + assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) + assert isinstance(name, str) + + self.device: Device = Fabricator.createDevice(port) + + self.verdict: str = "" + self.prevMsg: str = "" + self.job: Job | None = None + self.queue: Queue = Queue() + self.status: str = "idle" + + self.name: str = name + self.description = self.device.getDescription() + self.hwid = self.device.getHWID() + self.devicePort = self.device.getSerialPort().device self.date = datetime.now(timezone.utc).astimezone() + + self.device.connect() db.session.add(self) + db.session.commit() + + @staticmethod + def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: + """returns the model of the printer based on the response to M997""" + testName = serial.Serial(serialPort.device, 115200, timeout=10) + testName.write(b"M997\n") + while True: + response = testName.readline() + if b"MACHINE_NAME" in response: + testName.reset_input_buffer() + testName.close() + break + response = response.decode("utf-8") + # while testName.readline() != b'echo:Unknown command: "M420 S1"\n': + # pass + return response + + + @staticmethod + def createDevice(serialPort: ListPortInfo | SysFS | None): + """creates the correct printer object based on the serial port info""" + if serialPort is None: + return None + if serialPort.vid == PrusaPrinter.VENDORID: + if serialPort.pid == PrusaMK4.PRODUCTID: + return PrusaMK4(serialPort) + elif serialPort.pid == PrusaMK4S.PRODUCTID: + return PrusaMK4S(serialPort) + elif serialPort.pid == PrusaMK3.PRODUCTID: + return PrusaMK3(serialPort) + else: + return None + elif serialPort.vid == EnderPrinter.VENDORID: + model = Fabricator.getModelFromGcodeCommand(serialPort) + if "Ender-3 Pro" in model: + return Ender3Pro(serialPort) + elif "Ender-3" in model: + return Ender3(serialPort) + else: + return None + else: + return None @classmethod def queryAll(cls) -> list["Fabricator"]: + """return all printers in the database""" #return cls.query.all() pass - # def print(self): - # # TODO: implement print method - # # TODO: start print - # # TODO: implement loop - # # TODO: check for errors - # # TODO: check for completion - # # TODO: check for cancellation - # # TODO: update progress - # # TODO: update status - # # TODO: end print - # pass - # - # def pausePrint(self): - # if not isinstance(self.__device, canPause): - # return # TODO: return error message - # self.__device.pause() - # self.setStatus("paused") - # #self.__job.setStatus("paused") - # - # def resumePrint(self): - # if not isinstance(self.__device, canPause): - # return #TODO: return error message - # self.__device.resume() - # self.setStatus("printing") - # #self.__job.setStatus("printing") - # - # def cancelPrint(self): - # # TODO: implement cancel print - # pass - # - # def getStatus(self): - # return self.__status - # def setStatus(self, newStatus): - # try: - # print("SETTING STATUS TO:", newStatus) - # if self.__status == "error" and newStatus!= "error": - # self.__device.hardReset(newStatus) - # else: - # self.__status = newStatus - # - # current_app.socketio.emit( - # "status_update", {"printer_id": self.id, "status": newStatus} - # ) - # except Exception as e: - # print("Error setting status:", e) - # - # - # def handleVerdict(self, verdict: str, job): - # if verdict == "complete": - # self.__device.disconnect() - # #self.sendStatusToJob(job, job.id, "complete") - # self.setStatus("complete") - # #self.__job.setStatus("complete") - # elif verdict == "error": - # self.__device.disconnect() - # #self.__queue.deleteJob(job.id, self.id) - # self.setStatus("error") - # #self.sendStatusToJob(job, job.id, "error") - # # self.setError("Error") - # elif verdict == "cancelled": - # if isinstance(self.__device, hasEndingSequence): - # self.__device.endSequence() - # else: - # self.__device.home() - # #self.sendStatusToJob(job, job.id, "cancelled") - # self.setStatus("cancelled") - # #self.__job.setStatus("cancelled") - # self.__device.disconnect() - # elif verdict== "misprint": - # self.setStatus("misprint") - # #self.__job.setStatus("misprint") - # #self.sendStatusToJob(job, job.id, "cancelled") + def begin(self): + assert self.status == "idle" + assert self.queue is not None + assert len(self.queue) > 0 + + self.job = self.queue.getJob(self.id) + + # TODO: test if the file has been sliced for the device of this fabricator + + if issubclass(self.device, hasStartupSequence): + self.device.startupSequence(self) + + self.job = self.queue.getJob(self.id) + if self.job is None: + self.setStatus("idle") + + self.setStatus("printing") + self.device.parseGcode(self.job.file) # this is the actual command to read the file and fabricate. + self.handleVerdict(self.verdict, self.job) + + + + def pause(self): + if not isinstance(self.device, canPause): + return # TODO: return error message + self.device.pause() + self.setStatus("paused") + #self.job.setStatus("paused") + + def resume(self): + if not isinstance(self.device, canPause): + return #TODO: return error message + self.device.resume() + self.setStatus("printing") + #self.job.setStatus("printing") + + def cancel(self): + # TODO: implement cancel print + pass + + def getStatus(self): + return self.status + + def setStatus(self, newStatus): + try: + print("SETTING STATUS TO:", newStatus) + if self.status == "error" and newStatus!= "error": + self.device.hardReset(newStatus) + else: + self.status = newStatus + + current_app.socketio.emit( + "status_update", {"printer_id": self.id, "status": newStatus} + ) + except Exception as e: + print("Error setting status:", e) + + + def handleVerdict(self, verdict: str, job): + if verdict == "complete": + self.device.disconnect() + #self.sendStatusToJob(job, job.id, "complete") + self.setStatus("complete") + #self.job.setStatus("complete") + self.queue.removeJob() + elif verdict == "error": + self.device.disconnect() + #self.queue.deleteJob(job.id, self.id) + self.setStatus("error") + #self.sendStatusToJob(job, job.id, "error") + # self.setError("Error") + elif verdict == "cancelled": + if isinstance(self.device, hasEndingSequence): + self.device.endSequence() + else: + self.device.home() + #self.sendStatusToJob(job, job.id, "cancelled") + self.setStatus("cancelled") + #self.job.setStatus("cancelled") + self.device.disconnect() + elif verdict== "misprint": + self.setStatus("misprint") + #self.job.setStatus("misprint") + #self.sendStatusToJob(job, job.id, "cancelled") def getName(self): return self.name diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3.py b/server/Classes/Fabricators/Printers/Ender/Ender3.py index 66886f57..47cb8875 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3.py @@ -1,9 +1,6 @@ -from abc import ABC - from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter - class Ender3(EnderPrinter): MODEL = "Ender 3" PRODUCTID = 0x7523 - DESCRIPTION = "Ender 3 - CDC" \ No newline at end of file + DESCRIPTION = "Ender 3 - CDC" diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py index bd723231..36c1859e 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py @@ -1,12 +1,6 @@ -from abc import ABC - from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 class Ender3Pro(Ender3): MODEL = "Ender 3 Pro" - DESCRIPTION = "Ender 3 Pro - CDC" - - def home(self): - # TODO: make the home work without crashing the printer - pass \ No newline at end of file + DESCRIPTION = "Ender 3 Pro - CDC" \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 138c2074..f57e1106 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,27 +1,57 @@ from abc import ABCMeta +from typing import Callable +from typing_extensions import Buffer from Classes.Fabricators.Device import Device +from Classes.Vector3 import Vector3 +from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import hasResponsecodes, checkOK, alwaysTrue +from Mixins.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Device, hasEndingSequence, metaclass=ABCMeta): + +class EnderPrinter(Device, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x1A86 + homePosition = Vector3(-3.0,-10.0,0.0) + + def connect(self): + return usesMarlinGcode.connect(self) + + def disconnect(self): + return usesMarlinGcode.disconnect(self) def endSequence(self): - self.gcodeEnding("G91") # Relative positioning - self.gcodeEnding("G1 E-2 F2700") # Retract a bit - self.gcodeEnding("G1 E-2 Z0.2 F2400") # Retract and raise Z - self.gcodeEnding("G1 X5 Y5 F3000") # Wipe out - self.gcodeEnding("G1 Z10") # Raise Z more - self.gcodeEnding("G90") # Absolute positioning - self.gcodeEnding("G1 X0 Y220") # Present print - self.gcodeEnding("M106 S0") # Turn-off fan - self.gcodeEnding("M104 S0") # Turn-off hotend - self.gcodeEnding("M140 S0") # Turn-off bed - self.gcodeEnding("M84 X Y E") # Disable all steppers but Z - - - @classmethod - def __subclasshook__(cls, subclass): - if cls is EnderPrinter: - if any("Ender" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + self.sendGcode(b"G91\n", alwaysTrue) # Relative positioning + self.sendGcode(b"G1 E-2 F2700\n", alwaysTrue) # Retract a bit + self.sendGcode(b"G1 E-2 Z0.2 F2400\n", alwaysTrue) # Retract and raise Z + self.sendGcode(b"G1 X5 Y5 F3000\n", alwaysTrue) # Wipe out + self.sendGcode(b"G1 Z10\n", alwaysTrue) # Raise Z more + self.sendGcode(b"G90\n", alwaysTrue) # Absolute positioning + self.sendGcode(b"G1 X0 Y220\n", alwaysTrue) # Present print + self.sendGcode(b"M106 S0\n", alwaysTrue) # Turn-off fan + self.sendGcode(b"M104 S0\n", alwaysTrue) # Turn-off hotend + self.sendGcode(b"M140 S0\n", alwaysTrue) # Turn-off bed + self.sendGcode(b"M84 X Y E\n", alwaysTrue) # Disable all steppers but Z + + def goTo(self, loc: Vector3): + return usesMarlinGcode.goTo(self, loc) + + def pause(self): + self.sendGcode(usesMarlinGcode.pause, checkOK) + + def resume(self): + self.sendGcode(usesMarlinGcode.resume, checkOK) + + def getPrintTime(self): + pass + + def getPrintHeadLocation(self) -> Vector3: + return usesMarlinGcode.getPrintHeadLocation(self) + + def parseGcode(self, file): + usesMarlinGcode.parseGcode(self, file) + + def sendGcode(self, gcode: Buffer, checkFunction: Callable): + usesMarlinGcode.sendGcode(self, gcode, checkFunction) + + def home(self): + return usesMarlinGcode.home(self) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index c69523d4..f2668677 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -1,8 +1,5 @@ from abc import ABCMeta - -from typing_extensions import Buffer - -from Classes.Fabricators.Device import Device +from typing_extensions import Buffer, Callable from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer from Mixins.canPause import canPause @@ -14,43 +11,21 @@ class PrusaPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x2C99 - def sendGcode(self, gcode: Buffer, checkFunction): - self.serialConnection.write(gcode) - while self.serialConnection.readline() == b'ok\n': - pass - while True: - try: - line = self.serialConnection.readline() - print(line) - if checkFunction(line): - break - except Exception as e: - print(e) - break + def sendGcode(self, gcode: Buffer, checkFunction: Callable): + return usesMarlinGcode.sendGcode(self, gcode, checkFunction) def connect(self): - Device.connect(self) - try: - if self.serialConnection: - self.serialConnection.write(usesMarlinGcode.connect) - return True - except Exception as e: - return e + return usesMarlinGcode.connect(self) def disconnect(self): - if self.serialConnection: - self.serialConnection.write(usesMarlinGcode.disconnect) - self.serialConnection.close() - self.serialConnection = None + usesMarlinGcode.disconnect(self) def home(self): - self.sendGcode(usesMarlinGcode.home, checkOK) - return self.getHomePosition() == self.getPrintHeadLocation() + return usesMarlinGcode.home(self) def goTo(self, loc: Vector3): - self.sendGcode(usesMarlinGcode.goTo(loc), checkXYZ) - return loc == self.getPrintHeadLocation() + return usesMarlinGcode.goTo(self, loc) def pause(self): self.sendGcode(usesMarlinGcode.pause, checkOK) @@ -65,21 +40,4 @@ def getPrintTime(self): pass def getPrintHeadLocation(self) -> Vector3: - self.serialConnection.write("M114\n".encode("utf-8")) - response = "" - while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): - response = self.serialConnection.readline() - print(response) - response = response.decode("utf-8") - x = float(response.split("X:")[1].split(" ")[0]) - y = float(response.split("Y:")[1].split(" ")[0]) - z = float(response.split("Z:")[1].split(" ")[0]) - return Vector3(x,y,z) - - - @classmethod - def __subclasshook__(cls, subclass): - if cls is PrusaPrinter: - if any("Prusa" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): - return True - return NotImplemented + return usesMarlinGcode.getPrintHeadLocation(self) \ No newline at end of file diff --git a/server/Classes/LocationResponse.py b/server/Classes/LocationResponse.py new file mode 100644 index 00000000..d89f4bc6 --- /dev/null +++ b/server/Classes/LocationResponse.py @@ -0,0 +1,11 @@ +import re +class LocationResponse: + def __init__(self, response: str): + loc = [item for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item and item.strip()] + self.x = float(loc[0]) + self.y = float(loc[1]) + self.z = float(loc[2]) + self.e = float(loc[3]) + self.count_x = int(loc[4]) + self.count_y = int(loc[5]) + self.count_z = int(loc[6]) \ No newline at end of file diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 8a3ec72f..788b1e11 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -11,6 +11,7 @@ def getPorts(): @staticmethod def getPortByName(name: str) -> ListPortInfo | SysFS | None: + assert isinstance(name, str) ports = Ports.getPorts() for port in ports: if port.device == name: @@ -19,6 +20,7 @@ def getPortByName(name: str) -> ListPortInfo | SysFS | None: @staticmethod def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: + assert isinstance(hwid, str) ports = Ports.getPorts() for port in ports: if hwid in port.hwid: diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 04b9557b..9ab1f8dc 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -9,6 +9,9 @@ def __init__(self): def __iter__(self): # iterate over queue return iter(self.__queue) + + def __len__(self): + return len(self.__queue) # def setToInQueue(self): # for job in self.__queue: diff --git a/server/Mixins/canPause.py b/server/Mixins/canPause.py index 792cc719..366aa7f8 100644 --- a/server/Mixins/canPause.py +++ b/server/Mixins/canPause.py @@ -7,11 +7,4 @@ def pause(self): @abstractmethod def resume(self): - pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is canPause: - if any("pause" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + pass \ No newline at end of file diff --git a/server/Mixins/hasEndingSequence.py b/server/Mixins/hasEndingSequence.py index 03c80924..5acbdb26 100644 --- a/server/Mixins/hasEndingSequence.py +++ b/server/Mixins/hasEndingSequence.py @@ -4,11 +4,4 @@ class hasEndingSequence(metaclass=ABCMeta): @abstractmethod def endSequence(self): - pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasEndingSequence: - if any("endSequence" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + pass \ No newline at end of file diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 3ca22ca8..3856de5b 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -12,16 +12,11 @@ def getPrintTime(self): def getPrintHeadLocation(self) -> Vector3: pass - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasResponsecodes: - if any("getPrintTime" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented - - def checkOK(line): return line == b'ok\n' def checkXYZ(line): - return ("X:" in line) and ("Y:" in line) and ("Z:" in line) \ No newline at end of file + return ("X:" in line) and ("Y:" in line) and ("Z:" in line) + +def alwaysTrue(line): + return True \ No newline at end of file diff --git a/server/Mixins/hasStartupSequence.py b/server/Mixins/hasStartupSequence.py index f17d1842..ca1abda7 100644 --- a/server/Mixins/hasStartupSequence.py +++ b/server/Mixins/hasStartupSequence.py @@ -4,10 +4,3 @@ class hasStartupSequence(metaclass=ABCMeta): @abstractmethod def startupSequence(self): pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasStartupSequence: - if any("startupSequence" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index c7f7fcac..ad83885a 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -1,18 +1,69 @@ from abc import ABCMeta +from typing import Callable from typing_extensions import Buffer -from Classes.Vector3 import Vector3 +from Classes.Fabricators.Device import Device +from Classes.LocationResponse import LocationResponse +from Classes.Vector3 import Vector3 +from Mixins.hasResponseCodes import checkOK, checkXYZ class usesMarlinGcode(metaclass=ABCMeta): - home: Buffer = "G28\n".encode("utf-8") + homeCMD: Buffer = "G28\n".encode("utf-8") pause: Buffer = "M601\n".encode("utf-8") + "M113 S1\n".encode("utf-8") resume: Buffer = "M602\n".encode("utf-8") cancel: Buffer = "M112\n".encode("utf-8") status: Buffer = "M115\n".encode("utf-8") - disconnect: Buffer = "M155 S0\n".encode("utf-8") + "M84\n".encode("utf-8") - connect: Buffer = "M155 S5 C7\n".encode("utf-8") + getLocation: Buffer = "M114\n".encode("utf-8") + getMachineName: Buffer = "M997\n".encode("utf-8") + + def goTo(self: Device, loc: Vector3): + self.sendGcode(f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8"), checkXYZ) + return loc == self.getPrintHeadLocation() + + def parseGcode(self, file): + with open(file, "r") as f: + for line in f: + self.sendGcode(line.encode("utf-8"), checkOK) + + + def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable): + assert callable(checkFunction) + assert isinstance(gcode, bytes) + self.serialConnection.write(gcode) + while self.serialConnection.readline() == b'ok\n': + pass + while True: + try: + line = self.serialConnection.readline() + if checkFunction(line): + break + except Exception as e: + print(e) + break + + def getPrintHeadLocation(self: Device) -> Vector3: + self.serialConnection.write(usesMarlinGcode.getLocation) + response = "" + while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): + response = self.serialConnection.readline().decode("utf-8") + loc = LocationResponse(response) + return Vector3(loc.x, loc.y, loc.z) + + def home(self: Device): + self.sendGcode("G28\n".encode("utf-8"), checkOK) + return self.getHomePosition() == self.getPrintHeadLocation() + + def connect(self: Device): + Device.connect(self) + try: + if self.serialConnection: + self.serialConnection.write("M155 S5 C7\n".encode("utf-8")) + return True + except Exception as e: + return e - @staticmethod - def goTo(loc: Vector3) -> Buffer: - return f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8") \ No newline at end of file + def disconnect(self: Device): + if self.serialConnection: + self.serialConnection.write("M155 S0\n".encode("utf-8") + "M84\n".encode("utf-8")) + self.serialConnection.close() \ No newline at end of file diff --git a/server/models/issues.py b/server/models/issues.py index 2ebea67e..e6216595 100644 --- a/server/models/issues.py +++ b/server/models/issues.py @@ -1,20 +1,7 @@ -import asyncio -import base64 -from operator import or_ -import os -import re from models.db import db -from datetime import datetime, timezone, timedelta -from sqlalchemy import Column, String, LargeBinary, DateTime, ForeignKey -from sqlalchemy.orm import relationship -from flask import jsonify, current_app +from flask import jsonify from sqlalchemy.exc import SQLAlchemyError -from datetime import datetime -from tzlocal import get_localzone -from io import BytesIO -from werkzeug.datastructures import FileStorage -import time -import gzip + class Issue(db.Model): id = db.Column(db.Integer, primary_key=True) From 6a6f767b57ca4e62bc66026881826ee13900bee6 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 10 Oct 2024 21:37:18 -0400 Subject: [PATCH 016/194] refactored a bunch of the methods to usesMarlinGcode.py, because then it doesn't need to be copied in prusa and ender. --- server/Classes/FabricatorList.py | 8 +- server/Classes/Fabricators/Device.py | 32 +- server/Classes/Fabricators/Fabricator.py | 287 +++++++++++------- .../Fabricators/Printers/Ender/Ender3.py | 5 +- .../Fabricators/Printers/Ender/Ender3Pro.py | 8 +- .../Printers/Ender/EnderPrinter.py | 70 +++-- .../Fabricators/Printers/Prusa/PrusaMK4.py | 7 +- .../Printers/Prusa/PrusaPrinter.py | 58 +--- server/Classes/LocationResponse.py | 11 + server/Classes/Ports.py | 2 + server/Classes/Queue.py | 3 + server/Mixins/canPause.py | 9 +- server/Mixins/hasEndingSequence.py | 9 +- server/Mixins/hasResponseCodes.py | 13 +- server/Mixins/hasStartupSequence.py | 7 - server/Mixins/usesMarlinGcode.py | 65 +++- server/models/issues.py | 17 +- 17 files changed, 335 insertions(+), 276 deletions(-) create mode 100644 server/Classes/LocationResponse.py diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 56cffadd..91873231 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -11,6 +11,10 @@ class FabricatorList(): def __iter__(): return iter(FabricatorList.fabricators) + @staticmethod + def __len__(): + return len(FabricatorList.fabricators) + @staticmethod def addFabricator(serialPortName: str, name: str): """add a printer to the list, and to the database""" @@ -51,10 +55,6 @@ def deleteFabricator(printerid): # 500, # ) @staticmethod - def getFabricatorCount(): - return len(FabricatorList.fabricators) - - @staticmethod def getFabricatorByName(name) -> Fabricator | None: """find the first printer with the given name""" for fabricator in FabricatorList.__iter__(): diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 3a0bc089..21b396c6 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -33,30 +33,11 @@ def __init__(self, serialPort: ListPortInfo | SysFS): def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" - # @staticmethod - # def createDevice(serialPort: ListPortInfo | SysFS | None): - # """creates the correct printer object based on the serial port info""" - # if serialPort is None: - # return None - # if serialPort.vid == PrusaPrinter.__VENDORID: - # if serialPort.pid == PrusaMK4.__PRODUCTID: - # return PrusaMK4(serialPort) - # elif serialPort.pid == PrusaMK4S.__PRODUCTID: - # return PrusaMK4S(serialPort) - # elif serialPort.pid == PrusaMK3.__PRODUCTID: - # return PrusaMK3(serialPort) - # else: - # return None - # elif serialPort.vid == EnderPrinter.__VENDORID: - # if serialPort.pid == Ender3.__PRODUCTID: - # return Ender3(serialPort) - # elif serialPort.pid == Ender3Pro.__PRODUCTID: - # return Ender3Pro(serialPort) - def connect(self): try: self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=10) + self.serialConnection.reset_input_buffer() return True except Exception as e: # let the printer parent class deal with the error @@ -71,12 +52,21 @@ def disconnect(self): def home(self): pass - def parseGcode(self): + @abstractmethod + def goTo(self, loc: Vector3): + pass + + def parseGcode(self, file): pass + @abstractmethod def sendGcode(self, gcode: Buffer, checkFunction): pass + @abstractmethod + def getPrintHeadLocation(self) -> Vector3: + pass + def repair(self): pass diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index f269644f..d68525bb 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,130 +1,191 @@ +import serial from flask import current_app +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS + from Classes.Fabricators.Device import Device +from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 +from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro +from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter +from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 +from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 +from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S +from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Queue import Queue -#from models.jobs import Job +from Mixins.canPause import canPause +from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasStartupSequence import hasStartupSequence +from models.jobs import Job from models.db import db from datetime import datetime, timezone -# class Fabricator(db.Model): -# dbID: int = db.Column(db.Integer, primary_key=True) -# description: str = db.Column(db.String(50), nullable=False) -# hwid: str = db.Column(db.String(150), nullable=False) -# name: str = db.Column(db.String(50), nullable=False) -# date: datetime = db.Column( -# db.DateTime, -# default=lambda: datetime.now(timezone.utc).astimezone(), -# nullable=False, -# ) -# devicePort: str = db.Column(db.String(50), nullable=False) -# device: Device = None -# verdict: str = None -# prevMsg: str = None -# #job: Job = None -# queue: Queue = None -# status: str = None - -class Fabricator: - dbID: int = None - description: str = None - hwid: str = None - name: str = None - date: datetime = None - devicePort: None - device: Device = None - verdict: str = None - prevMsg: str = None - # job: Job = None - queue: Queue = None - status: str = None - - - def __init__(self, device: Device, name): - self.device = device - self.name = name - self.description = device.getDescription() - self.hwid = device.getHWID() - self.devicePort = device.getSerialPort().device - self.device.connect() +class Fabricator(db.Model): + dbID = db.Column(db.Integer, primary_key=True) + description = db.Column(db.String(50), nullable=False) + hwid = db.Column(db.String(150), nullable=False) + name = db.Column(db.String(50), nullable=False) + date = db.Column( + db.DateTime, + default=lambda: datetime.now(timezone.utc).astimezone(), + nullable=False, + ) + devicePort = db.Column(db.String(50), nullable=False) + + + def __init__(self, port: ListPortInfo | SysFS, name: str): + assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) + assert isinstance(name, str) + + self.device: Device = Fabricator.createDevice(port) + + self.verdict: str = "" + self.prevMsg: str = "" + self.job: Job | None = None + self.queue: Queue = Queue() + self.status: str = "idle" + + self.name: str = name + self.description = self.device.getDescription() + self.hwid = self.device.getHWID() + self.devicePort = self.device.getSerialPort().device self.date = datetime.now(timezone.utc).astimezone() + + self.device.connect() db.session.add(self) + db.session.commit() + + @staticmethod + def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: + """returns the model of the printer based on the response to M997""" + testName = serial.Serial(serialPort.device, 115200, timeout=10) + testName.write(b"M997\n") + while True: + response = testName.readline() + if b"MACHINE_NAME" in response: + testName.reset_input_buffer() + testName.close() + break + response = response.decode("utf-8") + # while testName.readline() != b'echo:Unknown command: "M420 S1"\n': + # pass + return response + + + @staticmethod + def createDevice(serialPort: ListPortInfo | SysFS | None): + """creates the correct printer object based on the serial port info""" + if serialPort is None: + return None + if serialPort.vid == PrusaPrinter.VENDORID: + if serialPort.pid == PrusaMK4.PRODUCTID: + return PrusaMK4(serialPort) + elif serialPort.pid == PrusaMK4S.PRODUCTID: + return PrusaMK4S(serialPort) + elif serialPort.pid == PrusaMK3.PRODUCTID: + return PrusaMK3(serialPort) + else: + return None + elif serialPort.vid == EnderPrinter.VENDORID: + model = Fabricator.getModelFromGcodeCommand(serialPort) + if "Ender-3 Pro" in model: + return Ender3Pro(serialPort) + elif "Ender-3" in model: + return Ender3(serialPort) + else: + return None + else: + return None @classmethod def queryAll(cls) -> list["Fabricator"]: + """return all printers in the database""" #return cls.query.all() pass - # def print(self): - # # TODO: implement print method - # # TODO: start print - # # TODO: implement loop - # # TODO: check for errors - # # TODO: check for completion - # # TODO: check for cancellation - # # TODO: update progress - # # TODO: update status - # # TODO: end print - # pass - # - # def pausePrint(self): - # if not isinstance(self.__device, canPause): - # return # TODO: return error message - # self.__device.pause() - # self.setStatus("paused") - # #self.__job.setStatus("paused") - # - # def resumePrint(self): - # if not isinstance(self.__device, canPause): - # return #TODO: return error message - # self.__device.resume() - # self.setStatus("printing") - # #self.__job.setStatus("printing") - # - # def cancelPrint(self): - # # TODO: implement cancel print - # pass - # - # def getStatus(self): - # return self.__status - # def setStatus(self, newStatus): - # try: - # print("SETTING STATUS TO:", newStatus) - # if self.__status == "error" and newStatus!= "error": - # self.__device.hardReset(newStatus) - # else: - # self.__status = newStatus - # - # current_app.socketio.emit( - # "status_update", {"printer_id": self.id, "status": newStatus} - # ) - # except Exception as e: - # print("Error setting status:", e) - # - # - # def handleVerdict(self, verdict: str, job): - # if verdict == "complete": - # self.__device.disconnect() - # #self.sendStatusToJob(job, job.id, "complete") - # self.setStatus("complete") - # #self.__job.setStatus("complete") - # elif verdict == "error": - # self.__device.disconnect() - # #self.__queue.deleteJob(job.id, self.id) - # self.setStatus("error") - # #self.sendStatusToJob(job, job.id, "error") - # # self.setError("Error") - # elif verdict == "cancelled": - # if isinstance(self.__device, hasEndingSequence): - # self.__device.endSequence() - # else: - # self.__device.home() - # #self.sendStatusToJob(job, job.id, "cancelled") - # self.setStatus("cancelled") - # #self.__job.setStatus("cancelled") - # self.__device.disconnect() - # elif verdict== "misprint": - # self.setStatus("misprint") - # #self.__job.setStatus("misprint") - # #self.sendStatusToJob(job, job.id, "cancelled") + def begin(self): + assert self.status == "idle" + assert self.queue is not None + assert len(self.queue) > 0 + + self.job = self.queue.getJob(self.id) + + # TODO: test if the file has been sliced for the device of this fabricator + + if issubclass(self.device, hasStartupSequence): + self.device.startupSequence(self) + + self.job = self.queue.getJob(self.id) + if self.job is None: + self.setStatus("idle") + + self.setStatus("printing") + self.device.parseGcode(self.job.file) # this is the actual command to read the file and fabricate. + self.handleVerdict(self.verdict, self.job) + + + + def pause(self): + if not isinstance(self.device, canPause): + return # TODO: return error message + self.device.pause() + self.setStatus("paused") + #self.job.setStatus("paused") + + def resume(self): + if not isinstance(self.device, canPause): + return #TODO: return error message + self.device.resume() + self.setStatus("printing") + #self.job.setStatus("printing") + + def cancel(self): + # TODO: implement cancel print + pass + + def getStatus(self): + return self.status + + def setStatus(self, newStatus): + try: + print("SETTING STATUS TO:", newStatus) + if self.status == "error" and newStatus!= "error": + self.device.hardReset(newStatus) + else: + self.status = newStatus + + current_app.socketio.emit( + "status_update", {"printer_id": self.id, "status": newStatus} + ) + except Exception as e: + print("Error setting status:", e) + + + def handleVerdict(self, verdict: str, job): + if verdict == "complete": + self.device.disconnect() + #self.sendStatusToJob(job, job.id, "complete") + self.setStatus("complete") + #self.job.setStatus("complete") + self.queue.removeJob() + elif verdict == "error": + self.device.disconnect() + #self.queue.deleteJob(job.id, self.id) + self.setStatus("error") + #self.sendStatusToJob(job, job.id, "error") + # self.setError("Error") + elif verdict == "cancelled": + if isinstance(self.device, hasEndingSequence): + self.device.endSequence() + else: + self.device.home() + #self.sendStatusToJob(job, job.id, "cancelled") + self.setStatus("cancelled") + #self.job.setStatus("cancelled") + self.device.disconnect() + elif verdict== "misprint": + self.setStatus("misprint") + #self.job.setStatus("misprint") + #self.sendStatusToJob(job, job.id, "cancelled") def getName(self): return self.name diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3.py b/server/Classes/Fabricators/Printers/Ender/Ender3.py index 66886f57..47cb8875 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3.py @@ -1,9 +1,6 @@ -from abc import ABC - from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter - class Ender3(EnderPrinter): MODEL = "Ender 3" PRODUCTID = 0x7523 - DESCRIPTION = "Ender 3 - CDC" \ No newline at end of file + DESCRIPTION = "Ender 3 - CDC" diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py index bd723231..36c1859e 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py @@ -1,12 +1,6 @@ -from abc import ABC - from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 class Ender3Pro(Ender3): MODEL = "Ender 3 Pro" - DESCRIPTION = "Ender 3 Pro - CDC" - - def home(self): - # TODO: make the home work without crashing the printer - pass \ No newline at end of file + DESCRIPTION = "Ender 3 Pro - CDC" \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 138c2074..f57e1106 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,27 +1,57 @@ from abc import ABCMeta +from typing import Callable +from typing_extensions import Buffer from Classes.Fabricators.Device import Device +from Classes.Vector3 import Vector3 +from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import hasResponsecodes, checkOK, alwaysTrue +from Mixins.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Device, hasEndingSequence, metaclass=ABCMeta): + +class EnderPrinter(Device, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x1A86 + homePosition = Vector3(-3.0,-10.0,0.0) + + def connect(self): + return usesMarlinGcode.connect(self) + + def disconnect(self): + return usesMarlinGcode.disconnect(self) def endSequence(self): - self.gcodeEnding("G91") # Relative positioning - self.gcodeEnding("G1 E-2 F2700") # Retract a bit - self.gcodeEnding("G1 E-2 Z0.2 F2400") # Retract and raise Z - self.gcodeEnding("G1 X5 Y5 F3000") # Wipe out - self.gcodeEnding("G1 Z10") # Raise Z more - self.gcodeEnding("G90") # Absolute positioning - self.gcodeEnding("G1 X0 Y220") # Present print - self.gcodeEnding("M106 S0") # Turn-off fan - self.gcodeEnding("M104 S0") # Turn-off hotend - self.gcodeEnding("M140 S0") # Turn-off bed - self.gcodeEnding("M84 X Y E") # Disable all steppers but Z - - - @classmethod - def __subclasshook__(cls, subclass): - if cls is EnderPrinter: - if any("Ender" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + self.sendGcode(b"G91\n", alwaysTrue) # Relative positioning + self.sendGcode(b"G1 E-2 F2700\n", alwaysTrue) # Retract a bit + self.sendGcode(b"G1 E-2 Z0.2 F2400\n", alwaysTrue) # Retract and raise Z + self.sendGcode(b"G1 X5 Y5 F3000\n", alwaysTrue) # Wipe out + self.sendGcode(b"G1 Z10\n", alwaysTrue) # Raise Z more + self.sendGcode(b"G90\n", alwaysTrue) # Absolute positioning + self.sendGcode(b"G1 X0 Y220\n", alwaysTrue) # Present print + self.sendGcode(b"M106 S0\n", alwaysTrue) # Turn-off fan + self.sendGcode(b"M104 S0\n", alwaysTrue) # Turn-off hotend + self.sendGcode(b"M140 S0\n", alwaysTrue) # Turn-off bed + self.sendGcode(b"M84 X Y E\n", alwaysTrue) # Disable all steppers but Z + + def goTo(self, loc: Vector3): + return usesMarlinGcode.goTo(self, loc) + + def pause(self): + self.sendGcode(usesMarlinGcode.pause, checkOK) + + def resume(self): + self.sendGcode(usesMarlinGcode.resume, checkOK) + + def getPrintTime(self): + pass + + def getPrintHeadLocation(self) -> Vector3: + return usesMarlinGcode.getPrintHeadLocation(self) + + def parseGcode(self, file): + usesMarlinGcode.parseGcode(self, file) + + def sendGcode(self, gcode: Buffer, checkFunction: Callable): + usesMarlinGcode.sendGcode(self, gcode, checkFunction) + + def home(self): + return usesMarlinGcode.home(self) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index 1ca7dce7..5dc19995 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -1,5 +1,6 @@ from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 +from Mixins.hasResponseCodes import alwaysTrue class PrusaMK4(PrusaPrinter): @@ -10,7 +11,7 @@ class PrusaMK4(PrusaPrinter): def endSequence(self): # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") - self.gcodeEnding("M104 S0") # ; turn off temperature - self.gcodeEnding("M140 S0") # ; turn off heatbed - self.gcodeEnding("M107") # ; turn off fan + self.sendGcode(b"M104 S0\n", alwaysTrue) # ; turn off temperature + self.sendGcode(b"M140 S0\n", alwaysTrue) # ; turn off heatbed + self.sendGcode(b"M107\n", alwaysTrue) # ; turn off fan diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index c69523d4..f2668677 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -1,8 +1,5 @@ from abc import ABCMeta - -from typing_extensions import Buffer - -from Classes.Fabricators.Device import Device +from typing_extensions import Buffer, Callable from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer from Mixins.canPause import canPause @@ -14,43 +11,21 @@ class PrusaPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x2C99 - def sendGcode(self, gcode: Buffer, checkFunction): - self.serialConnection.write(gcode) - while self.serialConnection.readline() == b'ok\n': - pass - while True: - try: - line = self.serialConnection.readline() - print(line) - if checkFunction(line): - break - except Exception as e: - print(e) - break + def sendGcode(self, gcode: Buffer, checkFunction: Callable): + return usesMarlinGcode.sendGcode(self, gcode, checkFunction) def connect(self): - Device.connect(self) - try: - if self.serialConnection: - self.serialConnection.write(usesMarlinGcode.connect) - return True - except Exception as e: - return e + return usesMarlinGcode.connect(self) def disconnect(self): - if self.serialConnection: - self.serialConnection.write(usesMarlinGcode.disconnect) - self.serialConnection.close() - self.serialConnection = None + usesMarlinGcode.disconnect(self) def home(self): - self.sendGcode(usesMarlinGcode.home, checkOK) - return self.getHomePosition() == self.getPrintHeadLocation() + return usesMarlinGcode.home(self) def goTo(self, loc: Vector3): - self.sendGcode(usesMarlinGcode.goTo(loc), checkXYZ) - return loc == self.getPrintHeadLocation() + return usesMarlinGcode.goTo(self, loc) def pause(self): self.sendGcode(usesMarlinGcode.pause, checkOK) @@ -65,21 +40,4 @@ def getPrintTime(self): pass def getPrintHeadLocation(self) -> Vector3: - self.serialConnection.write("M114\n".encode("utf-8")) - response = "" - while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): - response = self.serialConnection.readline() - print(response) - response = response.decode("utf-8") - x = float(response.split("X:")[1].split(" ")[0]) - y = float(response.split("Y:")[1].split(" ")[0]) - z = float(response.split("Z:")[1].split(" ")[0]) - return Vector3(x,y,z) - - - @classmethod - def __subclasshook__(cls, subclass): - if cls is PrusaPrinter: - if any("Prusa" in B.__dict__.get('__DESCRIPTION__', '') for B in subclass.__mro__): - return True - return NotImplemented + return usesMarlinGcode.getPrintHeadLocation(self) \ No newline at end of file diff --git a/server/Classes/LocationResponse.py b/server/Classes/LocationResponse.py new file mode 100644 index 00000000..d89f4bc6 --- /dev/null +++ b/server/Classes/LocationResponse.py @@ -0,0 +1,11 @@ +import re +class LocationResponse: + def __init__(self, response: str): + loc = [item for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item and item.strip()] + self.x = float(loc[0]) + self.y = float(loc[1]) + self.z = float(loc[2]) + self.e = float(loc[3]) + self.count_x = int(loc[4]) + self.count_y = int(loc[5]) + self.count_z = int(loc[6]) \ No newline at end of file diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 8a3ec72f..788b1e11 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -11,6 +11,7 @@ def getPorts(): @staticmethod def getPortByName(name: str) -> ListPortInfo | SysFS | None: + assert isinstance(name, str) ports = Ports.getPorts() for port in ports: if port.device == name: @@ -19,6 +20,7 @@ def getPortByName(name: str) -> ListPortInfo | SysFS | None: @staticmethod def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: + assert isinstance(hwid, str) ports = Ports.getPorts() for port in ports: if hwid in port.hwid: diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 04b9557b..9ab1f8dc 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -9,6 +9,9 @@ def __init__(self): def __iter__(self): # iterate over queue return iter(self.__queue) + + def __len__(self): + return len(self.__queue) # def setToInQueue(self): # for job in self.__queue: diff --git a/server/Mixins/canPause.py b/server/Mixins/canPause.py index 792cc719..366aa7f8 100644 --- a/server/Mixins/canPause.py +++ b/server/Mixins/canPause.py @@ -7,11 +7,4 @@ def pause(self): @abstractmethod def resume(self): - pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is canPause: - if any("pause" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + pass \ No newline at end of file diff --git a/server/Mixins/hasEndingSequence.py b/server/Mixins/hasEndingSequence.py index 03c80924..5acbdb26 100644 --- a/server/Mixins/hasEndingSequence.py +++ b/server/Mixins/hasEndingSequence.py @@ -4,11 +4,4 @@ class hasEndingSequence(metaclass=ABCMeta): @abstractmethod def endSequence(self): - pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasEndingSequence: - if any("endSequence" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file + pass \ No newline at end of file diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 3ca22ca8..3856de5b 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -12,16 +12,11 @@ def getPrintTime(self): def getPrintHeadLocation(self) -> Vector3: pass - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasResponsecodes: - if any("getPrintTime" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented - - def checkOK(line): return line == b'ok\n' def checkXYZ(line): - return ("X:" in line) and ("Y:" in line) and ("Z:" in line) \ No newline at end of file + return ("X:" in line) and ("Y:" in line) and ("Z:" in line) + +def alwaysTrue(line): + return True \ No newline at end of file diff --git a/server/Mixins/hasStartupSequence.py b/server/Mixins/hasStartupSequence.py index f17d1842..ca1abda7 100644 --- a/server/Mixins/hasStartupSequence.py +++ b/server/Mixins/hasStartupSequence.py @@ -4,10 +4,3 @@ class hasStartupSequence(metaclass=ABCMeta): @abstractmethod def startupSequence(self): pass - - @classmethod - def __subclasshook__(cls, subclass): - if cls is hasStartupSequence: - if any("startupSequence" in B.__dict__ for B in subclass.__mro__): - return True - return NotImplemented \ No newline at end of file diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index c7f7fcac..ad83885a 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -1,18 +1,69 @@ from abc import ABCMeta +from typing import Callable from typing_extensions import Buffer -from Classes.Vector3 import Vector3 +from Classes.Fabricators.Device import Device +from Classes.LocationResponse import LocationResponse +from Classes.Vector3 import Vector3 +from Mixins.hasResponseCodes import checkOK, checkXYZ class usesMarlinGcode(metaclass=ABCMeta): - home: Buffer = "G28\n".encode("utf-8") + homeCMD: Buffer = "G28\n".encode("utf-8") pause: Buffer = "M601\n".encode("utf-8") + "M113 S1\n".encode("utf-8") resume: Buffer = "M602\n".encode("utf-8") cancel: Buffer = "M112\n".encode("utf-8") status: Buffer = "M115\n".encode("utf-8") - disconnect: Buffer = "M155 S0\n".encode("utf-8") + "M84\n".encode("utf-8") - connect: Buffer = "M155 S5 C7\n".encode("utf-8") + getLocation: Buffer = "M114\n".encode("utf-8") + getMachineName: Buffer = "M997\n".encode("utf-8") + + def goTo(self: Device, loc: Vector3): + self.sendGcode(f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8"), checkXYZ) + return loc == self.getPrintHeadLocation() + + def parseGcode(self, file): + with open(file, "r") as f: + for line in f: + self.sendGcode(line.encode("utf-8"), checkOK) + + + def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable): + assert callable(checkFunction) + assert isinstance(gcode, bytes) + self.serialConnection.write(gcode) + while self.serialConnection.readline() == b'ok\n': + pass + while True: + try: + line = self.serialConnection.readline() + if checkFunction(line): + break + except Exception as e: + print(e) + break + + def getPrintHeadLocation(self: Device) -> Vector3: + self.serialConnection.write(usesMarlinGcode.getLocation) + response = "" + while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): + response = self.serialConnection.readline().decode("utf-8") + loc = LocationResponse(response) + return Vector3(loc.x, loc.y, loc.z) + + def home(self: Device): + self.sendGcode("G28\n".encode("utf-8"), checkOK) + return self.getHomePosition() == self.getPrintHeadLocation() + + def connect(self: Device): + Device.connect(self) + try: + if self.serialConnection: + self.serialConnection.write("M155 S5 C7\n".encode("utf-8")) + return True + except Exception as e: + return e - @staticmethod - def goTo(loc: Vector3) -> Buffer: - return f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8") \ No newline at end of file + def disconnect(self: Device): + if self.serialConnection: + self.serialConnection.write("M155 S0\n".encode("utf-8") + "M84\n".encode("utf-8")) + self.serialConnection.close() \ No newline at end of file diff --git a/server/models/issues.py b/server/models/issues.py index 2ebea67e..e6216595 100644 --- a/server/models/issues.py +++ b/server/models/issues.py @@ -1,20 +1,7 @@ -import asyncio -import base64 -from operator import or_ -import os -import re from models.db import db -from datetime import datetime, timezone, timedelta -from sqlalchemy import Column, String, LargeBinary, DateTime, ForeignKey -from sqlalchemy.orm import relationship -from flask import jsonify, current_app +from flask import jsonify from sqlalchemy.exc import SQLAlchemyError -from datetime import datetime -from tzlocal import get_localzone -from io import BytesIO -from werkzeug.datastructures import FileStorage -import time -import gzip + class Issue(db.Model): id = db.Column(db.Integer, primary_key=True) From a815b8109ffe713a19d8739698f461efe65dddd2 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 14 Oct 2024 11:24:45 -0400 Subject: [PATCH 017/194] Device.uml added to git --- server/Classes/Device.uml | 118 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 server/Classes/Device.uml diff --git a/server/Classes/Device.uml b/server/Classes/Device.uml new file mode 100644 index 00000000..0ff374da --- /dev/null +++ b/server/Classes/Device.uml @@ -0,0 +1,118 @@ + + + Python + #C:/Users/Ari Yeger/Desktop/school/F24/Projects/QView3D/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py + + Mixins.canPause.canPause + Classes.Fabricators.Printers.Prusa.PrusaMK4.PrusaMK4 + Classes.Fabricators.Device.Device + Classes.Fabricators.Printers.Prusa.PrusaPrinter.PrusaPrinter + Classes.Fabricators.Printers.Printer.Printer + Classes.Fabricators.Printers.Prusa.PrusaMK4S.PrusaMK4S + Classes.FabricatorList.FabricatorList + Mixins.hasResponseCodes.hasResponsecodes + Mixins.hasEndingSequence.hasEndingSequence + Classes.Fabricators.Printers.Ender.EnderPrinter.EnderPrinter + Classes.Fabricators.Printers.Prusa.PrusaMK3.PrusaMK3 + Classes.Fabricators.Printers.Ender.Ender3.Ender3 + Mixins.usesMarlinGcode.usesMarlinGcode + Classes.Fabricators.Printers.Ender.Ender3Pro.Ender3Pro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Classes.FabricatorList.FabricatorList + + + + From 6877ba0d91b638544ccd5f45e14eb1cddd9424ce Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 14 Oct 2024 11:41:07 -0400 Subject: [PATCH 018/194] test lvl 1 for nate --- server/test.py | 70 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/server/test.py b/server/test.py index d940482a..92b04056 100644 --- a/server/test.py +++ b/server/test.py @@ -1,18 +1,56 @@ -import serial.tools.list_ports +from Classes.FabricatorList import FabricatorList +from Classes.Fabricators.Fabricator import Fabricator from Classes.Vector3 import Vector3 +from Classes.Ports import Ports +from app import app -homeLocation = Vector3(14.0,-4.0,2.0) - -printer = serial.Serial("ttyACM0", 115200, timeout=10) -printer.write("G28\n".encode("utf-8")) -printer.write("M114\n".encode("utf-8")) -while True: - line = printer.readline() - try: - print(line) - if homeLocation.__repr__() in line.decode("utf-8"): - break - except KeyboardInterrupt: - break -printer.write("M84\n".encode("utf-8")) -printer.close() \ No newline at end of file +red = '\033[31m' +green = '\033[32m' +yellow = '\033[33m' +blue = '\033[34m' +magenta = '\033[35m' +cyan = '\033[36m' +reset = '\033[0m' + +def printColor(color, message): + print(color + message + reset) + +def setupTest(): + pass + +def test(printer: Fabricator): + print(f"Testing {printer.getDescription()}") + if printer.device.home(): + printColor(green, "Homed") + else: + printColor(red, "Failed to home") + + if printer.device.goTo(endLocation): + printColor(green, "Moved to end location") + else: + printColor(red, "Failed to move to end location") + + printer.device.disconnect() + +def teardownTest(): + pass + +endLocation = Vector3(125.0,100.0,2.0) +with app.app_context(): + # home prusa mk4s + # FabricatorList.init() + # FabricatorList.addFabricator("COM5") + # HomePrinter = FabricatorList.fabricators[0].setName("Home Printer") + #test(HomePrinter) + + # nate's Ender 3 Pro + EnderPro = Fabricator(Ports.getPortByName("COM5"), "Ender 3 Pro") + test(EnderPro) + + # school prusa mk4 + # PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4") + # test(PrusaMK4) + + # school Ender 3 + # Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3") + # test(Ender3) \ No newline at end of file From 0f01a8cd7adafb6447dfe079caa78c93d9fa84db Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 20 Oct 2024 13:48:08 -0400 Subject: [PATCH 019/194] added cnc and laser for future use --- server/Classes/Fabricators/CNCMachines/CNCMachine.py | 12 ++++++++++++ .../Classes/Fabricators/LaserCutters/LaserCutter.py | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 server/Classes/Fabricators/CNCMachines/CNCMachine.py create mode 100644 server/Classes/Fabricators/LaserCutters/LaserCutter.py diff --git a/server/Classes/Fabricators/CNCMachines/CNCMachine.py b/server/Classes/Fabricators/CNCMachines/CNCMachine.py new file mode 100644 index 00000000..631bd0d3 --- /dev/null +++ b/server/Classes/Fabricators/CNCMachines/CNCMachine.py @@ -0,0 +1,12 @@ +from abc import ABCMeta +from Classes.Fabricators.Device import Device + +class CNCMachine(Device, metaclass=ABCMeta): + toolDiameter: float | None = None + toolType: str | None = None + spindleSpeed: int | None = None + feedRate: int | None = None + depthOfCut: float | None = None + + def changeTool(self): + pass \ No newline at end of file diff --git a/server/Classes/Fabricators/LaserCutters/LaserCutter.py b/server/Classes/Fabricators/LaserCutters/LaserCutter.py new file mode 100644 index 00000000..6af6af09 --- /dev/null +++ b/server/Classes/Fabricators/LaserCutters/LaserCutter.py @@ -0,0 +1,10 @@ +from abc import ABCMeta +from Classes.Fabricators.Device import Device + +class LaserCutter(Device, metaclass=ABCMeta): + laserPower: int | None = None + feedRate: int | None = None + focusHeight: float | None = None + + def changeFocusHeight(self): + pass \ No newline at end of file From e6d97f8dedcb536bde0dc9e15d7e66303c5693c3 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 20 Oct 2024 13:50:34 -0400 Subject: [PATCH 020/194] added a verbose option for debugging --- server/Classes/Fabricators/Device.py | 6 +++--- .../Fabricators/Printers/Ender/EnderPrinter.py | 16 ++++++++-------- .../Fabricators/Printers/Prusa/PrusaPrinter.py | 14 +++++++------- server/Mixins/usesMarlinGcode.py | 14 ++++++++------ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 21b396c6..023ad47f 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -49,18 +49,18 @@ def disconnect(self): self.serialConnection = None @abstractmethod - def home(self): + def home(self, isVerbose: bool = False): pass @abstractmethod - def goTo(self, loc: Vector3): + def goTo(self, loc: Vector3, isVerbose: bool = False): pass def parseGcode(self, file): pass @abstractmethod - def sendGcode(self, gcode: Buffer, checkFunction): + def sendGcode(self, gcode: Buffer, checkFunction, isVerbose: bool = False): pass @abstractmethod diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index f57e1106..0f44913f 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,7 +1,7 @@ from abc import ABCMeta from typing import Callable from typing_extensions import Buffer -from Classes.Fabricators.Device import Device +from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence @@ -9,7 +9,7 @@ from Mixins.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Device, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): +class EnderPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x1A86 homePosition = Vector3(-3.0,-10.0,0.0) @@ -32,8 +32,8 @@ def endSequence(self): self.sendGcode(b"M140 S0\n", alwaysTrue) # Turn-off bed self.sendGcode(b"M84 X Y E\n", alwaysTrue) # Disable all steppers but Z - def goTo(self, loc: Vector3): - return usesMarlinGcode.goTo(self, loc) + def goTo(self, loc: Vector3, isVerbose: bool = False): + return usesMarlinGcode.goTo(self, loc, isVerbose) def pause(self): self.sendGcode(usesMarlinGcode.pause, checkOK) @@ -50,8 +50,8 @@ def getPrintHeadLocation(self) -> Vector3: def parseGcode(self, file): usesMarlinGcode.parseGcode(self, file) - def sendGcode(self, gcode: Buffer, checkFunction: Callable): - usesMarlinGcode.sendGcode(self, gcode, checkFunction) + def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): + usesMarlinGcode.sendGcode(self, gcode, checkFunction, isVerbose) - def home(self): - return usesMarlinGcode.home(self) \ No newline at end of file + def home(self, isVerbose: bool = False): + return usesMarlinGcode.home(self, isVerbose) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index f2668677..f3453e04 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -4,7 +4,7 @@ from Classes.Fabricators.Printers.Printer import Printer from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import hasResponsecodes, checkXYZ, checkOK +from Mixins.hasResponseCodes import hasResponsecodes, checkOK from Mixins.usesMarlinGcode import usesMarlinGcode @@ -12,8 +12,8 @@ class PrusaPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesM VENDORID = 0x2C99 - def sendGcode(self, gcode: Buffer, checkFunction: Callable): - return usesMarlinGcode.sendGcode(self, gcode, checkFunction) + def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): + return usesMarlinGcode.sendGcode(self, gcode, checkFunction, isVerbose) def connect(self): return usesMarlinGcode.connect(self) @@ -21,11 +21,11 @@ def connect(self): def disconnect(self): usesMarlinGcode.disconnect(self) - def home(self): - return usesMarlinGcode.home(self) + def home(self, isVerbose: bool = False): + return usesMarlinGcode.home(self, isVerbose) - def goTo(self, loc: Vector3): - return usesMarlinGcode.goTo(self, loc) + def goTo(self, loc: Vector3, isVerbose: bool = False): + return usesMarlinGcode.goTo(self, loc, isVerbose) def pause(self): self.sendGcode(usesMarlinGcode.pause, checkOK) diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index ad83885a..7ea67bf8 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -17,8 +17,8 @@ class usesMarlinGcode(metaclass=ABCMeta): getLocation: Buffer = "M114\n".encode("utf-8") getMachineName: Buffer = "M997\n".encode("utf-8") - def goTo(self: Device, loc: Vector3): - self.sendGcode(f"G1 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8"), checkXYZ) + def goTo(self: Device, loc: Vector3, isVerbose: bool = False): + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8"), checkXYZ, isVerbose=isVerbose) return loc == self.getPrintHeadLocation() def parseGcode(self, file): @@ -27,7 +27,7 @@ def parseGcode(self, file): self.sendGcode(line.encode("utf-8"), checkOK) - def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable): + def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): assert callable(checkFunction) assert isinstance(gcode, bytes) self.serialConnection.write(gcode) @@ -36,22 +36,24 @@ def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable): while True: try: line = self.serialConnection.readline() + if isVerbose: print(line) if checkFunction(line): break except Exception as e: print(e) break - def getPrintHeadLocation(self: Device) -> Vector3: + def getPrintHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: self.serialConnection.write(usesMarlinGcode.getLocation) response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") + if isVerbose: print(response) loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) - def home(self: Device): - self.sendGcode("G28\n".encode("utf-8"), checkOK) + def home(self: Device, isVerbose: bool = False): + self.sendGcode("G28\n".encode("utf-8"), checkOK, isVerbose=isVerbose) return self.getHomePosition() == self.getPrintHeadLocation() def connect(self: Device): From 9e5825f07f4ea4d2c7b582f07e585a82cb94ab66 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 20 Oct 2024 13:52:09 -0400 Subject: [PATCH 021/194] updated to work with db --- server/Classes/FabricatorList.py | 50 ++++++++++++++---------- server/Classes/Fabricators/Fabricator.py | 35 +++++++++++++---- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 91873231..dd0268cc 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -1,12 +1,14 @@ import serial.tools.list_ports from flask import jsonify +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS from Classes.Fabricators.Device import Device from Classes.Ports import Ports from Classes.Fabricators.Fabricator import Fabricator class FabricatorList(): - fabricators = Fabricator.queryAll() + fabricators: [Fabricator] = [] @staticmethod def __iter__(): return iter(FabricatorList.fabricators) @@ -16,25 +18,37 @@ def __len__(): return len(FabricatorList.fabricators) @staticmethod - def addFabricator(serialPortName: str, name: str): + def init(): + """initialize the list of printers""" + FabricatorList.fabricators = Fabricator.queryAll() + + @staticmethod + def addFabricator(serialPortName: str, name: str = ""): """add a printer to the list, and to the database""" - # TODO: test if the printer is already in the list - serialPort = Ports.getPortByName(serialPortName) - if FabricatorList.getPrinterByHwid(serialPort.hwid): - return None # TODO: return error message - FabricatorList.fabricators.append(Fabricator(Device.createDevice(serialPort), name)) + serialPort: ListPortInfo | SysFS | None = Ports.getPortByName(serialPortName) + # TODO: check if the fabricator is in the db + dbFab: Fabricator | None = next((fabricator for fabricator in Fabricator.queryAll() if fabricator.hwid == serialPort.hwid.split(' LOCATION=')[0]), None) + listFab: Fabricator | None = next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) + if dbFab is not None: # means that the fabricator is in the db + if listFab is not None: # means that the fabricator is in the list and the db + print("Fabricator is already in the list and the db") + else: # means that the fabricator is in the db but not in the list + FabricatorList.fabricators.append(Fabricator(serialPort, name=dbFab.getname())) + else: # means that the fabricator is not in the db + if listFab is not None: # means that the fabricator is in the list but not in the db + listFab.addToDB() + else: # means that the fabricator is not in the list or the db + FabricatorList.fabricators.append(Fabricator(serialPort, name=name, addToDB=True)) + # TODO: check that printerlist and database are in sync dbPrinters = Fabricator.queryAll() - if len(FabricatorList.fabricators) != len(dbPrinters): - return None # TODO: return error message - for printer in dbPrinters: - if printer not in FabricatorList.fabricators: - return None # TODO: return error message + assert(len(FabricatorList.fabricators) == len(dbPrinters)) + assert all(printer in FabricatorList.fabricators for printer in dbPrinters) @staticmethod def deleteFabricator(printerid): """delete a printer from the list, and from the database""" - # TODO: Implement deletePrinter + # TODO: Implement deleteFabricator pass # try: # ports = serial.tools.list_ports.comports() @@ -57,18 +71,12 @@ def deleteFabricator(printerid): @staticmethod def getFabricatorByName(name) -> Fabricator | None: """find the first printer with the given name""" - for fabricator in FabricatorList.__iter__(): - if fabricator.getName() == name: - return fabricator - return None + return next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getName() == name), None) @staticmethod def getPrinterByHwid(hwid) -> Fabricator | None: """find the first printer with the given hwid""" - for fabricator in FabricatorList.__iter__(): - if fabricator.getHwid() == hwid: - return fabricator - return None + return next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getHwid() == hwid), None) @staticmethod def diagnose(device: Device): diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index d68525bb..aa6096a9 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,7 +1,8 @@ import serial -from flask import current_app +from flask import current_app, jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS +from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Device import Device from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 @@ -11,6 +12,7 @@ from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Classes.Ports import Ports from Classes.Queue import Queue from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence @@ -32,7 +34,7 @@ class Fabricator(db.Model): devicePort = db.Column(db.String(50), nullable=False) - def __init__(self, port: ListPortInfo | SysFS, name: str): + def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = False): assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) assert isinstance(name, str) @@ -51,8 +53,9 @@ def __init__(self, port: ListPortInfo | SysFS, name: str): self.date = datetime.now(timezone.utc).astimezone() self.device.connect() - db.session.add(self) - db.session.commit() + if addToDB: + db.session.add(self) + db.session.commit() @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: @@ -98,9 +101,15 @@ def createDevice(serialPort: ListPortInfo | SysFS | None): @classmethod def queryAll(cls) -> list["Fabricator"]: - """return all printers in the database""" - #return cls.query.all() - pass + """return all fabricators in the database""" + fabList = [] + for fab in cls.query.all(): + fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) + return fabList + + def addToDB(self): + db.session.add(self) + db.session.commit() def begin(self): assert self.status == "idle" @@ -110,6 +119,8 @@ def begin(self): self.job = self.queue.getJob(self.id) # TODO: test if the file has been sliced for the device of this fabricator + # if issubclass(self.device, PrusaPrinter): + # self.device.sendGcode(f"M862.3 P {self.device.getDescription(self.device).split(' ')[1]}\n".encode("utf-8"), lambda x: x == b'ok\n') if issubclass(self.device, hasStartupSequence): self.device.startupSequence(self) @@ -190,6 +201,16 @@ def handleVerdict(self, verdict: str, job): def getName(self): return self.name + def setName(self, name: str) -> Response: + try: + Fabricator.query.filter_by(hwid=self.hwid).first().name = name + self.name = name + db.session.commit() + return jsonify({"success": True, "message": "Printer name successfully updated.", "code": 200}) + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to update printer name. Database error", "code": 500}) + def getHwid(self): return self.hwid From 43654f8bb9501944caddde162011b92bd9fee852 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 21 Oct 2024 15:56:51 -0400 Subject: [PATCH 022/194] updates for new stuff. --- server/Classes/Fabricators/Device.py | 14 +++---- .../Fabricators/Printers/Ender/Ender3.py | 3 ++ .../Fabricators/Printers/Ender/Ender3Pro.py | 1 - .../Printers/Ender/EnderPrinter.py | 15 ++----- .../Printers/MakerBot/MakerBotPrinter.py | 39 +++++++++++++++++++ .../Printers/MakerBot/Replicator2.py | 35 +++++++++++++++++ .../Fabricators/Printers/Prusa/PrusaMK4.py | 4 ++ .../Printers/Prusa/PrusaPrinter.py | 16 +++----- server/Mixins/hasResponseCodes.py | 3 +- server/Mixins/usesMarlinGcode.py | 35 ++++++++++------- 10 files changed, 119 insertions(+), 46 deletions(-) create mode 100644 server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py create mode 100644 server/Classes/Fabricators/Printers/MakerBot/Replicator2.py diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 023ad47f..56aeac7d 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -4,13 +4,6 @@ from serial.tools.list_ports_linux import SysFS from typing_extensions import Buffer -# from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro -# from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 -# from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter -# from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 -# from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 -# from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S -# from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 import serial import serial.tools.list_ports @@ -21,6 +14,7 @@ class Device(ABC): VENDORID: int | None = None PRODUCTID: int | None = None DESCRIPTION: str | None = None + MAXFEEDRATE: int | None = None serialID: str | None = None serialConnection: serial.Serial | None = None serialPort: ListPortInfo | SysFS | None = None @@ -33,7 +27,6 @@ def __init__(self, serialPort: ListPortInfo | SysFS): def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" - def connect(self): try: self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=10) @@ -64,7 +57,7 @@ def sendGcode(self, gcode: Buffer, checkFunction, isVerbose: bool = False): pass @abstractmethod - def getPrintHeadLocation(self) -> Vector3: + def getToolHeadLocation(self) -> Vector3: pass def repair(self): @@ -90,3 +83,6 @@ def getDescription(self): def getHomePosition(self): return self.homePosition + + def getMaxFeedRate(self): + return self.MAXFEEDRATE diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3.py b/server/Classes/Fabricators/Printers/Ender/Ender3.py index 47cb8875..65444390 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3.py @@ -4,3 +4,6 @@ class Ender3(EnderPrinter): MODEL = "Ender 3" PRODUCTID = 0x7523 DESCRIPTION = "Ender 3 - CDC" + MAXFEEDRATE = 12000 + + diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py index 36c1859e..a34fcca5 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py @@ -1,6 +1,5 @@ from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 - class Ender3Pro(Ender3): MODEL = "Ender 3 Pro" DESCRIPTION = "Ender 3 Pro - CDC" \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 0f44913f..45dba366 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -3,13 +3,12 @@ from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 -from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import hasResponsecodes, checkOK, alwaysTrue +from Mixins.hasResponseCodes import hasResponsecodes, alwaysTrue from Mixins.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): +class EnderPrinter(Printer, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x1A86 homePosition = Vector3(-3.0,-10.0,0.0) @@ -35,17 +34,11 @@ def endSequence(self): def goTo(self, loc: Vector3, isVerbose: bool = False): return usesMarlinGcode.goTo(self, loc, isVerbose) - def pause(self): - self.sendGcode(usesMarlinGcode.pause, checkOK) - - def resume(self): - self.sendGcode(usesMarlinGcode.resume, checkOK) - def getPrintTime(self): pass - def getPrintHeadLocation(self) -> Vector3: - return usesMarlinGcode.getPrintHeadLocation(self) + def getToolHeadLocation(self) -> Vector3: + return usesMarlinGcode.getToolHeadLocation(self) def parseGcode(self, file): usesMarlinGcode.parseGcode(self, file) diff --git a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py new file mode 100644 index 00000000..50006eb7 --- /dev/null +++ b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py @@ -0,0 +1,39 @@ +from abc import ABCMeta +from typing import Callable +from typing_extensions import Buffer +from Classes.Fabricators.Printers.Printer import Printer +from Classes.Vector3 import Vector3 +from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import hasResponsecodes, checkOK, alwaysTrue +from Mixins.usesMarlinGcode import usesMarlinGcode + +class MakerBotPrinter(Printer, hasEndingSequence, hasResponsecodes, metaclass=ABCMeta): + VENDORID = 0x23C1 + homePosition = Vector3(0.0, 0.0, 0.0) + + def connect(self): + return usesMarlinGcode.connect(self) + + def disconnect(self): + return usesMarlinGcode.disconnect(self) + + def endSequence(self): + pass + + def goTo(self, loc: Vector3, isVerbose: bool = False): + pass + + def getPrintTime(self): + pass + + def getToolHeadLocation(self) -> Vector3: + pass + + def parseGcode(self, file): + pass + + def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): + pass + + def home(self, isVerbose: bool = False): + pass \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/MakerBot/Replicator2.py b/server/Classes/Fabricators/Printers/MakerBot/Replicator2.py new file mode 100644 index 00000000..61edc0c2 --- /dev/null +++ b/server/Classes/Fabricators/Printers/MakerBot/Replicator2.py @@ -0,0 +1,35 @@ +from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter +from Mixins.hasResponseCodes import hasResponsecodes +from Mixins.hasStartupSequence import hasStartupSequence + + +class Replicator2(MakerBotPrinter, hasStartupSequence): + MODEL = "Replicator 2" + PRODUCTID = 0xB016 + DESCRIPTION = "Replicator 2 - CDC" + + def startupSequence(self): + self.serialConnection.write("M73 P0 ; enable build progress".encode("utf-8")) + self.serialConnection.write("G162 X Y F3000 ; home XY maximum".encode("utf-8")) + self.serialConnection.write("G161 Z F1200 ; home Z minimum".encode("utf-8")) + self.serialConnection.write("G92 Z-5 ; set Z to -5".encode("utf-8")) + self.serialConnection.write("G1 Z0 ; move Z to 0".encode("utf-8")) + self.serialConnection.write("G161 Z F100 ; home Z slowly".encode("utf-8")) + self.serialConnection.write("M73 P0 ; enable build progress".encode("utf-8")) + self.serialConnection.write("M73 P0 ; enable build progress".encode("utf-8")) + self.serialConnection.write("M73 P0 ; enable build progress".encode("utf-8")) + self.serialConnection.write("M73 P0 ; enable build progress".encode("utf-8")) + self.serialConnection.write("M132 X Y Z A B ; recall home offsets".encode("utf-8")) + self.serialConnection.write("G1 X-145 Y-75 Z30 F9000 ; move to wait position off table".encode("utf-8")) + self.serialConnection.write("G130 X20 Y20 Z20 A20 B20 ; lower stepper Vrefs while heating".encode("utf-8")) + self.serialConnection.write("M126 S[fan_speed_pwm]".encode("utf-8")) + self.serialConnection.write("M104 S[extruder0_temperature] T0".encode("utf-8")) + self.serialConnection.write("M133 T0 ; stabilize extruder temperature".encode("utf-8")) + self.serialConnection.write("G130 X127 Y127 Z40 A127 B127 ; default stepper Vrefs".encode("utf-8")) + self.serialConnection.write("G92 A0 ; zero extruder".encode("utf-8")) + self.serialConnection.write("G1 Z0.4 ; position nozzle".encode("utf-8")) + self.serialConnection.write("G1 E25 F300 ; purge nozzle".encode("utf-8")) + self.serialConnection.write("G1 X-140 Y-70 Z0.15 F1200 ; slow wipe".encode("utf-8")) + self.serialConnection.write("G1 X-135 Y-65 Z0.5 F1200 ; lift".encode("utf-8")) + self.serialConnection.write("G92 A0 ; zero extruder".encode("utf-8")) + self.serialConnection.write("M73 P1 ;@body (notify GPX body has started)".encode("utf-8")) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index 5dc19995..a11aa0c8 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -7,6 +7,7 @@ class PrusaMK4(PrusaPrinter): MODEL = "Prusa MK4" PRODUCTID = 0x000D DESCRIPTION = "Original Prusa MK4 - CDC" + MAXFEEDRATE = 36000 homePosition = Vector3(14.0, -4.0, 2.0) def endSequence(self): @@ -15,3 +16,6 @@ def endSequence(self): self.sendGcode(b"M140 S0\n", alwaysTrue) # ; turn off heatbed self.sendGcode(b"M107\n", alwaysTrue) # ; turn off fan + def getPrintTime(self): + pass + diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index f3453e04..6fa4d2ec 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -1,4 +1,4 @@ -from abc import ABCMeta +from abc import ABCMeta, abstractmethod from typing_extensions import Buffer, Callable from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer @@ -8,7 +8,7 @@ from Mixins.usesMarlinGcode import usesMarlinGcode -class PrusaPrinter(Printer, canPause, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): +class PrusaPrinter(Printer, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x2C99 @@ -27,17 +27,13 @@ def home(self, isVerbose: bool = False): def goTo(self, loc: Vector3, isVerbose: bool = False): return usesMarlinGcode.goTo(self, loc, isVerbose) - def pause(self): - self.sendGcode(usesMarlinGcode.pause, checkOK) - - def resume(self): - self.sendGcode(usesMarlinGcode.resume, checkOK) - + @abstractmethod def endSequence(self): pass + @abstractmethod def getPrintTime(self): pass - def getPrintHeadLocation(self) -> Vector3: - return usesMarlinGcode.getPrintHeadLocation(self) \ No newline at end of file + def getToolHeadLocation(self) -> Vector3: + return usesMarlinGcode.getToolHeadLocation(self) \ No newline at end of file diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 3856de5b..63ab7e16 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -9,13 +9,14 @@ def getPrintTime(self): pass @abstractmethod - def getPrintHeadLocation(self) -> Vector3: + def getToolHeadLocation(self) -> Vector3: pass def checkOK(line): return line == b'ok\n' def checkXYZ(line): + line = line.decode("utf-8") return ("X:" in line) and ("Y:" in line) and ("Z:" in line) def alwaysTrue(line): diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index 7ea67bf8..75926f31 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -5,21 +5,22 @@ from Classes.Fabricators.Device import Device from Classes.LocationResponse import LocationResponse from Classes.Vector3 import Vector3 -from Mixins.hasResponseCodes import checkOK, checkXYZ +from Mixins.canPause import canPause +from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue -class usesMarlinGcode(metaclass=ABCMeta): +class usesMarlinGcode(canPause, metaclass=ABCMeta): homeCMD: Buffer = "G28\n".encode("utf-8") - pause: Buffer = "M601\n".encode("utf-8") + "M113 S1\n".encode("utf-8") - resume: Buffer = "M602\n".encode("utf-8") - cancel: Buffer = "M112\n".encode("utf-8") - status: Buffer = "M115\n".encode("utf-8") - getLocation: Buffer = "M114\n".encode("utf-8") - getMachineName: Buffer = "M997\n".encode("utf-8") + pauseCMD: Buffer = "M601\n".encode("utf-8") + "M113 S1\n".encode("utf-8") + resumeCMD: Buffer = "M602\n".encode("utf-8") + cancelCMD: Buffer = "M112\n".encode("utf-8") + statusCMD: Buffer = "M115\n".encode("utf-8") + getLocationCMD: Buffer = "M114\n".encode("utf-8") + getMachineNameCMD: Buffer = "M997\n".encode("utf-8") def goTo(self: Device, loc: Vector3, isVerbose: bool = False): - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z}\n".encode("utf-8"), checkXYZ, isVerbose=isVerbose) - return loc == self.getPrintHeadLocation() + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{self.getMaxFeedRate}\n".encode("utf-8") + "M114\n".encode("utf-8"), alwaysTrue, isVerbose=isVerbose) + return loc == self.getToolHeadLocation() def parseGcode(self, file): with open(file, "r") as f: @@ -43,8 +44,8 @@ def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable, isVerbose: b print(e) break - def getPrintHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: - self.serialConnection.write(usesMarlinGcode.getLocation) + def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: + self.serialConnection.write(usesMarlinGcode.getLocationCMD) response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") @@ -53,8 +54,14 @@ def getPrintHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: return Vector3(loc.x, loc.y, loc.z) def home(self: Device, isVerbose: bool = False): - self.sendGcode("G28\n".encode("utf-8"), checkOK, isVerbose=isVerbose) - return self.getHomePosition() == self.getPrintHeadLocation() + self.sendGcode(usesMarlinGcode.homeCMD, checkOK, isVerbose=isVerbose) + return self.getHomePosition() == self.getToolHeadLocation() + + def pause(self): + self.sendGcode(usesMarlinGcode.pauseCMD, checkOK) + + def resume(self): + self.sendGcode(usesMarlinGcode.resumeCMD, checkOK) def connect(self: Device): Device.connect(self) From 9f38afbf0e0a99865c50380e355b37ef23f0521a Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 28 Oct 2024 02:13:13 -0400 Subject: [PATCH 023/194] actual proper tests! --- Tests/test_runner.py | 95 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Tests/test_runner.py diff --git a/Tests/test_runner.py b/Tests/test_runner.py new file mode 100644 index 00000000..42e97921 --- /dev/null +++ b/Tests/test_runner.py @@ -0,0 +1,95 @@ +from datetime import datetime +import pytest +from Classes.Ports import Ports +from Classes.Fabricators.Fabricator import Fabricator +from Classes.Vector3 import Vector3 +from Mixins.canPause import canPause + +testLevelToRun = 5 + +@pytest.fixture(scope="module") +def test_setup(): + fabricators = [] + for port in ["COM3", "COM4", "COM5", "COM6", "COM7"]: + if Ports.getPortByName(port) is not None: + fabricators.append(Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False)) + yield fabricators + + for fabricator in fabricators: + fabricator.device.disconnect() + +@pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") +def test_connection(test_setup): + fabricators = test_setup + for fabricator in fabricators: + assert fabricator.device is not None, f"No printer connected on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") +def test_home(test_setup): + fabricators = test_setup + for fabricator in fabricators: + assert fabricator.device.home(isVerbose=False), f"Failed to home {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_square(test_setup): + fabricators = test_setup + for fabricator in fabricators: + squarePasses = 0 + for point in [Vector3(50.0,50.0,2.0), Vector3(200.0,50.0,2.0), Vector3(200.0,150.0,2.0), Vector3(50.0,150.0,2.0)]: + squarePasses += 1 if fabricator.device.goTo(point) else 0 + assert squarePasses == 4, f"Failed to draw square on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_octagon(test_setup): + fabricators = test_setup + for fabricator in fabricators: + octagonPasses = 0 + for point in [Vector3(50.0,100.0,2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0,50.0,2.0), Vector3(200.0, 100.0, 2), Vector3(200.0,150.0,2.0), Vector3(150.0, 200.0, 2), Vector3(100.0,200.0,2.0), Vector3(50.0, 150.0, 2.0)]: + octagonPasses += 1 if fabricator.device.goTo(point) else 0 + assert octagonPasses == 8, f"Failed to draw octagon on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_center(test_setup): + fabricators = test_setup + for fabricator in fabricators: + assert fabricator.device.goTo(Vector3(125.0,100.0,2.0)), f"Failed to go to center on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") +def test_pause(test_setup): + fabricators = test_setup + for fabricator in fabricators: + if not isinstance(fabricator.device, canPause): + continue + assert fabricator.device.pause(), f"Failed to pause on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") +def test_resume(test_setup): + fabricators = test_setup + for fabricator in fabricators: + if not isinstance(fabricator.device, canPause): + continue + assert fabricator.device.resume(), f"Failed to resume on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") +def test_cancel(test_setup): + fabricators = test_setup + for fabricator in fabricators: + assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") +def test_status(test_setup): + fabricators = test_setup + for fabricator in fabricators: + assert fabricator.getStatus(), f"Failed to get status on {fabricator.getDescription()}" + +@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +def test_gcode(test_setup): + fabricators = test_setup + for fabricator in fabricators: + expectedTime = 2040 # for my personal home test, 1072 + expectedMinutes, expectedSeconds = divmod(expectedTime, 60) + time = datetime.now() + assert fabricator.device.parseGcode("20mm_calibration.gcode"), f"Failed to parse Gcode on {fabricator.getDescription()}" + time = datetime.now() - time + minutes, seconds = divmod(time.seconds, 60) + assert abs(expectedTime - time.seconds) < 60, f"Failed to print within 1 minute of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" From 1ab58934571475a06d390682d02ac0dbde117bf0 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 29 Oct 2024 14:04:53 -0400 Subject: [PATCH 024/194] added package-lock.json to the gitignore --- client/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/.gitignore b/client/.gitignore index 8ee54e8d..effa2d06 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -27,4 +27,6 @@ coverage *.sln *.sw? +package-lock.json + *.tsbuildinfo From 803118371f822f7cf8b35a3ceb4ad19ddbfcf036 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 29 Oct 2024 17:29:40 -0400 Subject: [PATCH 025/194] feat: add very basic printer emulator --- printeremu/cmd/test_printer.go | 17 +++++++++++++ printeremu/documentation.txt | 3 +++ printeremu/go.mod | 9 +++++++ printeremu/go.sum | 6 +++++ printeremu/src/emulator.go | 31 ++++++++++++++++++++++++ printeremu/src/extruder.go | 43 +++++++++++++++++++++++++++++++++ printeremu/src/printer.go | 44 ++++++++++++++++++++++++++++++++++ 7 files changed, 153 insertions(+) create mode 100644 printeremu/cmd/test_printer.go create mode 100644 printeremu/documentation.txt create mode 100644 printeremu/go.mod create mode 100644 printeremu/go.sum create mode 100644 printeremu/src/emulator.go create mode 100644 printeremu/src/extruder.go create mode 100644 printeremu/src/printer.go diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go new file mode 100644 index 00000000..b02507d6 --- /dev/null +++ b/printeremu/cmd/test_printer.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + + "printeremu/src" +) + +func main() { + err := src.Init() + + if err != nil { + fmt.Println(err) + } + + src.Run() +} \ No newline at end of file diff --git a/printeremu/documentation.txt b/printeremu/documentation.txt new file mode 100644 index 00000000..36c27c92 --- /dev/null +++ b/printeremu/documentation.txt @@ -0,0 +1,3 @@ +cd printeremu + +go run ./cmd/test_printer.go \ No newline at end of file diff --git a/printeremu/go.mod b/printeremu/go.mod new file mode 100644 index 00000000..559df761 --- /dev/null +++ b/printeremu/go.mod @@ -0,0 +1,9 @@ +module printeremu + +go 1.23.2 + +require ( + github.com/creack/goselect v0.1.2 // indirect + go.bug.st/serial v1.6.2 // indirect + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect +) diff --git a/printeremu/go.sum b/printeremu/go.sum new file mode 100644 index 00000000..2f35f6fb --- /dev/null +++ b/printeremu/go.sum @@ -0,0 +1,6 @@ +github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= +github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= +go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go new file mode 100644 index 00000000..b29b69d0 --- /dev/null +++ b/printeremu/src/emulator.go @@ -0,0 +1,31 @@ +package src + +import ( + "fmt" + "time" +) + +func main() { + +} + +func Init() error { + // TODO: add! + + fmt.Println("Reading up...") + + // Correctly create a Vector3 instance + position := Vector3{X: 100, Y: 27, Z: 76} + extruder := NewExtruder(position, 250, 100, 125) // Use the Vector3 + + // Create a new Printer instance + printer := NewPrinter(0, "Ender3", "Creality Brand", "Hwid", "Test Ender3", "Init", time.Now().String(), extruder) + + fmt.Println(printer.String()) + + return nil +} + +func Run() { + fmt.Println("Hello world!") +} \ No newline at end of file diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go new file mode 100644 index 00000000..a0157c79 --- /dev/null +++ b/printeremu/src/extruder.go @@ -0,0 +1,43 @@ +package src + +import ( + "fmt" +) + +type Vector3 struct { + X float64 + Y float64 + Z float64 +} + +type Extruder struct { + Position Vector3 + ExtruderTemp float64 + BedTemp float64 + FanSpeed float64 +} + +func NewExtruder(position Vector3, extruderTemp, bedTemp, fanSpeed float64) *Extruder { + return &Extruder{ + Position: position, + ExtruderTemp: extruderTemp, + BedTemp: bedTemp, + FanSpeed: fanSpeed, + } +} + +func (e *Extruder) SetExtruderTemp(temp float64) { + e.ExtruderTemp = temp +} + +func (e *Extruder) SetBedTemp(temp float64) { + e.BedTemp = temp +} + +func (e *Extruder) SetFanSpeed(speed float64) { + e.FanSpeed = speed +} + +func (e *Extruder) String() string { + return fmt.Sprintf("{Position: %v, ExtruderTemp: %vc, BedTemp: %vc, FanSpeed: %v}", e.Position, e.ExtruderTemp, e.BedTemp, e.FanSpeed) +} \ No newline at end of file diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go new file mode 100644 index 00000000..91e7af68 --- /dev/null +++ b/printeremu/src/printer.go @@ -0,0 +1,44 @@ +package src + +import "fmt" + +type Printer struct { + id int + device string + description string + hwid string + name string + status string + date string + extruder *Extruder // Keep this as a pointer +} + +func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder) *Printer { + return &Printer{ + id: id, + device: device, + description: description, + hwid: hwid, + name: name, + status: status, + date: date, + extruder: extruder, + } +} + +func (p *Printer) SetExtruderTemp(temp float64) { + p.extruder.SetExtruderTemp(temp) +} + +func (p *Printer) GetExtruderTemp() float64 { + return p.extruder.ExtruderTemp +} + +func (p *Printer) GetExtruder() *Extruder { + return p.extruder +} + +func (p *Printer) String() string { + return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v}", + p.id, p.device, p.description, p.hwid, p.name, p.status, p.date, p.extruder.String()) +} From cc3a314278f627fa71e6b337ab85b3d4fa65a1bb Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 29 Oct 2024 18:17:07 -0400 Subject: [PATCH 026/194] feat: easier to create emulated printer, testing setting extruder and bed temp --- printeremu/cmd/test_printer.go | 5 +++-- printeremu/src/emulator.go | 29 ++++++++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index b02507d6..71d95d32 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -7,11 +7,12 @@ import ( ) func main() { - err := src.Init() + extruder, printer, err := src.Init(1, "Ender3", "Creality", "hwid:032uhb3293n2", "Testing Printer", "Init") if err != nil { fmt.Println(err) + return } - src.Run() + src.Run(extruder, printer) } \ No newline at end of file diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index b29b69d0..302eb9f3 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -9,23 +9,30 @@ func main() { } -func Init() error { +func Init(id int, device string, description string, hwid string, name string, status string) (*Extruder, *Printer, error) { // TODO: add! - fmt.Println("Reading up...") - // Correctly create a Vector3 instance position := Vector3{X: 100, Y: 27, Z: 76} - extruder := NewExtruder(position, 250, 100, 125) // Use the Vector3 - - // Create a new Printer instance - printer := NewPrinter(0, "Ender3", "Creality Brand", "Hwid", "Test Ender3", "Init", time.Now().String(), extruder) + extruder := NewExtruder(position, 250, 100, 125) + + printer := NewPrinter(id, device, description, hwid, name, status, time.Now().String(), extruder) fmt.Println(printer.String()) - - return nil + + return extruder, printer, nil } -func Run() { - fmt.Println("Hello world!") +func Run(extruder *Extruder, printer *Printer) { + fmt.Println("Running...") + + extruder.SetExtruderTemp(200) + + printer.GetExtruder().SetBedTemp(50) + + printer.GetExtruder().SetFanSpeed(100) + + fmt.Println(printer.String()) + + fmt.Println("Done!") } \ No newline at end of file From 9485c8b2f1fc4a41afb650e73080fa4cb4016a48 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 29 Oct 2024 19:41:19 -0400 Subject: [PATCH 027/194] my big push for all the updates to the refactor. --- .gitignore | 4 +- server/Classes/FabricatorList.py | 3 - server/Classes/Fabricators/Device.py | 6 +- server/Classes/Fabricators/Fabricator.py | 18 +- .../Printers/Ender/EnderPrinter.py | 32 ++-- .../Printers/MakerBot/MakerBotPrinter.py | 4 +- .../Fabricators/Printers/Prusa/PrusaMK3.py | 13 +- .../Fabricators/Printers/Prusa/PrusaMK4.py | 8 +- .../Fabricators/Printers/Prusa/PrusaMK4S.py | 1 + .../Printers/Prusa/PrusaPrinter.py | 9 +- server/Classes/serialCommunication.py | 4 +- server/Mixins/hasResponseCodes.py | 28 +++- server/Mixins/usesMarlinGcode.py | 97 +++++++---- server/models/PrinterStatusService.py | 4 +- server/test.py | 158 +++++++++++++++--- 15 files changed, 284 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index a27279a2..a29cf8bf 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ migrations/ server/qview3dserver.egg-info/ server/dist -.DS_Store \ No newline at end of file +.DS_Store +/qodana.yaml +/Tests/logs/ diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index dd0268cc..28f7cf93 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -26,7 +26,6 @@ def init(): def addFabricator(serialPortName: str, name: str = ""): """add a printer to the list, and to the database""" serialPort: ListPortInfo | SysFS | None = Ports.getPortByName(serialPortName) - # TODO: check if the fabricator is in the db dbFab: Fabricator | None = next((fabricator for fabricator in Fabricator.queryAll() if fabricator.hwid == serialPort.hwid.split(' LOCATION=')[0]), None) listFab: Fabricator | None = next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) if dbFab is not None: # means that the fabricator is in the db @@ -39,8 +38,6 @@ def addFabricator(serialPortName: str, name: str = ""): listFab.addToDB() else: # means that the fabricator is not in the list or the db FabricatorList.fabricators.append(Fabricator(serialPort, name=name, addToDB=True)) - - # TODO: check that printerlist and database are in sync dbPrinters = Fabricator.queryAll() assert(len(FabricatorList.fabricators) == len(dbPrinters)) assert all(printer in FabricatorList.fabricators for printer in dbPrinters) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 56aeac7d..8bc26eda 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -29,7 +29,7 @@ def __repr__(self): def connect(self): try: - self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=10) + self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) self.serialConnection.reset_input_buffer() return True except Exception as e: @@ -49,11 +49,11 @@ def home(self, isVerbose: bool = False): def goTo(self, loc: Vector3, isVerbose: bool = False): pass - def parseGcode(self, file): + def parseGcode(self, file, isVerbose=False): pass @abstractmethod - def sendGcode(self, gcode: Buffer, checkFunction, isVerbose: bool = False): + def sendGcode(self, gcode: Buffer, isVerbose: bool = False): pass @abstractmethod diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index aa6096a9..e7e832e5d 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -8,6 +8,8 @@ from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter +from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter +from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S @@ -17,7 +19,7 @@ from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasStartupSequence import hasStartupSequence -from models.jobs import Job +#from models.jobs import Job from models.db import db from datetime import datetime, timezone @@ -42,7 +44,7 @@ def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = F self.verdict: str = "" self.prevMsg: str = "" - self.job: Job | None = None + #self.job: Job | None = None self.queue: Queue = Queue() self.status: str = "idle" @@ -69,8 +71,6 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: testName.close() break response = response.decode("utf-8") - # while testName.readline() != b'echo:Unknown command: "M420 S1"\n': - # pass return response @@ -96,6 +96,9 @@ def createDevice(serialPort: ListPortInfo | SysFS | None): return Ender3(serialPort) else: return None + elif serialPort.vid == MakerBotPrinter.VENDORID: + if serialPort.pid == Replicator2.PRODUCTID: + return Replicator2(serialPort) else: return None @@ -104,7 +107,8 @@ def queryAll(cls) -> list["Fabricator"]: """return all fabricators in the database""" fabList = [] for fab in cls.query.all(): - fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) + if Ports.getPortByName(fab.devicePort) is not None: + fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) return fabList def addToDB(self): @@ -122,8 +126,8 @@ def begin(self): # if issubclass(self.device, PrusaPrinter): # self.device.sendGcode(f"M862.3 P {self.device.getDescription(self.device).split(' ')[1]}\n".encode("utf-8"), lambda x: x == b'ok\n') - if issubclass(self.device, hasStartupSequence): - self.device.startupSequence(self) + if isinstance(self.device, hasStartupSequence): + self.device.startupSequence() self.job = self.queue.getJob(self.id) if self.job is None: diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 45dba366..eb2ae8f5 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -8,7 +8,7 @@ from Mixins.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Printer, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): +class EnderPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x1A86 homePosition = Vector3(-3.0,-10.0,0.0) @@ -19,17 +19,17 @@ def disconnect(self): return usesMarlinGcode.disconnect(self) def endSequence(self): - self.sendGcode(b"G91\n", alwaysTrue) # Relative positioning - self.sendGcode(b"G1 E-2 F2700\n", alwaysTrue) # Retract a bit - self.sendGcode(b"G1 E-2 Z0.2 F2400\n", alwaysTrue) # Retract and raise Z - self.sendGcode(b"G1 X5 Y5 F3000\n", alwaysTrue) # Wipe out - self.sendGcode(b"G1 Z10\n", alwaysTrue) # Raise Z more - self.sendGcode(b"G90\n", alwaysTrue) # Absolute positioning - self.sendGcode(b"G1 X0 Y220\n", alwaysTrue) # Present print - self.sendGcode(b"M106 S0\n", alwaysTrue) # Turn-off fan - self.sendGcode(b"M104 S0\n", alwaysTrue) # Turn-off hotend - self.sendGcode(b"M140 S0\n", alwaysTrue) # Turn-off bed - self.sendGcode(b"M84 X Y E\n", alwaysTrue) # Disable all steppers but Z + self.sendGcode(b"G91\n") # Relative positioning + self.sendGcode(b"G1 E-2 F2700\n") # Retract a bit + self.sendGcode(b"G1 E-2 Z0.2 F2400\n") # Retract and raise Z + self.sendGcode(b"G1 X5 Y5 F3000\n") # Wipe out + self.sendGcode(b"G1 Z10\n") # Raise Z more + self.sendGcode(b"G90\n") # Absolute positioning + self.sendGcode(b"G1 X0 Y220\n") # Present print + self.sendGcode(b"M106 S0\n") # Turn-off fan + self.sendGcode(b"M104 S0\n") # Turn-off hotend + self.sendGcode(b"M140 S0\n") # Turn-off bed + self.sendGcode(b"M84 X Y E\n") # Disable all steppers but Z def goTo(self, loc: Vector3, isVerbose: bool = False): return usesMarlinGcode.goTo(self, loc, isVerbose) @@ -40,11 +40,11 @@ def getPrintTime(self): def getToolHeadLocation(self) -> Vector3: return usesMarlinGcode.getToolHeadLocation(self) - def parseGcode(self, file): - usesMarlinGcode.parseGcode(self, file) + def parseGcode(self, file, isVerbose: bool = False): + return usesMarlinGcode.parseGcode(self, file, isVerbose) - def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): - usesMarlinGcode.sendGcode(self, gcode, checkFunction, isVerbose) + def sendGcode(self, gcode: Buffer, isVerbose: bool = False): + usesMarlinGcode.sendGcode(self, gcode, isVerbose) def home(self, isVerbose: bool = False): return usesMarlinGcode.home(self, isVerbose) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py index 50006eb7..178d29e3 100644 --- a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py +++ b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py @@ -29,10 +29,10 @@ def getPrintTime(self): def getToolHeadLocation(self) -> Vector3: pass - def parseGcode(self, file): + def parseGcode(self, file, isVerbose: bool = False): pass - def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): + def sendGcode(self, gcode: Buffer, isVerbose: bool = False): pass def home(self, isVerbose: bool = False): diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index ac99c96b..e711ee63 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -6,8 +6,11 @@ class PrusaMK3(PrusaPrinter): DESCRIPTION = "Original Prusa MK3 - CDC" def endSequence(self): - self.gcodeEnding("M104 S0") # turn off extruder - self.gcodeEnding("M140 S0") # turn off heatbed - self.gcodeEnding("M107") # turn off fan - self.gcodeEnding("G1 X0 Y210") # home X axis and push Y forward - self.gcodeEnding("M84") # disable motors \ No newline at end of file + self.sendGcode(b"M104 S0\n") # turn off extruder + self.sendGcode(b"M140 S0\n") # turn off heatbed + self.sendGcode(b"M107\n") # turn off fan + self.sendGcode(b"G1 X0 Y210\n") # home X axis and push Y forward + self.sendGcode(b"M84\n") # disable motors + + def getPrintTime(self): + pass \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index a11aa0c8..83a7c5ea 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -1,7 +1,5 @@ from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 -from Mixins.hasResponseCodes import alwaysTrue - class PrusaMK4(PrusaPrinter): MODEL = "Prusa MK4" @@ -12,9 +10,9 @@ class PrusaMK4(PrusaPrinter): def endSequence(self): # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") - self.sendGcode(b"M104 S0\n", alwaysTrue) # ; turn off temperature - self.sendGcode(b"M140 S0\n", alwaysTrue) # ; turn off heatbed - self.sendGcode(b"M107\n", alwaysTrue) # ; turn off fan + self.sendGcode(b"M104 S0\n") # ; turn off temperature + self.sendGcode(b"M140 S0\n") # ; turn off heatbed + self.sendGcode(b"M107\n") # ; turn off fan def getPrintTime(self): pass diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py index dfb605a4..06624b4a 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py @@ -4,3 +4,4 @@ class PrusaMK4S(PrusaMK4): MODEL = "Prusa MK4S" PRODUCTID = 0x001A DESCRIPTION = "Original Prusa MK4S - CDC" + MAXFEEDRATE = 36000 diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 6fa4d2ec..12ef2389 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -8,12 +8,15 @@ from Mixins.usesMarlinGcode import usesMarlinGcode -class PrusaPrinter(Printer, hasEndingSequence, hasResponsecodes, usesMarlinGcode, metaclass=ABCMeta): +class PrusaPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): VENDORID = 0x2C99 - def sendGcode(self, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): - return usesMarlinGcode.sendGcode(self, gcode, checkFunction, isVerbose) + def sendGcode(self, gcode: Buffer, isVerbose: bool = False): + return usesMarlinGcode.sendGcode(self, gcode, isVerbose) + + def parseGcode(self, file, isVerbose=False): + return usesMarlinGcode.parseGcode(self, file, isVerbose) def connect(self): return usesMarlinGcode.connect(self) diff --git a/server/Classes/serialCommunication.py b/server/Classes/serialCommunication.py index 0030737e..a597934f 100644 --- a/server/Classes/serialCommunication.py +++ b/server/Classes/serialCommunication.py @@ -8,9 +8,9 @@ def get3DPrinterList(): ports = serial.tools.list_ports.comports() # print(ports) # Print out the list of ports. + # Save the port and description to list. With key value pairs of port and description. + printerList = [] for port in ports: - # Save the port and description to list. With key value pairs of port and description. - printerList = [] # Keep a list of supported printers. supportedPrinters = ["Original Prusa i3 MK3", "Makerbot"] diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 63ab7e16..406a72e4 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -20,4 +20,30 @@ def checkXYZ(line): return ("X:" in line) and ("Y:" in line) and ("Z:" in line) def alwaysTrue(line): - return True \ No newline at end of file + return True + +def anyResponse(line): + return line != b'' + +def checkEcho(line): + return line.decode("utf-8").startswith("echo") + +def checkBedTemp(line): + try: + temps = line.decode("utf-8").split("B:")[1].split("X:")[0].split("/") + if len(temps) == 2: + if float(temps[1]) == 0.0: return True + return float(temps[1]) - float(temps[0]) < 0.25 + return True + except Exception as e: + return False + +def checkExtruderTemp(line): + try: + temps = line.decode("utf-8").split("T:")[1].split("B:")[0].split("/") + if len(temps) == 2: + if float(temps[1]) == 0.0: return True + return float(temps[1]) - float(temps[0]) < 0.25 + return True + except Exception as e: + return False \ No newline at end of file diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index 75926f31..72960eff 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -1,48 +1,77 @@ from abc import ABCMeta -from typing import Callable +from time import sleep from typing_extensions import Buffer from Classes.Fabricators.Device import Device from Classes.LocationResponse import LocationResponse from Classes.Vector3 import Vector3 from Mixins.canPause import canPause -from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue +from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue, checkBedTemp, checkExtruderTemp, hasResponsecodes -class usesMarlinGcode(canPause, metaclass=ABCMeta): +class usesMarlinGcode(canPause, hasResponsecodes, metaclass=ABCMeta): homeCMD: Buffer = "G28\n".encode("utf-8") - pauseCMD: Buffer = "M601\n".encode("utf-8") + "M113 S1\n".encode("utf-8") - resumeCMD: Buffer = "M602\n".encode("utf-8") cancelCMD: Buffer = "M112\n".encode("utf-8") + keepAliveCMD: Buffer = "M113 S1\n".encode("utf-8") + doNotKeepAliveCMD: Buffer = "M113 S0\n".encode("utf-8") statusCMD: Buffer = "M115\n".encode("utf-8") getLocationCMD: Buffer = "M114\n".encode("utf-8") + pauseCMD: Buffer = "M601\n".encode("utf-8") + resumeCMD: Buffer = "M602\n".encode("utf-8") getMachineNameCMD: Buffer = "M997\n".encode("utf-8") + callablesHashtable = { + "G0": [alwaysTrue, checkOK], # Move + "G1": [alwaysTrue, checkOK], # Move + "G28": [alwaysTrue, checkOK], # Home + "G29": [alwaysTrue, checkOK], # Auto bed leveling + "M73": [alwaysTrue, checkOK], # Set build percentage + "M109": [alwaysTrue, checkExtruderTemp], # Wait for hotend to reach target temp + "M114": [alwaysTrue, checkXYZ], # Get current position + "M140": [alwaysTrue, checkOK], # Set bed temp + "M190": [alwaysTrue, checkBedTemp], # Wait for bed to reach target temp + } + def goTo(self: Device, loc: Vector3, isVerbose: bool = False): - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{self.getMaxFeedRate}\n".encode("utf-8") + "M114\n".encode("utf-8"), alwaysTrue, isVerbose=isVerbose) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) + self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) return loc == self.getToolHeadLocation() - def parseGcode(self, file): - with open(file, "r") as f: - for line in f: - self.sendGcode(line.encode("utf-8"), checkOK) + def parseGcode(self: Device, file, isVerbose: bool = False): + try: + with open(file, "r") as f: + for line in f: + if line.startswith(";") or line == "\n": + continue + if isVerbose: + print(line, end="") + self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) + return True + except Exception as e: + print(e) + return False - def sendGcode(self: Device, gcode: Buffer, checkFunction: Callable, isVerbose: bool = False): - assert callable(checkFunction) + def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): assert isinstance(gcode, bytes) self.serialConnection.write(gcode) - while self.serialConnection.readline() == b'ok\n': - pass - while True: - try: - line = self.serialConnection.readline() - if isVerbose: print(line) - if checkFunction(line): - break - except Exception as e: - print(e) - break + callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) + if callables[0] != alwaysTrue: + while not callables[0](self.serialConnection.readline()): + pass + if callables[1] != alwaysTrue: + while True: + try: + line = self.serialConnection.readline() + if "processing" in line.decode("utf-8"): + continue + if isVerbose: print(line) + if callables[1](line): + return True + except Exception as e: + print(e) + return False + def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: self.serialConnection.write(usesMarlinGcode.getLocationCMD) @@ -53,26 +82,36 @@ def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) + def home(self: Device, isVerbose: bool = False): - self.sendGcode(usesMarlinGcode.homeCMD, checkOK, isVerbose=isVerbose) + self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) return self.getHomePosition() == self.getToolHeadLocation() + def pause(self): - self.sendGcode(usesMarlinGcode.pauseCMD, checkOK) + self.sendGcode(usesMarlinGcode.keepAliveCMD) + self.sendGcode(usesMarlinGcode.pauseCMD) + def resume(self): - self.sendGcode(usesMarlinGcode.resumeCMD, checkOK) + self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) + self.sendGcode(usesMarlinGcode.resumeCMD) + def connect(self: Device): Device.connect(self) try: if self.serialConnection: - self.serialConnection.write("M155 S5 C7\n".encode("utf-8")) + self.serialConnection.write(b"M155 S1\n") return True except Exception as e: return e + def disconnect(self: Device): if self.serialConnection: - self.serialConnection.write("M155 S0\n".encode("utf-8") + "M84\n".encode("utf-8")) - self.serialConnection.close() \ No newline at end of file + self.sendGcode(b"M155 S0\n") + self.sendGcode(b"M104 S0\n") + self.sendGcode(b"M140 S0\n") + self.sendGcode(b"M84\n") + self.serialConnection.close() diff --git a/server/models/PrinterStatusService.py b/server/models/PrinterStatusService.py index 600919d7..bfcf5e00 100644 --- a/server/models/PrinterStatusService.py +++ b/server/models/PrinterStatusService.py @@ -15,6 +15,7 @@ def __init__(self, printer, *args, **kwargs): class PrinterStatusService: # in order to access the app context, we need to pass the app to the PrinterStatusService, mainly for the websockets def __init__(self, app): + self.ping_thread = None self.app = app self.printer_threads = [] # array of printer threads @@ -216,9 +217,6 @@ def pingForStatus(self): GCODE for print status """ pass - - def getThreadArray(self): - return self.printer_threads def movePrinterList(self, printer_ids): # printer_ids is a list of printer ids in the order they should be displayed diff --git a/server/test.py b/server/test.py index 92b04056..210376ae 100644 --- a/server/test.py +++ b/server/test.py @@ -1,3 +1,5 @@ +import re +from datetime import datetime from Classes.FabricatorList import FabricatorList from Classes.Fabricators.Fabricator import Fabricator from Classes.Vector3 import Vector3 @@ -14,43 +16,149 @@ def printColor(color, message): print(color + message + reset) + +def test(testToRun: bool): + if testToRun: + printColor(green, "Pass") + return 1 + else: + printColor(red, "Fail") + return 0 -def setupTest(): - pass +def runTests(fabricator: Fabricator, isVerbose=False): + # setup Tests + tests = 0 + passes = 0 + print(f"Testing {fabricator.getDescription()}") -def test(printer: Fabricator): - print(f"Testing {printer.getDescription()}") - if printer.device.home(): - printColor(green, "Homed") - else: - printColor(red, "Failed to home") + # run tests + tests += 1 + print(f"test {tests}: Connect", end=" ") + passes += test(fabricator.device is not None) - if printer.device.goTo(endLocation): - printColor(green, "Moved to end location") - else: - printColor(red, "Failed to move to end location") + # tests += 1 + # print(f"test {tests}: Home", end=" ") + # passes += test(fabricator.device.home(isVerbose=isVerbose)) + # + # tests += 1 + # print(f"test {tests}: Square", end=" ") + # squarePasses = 0 + # for point in patterns[0]: + # squarePasses += 1 if fabricator.device.goTo(point) else 0 + # passes += test(squarePasses == len(patterns[0])) + # + # tests += 1 + # print(f"test {tests}: Octagon", end=" ") + # squarePasses = 0 + # for point in patterns[1]: + # squarePasses += 1 if fabricator.device.goTo(point) else 0 + # passes += test(squarePasses == len(patterns[1])) + + # tests += 1 + # print(f"test {tests}: Center", end=" ") + # passes += test(fabricator.device.goTo(endLocation)) - printer.device.disconnect() + tests += 1 + print(f"test {tests}: Parse Gcode", end=" ") + expectedTime = 3 * 60 + expectedMinutes, expectedSeconds = divmod(expectedTime, 60) + time = datetime.now() + passes += test(fabricator.device.parseGcode("server/xyz-cali-cube-mini_MK4.gcode", isVerbose=True)) + time = datetime.now() - time + minutes, seconds = divmod(time.seconds, 60) + print(f"\texpect print time: {int(expectedMinutes):02}:{int(expectedSeconds):02}") + print(f"\tactual print time: {int(minutes):02}:{int(seconds):02}") + + # tests += 1 + # print(f"test {tests}: Pause", end=" ") + # passes += test(fabricator.device.pause()) + # + # tests += 1 + # print(f"test {tests}: Resume", end=" ") + # passes += test(fabricator.device.resume()) + # + # tests += 1 + # print(f"test {tests}: Cancel", end=" ") + # passes += test(fabricator.device.cancel()) + # + # + # tests += 1 + # print(f"test {tests}: Status", end=" ") + # passes += test(fabricator.device.status()) + + # teardown tests + fabricator.device.disconnect() + if passes == tests: + printColor(green, f"All tests passed ({passes}/{tests})") + else: + printColor(red, f"{passes}/{tests} tests passed") -def teardownTest(): - pass endLocation = Vector3(125.0,100.0,2.0) +patterns = [[Vector3(50.0,50.0,2.0), Vector3(200.0,50.0,2.0), Vector3(200.0,150.0,2.0), Vector3(50.0,150.0,2.0)], #square + [Vector3(50.0,100.0,2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0,50.0,2.0), Vector3(200.0, 100.0, 2), Vector3(200.0,150.0,2.0), Vector3(150.0, 200.0, 2), Vector3(100.0,200.0,2.0), Vector3(50.0, 150.0, 2.0)], # octagon + ] with app.app_context(): - # home prusa mk4s + PrusaMK4S = None + PrusaMK4 = None + Ender3 = None + MakerBot = None + EnderPro = None + # FabricatorList.init() - # FabricatorList.addFabricator("COM5") - # HomePrinter = FabricatorList.fabricators[0].setName("Home Printer") - #test(HomePrinter) + # for printer in FabricatorList.fabricators: + # runTests(printer) + + # ari's MK4S + if Ports.getPortByName("COM5") is not None: + PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S", addToDB=False) + runTests(PrusaMK4S) # nate's Ender 3 Pro - EnderPro = Fabricator(Ports.getPortByName("COM5"), "Ender 3 Pro") - test(EnderPro) + if Ports.getPortByName("COM6") is not None: + EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro", addToDB=False) + runTests(EnderPro) # school prusa mk4 - # PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4") - # test(PrusaMK4) + if Ports.getPortByName("COM3") is not None: + PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4", addToDB=False) + PrusaMK4.device.serialConnection.write(b"M31\n") + line = "" + while True: + try: + line = PrusaMK4.device.serialConnection.readline().decode("utf-8") + if re.search(r"\d+m \d+s", line): + printColor(green, line) + break + print(line, end="") + except KeyboardInterrupt: + break + min, sec = map(int, re.findall(r"\d+", line)) + printTime = min * 60 + sec + print(f"Print time: {min:02}:{sec:02}") + #runTests(PrusaMK4) # school Ender 3 - # Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3") - # test(Ender3) \ No newline at end of file + if Ports.getPortByName("COM8") is not None: + Ender3 = Fabricator(Ports.getPortByName("COM8"), "Ender 3", addToDB=False) + #runTests(Ender3) + #Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) + + # school Makerbot + if Ports.getPortByName("COM7") is not None: + MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot", addToDB=False) + runTests(MakerBot) + + if PrusaMK4 is not None and PrusaMK4.device.serialConnection.is_open: + PrusaMK4.device.disconnect() + + if Ender3 is not None and Ender3.device.serialConnection.is_open: + Ender3.device.disconnect() + + if MakerBot is not None and MakerBot.device.serialConnection.is_open: + MakerBot.device.disconnect() + + if EnderPro is not None and EnderPro.device.serialConnection.is_open: + EnderPro.device.disconnect() + + From 39cb4470ee56b9b1d6fc49cf02ff02fa4baafed9 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 29 Oct 2024 19:43:11 -0400 Subject: [PATCH 028/194] PARALLEL TESTS ARE REAL!!! all tests run in parallel and each thread writes its own log file. --- Tests/parallel_test_runner.py | 79 + Tests/test_runner.py | 165 +- pytest.ini | 2 + requirements.txt | 3 + server/xyz-cali-cube-mini_ENDER3.gcode | 3069 +++ server/xyz-cali-cube-mini_MK4.gcode | 11219 +++++++++ server/xyz-cali-cube-mini_MK4S.gcode | 3855 +++ server/xyz-cali-cube_MK4.gcode | 28674 +++++++++++++++++++++++ 8 files changed, 47006 insertions(+), 60 deletions(-) create mode 100644 Tests/parallel_test_runner.py create mode 100644 pytest.ini create mode 100644 server/xyz-cali-cube-mini_ENDER3.gcode create mode 100644 server/xyz-cali-cube-mini_MK4.gcode create mode 100644 server/xyz-cali-cube-mini_MK4S.gcode create mode 100644 server/xyz-cali-cube_MK4.gcode diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py new file mode 100644 index 00000000..4a589efc --- /dev/null +++ b/Tests/parallel_test_runner.py @@ -0,0 +1,79 @@ +import os +import re +import subprocess +import threading +import platform + +from Classes.Ports import Ports + +red = '\033[31m' +green = '\033[32m' +yellow = '\033[33m' +blue = '\033[34m' +magenta = '\033[35m' +cyan = '\033[36m' +reset = '\033[0m' +PORTS = [] +# List of available ports for testing +if platform.system() == "Windows": + import winreg + path = "HARDWARE\\DEVICEMAP\\SERIALCOMM" + try: + key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path) + for i in range(256): + try: + val = winreg.EnumValue(key, i) + if re.match(r"COM\d+|LPT\d+", val[1]): + PORTS.append(val[1]) + except OSError: + break + except FileNotFoundError: + pass + +elif platform.system() == "Darwin": + import glob + PORTS = glob.glob("/dev/tty.*") +else: + import glob + PORTS = glob.glob("/dev/tty[A-Za-z]*") + +def printColor(color, message): + print(color + message + reset) + + +# Function to run pytest for a specific port +def run_tests_for_port(port): + env = os.environ.copy() + env["PORT"] = port + log_folder = "logs" + os.makedirs(log_folder, exist_ok=True) + + from datetime import datetime + timestamp = datetime.now().strftime("%m-%d-%Y_%H-%M-%S") + subfolder = os.path.join(log_folder, timestamp) + os.makedirs(subfolder, exist_ok=True) + + log_file_path = os.path.join(subfolder, f"test_{port}.log") + print(f"Running tests for {port}") + with open(log_file_path, "w") as log_file: + process = subprocess.Popen(["pytest", "test_runner.py", "-s"], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + for line in process.stdout: + log_file.write(line.decode().rstrip('\n')) + print(line.decode(), end='') + for line in process.stderr: + log_file.write(line.decode().rstrip('\n')) + print(line.decode(), end='') + printColor(green,f"Tests for {port} completed. Log file: {log_file_path}") + +# Create and start a thread for each port +threads = [] +for port in PORTS: + if Ports.getPortByName(port) is None: + continue + thread = threading.Thread(target=run_tests_for_port, args=(port,)) + thread.start() + threads.append(thread) + +# Wait for all threads to complete +for thread in threads: + thread.join() diff --git a/Tests/test_runner.py b/Tests/test_runner.py index 42e97921..308c53f1 100644 --- a/Tests/test_runner.py +++ b/Tests/test_runner.py @@ -1,95 +1,140 @@ +import os from datetime import datetime +import re import pytest +from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 +from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 +from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S from Classes.Ports import Ports from Classes.Fabricators.Fabricator import Fabricator from Classes.Vector3 import Vector3 from Mixins.canPause import canPause -testLevelToRun = 5 +class TestFabricator: + testLevelToRun = 5 + shortTest = True -@pytest.fixture(scope="module") -def test_setup(): - fabricators = [] - for port in ["COM3", "COM4", "COM5", "COM6", "COM7"]: - if Ports.getPortByName(port) is not None: - fabricators.append(Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False)) - yield fabricators + @classmethod + @pytest.fixture(scope="session", autouse=True) + def function_setup(cls, request): + port = os.getenv("PORT") + if not port: + pytest.skip("No port specified") - for fabricator in fabricators: - fabricator.device.disconnect() + fabricator_instance = Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False) -@pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") -def test_connection(test_setup): - fabricators = test_setup - for fabricator in fabricators: + yield fabricator_instance + + fabricator_instance.device.disconnect() + + @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") + @pytest.mark.order(1) + def test_connection(self, function_setup): + fabricator = function_setup assert fabricator.device is not None, f"No printer connected on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") -def test_home(test_setup): - fabricators = test_setup - for fabricator in fabricators: + @pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") + @pytest.mark.order(2) + def test_home(self, function_setup): + fabricator = function_setup assert fabricator.device.home(isVerbose=False), f"Failed to home {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_square(test_setup): - fabricators = test_setup - for fabricator in fabricators: + @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.order(3) + def test_square(self, function_setup): + fabricator = function_setup squarePasses = 0 - for point in [Vector3(50.0,50.0,2.0), Vector3(200.0,50.0,2.0), Vector3(200.0,150.0,2.0), Vector3(50.0,150.0,2.0)]: + for point in [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), + Vector3(50.0, 150.0, 2.0)]: squarePasses += 1 if fabricator.device.goTo(point) else 0 assert squarePasses == 4, f"Failed to draw square on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_octagon(test_setup): - fabricators = test_setup - for fabricator in fabricators: + + @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.order(4) + def test_octagon(self, function_setup): + fabricator = function_setup octagonPasses = 0 - for point in [Vector3(50.0,100.0,2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0,50.0,2.0), Vector3(200.0, 100.0, 2), Vector3(200.0,150.0,2.0), Vector3(150.0, 200.0, 2), Vector3(100.0,200.0,2.0), Vector3(50.0, 150.0, 2.0)]: + for point in [Vector3(50.0, 100.0, 2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0, 50.0, 2.0), + Vector3(200.0, 100.0, 2), Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), + Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)]: octagonPasses += 1 if fabricator.device.goTo(point) else 0 assert octagonPasses == 8, f"Failed to draw octagon on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_center(test_setup): - fabricators = test_setup - for fabricator in fabricators: - assert fabricator.device.goTo(Vector3(125.0,100.0,2.0)), f"Failed to go to center on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") -def test_pause(test_setup): - fabricators = test_setup - for fabricator in fabricators: + @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.order(5) + def test_center(self, function_setup): + fabricator = function_setup + assert fabricator.device.goTo( + Vector3(125.0, 100.0, 2.0)), f"Failed to go to center on {fabricator.getDescription()}" + + @pytest.mark.skip(reason="Pause isn't implemented yet") + @pytest.mark.order(6) + # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + def test_pause(self, function_setup): + fabricator = function_setup if not isinstance(fabricator.device, canPause): - continue + pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") assert fabricator.device.pause(), f"Failed to pause on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") -def test_resume(test_setup): - fabricators = test_setup - for fabricator in fabricators: + + @pytest.mark.skip(reason="Resume isn't implemented yet") + @pytest.mark.order(7) + # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + def test_resume(self, function_setup): + fabricator = function_setup if not isinstance(fabricator.device, canPause): - continue + pytest.skip(f"{fabricator.getDescription()} doesn't support resuming") assert fabricator.device.resume(), f"Failed to resume on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") -def test_cancel(test_setup): - fabricators = test_setup - for fabricator in fabricators: + + @pytest.mark.skip(reason="Cancel isn't implemented yet") + @pytest.mark.order(8) + # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + def test_cancel(self, function_setup): + fabricator = function_setup assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}" -@pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") -def test_status(test_setup): - fabricators = test_setup - for fabricator in fabricators: - assert fabricator.getStatus(), f"Failed to get status on {fabricator.getDescription()}" - -@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") -def test_gcode(test_setup): - fabricators = test_setup - for fabricator in fabricators: - expectedTime = 2040 # for my personal home test, 1072 + + @pytest.mark.skip(reason="getStatus isn't implemented yet") + @pytest.mark.order(9) + # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + def test_status(self, function_setup): + fabricator = function_setup + assert fabricator.getStatus() is not None, f"Failed to get status on {fabricator.getDescription()}" + + + @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") + @pytest.mark.order(10) + def test_gcode(self, function_setup): + fabricator = function_setup + expectedTime = 3 * 60 + file = "../server/xyz-cali-cube" + if self.shortTest: + file = file + "-mini" + if isinstance(fabricator.device, Ender3): + file = file + "_ENDER3.gcode" + expectedTime = 4 * 60 + 15 if self.shortTest else 28 * 60 + elif isinstance(fabricator.device, PrusaMK4S): + file = file + "_MK4S.gcode" + expectedTime = 180 if self.shortTest else 1800 + elif isinstance(fabricator.device, PrusaMK4): + file = file + "_MK4.gcode" + expectedTime = 10 * 60 if self.shortTest else 1800 + # expectedTime = 2040 # for my personal home test, 1072 expectedMinutes, expectedSeconds = divmod(expectedTime, 60) time = datetime.now() - assert fabricator.device.parseGcode("20mm_calibration.gcode"), f"Failed to parse Gcode on {fabricator.getDescription()}" + assert fabricator.device.parseGcode(file), f"Failed to parse Gcode on {fabricator.getDescription()}" time = datetime.now() - time minutes, seconds = divmod(time.seconds, 60) - assert abs(expectedTime - time.seconds) < 60, f"Failed to print within 1 minute of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" + fabricator.device.serialConnection.write(b"M31\n") + line = "" + while not re.search(r"\d+m \d+s", line): + line = fabricator.device.serialConnection.readline().decode("utf-8") + printMinutes, printSeconds = map(int, re.findall(r"\d+", line)) + printTime = printMinutes * 60 + printSeconds + + timeBoundary = max(120, expectedTime // 5) + assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" + assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..c9bdce5d --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --color=yes -s -vv \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7f453c1e..7607bc2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,9 @@ flask_migrate==4.0.7 flask_socketio==5.3.6 flask_sqlalchemy==3.1.1 pyserial==3.5 +pytest==8.3.3 +pytest-order==1.3.0 +pytest-xdist==3.6.1 python-dotenv==1.0.1 Requests==2.32.3 SQLAlchemy diff --git a/server/xyz-cali-cube-mini_ENDER3.gcode b/server/xyz-cali-cube-mini_ENDER3.gcode new file mode 100644 index 00000000..ec3864d6 --- /dev/null +++ b/server/xyz-cali-cube-mini_ENDER3.gcode @@ -0,0 +1,3069 @@ +; generated by PrusaSlicer 2.8.1+win64 on 2024-10-28 at 17:53:53 UTC + +; + +; external perimeters extrusion width = 0.42mm +; perimeters extrusion width = 0.44mm +; infill extrusion width = 0.44mm +; solid infill extrusion width = 0.44mm +; top infill extrusion width = 0.40mm +; first layer extrusion width = 0.42mm + +M201 X500 Y500 Z100 E5000 ; sets maximum accelerations, mm/sec^2 +M203 X500 Y500 Z10 E60 ; sets maximum feedrates, mm / sec +M204 S500 T1000 ; sets acceleration (S) and retract acceleration (R), mm/sec^2 +M205 X8.00 Y8.00 Z0.40 E5.00 ; sets the jerk limits, mm/sec +M205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec + +; printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 +; stop printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 + +;TYPE:Custom +G90 ; use absolute coordinates +M83 ; extruder relative mode +M104 S150 ; set temporary nozzle temp to prevent oozing during homing +M140 S60 ; set final bed temp +G4 S30 ; allow partial nozzle warmup +G28 ; home all axis +G1 Z50 F240 +G1 X2.0 Y10 F3000 +M140 S60 ; wait for bed temp to stabilize +M109 S210 ; wait for nozzle temp to stabilize +G1 Z0.28 F240 +G92 E0 +G1 X2.0 Y140 E10 F1500 ; prime the nozzle +G1 X2.3 Y140 F5000 +G92 E0 +G1 X2.3 Y10 E10 F1200 ; prime the nozzle +G92 E0 +G21 ; set units to millimeters +G90 ; use absolute coordinates +M83 ; use relative distances for extrusion +; Filament gcode +M107 +;LAYER_CHANGE +;Z:0.2 +;HEIGHT:0.2 +G1 E-5 F3600 +G1 Z.2 F9000 +G1 X115.579 Y98.508 +G1 Z.2 +G1 E5 F2400 +M204 S500 +;TYPE:Skirt/Brim +;WIDTH:0.42 +G1 F1200 +G1 X118.508 Y95.579 E.12988 +G1 X119.939 Y94.705 E.05257 +G1 X121.029 Y94.534 E.03459 +G1 X128.971 Y94.534 E.24902 +G1 X130.601 Y94.929 E.05259 +G1 X131.492 Y95.579 E.03458 +G1 X134.421 Y98.508 E.12988 +G1 X135.295 Y99.939 E.05257 +G1 X135.466 Y101.029 E.03459 +G1 X135.466 Y108.971 E.24902 +G1 X135.071 Y110.601 E.05259 +G1 X134.421 Y111.492 E.03458 +G1 X131.492 Y114.421 E.12988 +G1 X130.061 Y115.295 E.05257 +G1 X128.971 Y115.466 E.03459 +G1 X121.029 Y115.466 E.24902 +G1 X119.399 Y115.071 E.05259 +G1 X118.508 Y114.421 E.03458 +G1 X115.579 Y111.492 E.12988 +G1 X114.705 Y110.061 E.05257 +G1 X114.534 Y108.971 E.03459 +G1 X114.534 Y101.029 E.24902 +G1 X114.929 Y99.399 E.05259 +G1 X115.543 Y98.556 E.0327 +G1 X115.543 Y98.556 F9000 +G1 X115.841 Y98.78 +G1 F1200 +G1 X115.845 Y98.774 E.00023 +G1 X118.774 Y95.845 E.12988 +G1 X120.138 Y95.038 E.04969 +G1 X121.029 Y94.911 E.02822 +G1 X128.971 Y94.911 E.24902 +G1 X130.506 Y95.305 E.04969 +G1 X131.226 Y95.845 E.02822 +G1 X134.155 Y98.774 E.12988 +G1 X134.962 Y100.138 E.04969 +G1 X135.089 Y101.029 E.02822 +G1 X135.089 Y108.971 E.24902 +G1 X134.695 Y110.506 E.04969 +G1 X134.155 Y111.226 E.02822 +G1 X131.226 Y114.155 E.12988 +G1 X129.862 Y114.962 E.04969 +G1 X128.971 Y115.089 E.02822 +G1 X121.029 Y115.089 E.24902 +G1 X119.494 Y114.695 E.04969 +G1 X118.774 Y114.155 E.02822 +G1 X115.845 Y111.226 E.12988 +G1 X115.038 Y109.862 E.04969 +G1 X114.911 Y108.971 E.02822 +G1 X114.911 Y101.029 E.24902 +G1 X115.305 Y99.494 E.04969 +G1 X115.805 Y98.828 E.02611 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X115.841 Y98.78 E-.0285 +G1 X115.845 Y98.774 E-.00343 +G1 X118.031 Y96.589 E-1.46807 +;WIPE_END +G1 X119.104 Y100.532 F9000 +G1 E5 F2400 +G1 F1200 +G1 X120.525 Y99.109 E.06305 +G1 X121.301 Y98.708 E.02739 +G1 X122.123 Y98.454 E.02698 +G1 X127.72 Y98.429 E.17549 +G1 X128.504 Y98.63 E.02538 +G1 X129.264 Y98.977 E.0262 +G1 X130.453 Y99.981 E.04879 +G1 X131.112 Y100.91 E.03571 +G1 X131.516 Y101.981 E.03589 +G1 X131.615 Y107.39 E.16962 +G1 X131.316 Y108.631 E.04002 +G1 X130.896 Y109.468 E.02936 +G1 X129.468 Y110.896 E.06332 +G1 X128.633 Y111.315 E.02929 +G1 X127.502 Y111.602 E.03659 +G1 X122.59 Y111.613 E.15401 +G1 X121.367 Y111.315 E.03947 +G1 X120.532 Y110.896 E.02929 +G1 X119.104 Y109.468 E.06332 +G1 X118.685 Y108.633 E.02929 +G1 X118.398 Y107.502 E.03659 +G1 X118.387 Y102.59 E.15401 +G1 X118.685 Y101.367 E.03947 +G1 X119.077 Y100.585 E.02743 +G1 X119.077 Y100.585 F9000 +G1 X119.413 Y100.756 +G1 F1200 +G1 X119.427 Y100.729 E.00095 +G1 X120.725 Y99.43 E.05758 +G1 X121.429 Y99.063 E.02489 +G1 X122.208 Y98.823 E.02556 +G1 X127.649 Y98.8 E.1706 +G1 X128.393 Y98.991 E.02408 +G1 X129.082 Y99.308 E.02378 +G1 X130.189 Y100.251 E.0456 +G1 X130.789 Y101.106 E.03275 +G1 X131.152 Y102.082 E.03265 +G1 X131.237 Y107.371 E.16585 +G1 X130.955 Y108.519 E.03706 +G1 X130.573 Y109.271 E.02645 +G1 X129.271 Y110.573 E.05773 +G1 X128.52 Y110.955 E.02642 +G1 X127.432 Y111.231 E.03519 +G1 X122.614 Y111.236 E.15107 +G1 X121.48 Y110.955 E.03663 +G1 X120.729 Y110.573 E.02642 +G1 X119.427 Y109.271 E.05773 +G1 X119.045 Y108.52 E.02642 +G1 X118.769 Y107.432 E.03519 +G1 X118.764 Y102.614 E.15107 +G1 X119.045 Y101.48 E.03663 +G1 X119.386 Y100.809 E.0236 +G1 X119.386 Y100.809 F9000 +G1 X119.721 Y100.982 +G1 F1200 +G1 X119.748 Y100.928 E.00189 +G1 X120.925 Y99.75 E.05221 +G1 X121.557 Y99.418 E.02238 +G1 X122.292 Y99.191 E.02412 +G1 X127.58 Y99.171 E.1658 +G1 X128.252 Y99.341 E.02173 +G1 X128.9 Y99.639 E.02236 +G1 X129.925 Y100.522 E.04242 +G1 X130.444 Y101.26 E.02829 +G1 X130.787 Y102.183 E.03087 +G1 X130.86 Y107.351 E.16206 +G1 X130.595 Y108.405 E.03408 +G1 X130.252 Y109.072 E.02352 +G1 X129.072 Y110.252 E.05232 +G1 X128.406 Y110.595 E.02349 +G1 X127.362 Y110.859 E.03376 +G1 X122.638 Y110.859 E.14812 +G1 X121.594 Y110.595 E.03376 +G1 X120.928 Y110.252 E.02349 +G1 X119.748 Y109.072 E.05232 +G1 X119.405 Y108.406 E.02349 +G1 X119.141 Y107.362 E.03376 +G1 X119.141 Y102.638 E.14812 +G1 X119.405 Y101.594 E.03376 +G1 X119.693 Y101.035 E.01972 +G1 X119.693 Y101.035 F9000 +G1 X120.026 Y101.209 +G1 F1200 +G1 X120.069 Y101.129 E.00285 +G1 X121.127 Y100.069 E.04696 +G1 X121.685 Y99.773 E.0198 +G1 X122.377 Y99.56 E.0227 +G1 X127.511 Y99.542 E.16097 +G1 X128.142 Y99.702 E.02041 +G1 X128.716 Y99.969 E.01985 +G1 X129.66 Y100.79 E.03923 +G1 X130.12 Y101.454 E.02533 +G1 X130.422 Y102.281 E.0276 +G1 X130.483 Y107.33 E.15832 +G1 X130.236 Y108.289 E.03105 +G1 X129.931 Y108.871 E.0206 +G1 X128.871 Y109.931 E.047 +G1 X128.289 Y110.235 E.02059 +G1 X127.338 Y110.482 E.03081 +G1 X122.662 Y110.482 E.14661 +G1 X121.711 Y110.235 E.03081 +G1 X121.129 Y109.931 E.02059 +G1 X120.069 Y108.871 E.047 +G1 X119.765 Y108.289 E.02059 +G1 X119.518 Y107.338 E.03081 +G1 X119.518 Y102.662 E.14661 +G1 X119.765 Y101.711 E.03081 +G1 X119.999 Y101.262 E.01588 +G1 X119.999 Y101.262 F9000 +G1 X120.331 Y101.439 +G1 F1200 +G1 X120.388 Y101.331 E.00383 +G1 X121.33 Y100.388 E.04179 +G1 X121.815 Y100.128 E.01725 +G1 X122.461 Y99.929 E.02119 +G1 X127.443 Y99.914 E.15621 +G1 X128 Y100.052 E.01799 +G1 X128.532 Y100.3 E.0184 +G1 X129.392 Y101.057 E.03592 +G1 X129.795 Y101.646 E.02238 +G1 X130.056 Y102.377 E.02434 +G1 X130.105 Y107.308 E.15462 +G1 X129.877 Y108.17 E.02796 +G1 X129.612 Y108.669 E.01772 +G1 X128.669 Y109.612 E.04181 +G1 X128.171 Y109.877 E.01769 +G1 X127.313 Y110.105 E.02784 +G1 X122.687 Y110.105 E.14504 +G1 X121.829 Y109.877 E.02784 +G1 X121.331 Y109.612 E.01769 +G1 X120.388 Y108.669 E.04181 +G1 X120.123 Y108.171 E.01769 +G1 X119.895 Y107.313 E.02784 +G1 X119.895 Y102.687 E.14504 +G1 X120.123 Y101.829 E.02784 +G1 X120.302 Y101.492 E.01196 +G1 X120.302 Y101.492 F9000 +G1 X120.632 Y101.671 +G1 F1200 +G1 X120.706 Y101.535 E.00485 +G1 X121.535 Y100.706 E.03676 +G1 X121.944 Y100.483 E.01461 +G1 X122.545 Y100.297 E.01973 +G1 X127.375 Y100.286 E.15144 +G1 X127.89 Y100.413 E.01663 +G1 X128.346 Y100.628 E.01581 +G1 X129.122 Y101.321 E.03262 +G1 X129.467 Y101.835 E.01941 +G1 X129.69 Y102.471 E.02113 +G1 X129.728 Y107.284 E.15091 +G1 X129.52 Y108.049 E.02486 +G1 X129.294 Y108.465 E.01484 +G1 X128.465 Y109.294 E.03676 +G1 X128.049 Y109.52 E.01484 +G1 X127.287 Y109.728 E.02477 +G1 X122.713 Y109.728 E.14341 +G1 X121.951 Y109.52 E.02477 +G1 X121.535 Y109.294 E.01484 +G1 X120.706 Y108.465 E.03676 +G1 X120.48 Y108.049 E.01484 +G1 X120.272 Y107.287 E.02477 +G1 X120.272 Y102.713 E.14341 +G1 X120.48 Y101.951 E.02477 +G1 X120.604 Y101.723 E.00814 +G1 X120.604 Y101.723 F9000 +G1 X120.932 Y101.905 +G1 F1200 +G1 X121.022 Y101.742 E.00584 +G1 X121.742 Y101.022 E.03193 +G1 X122.63 Y100.666 E.03 +G1 X127.309 Y100.658 E.14671 +G1 X127.747 Y100.763 E.01412 +G1 X128.159 Y100.957 E.01428 +G1 X128.851 Y101.584 E.02928 +G1 X129.139 Y102.02 E.01638 +G1 X129.323 Y102.563 E.01798 +G1 X129.351 Y107.259 E.14724 +G1 X129.163 Y107.924 E.02167 +G1 X128.978 Y108.258 E.01197 +G1 X128.258 Y108.978 E.03193 +G1 X127.924 Y109.163 E.01197 +G1 X127.26 Y109.351 E.02164 +G1 X122.74 Y109.351 E.14172 +G1 X122.076 Y109.163 E.02164 +G1 X121.742 Y108.978 E.01197 +G1 X121.022 Y108.258 E.03193 +G1 X120.837 Y107.924 E.01197 +G1 X120.649 Y107.26 E.02164 +G1 X120.649 Y102.74 E.14172 +G1 X120.837 Y102.076 E.02164 +G1 X120.903 Y101.958 E.00424 +G1 X120.903 Y101.958 F9000 +G1 X121.28 Y102.101 +G1 F1200 +G1 X121.337 Y101.951 E.00503 +G1 X121.951 Y101.337 E.02723 +G1 X122.714 Y101.034 E.02574 +G1 X127.246 Y101.03 E.1421 +G1 X127.971 Y101.285 E.0241 +G1 X128.576 Y101.843 E.02581 +G1 X128.808 Y102.203 E.01343 +G1 X128.955 Y102.651 E.01478 +G1 X128.974 Y107.232 E.14364 +G1 X128.663 Y108.049 E.02741 +G1 X128.049 Y108.663 E.02723 +G1 X127.232 Y108.974 E.02741 +G1 X122.768 Y108.974 E.13997 +G1 X121.951 Y108.663 E.02741 +G1 X121.337 Y108.049 E.02723 +G1 X121.026 Y107.232 E.02741 +G1 X121.026 Y102.768 E.13997 +G1 X121.258 Y102.157 E.02049 +G1 X121.258 Y102.157 F9000 +G1 X121.6 Y102.289 +G1 F1200 +G1 X121.648 Y102.164 E.0042 +G1 X122.164 Y101.648 E.02288 +G1 X122.798 Y101.403 E.02131 +G1 X127.224 Y101.407 E.13877 +G1 X127.782 Y101.613 E.01865 +G1 X128.301 Y102.101 E.02234 +G1 X128.587 Y102.74 E.02195 +G1 X128.597 Y107.203 E.13993 +G1 X128.352 Y107.836 E.02128 +G1 X127.836 Y108.352 E.02288 +G1 X127.202 Y108.597 E.02131 +G1 X122.798 Y108.597 E.13808 +G1 X122.164 Y108.352 E.02131 +G1 X121.648 Y107.836 E.02288 +G1 X121.403 Y107.202 E.02131 +G1 X121.403 Y102.798 E.13808 +G1 X121.578 Y102.345 E.01523 +G1 X121.578 Y102.345 F9000 +G1 X121.918 Y102.479 +G1 F1200 +G1 X121.956 Y102.383 E.00324 +G1 X122.383 Y101.956 E.01893 +G1 X122.829 Y101.78 E.01503 +G1 X127.202 Y101.784 E.13711 +G1 X127.592 Y101.94 E.01317 +G1 X128.023 Y102.357 E.0188 +G1 X128.22 Y102.829 E.01604 +G1 X128.22 Y107.171 E.13614 +G1 X128.044 Y107.617 E.01503 +G1 X127.617 Y108.044 E.01893 +G1 X127.171 Y108.22 E.01503 +G1 X122.829 Y108.22 E.13614 +G1 X122.383 Y108.044 E.01503 +G1 X121.956 Y107.617 E.01893 +G1 X121.78 Y107.171 E.01503 +G1 X121.78 Y102.829 E.13614 +G1 X121.896 Y102.535 E.00991 +G1 X121.896 Y102.535 F9000 +G1 X122.234 Y102.667 +G1 F1200 +G1 X122.257 Y102.61 E.00193 +G1 X122.61 Y102.257 E.01565 +G1 X122.864 Y102.157 E.00856 +G1 X127.182 Y102.164 E.13539 +G1 X127.4 Y102.267 E.00756 +G1 X127.743 Y102.61 E.01521 +G1 X127.843 Y102.864 E.00856 +G1 X127.843 Y107.136 E.13395 +G1 X127.743 Y107.39 E.00856 +G1 X127.39 Y107.743 E.01565 +G1 X127.136 Y107.843 E.00856 +G1 X122.864 Y107.843 E.13395 +G1 X122.61 Y107.743 E.00856 +G1 X122.257 Y107.39 E.01565 +G1 X122.157 Y107.136 E.00856 +G1 X122.157 Y102.864 E.13395 +G1 X122.213 Y102.723 E.00476 +G1 X122.213 Y102.723 F9000 +G1 X122.563 Y102.837 +G1 F1200 +G1 X122.906 Y102.534 E.01435 +G1 X127.094 Y102.534 E.13131 +G1 X127.466 Y102.906 E.0165 +G1 X127.437 Y107.163 E.13348 +G1 X127.094 Y107.466 E.01435 +G1 X122.837 Y107.437 E.13348 +G1 X122.534 Y107.094 E.01435 +G1 X122.563 Y102.897 E.1316 +G1 X122.563 Y102.897 F9000 +G1 X122.911 Y103.022 +G1 F1200 +G1 X123.022 Y102.911 E.00492 +G1 X126.978 Y102.911 E.12404 +G1 X127.089 Y103.022 E.00492 +G1 X127.089 Y106.978 E.12404 +G1 X126.978 Y107.089 E.00492 +G1 X123.022 Y107.089 E.12404 +G1 X122.911 Y106.978 E.00492 +G1 X122.911 Y103.082 E.12216 +; printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 +G1 X122.911 Y103.082 F9000 +G1 X124.064 Y104.064 +;TYPE:Perimeter +;WIDTH:0.419999 +G1 F1200 +G1 X125.936 Y104.064 E.0587 +G1 X125.936 Y105 E.02935 +G1 X125.936 Y105.936 E.02935 +G1 X124.064 Y105.936 E.0587 +G1 X124.064 Y104.124 E.05681 +G1 X124.064 Y104.124 F9000 +G1 X123.687 Y103.687 +G1 F1200 +G1 X126.313 Y103.687 E.08234 +G1 X126.313 Y105 E.04117 +G1 X126.313 Y106.313 E.04117 +G1 X123.687 Y106.313 E.08234 +G1 X123.687 Y103.747 E.08045 +G1 X123.31 Y103.31 F9000 +;TYPE:External perimeter +G1 F1200 +G1 X126.69 Y103.31 E.10598 +G1 X126.69 Y105 E.05299 +G1 X126.69 Y106.69 E.05299 +G1 X123.31 Y106.69 E.10598 +G1 X123.31 Y103.37 E.1041 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.31 Y103.31 E-.0285 +G1 X126.408 Y103.31 E-1.4715 +;WIPE_END +G1 X125.815 Y104.981 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.436085 +G1 F1200 +G1 X125.188 Y104.355 E.02897 +G1 X124.632 Y104.355 E.01818 +G1 X125.645 Y105.368 E.04683 +G1 X125.645 Y105.645 E.00906 +G1 X125.367 Y105.645 E.00909 +G1 X124.355 Y104.633 E.04679 +G1 X124.355 Y105.189 E.01818 +G1 X124.981 Y105.815 E.02894 +M106 S255 +;LAYER_CHANGE +;Z:0.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;0.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.355 Y105.189 E-.42052 +G1 X124.355 Y104.633 E-.2641 +G1 X125.367 Y105.645 E-.67981 +G1 X125.645 Y105.645 E-.13205 +G1 X125.645 Y105.638 E-.00352 +;WIPE_END +G1 Z.4 F9000 +;AFTER_LAYER_CHANGE +;0.4 +M104 S205 ; set temperature +; stop printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 +G1 Z.4 +G1 X124.981 Y115.466 +G1 E5 F2400 +;TYPE:Skirt/Brim +;WIDTH:0.42 +G1 F900 +G1 X121.029 Y115.466 E.12391 +G1 X119.399 Y115.071 E.05259 +G1 X118.508 Y114.421 E.03458 +G1 X115.579 Y111.492 E.12988 +G1 X114.705 Y110.061 E.05257 +G1 X114.534 Y108.971 E.03459 +G1 X114.534 Y101.029 E.24902 +G1 X114.929 Y99.399 E.05259 +G1 X115.579 Y98.508 E.03458 +G1 X118.508 Y95.579 E.12988 +G1 X119.939 Y94.705 E.05257 +G1 X121.029 Y94.534 E.03459 +G1 X128.971 Y94.534 E.24902 +G1 X130.601 Y94.929 E.05259 +G1 X131.492 Y95.579 E.03458 +G1 X134.421 Y98.508 E.12988 +G1 X135.295 Y99.939 E.05257 +G1 X135.466 Y101.029 E.03459 +G1 X135.466 Y108.971 E.24902 +G1 X135.071 Y110.601 E.05259 +G1 X134.421 Y111.492 E.03458 +G1 X131.492 Y114.421 E.12988 +G1 X130.061 Y115.295 E.05257 +G1 X128.971 Y115.466 E.03459 +G1 X125.041 Y115.466 E.12322 +G1 X125.041 Y115.466 F9000 +G1 X125.041 Y115.089 +G1 F900 +G1 X121.029 Y115.089 E.12579 +G1 X119.494 Y114.695 E.04969 +G1 X118.774 Y114.155 E.02822 +G1 X115.845 Y111.226 E.12988 +G1 X115.038 Y109.862 E.04969 +G1 X114.911 Y108.971 E.02822 +G1 X114.911 Y101.029 E.24902 +G1 X115.305 Y99.494 E.04969 +G1 X115.845 Y98.774 E.02822 +G1 X118.774 Y95.845 E.12988 +G1 X120.138 Y95.038 E.04969 +G1 X121.029 Y94.911 E.02822 +G1 X128.971 Y94.911 E.24902 +G1 X130.506 Y95.305 E.04969 +G1 X131.226 Y95.845 E.02822 +G1 X134.155 Y98.774 E.12988 +G1 X134.962 Y100.138 E.04969 +G1 X135.089 Y101.029 E.02822 +G1 X135.089 Y108.971 E.24902 +G1 X134.695 Y110.506 E.04969 +G1 X134.155 Y111.226 E.02822 +G1 X131.226 Y114.155 E.12988 +G1 X129.862 Y114.962 E.04969 +G1 X128.971 Y115.089 E.02822 +G1 X125.101 Y115.089 E.12134 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.041 Y115.089 E-.0285 +G1 X121.943 Y115.089 E-1.4715 +;WIPE_END +; printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 +G1 X126.006 Y106.006 F9000 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X123.994 Y106.006 E.06643 +G1 X123.994 Y103.994 E.06643 +G1 X126.006 Y103.994 E.06643 +G1 X126.006 Y105 E.03322 +G1 X126.006 Y105.946 E.03123 +G1 X126.006 Y105.946 F9000 +G1 X126.403 Y106.403 +G1 F900 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y103.597 E.09265 +G1 X126.403 Y103.597 E.09265 +G1 X126.403 Y105 E.04632 +G1 X126.403 Y106.343 E.04434 +G1 X126.79 Y106.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y103.21 E.11225 +G1 X126.79 Y103.21 E.11225 +G1 X126.79 Y105 E.05612 +G1 X126.79 Y106.73 E.05424 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.79 Y106.79 E-.0285 +G1 X123.692 Y106.79 E-1.4715 +;WIPE_END +G1 X125.502 Y104.3 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X124.3 Y104.3 E.03969 +G1 X124.3 Y105.7 E.04622 +G1 X125.7 Y105.7 E.04622 +G1 X125.7 Y104.3 E.04622 +G1 X125.251 Y104.749 E.02097 +;WIDTH:0.544473 +G1 X125.251 Y105.251 E.02094 +G1 X124.749 Y105.251 E.02094 +G1 X124.749 Y104.749 E.02094 +G1 X125.052 Y104.749 E.01264 +;LAYER_CHANGE +;Z:0.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;0.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.749 Y104.749 E-.14392 +G1 X124.749 Y105.251 E-.23845 +G1 X125.251 Y105.251 E-.23845 +G1 X125.251 Y104.749 E-.23845 +G1 X125.7 Y104.3 E-.30162 +G1 X125.7 Y105.014 E-.33911 +;WIPE_END +G1 Z.6 F9000 +;AFTER_LAYER_CHANGE +;0.6 +G1 Z.6 +G1 X126.006 Y103.994 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.006 Y105 E.03322 +G1 X126.006 Y106.006 E.03322 +G1 X123.994 Y106.006 E.06643 +G1 X123.994 Y103.994 E.06643 +G1 X125.946 Y103.994 E.06445 +G1 X125.946 Y103.994 F9000 +G1 X126.403 Y103.597 +G1 F900 +G1 X126.403 Y105 E.04632 +G1 X126.403 Y106.403 E.04632 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y103.597 E.09265 +G1 X126.343 Y103.597 E.09067 +G1 X126.79 Y103.21 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.79 Y105 E.05612 +G1 X126.79 Y106.79 E.05612 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y103.21 E.11225 +G1 X126.73 Y103.21 E.11037 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.79 Y103.21 E-.0285 +G1 X126.79 Y105 E-.85025 +G1 X126.79 Y106.308 E-.62125 +;WIPE_END +G1 X124.3 Y105.7 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X124.3 Y104.3 E.04622 +G1 X125.7 Y104.3 E.04622 +G1 X125.7 Y105.7 E.04622 +G1 X124.498 Y105.7 E.03969 +G1 X124.749 Y105.251 E.01698 +;WIDTH:0.544473 +G1 X124.749 Y104.749 E.02094 +G1 X125.251 Y104.749 E.02094 +G1 X125.251 Y105.251 E.02094 +G1 X124.948 Y105.251 E.01264 +;LAYER_CHANGE +;Z:0.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;0.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.251 Y105.251 E-.14392 +G1 X125.251 Y104.749 E-.23845 +G1 X124.749 Y104.749 E-.23845 +G1 X124.749 Y105.251 E-.23845 +G1 X124.498 Y105.7 E-.24434 +G1 X125.333 Y105.7 E-.39639 +;WIPE_END +G1 Z.8 F9000 +;AFTER_LAYER_CHANGE +;0.8 +G1 Z.8 +G1 X123.996 Y105.988 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.008 Y105.894 E.00313 +G1 X124.194 Y105.321 E.01989 +G1 X124.194 Y104.679 E.0212 +G1 X124.136 Y104.347 E.01113 +G1 X124.049 Y104.194 E.00581 +G1 X124.441 Y104.174 E.01296 +G1 X124.655 Y104.103 E.00744 +G1 X124.768 Y103.994 E.00518 +G1 X125.22 Y103.994 E.01492 +G1 X125.261 Y104.065 E.00271 +G1 X125.509 Y104.165 E.00883 +G1 X126.003 Y104.197 E.01634 +G1 X125.884 Y104.406 E.00794 +G1 X125.818 Y104.634 E.00784 +G1 X125.806 Y105.189 E.01833 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y106.006 E.00878 +G1 X124.036 Y106.006 E.06504 +G1 X124.036 Y106.006 F9000 +G1 X123.602 Y105.703 +G1 F900 +G1 X123.687 Y105.66 E.00315 +G1 X123.797 Y105.321 E.01177 +G1 X123.791 Y104.596 E.02394 +G1 X123.668 Y104.316 E.0101 +G1 X123.597 Y104.282 E.0026 +G1 X123.597 Y103.69 E.01955 +G1 X123.787 Y103.797 E.0072 +G1 X124.243 Y103.797 E.01506 +G1 X124.487 Y103.743 E.00825 +G1 X124.601 Y103.597 E.00612 +G1 X125.387 Y103.597 E.02595 +G1 X125.458 Y103.721 E.00472 +G1 X125.605 Y103.78 E.00523 +G1 X126.213 Y103.797 E.02008 +G1 X126.403 Y103.69 E.0072 +G1 X126.403 Y104.399 E.02341 +G1 X126.321 Y104.44 E.00303 +G1 X126.21 Y104.697 E.00924 +G1 X126.203 Y105.176 E.01582 +G1 X126.303 Y105.5 E.0112 +G1 X126.403 Y105.553 E.00374 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.76 E.02123 +G1 X123.345 Y105.448 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.374 Y105.432 E.00104 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.368 Y104.56 E.00396 +G1 X123.21 Y104.49 E.00542 +G1 X123.21 Y103.21 E.04013 +G1 X123.587 Y103.21 E.01182 +G1 X123.625 Y103.318 E.00359 +G1 X123.787 Y103.41 E.00584 +G1 X124.307 Y103.399 E.01631 +G1 X124.431 Y103.21 E.00709 +G1 X125.545 Y103.21 E.03493 +G1 X125.651 Y103.385 E.00642 +G1 X125.699 Y103.404 E.00162 +G1 X126.213 Y103.41 E.01612 +G1 X126.375 Y103.318 E.00584 +G1 X126.402 Y103.221 E.00316 +G1 X126.79 Y103.21 E.01217 +G1 X126.779 Y104.6 E.04358 +G1 X126.605 Y104.715 E.00654 +G1 X126.59 Y105.176 E.01446 +G1 X126.623 Y105.282 E.00348 +G1 X126.79 Y105.376 E.00601 +G1 X126.79 Y106.79 E.04433 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.292 Y105.477 E.00292 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.345 Y105.448 E-.0287 +G1 X123.374 Y105.432 E-.01573 +G1 X123.41 Y105.321 E-.05543 +G1 X123.41 Y104.679 E-.30495 +G1 X123.368 Y104.56 E-.05994 +G1 X123.21 Y104.49 E-.08209 +G1 X123.21 Y103.21 E-.608 +G1 X123.587 Y103.21 E-.17907 +G1 X123.625 Y103.318 E-.05438 +G1 X123.787 Y103.41 E-.08849 +G1 X123.836 Y103.409 E-.02322 +;WIPE_END +G1 X124.995 Y104.3 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X125.547 Y104.476 E.01913 +G1 X125.515 Y104.612 E.00461 +G1 X125.501 Y105.185 E.01892 +G1 X125.655 Y105.7 E.01775 +G1 X124.39 Y105.7 E.04177 +G1 X124.491 Y105.364 E.01158 +G1 X124.5 Y104.834 E.0175 +G1 X124.464 Y104.463 E.01231 +G1 X124.644 Y104.432 E.00603 +G1 X124.808 Y104.347 E.0061 +G1 X125.004 Y104.834 E.01733 +;WIDTH:0.654978 +G1 X125 Y105.2 E.01863 +;LAYER_CHANGE +;Z:1 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;1 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.004 Y104.834 E-.17386 +G1 X124.808 Y104.347 E-.24936 +G1 X124.644 Y104.432 E-.08774 +G1 X124.464 Y104.463 E-.08676 +G1 X124.5 Y104.834 E-.17705 +G1 X124.491 Y105.364 E-.25179 +G1 X124.39 Y105.7 E-.16665 +G1 X125.036 Y105.7 E-.30679 +;WIPE_END +G1 Z1 F9000 +;AFTER_LAYER_CHANGE +;1 +G1 Z1 +G1 X126.006 Y105.823 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.006 Y106.006 E.00604 +G1 X123.994 Y106.006 E.06643 +G1 X124.008 Y105.894 E.00373 +G1 X124.194 Y105.321 E.01989 +G1 X124.189 Y104.583 E.02437 +G1 X124.043 Y104.194 E.01372 +G1 X124.387 Y104.194 E.01136 +G1 X124.721 Y104.135 E.0112 +G1 X124.969 Y103.994 E.00942 +G1 X125.177 Y104.096 E.00765 +G1 X125.446 Y104.181 E.00931 +G1 X126.003 Y104.197 E.0184 +G1 X125.845 Y104.516 E.01175 +G1 X125.813 Y104.671 E.00523 +G1 X125.806 Y105.189 E.0171 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y105.763 E.00076 +G1 X126.006 Y105.763 F9000 +G1 X126.403 Y105.553 +G1 F900 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.706 E.02301 +G1 X123.687 Y105.66 E.00334 +G1 X123.797 Y105.321 E.01177 +G1 X123.794 Y104.622 E.02308 +G1 X123.676 Y104.326 E.01052 +G1 X123.597 Y104.282 E.00299 +G1 X123.597 Y103.632 E.02146 +G1 X123.935 Y103.797 E.01242 +G1 X124.458 Y103.793 E.01727 +G1 X124.728 Y103.685 E.0096 +G1 X124.773 Y103.597 E.00326 +G1 X125.218 Y103.597 E.01469 +G1 X125.262 Y103.685 E.00325 +G1 X125.51 Y103.789 E.00888 +G1 X126.07 Y103.797 E.01849 +G1 X126.403 Y103.632 E.01227 +G1 X126.403 Y104.399 E.02532 +G1 X126.321 Y104.44 E.00303 +G1 X126.226 Y104.627 E.00693 +G1 X126.203 Y105.176 E.01814 +G1 X126.303 Y105.5 E.0112 +G1 X126.35 Y105.525 E.00176 +G1 X126.35 Y105.525 F9000 +G1 X126.661 Y105.301 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.79 Y105.364 E.0045 +G1 X126.79 Y106.79 E.04471 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.374 Y105.432 E.00585 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.37 Y104.563 E.00385 +G1 X123.21 Y104.49 E.00551 +G1 X123.21 Y103.21 E.04013 +G1 X123.735 Y103.21 E.01646 +G1 X123.785 Y103.336 E.00425 +G1 X123.935 Y103.41 E.00524 +G1 X124.47 Y103.391 E.01679 +G1 X124.575 Y103.21 E.00656 +G1 X125.404 Y103.21 E.02599 +G1 X125.521 Y103.391 E.00676 +G1 X126.003 Y103.41 E.01512 +G1 X126.22 Y103.335 E.0072 +G1 X126.259 Y103.21 E.00411 +G1 X126.79 Y103.21 E.01665 +G1 X126.79 Y103.527 E.00994 +G1 X126.79 Y104.589 E.0333 +G1 X126.629 Y104.674 E.00571 +G1 X126.59 Y104.894 E.00701 +G1 X126.621 Y105.264 E.01164 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.661 Y105.301 E-.02588 +G1 X126.79 Y105.364 E-.06819 +G1 X126.79 Y106.79 E-.67735 +G1 X125.256 Y106.79 E-.72858 +;WIPE_END +G1 X124.5 Y104.671 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.5 Y105.338 E.02202 +G1 X124.422 Y105.578 E.00833 +G1 X125.51 Y104.49 E.0508 +G1 X124.991 Y104.339 E.01785 +G1 X124.487 Y104.487 E.01734 +G1 X125.655 Y105.7 E.0556 +G1 X124.452 Y105.7 E.03972 +;LAYER_CHANGE +;Z:1.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;1.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.655 Y105.7 E-.57143 +G1 X124.487 Y104.487 E-.79986 +G1 X124.747 Y104.411 E-.12871 +;WIPE_END +G1 Z1.2 F9000 +;AFTER_LAYER_CHANGE +;1.2 +G1 Z1.2 +G1 X123.996 Y105.988 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.008 Y105.894 E.00313 +G1 X124.194 Y105.321 E.01989 +G1 X124.194 Y104.679 E.0212 +G1 X124.084 Y104.194 E.01642 +G1 X124.786 Y104.16 E.02321 +G1 X124.997 Y104.059 E.00772 +G1 X125.077 Y104.114 E.00321 +G1 X125.462 Y104.194 E.01298 +G1 X126.001 Y104.196 E.0178 +G1 X125.821 Y104.621 E.01524 +G1 X125.806 Y105.189 E.01876 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y106.006 E.00878 +G1 X124.036 Y106.006 E.06504 +G1 X124.036 Y106.006 F9000 +G1 X123.602 Y105.703 +G1 F900 +G1 X123.687 Y105.66 E.00315 +G1 X123.797 Y105.321 E.01177 +G1 X123.797 Y104.679 E.0212 +G1 X123.726 Y104.401 E.00947 +G1 X123.597 Y104.283 E.00577 +G1 X123.597 Y103.597 E.02265 +G1 X123.983 Y103.788 E.01422 +G1 X124.084 Y103.797 E.00335 +G1 X124.682 Y103.777 E.01976 +G1 X124.997 Y103.597 E.01198 +G1 X125.234 Y103.75 E.00931 +G1 X125.363 Y103.788 E.00444 +G1 X125.927 Y103.797 E.01862 +G1 X126.403 Y103.597 E.01705 +G1 X126.403 Y104.399 E.02648 +G1 X126.321 Y104.44 E.00303 +G1 X126.212 Y104.689 E.00897 +G1 X126.203 Y105.176 E.01608 +G1 X126.303 Y105.5 E.0112 +G1 X126.403 Y105.553 E.00374 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.76 E.02123 +G1 X123.343 Y105.449 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.374 Y105.432 E.00111 +G1 X123.41 Y105.176 E.00811 +G1 X123.387 Y104.588 E.01845 +G1 X123.21 Y104.49 E.00634 +G1 X123.21 Y103.21 E.04013 +G1 X123.884 Y103.21 E.02113 +G1 X123.944 Y103.348 E.00472 +G1 X124.051 Y103.407 E.00383 +G1 X124.531 Y103.41 E.01505 +G1 X124.668 Y103.351 E.00468 +G1 X124.719 Y103.21 E.0047 +G1 X125.262 Y103.21 E.01703 +G1 X125.326 Y103.351 E.00486 +G1 X125.413 Y103.404 E.00319 +G1 X125.927 Y103.41 E.01612 +G1 X126.067 Y103.347 E.00481 +G1 X126.115 Y103.21 E.00455 +G1 X126.79 Y103.21 E.02116 +G1 X126.79 Y104.589 E.04324 +G1 X126.62 Y104.686 E.00614 +G1 X126.59 Y104.939 E.00799 +G1 X126.623 Y105.282 E.0108 +G1 X126.79 Y105.376 E.00601 +G1 X126.79 Y106.79 E.04433 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.29 Y105.478 E.00285 +G1 X124.452 Y105.7 F9000 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X125.655 Y105.7 E.03972 +G1 X124.497 Y104.497 E.05513 +G1 X124.993 Y104.411 E.01662 +G1 X125.5 Y104.5 E.017 +G1 X124.422 Y105.578 E.05034 +G1 X124.5 Y105.338 E.00833 +G1 X124.494 Y104.677 E.02183 +;LAYER_CHANGE +;Z:1.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;1.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.5 Y105.338 E-.31399 +G1 X124.422 Y105.578 E-.11987 +G1 X125.5 Y104.5 E-.72415 +G1 X124.993 Y104.411 E-.24451 +G1 X124.791 Y104.446 E-.09748 +;WIPE_END +G1 Z1.4 F9000 +;AFTER_LAYER_CHANGE +;1.4 +G1 Z1.4 +G1 X124.034 Y104.163 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.675 Y104.194 E.02119 +G1 X124.987 Y104.143 E.01044 +G1 X125.321 Y104.194 E.01116 +G1 X126.006 Y104.161 E.02264 +G1 X125.898 Y104.375 E.00791 +G1 X125.808 Y104.718 E.01171 +G1 X125.806 Y105.189 E.01555 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y106.006 E.00878 +G1 X123.994 Y106.006 E.06643 +G1 X124.008 Y105.894 E.00373 +G1 X124.194 Y105.321 E.01989 +G1 X124.194 Y104.679 E.0212 +G1 X124.132 Y104.338 E.01144 +G1 X124.063 Y104.215 E.00466 +G1 X124.063 Y104.215 F9000 +G1 X123.608 Y104.29 +G1 F900 +G1 X123.597 Y104.285 E.0004 +G1 X123.597 Y103.597 E.02272 +G1 X123.736 Y103.597 E.00459 +G1 X124.056 Y103.769 E.012 +G1 X124.232 Y103.797 E.00588 +G1 X124.86 Y103.767 E.02076 +G1 X124.998 Y103.646 E.00606 +G1 X125.137 Y103.767 E.00608 +G1 X125.321 Y103.797 E.00616 +G1 X125.902 Y103.785 E.01919 +G1 X126.201 Y103.597 E.01166 +G1 X126.403 Y103.597 E.00667 +G1 X126.403 Y104.4 E.02651 +G1 X126.319 Y104.442 E.0031 +G1 X126.204 Y104.747 E.01076 +G1 X126.203 Y105.176 E.01416 +G1 X126.303 Y105.5 E.0112 +G1 X126.403 Y105.553 E.00374 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.706 E.02301 +G1 X123.687 Y105.66 E.00334 +G1 X123.797 Y105.321 E.01177 +G1 X123.797 Y104.971 E.01156 +G1 X123.761 Y104.477 E.01635 +G1 X123.673 Y104.321 E.00591 +G1 X123.662 Y104.316 E.0004 +G1 X123.662 Y104.316 F9000 +G1 X123.344 Y104.55 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y104.49 E.0046 +G1 X123.21 Y103.21 E.04013 +G1 X124.032 Y103.21 E.02577 +G1 X124.101 Y103.357 E.00509 +G1 X124.174 Y103.401 E.00267 +G1 X124.675 Y103.41 E.01571 +G1 X124.844 Y103.306 E.00622 +G1 X124.864 Y103.221 E.00274 +G1 X125.121 Y103.21 E.00807 +G1 X125.153 Y103.306 E.00317 +G1 X125.261 Y103.4 E.00449 +G1 X125.784 Y103.41 E.0164 +G1 X125.915 Y103.356 E.00444 +G1 X125.972 Y103.21 E.00491 +G1 X126.79 Y103.21 E.02565 +G1 X126.79 Y104.589 E.04324 +G1 X126.608 Y104.709 E.00684 +G1 X126.59 Y104.971 E.00823 +G1 X126.623 Y105.282 E.00981 +G1 X126.79 Y105.376 E.00601 +G1 X126.79 Y106.79 E.04433 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.374 Y105.432 E.00585 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.38 Y104.592 E.00289 +G1 X123.38 Y104.592 F9000 +G1 X124.5 Y104.665 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.5 Y105.338 E.02222 +G1 X124.422 Y105.578 E.00833 +G1 X125.506 Y104.494 E.05062 +G1 X124.992 Y104.454 E.01702 +G1 X124.5 Y104.5 E.01632 +G1 X125.655 Y105.7 E.05499 +G1 X124.452 Y105.7 E.03972 +;LAYER_CHANGE +;Z:1.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;1.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.655 Y105.7 E-.57143 +G1 X124.5 Y104.5 E-.79113 +G1 X124.788 Y104.473 E-.13744 +;WIPE_END +G1 Z1.6 F9000 +;AFTER_LAYER_CHANGE +;1.6 +G1 Z1.6 +G1 X123.996 Y105.988 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.008 Y105.894 E.00313 +G1 X124.194 Y105.321 E.01989 +G1 X124.169 Y104.46 E.02844 +G1 X124.011 Y104.117 E.01247 +G1 X124.38 Y104.194 E.01245 +G1 X125.64 Y104.194 E.0416 +G1 X126.006 Y104.111 E.01239 +G1 X126.006 Y104.184 E.00241 +G1 X125.854 Y104.488 E.01122 +G1 X125.806 Y104.789 E.01006 +G1 X125.806 Y105.189 E.01321 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y106.006 E.00878 +G1 X124.036 Y106.006 E.06504 +G1 X124.036 Y106.006 F9000 +G1 X123.602 Y105.703 +G1 F900 +G1 X123.687 Y105.66 E.00315 +G1 X123.797 Y105.321 E.01177 +G1 X123.782 Y104.549 E.02549 +G1 X123.677 Y104.327 E.00811 +G1 X123.597 Y104.287 E.00295 +G1 X123.597 Y103.597 E.02278 +G1 X123.977 Y103.597 E.01255 +G1 X124.004 Y103.657 E.00217 +G1 X124.271 Y103.787 E.00981 +G1 X124.877 Y103.794 E.02001 +G1 X125 Y103.712 E.00488 +G1 X125.065 Y103.785 E.00323 +G1 X125.211 Y103.797 E.00484 +G1 X125.824 Y103.767 E.02026 +G1 X126.02 Y103.654 E.00747 +G1 X126.046 Y103.597 E.00207 +G1 X126.403 Y103.597 E.01179 +G1 X126.403 Y104.402 E.02658 +G1 X126.316 Y104.446 E.00322 +G1 X126.231 Y104.611 E.00613 +G1 X126.203 Y105.176 E.01868 +G1 X126.303 Y105.5 E.0112 +G1 X126.403 Y105.553 E.00374 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.76 E.02123 +G1 X123.343 Y105.449 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.374 Y105.432 E.00111 +G1 X123.41 Y105.176 E.00811 +G1 X123.394 Y104.602 E.018 +G1 X123.21 Y104.49 E.00675 +G1 X123.21 Y103.21 E.04013 +G1 X124.192 Y103.221 E.03079 +G1 X124.302 Y103.393 E.0064 +G1 X124.819 Y103.41 E.01622 +G1 X125 Y103.237 E.00785 +G1 X125.143 Y103.406 E.00694 +G1 X125.64 Y103.41 E.01558 +G1 X125.765 Y103.363 E.00419 +G1 X125.829 Y103.21 E.0052 +G1 X126.79 Y103.21 E.03013 +G1 X126.79 Y104.589 E.04324 +G1 X126.627 Y104.676 E.00579 +G1 X126.599 Y104.73 E.00191 +G1 X126.59 Y104.991 E.00819 +G1 X126.623 Y105.282 E.00918 +G1 X126.79 Y105.376 E.00601 +G1 X126.79 Y106.79 E.04433 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.29 Y105.478 E.00285 +G1 X124.452 Y105.7 F9000 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X125.655 Y105.7 E.03972 +G1 X124.497 Y104.497 E.05513 +G1 X125.507 Y104.493 E.03335 +G1 X124.422 Y105.578 E.05066 +G1 X124.5 Y105.338 E.00833 +G1 X124.5 Y104.674 E.02192 +;LAYER_CHANGE +;Z:1.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;1.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.5 Y105.338 E-.3154 +G1 X124.422 Y105.578 E-.11987 +G1 X125.507 Y104.493 E-.72885 +G1 X124.8 Y104.496 E-.33588 +;WIPE_END +G1 Z1.8 F9000 +;AFTER_LAYER_CHANGE +;1.8 +G1 Z1.8 +G1 X123.994 Y104.032 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.307 Y104.168 E.01127 +G1 X124.529 Y104.194 E.00738 +G1 X125.681 Y104.177 E.03804 +G1 X126.006 Y104.042 E.01162 +G1 X125.992 Y104.217 E.0058 +G1 X125.825 Y104.595 E.01364 +G1 X125.806 Y105.189 E.01962 +G1 X126.006 Y105.74 E.01935 +G1 X126.006 Y106.006 E.00878 +G1 X123.994 Y106.006 E.06643 +G1 X124.008 Y105.894 E.00373 +G1 X124.194 Y105.321 E.01989 +G1 X124.188 Y104.574 E.02466 +G1 X124.014 Y104.088 E.01704 +G1 X124.014 Y104.088 F9000 +G1 X123.605 Y104.294 +G1 F900 +G1 X123.597 Y104.291 E.00028 +G1 X123.597 Y103.597 E.02291 +G1 X124.135 Y103.597 E.01776 +G1 X124.235 Y103.716 E.00513 +G1 X124.529 Y103.797 E.01007 +G1 X125.606 Y103.787 E.03556 +G1 X125.857 Y103.671 E.00913 +G1 X125.892 Y103.597 E.0027 +G1 X126.403 Y103.597 E.01687 +G1 X126.403 Y104.404 E.02664 +G1 X126.313 Y104.45 E.00334 +G1 X126.214 Y104.674 E.00809 +G1 X126.203 Y105.176 E.01658 +G1 X126.303 Y105.5 E.0112 +G1 X126.403 Y105.553 E.00374 +G1 X126.403 Y106.403 E.02806 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.706 E.02301 +G1 X123.687 Y105.66 E.00334 +G1 X123.797 Y105.321 E.01177 +G1 X123.794 Y104.616 E.02328 +G1 X123.682 Y104.333 E.01005 +G1 X123.658 Y104.321 E.00089 +G1 X123.658 Y104.321 F9000 +G1 X123.343 Y104.552 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y104.49 E.0046 +G1 X123.21 Y103.21 E.04013 +G1 X124.329 Y103.21 E.03509 +G1 X124.433 Y103.384 E.00636 +G1 X124.957 Y103.41 E.01645 +G1 X124.999 Y103.378 E.00166 +G1 X125.033 Y103.41 E.00146 +G1 X125.497 Y103.41 E.01455 +G1 X125.615 Y103.369 E.00392 +G1 X125.686 Y103.21 E.00546 +G1 X126.79 Y103.21 E.03462 +G1 X126.779 Y104.6 E.04358 +G1 X126.604 Y104.717 E.0066 +G1 X126.59 Y104.958 E.00757 +G1 X126.623 Y105.282 E.01021 +G1 X126.79 Y105.376 E.00601 +G1 X126.79 Y106.79 E.04433 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.374 Y105.432 E.00585 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.381 Y104.591 E.00291 +G1 X123.381 Y104.591 F9000 +G1 X124.5 Y104.678 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.5 Y105.338 E.02179 +G1 X124.422 Y105.578 E.00833 +G1 X125.5 Y104.5 E.05034 +G1 X124.497 Y104.497 E.03312 +G1 X125.655 Y105.7 E.05513 +G1 X124.452 Y105.7 E.03972 +;LAYER_CHANGE +;Z:2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.655 Y105.7 E-.57143 +G1 X124.497 Y104.497 E-.79315 +G1 X124.782 Y104.498 E-.13542 +;WIPE_END +G1 Z2 F9000 +;AFTER_LAYER_CHANGE +;2 +G1 Z2 +G1 X123.996 Y105.988 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.008 Y105.894 E.00313 +G1 X124.194 Y105.321 E.01989 +G1 X124.161 Y104.427 E.02954 +G1 X124.008 Y104.106 E.01174 +G1 X123.994 Y103.994 E.00373 +G1 X124.063 Y103.994 E.00228 +G1 X124.427 Y104.161 E.01322 +G1 X125 Y104.194 E.01895 +G1 X125.588 Y104.166 E.01944 +G1 X126.006 Y103.994 E.01492 +G1 X126.006 Y104.105 E.00366 +G1 X125.848 Y104.44 E.01223 +G1 X125.806 Y104.723 E.00945 +G1 X125.806 Y105.244 E.0172 +G1 X126.006 Y105.815 E.01998 +G1 X126.006 Y106.006 E.00631 +G1 X124.036 Y106.006 E.06504 +G1 X124.036 Y106.006 F9000 +G1 X123.602 Y105.703 +G1 F900 +G1 X123.687 Y105.66 E.00315 +G1 X123.797 Y105.321 E.01177 +G1 X123.778 Y104.53 E.02612 +G1 X123.687 Y104.34 E.00696 +G1 X123.597 Y104.294 E.00334 +G1 X123.597 Y103.597 E.02301 +G1 X124.293 Y103.597 E.02298 +G1 X124.338 Y103.687 E.00332 +G1 X124.529 Y103.778 E.00699 +G1 X124.677 Y103.797 E.00493 +G1 X125.354 Y103.797 E.02235 +G1 X125.667 Y103.705 E.01077 +G1 X125.74 Y103.597 E.0043 +G1 X126.403 Y103.597 E.02189 +G1 X126.403 Y104.339 E.0245 +G1 X126.312 Y104.385 E.00337 +G1 X126.204 Y104.693 E.01078 +G1 X126.203 Y105.2 E.01674 +G1 X126.307 Y105.575 E.01285 +G1 X126.403 Y105.624 E.00356 +G1 X126.403 Y106.403 E.02572 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.76 E.02123 +G1 X123.345 Y105.448 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.374 Y105.432 E.00104 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.374 Y104.567 E.00369 +G1 X123.21 Y104.49 E.00568 +G1 X123.21 Y103.21 E.04013 +G1 X124.477 Y103.21 E.03973 +G1 X124.566 Y103.374 E.00585 +G1 X124.677 Y103.41 E.00366 +G1 X125.277 Y103.41 E.01881 +G1 X125.456 Y103.38 E.00569 +G1 X125.542 Y103.21 E.00597 +G1 X126.79 Y103.21 E.03913 +G1 X126.79 Y104.523 E.04117 +G1 X126.626 Y104.612 E.00585 +G1 X126.598 Y104.668 E.00196 +G1 X126.59 Y105 E.01041 +G1 X126.624 Y105.352 E.01109 +G1 X126.79 Y105.444 E.00595 +G1 X126.79 Y106.79 E.0422 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.292 Y105.477 E.00292 +G1 X123.292 Y105.477 F9000 +G1 X124.452 Y105.7 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X125.639 Y105.7 E.03919 +G1 X125.611 Y105.611 E.00308 +G1 X124.474 Y104.474 E.05309 +G1 X125.527 Y104.473 E.03477 +G1 X124.422 Y105.578 E.0516 +G1 X124.5 Y105.338 E.00833 +G1 X124.5 Y104.671 E.02202 +;LAYER_CHANGE +;Z:2.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;2.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.5 Y105.338 E-.31683 +G1 X124.422 Y105.578 E-.11987 +G1 X125.527 Y104.473 E-.74229 +G1 X124.851 Y104.474 E-.32101 +;WIPE_END +G1 Z2.2 F9000 +;AFTER_LAYER_CHANGE +;2.2 +G1 Z2.2 +G1 X123.994 Y103.996 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X123.994 Y103.994 E.00007 +G1 X124.134 Y103.994 E.00462 +G1 X124.479 Y104.153 E.01254 +G1 X124.707 Y104.193 E.00764 +G1 X125.248 Y104.194 E.01786 +G1 X125.567 Y104.14 E.01068 +G1 X125.819 Y104.009 E.00938 +G1 X126.003 Y103.997 E.00609 +G1 X125.83 Y104.375 E.01373 +G1 X125.806 Y104.589 E.00711 +G1 X125.806 Y105.383 E.02622 +G1 X126.006 Y105.969 E.02044 +G1 X126.006 Y106.006 E.00122 +G1 X123.994 Y106.006 E.06643 +G1 X124.008 Y105.894 E.00373 +G1 X124.194 Y105.321 E.01989 +G1 X124.167 Y104.449 E.0288 +G1 X124.008 Y104.106 E.01248 +G1 X124.002 Y104.056 E.00166 +G1 X124.002 Y104.056 F9000 +G1 X123.602 Y104.298 +G1 F900 +G1 X123.597 Y104.294 E.00021 +G1 X123.597 Y103.597 E.02301 +G1 X124.378 Y103.597 E.02579 +G1 X124.427 Y103.69 E.00347 +G1 X124.594 Y103.773 E.00616 +G1 X124.76 Y103.797 E.00554 +G1 X125.312 Y103.793 E.01823 +G1 X125.586 Y103.687 E.0097 +G1 X125.632 Y103.597 E.00334 +G1 X126.403 Y103.597 E.02546 +G1 X126.403 Y104.199 E.01988 +G1 X126.278 Y104.306 E.00543 +G1 X126.203 Y104.589 E.00967 +G1 X126.203 Y105.383 E.02622 +G1 X126.318 Y105.728 E.01201 +G1 X126.403 Y105.771 E.00315 +G1 X126.403 Y106.403 E.02087 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.706 E.02301 +G1 X123.687 Y105.66 E.00334 +G1 X123.797 Y105.321 E.01177 +G1 X123.797 Y104.679 E.0212 +G1 X123.708 Y104.371 E.01059 +G1 X123.651 Y104.332 E.00228 +G1 X123.651 Y104.332 F9000 +G1 X123.339 Y104.556 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y104.49 E.00454 +G1 X123.21 Y103.21 E.04013 +G1 X124.56 Y103.21 E.04233 +G1 X124.651 Y103.375 E.00591 +G1 X124.706 Y103.402 E.00192 +G1 X125.248 Y103.41 E.017 +G1 X125.359 Y103.374 E.00366 +G1 X125.437 Y103.21 E.00569 +G1 X126.79 Y103.21 E.04242 +G1 X126.79 Y104.389 E.03697 +G1 X126.629 Y104.475 E.00572 +G1 X126.59 Y104.589 E.00378 +G1 X126.59 Y105.2 E.01916 +G1 X126.628 Y105.496 E.00936 +G1 X126.79 Y105.583 E.00577 +G1 X126.79 Y106.79 E.03784 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.374 Y105.432 E.00585 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y105 E.01006 +G1 X123.382 Y104.591 E.01285 +G1 X123.382 Y104.591 F9000 +G1 X124.483 Y105.389 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.476 Y104.476 E.03015 +G1 X125.55 Y105.55 E.05015 +G1 X125.5 Y105.4 E.00522 +G1 X125.509 Y104.491 E.03001 +G1 X124.422 Y105.578 E.05076 +G1 X124.382 Y105.7 E.00424 +G1 X125.56 Y105.7 E.03889 +;LAYER_CHANGE +;Z:2.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;2.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.382 Y105.7 E-.55955 +G1 X124.422 Y105.578 E-.06099 +G1 X125.509 Y104.491 E-.73019 +G1 X125.506 Y104.805 E-.14927 +;WIPE_END +G1 Z2.4 F9000 +;AFTER_LAYER_CHANGE +;2.4 +G1 Z2.4 +G1 X126.868 Y105.006 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F900 +G1 X126.769 Y105.006 E.0028 +G1 X126.769 Y105.006 F9000 +G1 X125.855 Y105.66 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X125.978 Y106.006 E.01212 +G1 X123.994 Y106.006 E.06551 +G1 X124.008 Y105.894 E.00373 +G1 X124.194 Y105.321 E.01989 +G1 X124.194 Y105.002 E.01053 +G1 X124.153 Y104.399 E.01996 +G1 X124.004 Y104.101 E.011 +G1 X123.994 Y103.994 E.00355 +G1 X124.378 Y104.164 E.01387 +G1 X124.618 Y104.194 E.00799 +G1 X125.397 Y104.194 E.02572 +G1 X125.73 Y104.135 E.01117 +G1 X125.914 Y104.034 E.00693 +G1 X125.833 Y104.227 E.00691 +G1 X125.806 Y104.455 E.00758 +G1 X125.806 Y105.521 E.0352 +G1 X125.835 Y105.604 E.0029 +G1 X125.835 Y105.604 F9000 +G1 X126.392 Y105.913 +G1 F900 +G1 X126.403 Y105.918 E.0004 +G1 X126.403 Y106.403 E.01601 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.706 E.02301 +G1 X123.687 Y105.66 E.00334 +G1 X123.797 Y105.321 E.01177 +G1 X123.797 Y105.2 E.004 +G1 X123.797 Y104.654 E.01803 +G1 X123.685 Y104.337 E.0111 +G1 X123.597 Y104.293 E.00325 +G1 X123.597 Y103.597 E.02298 +G1 X124.23 Y103.597 E.0209 +G1 X124.272 Y103.682 E.00313 +G1 X124.476 Y103.779 E.00746 +G1 X124.679 Y103.797 E.00673 +G1 X125.462 Y103.793 E.02585 +G1 X125.752 Y103.675 E.01034 +G1 X125.79 Y103.597 E.00286 +G1 X126.403 Y103.597 E.02024 +G1 X126.403 Y104.057 E.01519 +G1 X126.334 Y104.089 E.00251 +G1 X126.219 Y104.32 E.00852 +G1 X126.203 Y104.895 E.01899 +G1 X126.248 Y105.006 E.00395 +G1 X126.207 Y105.045 E.00187 +G1 X126.203 Y105.521 E.01572 +G1 X126.332 Y105.884 E.01272 +G1 X126.338 Y105.887 E.00022 +G1 X126.338 Y105.887 F9000 +G1 X126.653 Y105.651 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.79 Y105.721 E.00482 +G1 X126.79 Y106.79 E.03352 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.521 E.03979 +G1 X123.374 Y105.432 E.00585 +G1 X123.41 Y105.321 E.00366 +G1 X123.41 Y104.679 E.02013 +G1 X123.373 Y104.567 E.0037 +G1 X123.21 Y104.49 E.00565 +G1 X123.21 Y103.21 E.04013 +G1 X124.418 Y103.21 E.03788 +G1 X124.505 Y103.372 E.00577 +G1 X124.618 Y103.41 E.00374 +G1 X125.397 Y103.41 E.02442 +G1 X125.513 Y103.37 E.00385 +G1 X125.586 Y103.21 E.00551 +G1 X126.79 Y103.21 E.03775 +G1 X126.79 Y104.255 E.03277 +G1 X126.618 Y104.357 E.00627 +G1 X126.59 Y104.895 E.01689 +G1 X126.769 Y105.006 E.0066 +G1 X126.591 Y105.093 E.00621 +G1 X126.59 Y105.521 E.01342 +G1 X126.62 Y105.605 E.0028 +G1 X126.62 Y105.605 F9000 +G1 X125.553 Y105.69 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.382 Y105.7 E.03866 +G1 X124.422 Y105.578 E.00424 +G1 X125.51 Y104.49 E.0508 +G1 X125.5 Y105.5 E.03335 +G1 X124.476 Y104.483 E.04765 +G1 X124.483 Y105.389 E.02991 +;LAYER_CHANGE +;Z:2.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;2.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.476 Y104.483 E-.43036 +G1 X125.5 Y105.5 E-.68553 +G1 X125.508 Y104.692 E-.38411 +;WIPE_END +G1 Z2.6 F9000 +;AFTER_LAYER_CHANGE +;2.6 +G1 Z2.6 +G1 X125.021 Y103.121 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F900 +G1 X125.021 Y103.23 E.00308 +G1 X125.021 Y103.23 F9000 +G1 X125.648 Y104.177 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X125.828 Y104.146 E.00603 +G1 X125.806 Y104.454 E.0102 +G1 X125.806 Y105.659 E.03979 +G1 X125.937 Y106.006 E.01225 +G1 X124.117 Y106.006 E.06009 +G1 X124.194 Y105.821 E.00662 +G1 X124.194 Y104.179 E.05421 +G1 X125.546 Y104.194 E.04464 +G1 X125.589 Y104.187 E.00144 +G1 X125.589 Y104.187 F9000 +G1 X125.941 Y103.613 +G1 F900 +G1 X125.949 Y103.597 E.00059 +G1 X126.403 Y103.597 E.01499 +G1 X126.403 Y103.913 E.01043 +G1 X126.302 Y103.999 E.00438 +G1 X126.203 Y104.322 E.01115 +G1 X126.222 Y104.916 E.01962 +G1 X126.321 Y105.007 E.00444 +G1 X126.222 Y105.098 E.00444 +G1 X126.203 Y105.659 E.01853 +G1 X126.348 Y106.042 E.01352 +G1 X126.403 Y106.066 E.00198 +G1 X126.403 Y106.403 E.01113 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y106.302 E.00333 +G1 X123.797 Y105.821 E.0172 +G1 X123.797 Y104.179 E.05421 +G1 X123.739 Y103.926 E.00857 +G1 X123.597 Y103.758 E.00726 +G1 X123.597 Y103.597 E.00532 +G1 X124.07 Y103.597 E.01562 +G1 X124.096 Y103.654 E.00207 +G1 X124.324 Y103.777 E.00855 +G1 X124.922 Y103.797 E.01976 +G1 X125.021 Y103.758 E.00351 +G1 X125.088 Y103.796 E.00254 +G1 X125.611 Y103.793 E.01727 +G1 X125.912 Y103.663 E.01083 +G1 X125.912 Y103.663 F9000 +G1 X125.677 Y103.345 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X125.735 Y103.21 E.00461 +G1 X126.79 Y103.21 E.03308 +G1 X126.79 Y104.122 E.0286 +G1 X126.638 Y104.196 E.0053 +G1 X126.596 Y104.274 E.00278 +G1 X126.596 Y104.818 E.01706 +G1 X126.779 Y104.959 E.00724 +G1 X126.79 Y105.007 E.00154 +G1 X126.596 Y105.195 E.00847 +G1 X126.59 Y105.659 E.01455 +G1 X126.638 Y105.785 E.00423 +G1 X126.79 Y105.859 E.0053 +G1 X126.79 Y106.79 E.02919 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y106.021 E.02411 +G1 X123.354 Y105.955 E.00497 +G1 X123.41 Y105.821 E.00455 +G1 X123.41 Y104.179 E.05148 +G1 X123.354 Y104.045 E.00455 +G1 X123.21 Y103.99 E.00483 +G1 X123.21 Y103.21 E.02446 +G1 X124.275 Y103.21 E.03339 +G1 X124.39 Y103.39 E.0067 +G1 X124.922 Y103.41 E.01669 +G1 X125.021 Y103.23 E.00644 +G1 X125.1 Y103.409 E.00613 +G1 X125.546 Y103.41 E.01398 +G1 X125.633 Y103.378 E.00291 +G1 X125.633 Y103.378 F9000 +G1 X125.5 Y105.302 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X125.501 Y104.499 E.02651 +G1 X124.5 Y105.5 E.04674 +G1 X124.5 Y104.5 E.03302 +G1 X125.5 Y105.5 E.04669 +G1 X125.508 Y105.7 E.00661 +G1 X124.5 Y105.699 E.03328 +;LAYER_CHANGE +;Z:2.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;2.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.508 Y105.7 E-.4788 +G1 X125.5 Y105.5 E-.09508 +G1 X124.5 Y104.5 E-.67175 +G1 X124.5 Y105.036 E-.25437 +;WIPE_END +G1 Z2.8 F9000 +;AFTER_LAYER_CHANGE +;2.8 +G1 Z2.8 +G1 X124.051 Y106.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.194 Y105.621 E.01356 +G1 X124.194 Y104.783 E.02767 +G1 X124.162 Y104.17 E.02027 +G1 X124.379 Y104.194 E.00721 +G1 X125.806 Y104.188 E.04712 +G1 X125.806 Y104.641 E.01496 +G1 X125.866 Y104.976 E.01124 +G1 X125.811 Y105.277 E.0101 +G1 X125.806 Y105.798 E.0172 +G1 X125.891 Y106.006 E.00742 +G1 X124.111 Y106.006 E.05877 +G1 X124.111 Y106.006 F9000 +G1 X123.61 Y106.02 +G1 F900 +G1 X123.657 Y105.998 E.00171 +G1 X123.797 Y105.621 E.01328 +G1 X123.797 Y104.783 E.02767 +;WIDTH:0.443938 +G1 X123.769 Y104.195 E.01963 +;WIDTH:0.47111 +G1 X123.667 Y103.99 E.00815 +G1 X123.613 Y103.965 E.00212 +;WIDTH:0.470966 +G1 X123.613 Y103.613 E.01253 +G1 X123.917 Y103.613 E.01082 +G1 X123.939 Y103.662 E.00191 +G1 X124.171 Y103.776 E.0092 +;WIDTH:0.443938 +G1 X124.379 Y103.797 E.00697 +;WIDTH:0.439999 +G1 X124.932 Y103.777 E.01827 +G1 X125.026 Y103.676 E.00456 +G1 X125.121 Y103.777 E.00458 +G1 X125.192 Y103.792 E.0024 +G1 X125.695 Y103.797 E.01661 +G1 X125.911 Y103.748 E.00731 +;WIDTH:0.427554 +G1 X126.096 Y103.574 E.00812 +;WIDTH:0.394268 +G1 X126.426 Y103.574 E.00964 +G1 X126.426 Y103.781 E.00605 +G1 X126.388 Y103.797 E.0012 +;WIDTH:0.427554 +G1 X126.23 Y104.034 E.00911 +;WIDTH:0.439999 +G1 X126.203 Y104.188 E.00516 +G1 X126.215 Y104.757 E.01879 +G1 X126.373 Y105.007 E.00976 +G1 X126.215 Y105.257 E.00976 +G1 X126.203 Y105.798 E.01787 +G1 X126.403 Y106.217 E.01533 +G1 X126.403 Y106.403 E.00614 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y106.072 E.01093 +G1 X123.597 Y106.072 F9000 +G1 X123.348 Y105.753 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.364 Y105.745 E.00056 +G1 X123.41 Y105.621 E.00415 +G1 X123.41 Y104.379 E.03894 +G1 X123.363 Y104.254 E.00419 +G1 X123.21 Y104.19 E.0052 +G1 X123.21 Y103.21 E.03073 +G1 X124.133 Y103.21 E.02894 +G1 X124.207 Y103.362 E.0053 +G1 X124.28 Y103.402 E.00261 +G1 X124.831 Y103.404 E.01728 +G1 X124.971 Y103.221 E.00722 +G1 X125.082 Y103.221 E.00348 +G1 X125.221 Y103.404 E.00721 +G1 X125.695 Y103.41 E.01486 +G1 X125.824 Y103.359 E.00435 +G1 X125.884 Y103.21 E.00504 +G1 X126.79 Y103.21 E.02841 +G1 X126.79 Y103.988 E.02439 +G1 X126.645 Y104.055 E.00501 +G1 X126.597 Y104.137 E.00298 +G1 X126.59 Y104.641 E.0158 +G1 X126.676 Y104.799 E.00564 +G1 X126.779 Y104.829 E.00336 +G1 X126.79 Y105.174 E.01082 +G1 X126.676 Y105.216 E.00381 +G1 X126.602 Y105.309 E.00373 +G1 X126.59 Y105.798 E.01534 +G1 X126.644 Y105.93 E.00447 +G1 X126.79 Y105.998 E.00505 +G1 X126.79 Y106.79 E.02483 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.821 E.03038 +G1 X123.294 Y105.78 E.00293 +G1 X123.294 Y105.78 F9000 +G1 X124.48 Y105.695 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X125.504 Y105.7 E.03381 +G1 X125.505 Y105.505 E.00644 +G1 X124.5 Y104.5 E.04693 +G1 X124.5 Y105.5 E.03302 +G1 X125.501 Y104.499 E.04674 +G1 X125.559 Y105.006 E.01685 +G1 X125.506 Y105.307 E.01009 +;LAYER_CHANGE +;Z:3 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;3 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.559 Y105.006 E-.14517 +G1 X125.501 Y104.499 E-.2424 +G1 X124.5 Y105.5 E-.67242 +G1 X124.5 Y104.574 E-.44001 +;WIPE_END +G1 Z3 F9000 +;AFTER_LAYER_CHANGE +;3 +G1 Z3 +G1 X125.918 Y104.924 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X125.959 Y105.008 E.00309 +G1 X125.888 Y105.114 E.00421 +G1 X125.821 Y105.332 E.00753 +G1 X125.806 Y105.936 E.01995 +G1 X125.837 Y106.006 E.00253 +G1 X123.996 Y106.006 E.06078 +G1 X124.194 Y105.421 E.02039 +G1 X124.194 Y104.641 E.02575 +G1 X124.141 Y104.263 E.0126 +G1 X124.093 Y104.176 E.00328 +G1 X124.579 Y104.194 E.01606 +G1 X124.988 Y104.13 E.01367 +G1 X125.302 Y104.187 E.01054 +G1 X125.806 Y104.194 E.01664 +G1 X125.841 Y104.768 E.01899 +G1 X125.891 Y104.871 E.00378 +G1 X125.891 Y104.871 F9000 +G1 X126.385 Y104.903 +G1 F900 +G1 X126.403 Y104.925 E.00094 +G1 X126.403 Y105.091 E.00548 +G1 X126.363 Y105.108 E.00144 +G1 X126.212 Y105.403 E.01094 +G1 X126.203 Y105.936 E.0176 +G1 X126.403 Y106.369 E.01575 +G1 X126.403 Y106.403 E.00112 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y105.812 E.01951 +G1 X123.678 Y105.772 E.00298 +G1 X123.797 Y105.421 E.01224 +G1 X123.797 Y104.989 E.01426 +G1 X123.766 Y104.392 E.01974 +G1 X123.663 Y104.21 E.0069 +G1 X123.597 Y104.179 E.00241 +G1 X123.597 Y103.597 E.01922 +G1 X123.707 Y103.597 E.00363 +G1 X123.985 Y103.759 E.01062 +G1 X124.19 Y103.797 E.00688 +G1 X124.765 Y103.784 E.01899 +G1 X125.031 Y103.617 E.01037 +G1 X125.217 Y103.759 E.00773 +G1 X125.49 Y103.797 E.0091 +;WIDTH:0.484531 +G1 X126.085 Y103.769 E.02187 +;WIDTH:0.514814 +G1 X126.18 Y103.724 E.00412 +;WIDTH:0.545096 +G1 X126.275 Y103.679 E.00439 +G1 X126.241 Y103.778 E.00437 +;WIDTH:0.514814 +G1 X126.207 Y103.877 E.00411 +;WIDTH:0.484531 +G1 X126.203 Y104.51 E.02324 +;WIDTH:0.439999 +G1 X126.252 Y104.742 E.00783 +G1 X126.347 Y104.857 E.00492 +G1 X126.347 Y104.857 F9000 +G1 X126.653 Y104.644 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.79 Y104.698 E.00462 +G1 X126.79 Y105.306 E.01906 +G1 X126.642 Y105.376 E.00513 +G1 X126.606 Y105.43 E.00203 +G1 X126.59 Y105.936 E.01587 +G1 X126.653 Y106.077 E.00484 +G1 X126.79 Y106.136 E.00468 +G1 X126.79 Y106.79 E.02051 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.621 E.03665 +G1 X123.371 Y105.536 E.00571 +G1 X123.41 Y105.421 E.00381 +G1 X123.41 Y104.641 E.02446 +G1 X123.366 Y104.458 E.0059 +G1 X123.21 Y104.39 E.00534 +G1 X123.21 Y103.21 E.037 +G1 X123.99 Y103.21 E.02446 +G1 X124.057 Y103.355 E.00501 +G1 X124.123 Y103.398 E.00247 +G1 X124.641 Y103.41 E.01625 +G1 X124.794 Y103.332 E.00538 +G1 X124.83 Y103.221 E.00366 +G1 X125.222 Y103.21 E.0123 +G1 X125.269 Y103.332 E.0041 +G1 X125.355 Y103.398 E.0034 +G1 X125.844 Y103.41 E.01534 +G1 X125.973 Y103.359 E.00435 +G1 X126.033 Y103.21 E.00504 +G1 X126.79 Y103.21 E.02374 +G1 X126.79 Y103.854 E.02019 +G1 X126.654 Y103.913 E.00465 +G1 X126.598 Y103.998 E.00319 +G1 X126.59 Y104.51 E.01606 +G1 X126.624 Y104.595 E.00287 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.653 Y104.644 E-.02705 +G1 X126.79 Y104.698 E-.06995 +G1 X126.79 Y105.306 E-.2888 +G1 X126.642 Y105.376 E-.07777 +G1 X126.606 Y105.43 E-.03083 +G1 X126.59 Y105.936 E-.24047 +G1 X126.653 Y106.077 E-.07336 +G1 X126.79 Y106.136 E-.07085 +G1 X126.79 Y106.79 E-.31065 +G1 X126.137 Y106.79 E-.31027 +;WIPE_END +G1 X124.5 Y104.686 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X124.469 Y105.531 E.02792 +G1 X125.506 Y104.494 E.04842 +G1 X125.031 Y104.436 E.0158 +G1 X124.5 Y104.5 E.01766 +G1 X125.511 Y105.511 E.04721 +G1 X125.504 Y105.7 E.00624 +G1 X124.432 Y105.7 E.03539 +;LAYER_CHANGE +;Z:3.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;3.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.504 Y105.7 E-.5092 +G1 X125.511 Y105.511 E-.08984 +G1 X124.5 Y104.5 E-.67914 +G1 X124.964 Y104.444 E-.22182 +;WIPE_END +G1 Z3.2 F9000 +;AFTER_LAYER_CHANGE +;3.2 +G1 Z3.2 +G1 X124.008 Y105.886 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X124.02 Y105.776 E.00365 +G1 X124.194 Y105.169 E.02085 +G1 X124.176 Y104.589 E.01916 +G1 X123.998 Y104.193 E.01433 +G1 X124.5 Y104.194 E.01657 +G1 X124.787 Y104.151 E.00958 +G1 X125.037 Y104.021 E.0093 +G1 X125.295 Y104.153 E.00957 +G1 X125.806 Y104.194 E.01693 +G1 X125.806 Y104.379 E.00611 +G1 X125.859 Y104.695 E.01058 +G1 X126.006 Y104.952 E.00978 +G1 X125.984 Y105.076 E.00416 +G1 X125.839 Y105.386 E.0113 +G1 X125.806 Y106.006 E.0205 +G1 X123.994 Y106.006 E.05983 +G1 X124.001 Y105.945 E.00203 +G1 X124.001 Y105.945 F9000 +G1 X123.599 Y105.6 +G1 F900 +G1 X123.694 Y105.55 E.00354 +G1 X123.797 Y105.221 E.01138 +G1 X123.797 Y104.779 E.01459 +G1 X123.698 Y104.456 E.01115 +G1 X123.597 Y104.388 E.00402 +G1 X123.597 Y103.607 E.02579 +G1 X124.048 Y103.797 E.01616 +G1 X124.612 Y103.786 E.01863 +G1 X124.876 Y103.657 E.0097 +G1 X124.904 Y103.597 E.00219 +G1 X125.17 Y103.597 E.00878 +G1 X125.197 Y103.657 E.00217 +G1 X125.473 Y103.788 E.01009 +G1 X125.993 Y103.797 E.01717 +G1 X126.233 Y103.743 E.00812 +G1 X126.203 Y103.92 E.00593 +G1 X126.216 Y104.502 E.01922 +G1 X126.309 Y104.711 E.00755 +G1 X126.403 Y104.76 E.0035 +G1 X126.403 Y105.257 E.01641 +G1 X126.309 Y105.305 E.00348 +G1 X126.223 Y105.489 E.00671 +G1 X126.203 Y106.075 E.01936 +G1 X126.366 Y106.403 E.01209 +G1 X123.597 Y106.403 E.09142 +G1 X123.597 Y105.658 E.0246 +G1 X123.597 Y105.658 F9000 +G1 X123.342 Y105.348 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.376 Y105.329 E.00122 +G1 X123.41 Y105.008 E.01012 +G1 X123.378 Y104.673 E.01055 +G1 X123.21 Y104.59 E.00588 +G1 X123.21 Y103.21 E.04327 +G1 X123.848 Y103.21 E.02 +G1 X123.906 Y103.346 E.00464 +G1 X124.037 Y103.41 E.00457 +G1 X124.5 Y103.41 E.01452 +G1 X124.623 Y103.364 E.00412 +G1 X124.688 Y103.21 E.00524 +G1 X125.374 Y103.21 E.02151 +G1 X125.45 Y103.364 E.00538 +G1 X125.621 Y103.41 E.00555 +;WIDTH:0.457644 +G1 X125.759 Y103.411 E.00476 +;WIDTH:0.495289 +G1 X125.898 Y103.412 E.00523 +;WIDTH:0.532934 +G1 X126.036 Y103.413 E.00562 +;WIDTH:0.570578 +G1 X126.174 Y103.413 E.00605 +G1 X126.235 Y103.285 E.00622 +;WIDTH:0.569467 +G1 X126.715 Y103.285 E.02102 +G1 X126.715 Y103.678 E.01721 +;WIDTH:0.570578 +G1 X126.567 Y103.763 E.00749 +G1 X126.573 Y103.802 E.00173 +;WIDTH:0.532934 +G1 X126.578 Y103.842 E.00164 +;WIDTH:0.495289 +G1 X126.584 Y103.881 E.00148 +;WIDTH:0.457644 +G1 X126.59 Y103.92 E.00136 +;WIDTH:0.419999 +G1 X126.6 Y104.44 E.01631 +G1 X126.79 Y104.579 E.00738 +G1 X126.779 Y105.449 E.02728 +G1 X126.601 Y105.573 E.0068 +G1 X126.59 Y106.075 E.01574 +G1 X126.665 Y106.225 E.00526 +G1 X126.79 Y106.275 E.00422 +G1 X126.79 Y106.79 E.01615 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y105.421 E.04292 +G1 X123.289 Y105.377 E.00284 +G1 X123.289 Y105.377 F9000 +G1 X124.875 Y104.849 +;TYPE:Solid infill +;WIDTH:0.403669 +G1 F900 +G1 X124.862 Y105.321 E.01416 +G1 X125.16 Y105.321 E.00894 +G1 X125.239 Y104.984 E.01038 +G1 X125.176 Y104.822 E.00521 +G1 X125.044 Y104.771 E.00424 +;WIDTH:0.439999 +G1 X124.855 Y104.425 E.01302 +G1 X124.684 Y104.48 E.00593 +G1 X124.449 Y104.492 E.00777 +G1 X124.496 Y104.721 E.00772 +G1 X124.492 Y105.264 E.01793 +G1 X124.363 Y105.7 E.01501 +G1 X125.513 Y105.7 E.03797 +G1 X125.564 Y105.257 E.01472 +G1 X125.678 Y105.014 E.00886 +G1 X125.56 Y104.758 E.00931 +G1 X125.518 Y104.493 E.00886 +G1 X125.243 Y104.441 E.00924 +G1 X125.044 Y104.364 E.00705 +;LAYER_CHANGE +;Z:3.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;3.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.243 Y104.441 E-.10135 +G1 X125.518 Y104.493 E-.13294 +G1 X125.56 Y104.758 E-.12745 +G1 X125.678 Y105.014 E-.1339 +G1 X125.564 Y105.257 E-.1275 +G1 X125.513 Y105.7 E-.21181 +G1 X124.363 Y105.7 E-.54625 +G1 X124.434 Y105.46 E-.1188 +;WIPE_END +G1 Z3.4 F9000 +;AFTER_LAYER_CHANGE +;3.4 +G1 Z3.4 +G1 X125.058 Y103.994 +G1 E5 F2400 +;TYPE:Perimeter +G1 F900 +G1 X125.175 Y103.994 E.00386 +G1 X125.201 Y104.041 E.00177 +G1 X125.372 Y104.128 E.00633 +G1 X125.806 Y104.194 E.01449 +G1 X125.806 Y104.273 E.00261 +G1 X125.905 Y104.677 E.01373 +G1 X126.006 Y104.77 E.00453 +G1 X126.006 Y105.248 E.01578 +G1 X125.932 Y105.291 E.00283 +G1 X125.806 Y105.77 E.01635 +G1 X125.806 Y106.006 E.00779 +G1 X123.994 Y106.006 E.05983 +G1 X123.994 Y104.194 E.05983 +G1 X124.358 Y104.194 E.01202 +G1 X124.782 Y104.097 E.01436 +G1 X124.908 Y103.994 E.00537 +G1 X124.998 Y103.994 E.00297 +G1 X124.998 Y103.994 F9000 +G1 X124.731 Y103.597 +G1 F900 +G1 X125.355 Y103.597 E.0206 +G1 X125.415 Y103.706 E.00411 +G1 X125.589 Y103.781 E.00626 +G1 X126.203 Y103.794 E.02028 +G1 X126.203 Y104.248 E.01499 +G1 X126.251 Y104.478 E.00776 +G1 X126.403 Y104.605 E.00654 +G1 X126.403 Y105.413 E.02668 +G1 X126.277 Y105.486 E.00481 +G1 X126.203 Y105.77 E.00969 +G1 X126.203 Y106.213 E.01463 +G1 X126.31 Y106.403 E.0072 +G1 X123.597 Y106.403 E.08958 +G1 X123.597 Y103.641 E.09119 +G1 X123.906 Y103.797 E.01143 +G1 X124.358 Y103.797 E.01492 +G1 X124.609 Y103.74 E.0085 +G1 X124.692 Y103.641 E.00427 +G1 X124.692 Y103.641 F9000 +G1 X124.468 Y103.33 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X124.547 Y103.21 E.0045 +G1 X125.525 Y103.21 E.03066 +G1 X125.643 Y103.391 E.00677 +G1 X126.142 Y103.41 E.01566 +;WIDTH:0.445034 +G1 X126.216 Y103.393 E.00254 +;WIDTH:0.470069 +G1 X126.289 Y103.377 E.00265 +;WIDTH:0.473996 +G1 X126.353 Y103.237 E.00552 +G1 X126.763 Y103.237 E.0147 +G1 X126.763 Y103.575 E.01212 +G1 X126.607 Y103.659 E.00635 +;WIDTH:0.470069 +G1 X126.586 Y103.787 E.00461 +;WIDTH:0.427403 +G1 X126.605 Y104.321 E.01708 +;WIDTH:0.419999 +G1 X126.79 Y104.437 E.00685 +G1 X126.79 Y105.57 E.03552 +G1 X126.604 Y105.699 E.0071 +G1 X126.59 Y106.213 E.01612 +G1 X126.682 Y106.375 E.00584 +G1 X126.79 Y106.413 E.00359 +G1 X126.79 Y106.79 E.01182 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y103.21 E.11225 +G1 X123.706 Y103.21 E.01555 +G1 X123.754 Y103.333 E.00414 +G1 X123.906 Y103.41 E.00534 +G1 X124.424 Y103.398 E.01625 +G1 X124.436 Y103.381 E.00065 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.468 Y103.33 E-.0286 +G1 X124.547 Y103.21 E-.06824 +G1 X125.525 Y103.21 E-.46455 +G1 X125.643 Y103.391 E-.10263 +G1 X126.142 Y103.41 E-.2372 +G1 X126.216 Y103.393 E-.03607 +G1 X126.289 Y103.377 E-.0355 +G1 X126.353 Y103.237 E-.07312 +G1 X126.763 Y103.237 E-.19475 +G1 X126.763 Y103.575 E-.16055 +G1 X126.607 Y103.659 E-.08416 +G1 X126.602 Y103.689 E-.01463 +;WIPE_END +G1 X124.3 Y105.7 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X124.3 Y104.483 E.04018 +G1 X124.709 Y104.446 E.01356 +G1 X125.009 Y104.306 E.01093 +G1 X125.539 Y104.478 E.0184 +G1 X125.586 Y104.695 E.00733 +G1 X125.7 Y104.903 E.00783 +G1 X125.7 Y105.115 E.007 +G1 X125.606 Y105.263 E.00579 +G1 X125.518 Y105.7 E.01472 +G1 X124.498 Y105.7 E.03368 +G1 X124.726 Y105.274 E.01595 +;WIDTH:0.49769 +G1 X124.726 Y104.894 E.01437 +G1 X125.047 Y104.767 E.01305 +G1 X125.181 Y104.826 E.00554 +G1 X125.256 Y105.009 E.00748 +G1 X125.165 Y105.25 E.00974 +G1 X124.924 Y105.263 E.00913 +;LAYER_CHANGE +;Z:3.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;3.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.165 Y105.25 E-.11464 +G1 X125.256 Y105.009 E-.12236 +G1 X125.181 Y104.826 E-.09394 +G1 X125.047 Y104.767 E-.06955 +G1 X124.726 Y104.894 E-.16397 +G1 X124.726 Y105.274 E-.1805 +G1 X124.498 Y105.7 E-.22951 +G1 X125.518 Y105.7 E-.4845 +G1 X125.535 Y105.615 E-.04103 +;WIPE_END +G1 Z3.6 F9000 +;AFTER_LAYER_CHANGE +;3.6 +G1 Z3.6 +G1 X123.994 Y106.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X123.994 Y103.994 E.06643 +G1 X126.006 Y103.994 E.06643 +G1 X126.006 Y105 E.03322 +G1 X126.006 Y106.006 E.03322 +G1 X124.054 Y106.006 E.06445 +G1 X124.054 Y106.006 F9000 +G1 X123.597 Y106.403 +G1 F900 +G1 X123.597 Y103.597 E.09265 +G1 X126.403 Y103.597 E.09265 +G1 X126.403 Y105 E.04632 +G1 X126.403 Y106.403 E.04632 +G1 X123.657 Y106.403 E.09067 +G1 X123.21 Y106.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y103.21 E.11225 +G1 X126.79 Y103.21 E.11225 +G1 X126.79 Y105 E.05612 +G1 X126.79 Y106.79 E.05612 +G1 X123.27 Y106.79 E.11037 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.21 Y106.79 E-.0285 +G1 X123.21 Y103.692 E-1.4715 +;WIPE_END +G1 X125.502 Y104.3 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X124.3 Y104.3 E.03969 +G1 X124.3 Y105.7 E.04622 +G1 X125.7 Y105.7 E.04622 +G1 X125.7 Y104.3 E.04622 +G1 X125.251 Y104.749 E.02097 +;WIDTH:0.544473 +G1 X125.251 Y105.251 E.02094 +G1 X124.749 Y105.251 E.02094 +G1 X124.749 Y104.749 E.02094 +G1 X125.052 Y104.749 E.01264 +;LAYER_CHANGE +;Z:3.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;3.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.749 Y104.749 E-.14392 +G1 X124.749 Y105.251 E-.23845 +G1 X125.251 Y105.251 E-.23845 +G1 X125.251 Y104.749 E-.23845 +G1 X125.7 Y104.3 E-.30162 +G1 X125.7 Y105.014 E-.33911 +;WIPE_END +G1 Z3.8 F9000 +;AFTER_LAYER_CHANGE +;3.8 +G1 Z3.8 +G1 X126.006 Y103.994 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.006 Y105 E.03322 +G1 X126.006 Y106.006 E.03322 +G1 X123.994 Y106.006 E.06643 +G1 X123.994 Y103.994 E.06643 +G1 X125.946 Y103.994 E.06445 +G1 X125.946 Y103.994 F9000 +G1 X126.403 Y103.597 +G1 F900 +G1 X126.403 Y105 E.04632 +G1 X126.403 Y106.403 E.04632 +G1 X123.597 Y106.403 E.09265 +G1 X123.597 Y103.597 E.09265 +G1 X126.343 Y103.597 E.09067 +G1 X126.79 Y103.21 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.79 Y105 E.05612 +G1 X126.79 Y106.79 E.05612 +G1 X123.21 Y106.79 E.11225 +G1 X123.21 Y103.21 E.11225 +G1 X126.73 Y103.21 E.11037 +G1 X125.881 Y104.854 F9000 +;TYPE:Top solid infill +;WIDTH:0.406026 +G1 F900 +G1 X125.307 Y104.28 E.02451 +G1 X124.793 Y104.28 E.01552 +G1 X125.72 Y105.207 E.03958 +G1 X125.72 Y105.72 E.01549 +G1 X124.28 Y104.28 E.06149 +G1 X124.28 Y104.793 E.01549 +G1 X125.207 Y105.72 E.03958 +G1 X124.693 Y105.72 E.01552 +G1 X124.119 Y105.146 E.02451 +;LAYER_CHANGE +;Z:4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.693 Y105.72 E-.38559 +G1 X125.207 Y105.72 E-.24415 +G1 X124.28 Y104.793 E-.62271 +G1 X124.28 Y104.28 E-.24368 +G1 X124.286 Y104.286 E-.00387 +;WIPE_END +G1 Z4 F9000 +;AFTER_LAYER_CHANGE +;4 +G1 Z4 +G1 X123.992 Y105.862 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X124.041 Y105.793 E.00265 +G1 X125.114 Y105.783 E.03364 +G1 X124.331 Y104.804 E.03931 +;WIDTH:0.453973 +G1 X123.79 Y104.155 E.02888 +;WIDTH:0.480776 +G1 X123.754 Y104.091 E.00267 +;WIDTH:0.507579 +G1 X123.718 Y104.027 E.00284 +G1 X123.718 Y103.679 E.01345 +;WIDTH:0.524205 +G1 X123.727 Y103.591 E.00354 +G1 X123.783 Y103.572 E.00237 +;WIDTH:0.486019 +G1 X123.839 Y103.552 E.00219 +;WIDTH:0.447833 +G1 X123.895 Y103.533 E.00199 +;WIDTH:0.409647 +G1 X123.951 Y103.514 E.0018 +;WIDTH:0.37146 +G1 X126.049 Y103.514 E.05731 +;WIDTH:0.409647 +G1 X126.105 Y103.533 E.0018 +;WIDTH:0.447833 +G1 X126.161 Y103.552 E.00199 +;WIDTH:0.48602 +G1 X126.217 Y103.572 E.00219 +;WIDTH:0.524206 +G1 X126.273 Y103.591 E.00237 +G1 X126.28 Y104.069 E.01913 +;WIDTH:0.511099 +G1 X126.165 Y104.143 E.00532 +;WIDTH:0.465549 +G1 X126.049 Y104.217 E.00484 +;WIDTH:0.419999 +G1 X124.81 Y104.217 E.03885 +G1 X125.654 Y105.254 E.04192 +;WIDTH:0.433316 +G1 X126.158 Y105.862 E.02564 +;WIDTH:0.481089 +G1 X126.204 Y105.924 E.00281 +;WIDTH:0.528861 +G1 X126.25 Y105.986 E.00312 +;WIDTH:0.541746 +G1 X126.243 Y106.401 E.01722 +G1 X126.184 Y106.422 E.0026 +;WIDTH:0.499175 +G1 X126.125 Y106.443 E.00238 +;WIDTH:0.456603 +G1 X126.066 Y106.464 E.00215 +;WIDTH:0.414032 +G1 X126.007 Y106.486 E.00194 +;WIDTH:0.37146 +G1 X124.102 Y106.486 E.05204 +;WIDTH:0.419999 +G1 X123.916 Y106.354 E.00715 +G1 X123.913 Y105.972 E.01198 +G1 X123.957 Y105.911 E.00236 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.992 Y105.862 E-.0286 +G1 X124.041 Y105.793 E-.0402 +G1 X125.114 Y105.783 E-.5097 +G1 X124.331 Y104.804 E-.59546 +G1 X123.892 Y104.277 E-.32604 +;WIPE_END +G1 X125.681 Y104.631 F9000 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.494061 +G1 F900 +G1 X126.123 Y104.626 E.01658 +G1 X126.376 Y104.386 E.01308 +G1 X126.376 Y105.485 E.04123 +G1 X125.719 Y104.677 E.03907 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.681 Y104.631 E-.02834 +G1 X126.123 Y104.626 E-.20996 +G1 X126.376 Y104.386 E-.16564 +G1 X126.376 Y105.485 E-.52203 +G1 X125.719 Y104.677 E-.49466 +;WIPE_END +G1 E-.07937 F3600 +G1 X123.567 Y106.433 F9000 +G1 E5 F2400 +;WIDTH:0.3803 +G1 F900 +G1 X123.561 Y105.972 E.01293 +G1 X123.561 Y105.972 F9000 +G1 X123.766 Y105.64 +;WIDTH:0.445442 +G1 F900 +G1 X123.678 Y105.783 E.00562 +;WIDTH:0.407136 +G1 X123.561 Y105.972 E.00673 +G1 X123.581 Y105.713 E.00787 +;WIDTH:0.445442 +G1 X123.6 Y105.455 E.00866 +;WIDTH:0.483747 +G1 X123.619 Y105.197 E.00948 +G1 X123.619 Y104.568 E.02306 +G1 X124.263 Y105.374 E.03782 +G1 X123.91 Y105.406 E.01299 +G1 X123.797 Y105.589 E.00788 +G1 X123.797 Y105.589 F9000 +G1 X123.21 Y106.79 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.21 Y105.197 E.04995 +;WIDTH:0.463789 +G1 X123.232 Y104.612 E.02049 +;WIDTH:0.507579 +G1 X123.254 Y104.027 E.02262 +;WIDTH:0.524205 +G1 X123.262 Y103.262 E.03062 +G1 X123.434 Y103.243 E.00693 +;WIDTH:0.486019 +G1 X123.606 Y103.224 E.00638 +;WIDTH:0.447833 +G1 X123.779 Y103.205 E.00586 +;WIDTH:0.409647 +G1 X123.951 Y103.186 E.00528 +;WIDTH:0.37146 +G1 X126.049 Y103.186 E.05731 +;WIDTH:0.409647 +G1 X126.161 Y103.205 E.00346 +;WIDTH:0.447833 +G1 X126.273 Y103.224 E.00382 +;WIDTH:0.48602 +G1 X126.385 Y103.243 E.00419 +;WIDTH:0.524206 +G1 X126.497 Y103.262 E.00455 +G1 X126.738 Y103.262 E.00964 +G1 X126.738 Y103.503 E.00964 +G1 X126.744 Y104.109 E.02425 +;WIDTH:0.511099 +G1 X126.767 Y104.48 E.01447 +;WIDTH:0.465549 +G1 X126.79 Y104.85 E.01303 +;WIDTH:0.433316 +G1 X126.783 Y105.64 E.02565 +;WIDTH:0.481089 +G1 X126.759 Y105.813 E.00636 +;WIDTH:0.528861 +G1 X126.736 Y105.986 E.00705 +;WIDTH:0.541746 +G1 X126.729 Y106.729 E.03082 +G1 X126.549 Y106.75 E.00752 +;WIDTH:0.499175 +G1 X126.368 Y106.772 E.00692 +;WIDTH:0.456603 +G1 X126.187 Y106.793 E.00627 +;WIDTH:0.414032 +G1 X126.007 Y106.814 E.00559 +;WIDTH:0.37146 +G1 X124.102 Y106.814 E.05204 +;WIDTH:0.419999 +G1 X123.27 Y106.792 E.0261 +; stop printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200;_WIPE +G1 X123.21 Y106.79 E-.02852 +G1 X123.21 Y105.197 E-.75668 +G1 X123.232 Y104.612 E-.27807 +G1 X123.254 Y104.027 E-.27807 +G1 X123.258 Y103.693 E-.15866 +;WIPE_END +M107 +;TYPE:Custom +; Filament-specific end gcode +;END gcode for filament +G1 Z6 F600 ; Move print head up +G1 X5 Y193.8 F9000 ; present print +G1 Z74 F600 ; Move print head further up +G1 Z150 F600 ; Move print head further up +M140 S0 ; turn off heatbed +M104 S0 ; turn off temperature +M107 ; turn off fan +M84 X Y E ; disable motors +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl id:0 copy 0","polygon":[[127.000,107.000],[123.000,107.000],[123.000,103.000],[127.000,103.000]]}]} +; filament used [mm] = 67.36 +; filament used [cm3] = 0.16 +; filament used [g] = 0.20 +; filament cost = 0.00 +; total filament used [g] = 0.20 +; total filament cost = 0.00 +; total filament used for wipe tower [g] = 0.00 +; estimated printing time (normal mode) = 2m 49s +; estimated first layer printing time (normal mode) = 1m 9s + +; prusaslicer_config = begin +; arc_fitting = disabled +; autoemit_temperature_commands = 1 +; avoid_crossing_curled_overhangs = 0 +; avoid_crossing_perimeters = 0 +; avoid_crossing_perimeters_max_detour = 0 +; bed_custom_model = +; bed_custom_texture = +; bed_shape = 3x3,228x3,228x228,3x228 +; bed_temperature = 60 +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0\n;{layer_z}\n\n +; between_objects_gcode = +; binary_gcode = 0 +; bottom_fill_pattern = monotonic +; bottom_solid_layers = 4 +; bottom_solid_min_thickness = 0 +; bridge_acceleration = 250 +; bridge_angle = 0 +; bridge_fan_speed = 100 +; bridge_flow_ratio = 0.95 +; bridge_speed = 25 +; brim_separation = 0 +; brim_type = outer_only +; brim_width = 5 +; chamber_minimal_temperature = 0 +; chamber_temperature = 0 +; color_change_gcode = M600 +; colorprint_heights = +; compatible_printers_condition_cummulative = "printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4";printer_notes=~/.*PRINTER_VENDOR_CREALITY.*/ +; complete_objects = 0 +; cooling = 1 +; cooling_tube_length = 5 +; cooling_tube_retraction = 91.5 +; default_acceleration = 500 +; default_filament_profile = "Generic PLA @CREALITY" +; default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @CREALITY" +; deretract_speed = 40 +; disable_fan_first_layers = 1 +; dont_support_bridges = 1 +; draft_shield = disabled +; duplicate_distance = 6 +; elefant_foot_compensation = 0.1 +; enable_dynamic_fan_speeds = 0 +; enable_dynamic_overhang_speeds = 0 +; end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n" +; end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors +; external_perimeter_acceleration = 0 +; external_perimeter_extrusion_width = 0.42 +; external_perimeter_speed = 25 +; external_perimeters_first = 0 +; extra_loading_move = -2 +; extra_perimeters = 0 +; extra_perimeters_on_overhangs = 0 +; extruder_clearance_height = 25 +; extruder_clearance_radius = 55 +; extruder_colour = #FCE94F +; extruder_offset = 0x0 +; extrusion_axis = E +; extrusion_multiplier = 1 +; extrusion_width = 0.44 +; fan_always_on = 1 +; fan_below_layer_time = 100 +; filament_abrasive = 0 +; filament_colour = #DDDDDD +; filament_cooling_final_speed = 3.4 +; filament_cooling_initial_speed = 2.2 +; filament_cooling_moves = 4 +; filament_cost = 20 +; filament_density = 1.24 +; filament_deretract_speed = nil +; filament_diameter = 1.75 +; filament_infill_max_crossing_speed = 0 +; filament_infill_max_speed = 0 +; filament_load_time = 0 +; filament_loading_speed = 28 +; filament_loading_speed_start = 3 +; filament_max_volumetric_speed = 15 +; filament_minimal_purge_on_wipe_tower = 15 +; filament_multitool_ramming = 0 +; filament_multitool_ramming_flow = 10 +; filament_multitool_ramming_volume = 10 +; filament_notes = "" +; filament_purge_multiplier = 100% +; filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" +; filament_retract_before_travel = nil +; filament_retract_before_wipe = nil +; filament_retract_layer_change = nil +; filament_retract_length = nil +; filament_retract_length_toolchange = nil +; filament_retract_lift = nil +; filament_retract_lift_above = nil +; filament_retract_lift_below = nil +; filament_retract_restart_extra = nil +; filament_retract_restart_extra_toolchange = nil +; filament_retract_speed = nil +; filament_settings_id = "Generic PLA @CREALITY" +; filament_shrinkage_compensation_xy = 0% +; filament_shrinkage_compensation_z = 0% +; filament_soluble = 0 +; filament_spool_weight = 0 +; filament_stamping_distance = 0 +; filament_stamping_loading_speed = 20 +; filament_toolchange_delay = 0 +; filament_travel_lift_before_obstacle = nil +; filament_travel_max_lift = nil +; filament_travel_ramping_lift = nil +; filament_travel_slope = nil +; filament_type = PLA +; filament_unload_time = 0 +; filament_unloading_speed = 90 +; filament_unloading_speed_start = 100 +; filament_vendor = Generic +; filament_wipe = nil +; fill_angle = 45 +; fill_density = 15% +; fill_pattern = grid +; first_layer_acceleration = 0 +; first_layer_acceleration_over_raft = 0 +; first_layer_bed_temperature = 60 +; first_layer_extrusion_width = 0.42 +; first_layer_height = 0.2 +; first_layer_speed = 20 +; first_layer_speed_over_raft = 30 +; first_layer_temperature = 210 +; full_fan_speed_layer = 0 +; fuzzy_skin = none +; fuzzy_skin_point_dist = 0.8 +; fuzzy_skin_thickness = 0.3 +; gap_fill_enabled = 1 +; gap_fill_speed = 30 +; gcode_comments = 0 +; gcode_flavor = marlin +; gcode_label_objects = octoprint +; gcode_resolution = 0.0125 +; gcode_substitutions = +; high_current_on_filament_swap = 0 +; host_type = prusalink +; idle_temperature = 150 +; infill_acceleration = 0 +; infill_anchor = 600% +; infill_anchor_max = 50 +; infill_every_layers = 1 +; infill_extruder = 1 +; infill_extrusion_width = 0.44 +; infill_first = 0 +; infill_overlap = 23% +; infill_speed = 50 +; interface_shells = 0 +; ironing = 0 +; ironing_flowrate = 10% +; ironing_spacing = 0.1 +; ironing_speed = 20 +; ironing_type = top +; layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z} +; layer_height = 0.2 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 5000 +; machine_max_acceleration_extruding = 500 +; machine_max_acceleration_retracting = 1000 +; machine_max_acceleration_travel = 500 +; machine_max_acceleration_x = 500 +; machine_max_acceleration_y = 500 +; machine_max_acceleration_z = 100 +; machine_max_feedrate_e = 60 +; machine_max_feedrate_x = 500 +; machine_max_feedrate_y = 500 +; machine_max_feedrate_z = 10 +; machine_max_jerk_e = 5 +; machine_max_jerk_x = 8 +; machine_max_jerk_y = 8 +; machine_max_jerk_z = 0.4 +; machine_min_extruding_rate = 0 +; machine_min_travel_rate = 0 +; max_fan_speed = 100 +; max_layer_height = 0.32 +; max_print_height = 250 +; max_print_speed = 100 +; max_volumetric_extrusion_rate_slope_negative = 0 +; max_volumetric_extrusion_rate_slope_positive = 0 +; max_volumetric_speed = 0 +; min_bead_width = 85% +; min_fan_speed = 100 +; min_feature_size = 25% +; min_layer_height = 0.06 +; min_print_speed = 15 +; min_skirt_length = 4 +; mmu_segmented_region_interlocking_depth = 0 +; mmu_segmented_region_max_width = 0 +; multimaterial_purging = 140 +; notes = +; nozzle_diameter = 0.4 +; nozzle_high_flow = 0 +; only_one_perimeter_first_layer = 0 +; only_retract_when_crossing_perimeters = 0 +; ooze_prevention = 0 +; output_filename_format = {input_filename_base}_{print_time}_{digits(layer_height,1,2)}mm_{temperature[0]}C_{filament_type[0]}_{printer_model}.gcode +; overhang_fan_speed_0 = 0 +; overhang_fan_speed_1 = 0 +; overhang_fan_speed_2 = 0 +; overhang_fan_speed_3 = 0 +; overhang_speed_0 = 15 +; overhang_speed_1 = 15 +; overhang_speed_2 = 20 +; overhang_speed_3 = 25 +; overhangs = 0 +; parking_pos_retraction = 92 +; pause_print_gcode = +; perimeter_acceleration = 0 +; perimeter_extruder = 1 +; perimeter_extrusion_width = 0.44 +; perimeter_generator = arachne +; perimeter_speed = 40 +; perimeters = 3 +; physical_printer_settings_id = +; post_process = +; prefer_clockwise_movements = 0 +; print_settings_id = 0.20 mm NORMAL (0.4 mm nozzle) @CREALITY +; printer_model = ENDER3 +; printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3\nPRINTER_HAS_BOWDEN +; printer_settings_id = Creality Ender-3 (0.4 mm nozzle) +; printer_technology = FFF +; printer_variant = 0.4 +; printer_vendor = +; raft_contact_distance = 0.1 +; raft_expansion = 1.5 +; raft_first_layer_density = 90% +; raft_first_layer_expansion = 3 +; raft_layers = 0 +; remaining_times = 0 +; resolution = 0 +; retract_before_travel = 2 +; retract_before_wipe = 70% +; retract_layer_change = 1 +; retract_length = 5 +; retract_length_toolchange = 1 +; retract_lift = 0 +; retract_lift_above = 0.2 +; retract_lift_below = 0 +; retract_restart_extra = 0 +; retract_restart_extra_toolchange = 0 +; retract_speed = 60 +; seam_position = nearest +; silent_mode = 0 +; single_extruder_multi_material = 0 +; single_extruder_multi_material_priming = 0 +; skirt_distance = 3 +; skirt_height = 2 +; skirts = 1 +; slice_closing_radius = 0.049 +; slicing_mode = regular +; slowdown_below_layer_time = 20 +; small_perimeter_speed = 25 +; solid_infill_acceleration = 0 +; solid_infill_below_area = 0 +; solid_infill_every_layers = 0 +; solid_infill_extruder = 1 +; solid_infill_extrusion_width = 0.44 +; solid_infill_speed = 40 +; spiral_vase = 0 +; staggered_inner_seams = 0 +; standby_temperature_delta = -5 +; start_filament_gcode = "; Filament gcode\n" +; start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nG4 S30 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +; support_material = 0 +; support_material_angle = 0 +; support_material_auto = 1 +; support_material_bottom_contact_distance = 0 +; support_material_bottom_interface_layers = -1 +; support_material_buildplate_only = 0 +; support_material_closing_radius = 2 +; support_material_contact_distance = 0.15 +; support_material_enforce_layers = 0 +; support_material_extruder = 0 +; support_material_extrusion_width = 0.36 +; support_material_interface_contact_loops = 0 +; support_material_interface_extruder = 0 +; support_material_interface_layers = 2 +; support_material_interface_pattern = rectilinear +; support_material_interface_spacing = 0.2 +; support_material_interface_speed = 100% +; support_material_pattern = rectilinear +; support_material_spacing = 1 +; support_material_speed = 40 +; support_material_style = grid +; support_material_synchronize_layers = 0 +; support_material_threshold = 40 +; support_material_with_sheath = 0 +; support_material_xy_spacing = 60% +; support_tree_angle = 40 +; support_tree_angle_slow = 25 +; support_tree_branch_diameter = 2 +; support_tree_branch_diameter_angle = 5 +; support_tree_branch_diameter_double_wall = 3 +; support_tree_branch_distance = 1 +; support_tree_tip_diameter = 0.8 +; support_tree_top_rate = 15% +; temperature = 205 +; template_custom_gcode = +; thick_bridges = 1 +; thin_walls = 0 +; thumbnails = +; thumbnails_format = PNG +; toolchange_gcode = +; top_fill_pattern = monotonic +; top_infill_extrusion_width = 0.4 +; top_one_perimeter_type = none +; top_solid_infill_acceleration = 0 +; top_solid_infill_speed = 30 +; top_solid_layers = 5 +; top_solid_min_thickness = 0 +; travel_acceleration = 0 +; travel_lift_before_obstacle = 0 +; travel_max_lift = 0 +; travel_ramping_lift = 0 +; travel_slope = 0 +; travel_speed = 150 +; travel_speed_z = 0 +; use_firmware_retraction = 0 +; use_relative_e_distances = 1 +; use_volumetric_e = 0 +; variable_layer_height = 1 +; wall_distribution_count = 1 +; wall_transition_angle = 10 +; wall_transition_filter_deviation = 25% +; wall_transition_length = 100% +; wipe = 1 +; wipe_into_infill = 0 +; wipe_into_objects = 0 +; wipe_tower = 0 +; wipe_tower_acceleration = 0 +; wipe_tower_bridging = 10 +; wipe_tower_brim_width = 2 +; wipe_tower_cone_angle = 0 +; wipe_tower_extra_flow = 100% +; wipe_tower_extra_spacing = 100% +; wipe_tower_extruder = 0 +; wipe_tower_no_sparse_layers = 0 +; wipe_tower_rotation_angle = 0 +; wipe_tower_width = 60 +; wipe_tower_x = 170 +; wipe_tower_y = 140 +; wiping_volumes_matrix = 0 +; wiping_volumes_use_custom_matrix = 0 +; xy_size_compensation = 0 +; z_offset = 0 +; prusaslicer_config = end diff --git a/server/xyz-cali-cube-mini_MK4.gcode b/server/xyz-cali-cube-mini_MK4.gcode new file mode 100644 index 00000000..d2915f5a --- /dev/null +++ b/server/xyz-cali-cube-mini_MK4.gcode @@ -0,0 +1,11219 @@ +; generated by PrusaSlicer 2.8.1+win64 on 2024-10-28 at 20:55:58 UTC + + +; +; thumbnail_QOI begin 16x16 652 +; cW9pZgAAABAAAAAQBAD+XV1dnYh/w/6PYTPA/mliXKYef38KwbCIm4gKwaHh/rNlF/7RaQDA/rZoGf +; 5qXVAKpYiwiI+ICsCpiKqIjoj+h2A5Nf6/YACcTDXAny/+j2I0GcCviJyICsCiiP6wby01/rldABI1 +; w/60Zhf+Y11WGREZ/mteUP7CZws1EjXE/sBhADUF/nlfRRnA/m9RNP6nVAD+zGcANcKbTaKW/rNaAB +; w1/ppNAP5mSi7+goKCGf52WDv+h0QAp/H+w2IANcH+wGAAEDUS/oJBAP5wOAD+jHBU/oCAgIqI/nBT +; NR7Aorb+sVkAHKK2wQ4d/o9IAB0bGcD+b1I0HqXDNx7+mU0A/shkABT+jkcAHaOl/opFAB0b/l1dXR +; kIHjejpR7AN/52OwAdwCwdwBsos4j+cVM2HsCcXP6VSwASHh3AorajlR0+Gyh//nRnW/6DTRYeVh7B +; HcD+mEwAHQL+bD8R/mxhVzoowKfE/n9aNP6VSwDAHsAdOwId/mhIKSjApYgowaqI/qCPfv6HTBEewB +; 0e/m86Bv5kTzoowVXAKLaI/p6env5vb2+Zw/6CVCUeHf5sPxH+XltXKMEZpYidiP6MjIymiAYowKaI +; /nxfQY9OKMEZwD5AAAAAAAAAAAE= +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 313x173 19860 +; cW9pZgAAATkAAACtBAD+WVlZ3n/Ms4j+zMzMuYj+mZmZ/l9fXzuiiP6Li4v+2tralIj+dHR0hojlf/ +; 3avYj+4uLim4j+h4eH/l1dXQqjiP6ampr+5OTk/r29vRgK/c9V41XcO83+kJCQ/ufn54aIPTvDGP7A +; wMD+4+Pj/peXlxk74Ar92xn+pKSk/vPz8/67u7sYCsO7iP7Q0NCpiP6JiYkZCv3PO+FV2zvMq4j+ur +; q6/uvr6/6oqKj+Y2NjO8UG/pmZmTP+v7+//mpqajvdCv3A/nFeSv6sZRz+gF8/CtexiP7R0dG3iP6V +; lZX+YGBgCsUC/qqqqv7m5ub+ra2t/mZmZgr90TveVdk7zf5/f3/+39/fmoj+hYWFCjvIv4gpsIgeO9 +; kK/cL+lmIt/tFpAMCfL/6PYTP+YlxVCtT+kZGR/u7u7v7Kysr+d3d3CskA/tra2iX+fHx8Cv3SVdxV +; 1zvNN/6oqKj+7Ozs/rm5uf5qamo7y6qIDf7m5ub+rq6uAjvUCv3D/mpdUP67ZhE1w/6zZRc0CtER/r +; +/v/7w8PD+pKSk/mRkZArLrogq/uPj4/6dnZ3+YWFhCv3SO9os1jvNt4j+09PTM/6Tk5P+XV1dO80K +; AP7X19eciP55eXk70Qr9xP6HYDk1xp8v/o9hMwrP/n5+fj4O/oKCgv5cXFwKzSj+lJSU/uHh4YKIFA +; r90zvYVdQ7zQr+l5eX/unp6f7Hx8f+c3NzO9Er/rm5uf7l5eX+nZ2dNzvMCv3FOv6lZCI1yf6zZRc6 +; Css3/qysrP7y8vL+tbW1KwrRt4j+y8vLsoj+j4+P/l1dXQr90zvWLNI7zq6I/sHBwf7q6ur+oqKi/m +; JiYjvTKP6Tk5P+39/fh4j+bm5uO8kK/cb+cV5K/sJnCzXLBf6AXz8KyRT+2dnZAhb+X19fCtMV/qOj +; oyD+tLS0/mlpaQr91DvUVdE7zv6Ghob+4uLikoj+gICAO9e6iB65iP6Ojo4KO8UK/cf+nWMo/tFpAM +; 8TOgrG/piYmP7x8fH+xMTEMgrX/n9/f/7X19ch/oKCggr91TvRu4gszzvOp4j+r6+v/u3t7f6ysrL+ +; Z2dnO9moiP6ioqIg/rW1tf5mZmY7wgr9x/5xXkobNdEbJArDPv7GxsYY/p6env5iYmIK2S/+tLS0/u +; bm5v6kpKT+Y2NjCv3UO84K/qCgoP74+PgszjvOH/7Y2NiniP6NjY0ZO9z+gICA/tPT0w4mCv3J/o9h +; M/7RaQDUBSw6CsD+hoaG/uvr64eIFxkK3AcGiYgfCv3VO8uwiP7Nzc3+////wP5ZWVnMO84Z/p+fnz +; r+wsLC/m9vbzvfDf6zs7Mg/qWlpf5hYWEK/cWh4f6sZRw11/6zZRf+b2JVMv7y8vL+rq6uDQrfs4j+ +; xcXFu4j+lJSU/l5eXgr91TvI/o2Njf7w8PAmwSzLO86xiP7Jycm+iP6bm5v+YGBgO99/wKKIOP7c3N +; wp/nJycgr9w/54XkT+ymgGNdmoL/7btIz+ioqKKArhN/6dnZ3+5OTkKv5sbGwK/dU7xTMq/v7+/ibC +; LMk7zzgviYj+enp6O98KxLWIE/7i4uIw/l1dXQr9wP6dYyg13f6lZCL+YlxVCuO+iAMd/oeHh/5cXF +; wK/dU7whv+5eXlJsMaLMg7zqqI/re3t/7s7Oz+q6urETveCsck/pycnP7j4+P+vLy8HAr7/nFeSv7C +; Zws13/67ZhH+gF8/CuMR/q2trS/+qqqq/mVlZQr91TuiiP6oqKj++vr6JsKYiP6enp47LMY7z/58fH +; z+3d3dnYj+iIiIClXdCsv+e3t7FqyI/oWFhf5bW1v5/o9hMzXiny8s/mJcVQrjHv7c3NyRiP56enoK +; /dS0iP7V1dUmw/7e3t7+dHR0O8BVxTvOpIj+paWl/uzs7P68vLz+a2trO90KzqqI/qysrC/+q6ur/m +; NjYwr1oeH+s2UXNeX+tGYX/mteUP5cXFzQCtGviP6+vr4z/pubmxWbiP3RIf7z8/Mmwp2I/rCwsDc7 +; wSzDO882JbOI/pWVlTc73ArRGS3+2dnZmIj+eHh4CvP+h2A5NeifL/6PYjQZ2QrIooj+l5eXMwQ2Cv +; 3Oq4giJsOMiP6Dg4M7w1XCO88h/ujo6IKIHzvcCtWxiP68vLz+5OTk/pubmzediPAGNev+tGYX/mNd +; VhneCsMB/s3Nza+I/o2NjSgK/cv+goKC/urq6ibDMS8KO8MswTvOrYgXK/6kpKT+YmJiO9wK16SI/p +; aWlv7g4OAx/m1tbQrtJP7CZws17QX+gWA//lxcXOMz/qenp/7m5ub+sbGx/mhoaAr9yAb+r6+v/vz8 +; /CbClYg/CsI7wiw7zyL+4eHhlYgTO9wK27yI/srKyrSI/oyMjArr/pZiLTXx/p5jKAkZ4xMdA/5/f3 +; 8K/ca5iP7d3d3+////w/7X19f+b29vCsU70Ab+rKys/uzs7BANO9sK3qiI/qWlpS/+srKy/mVlZQrn +; /nFeSv67ZhE18/67ZxH+cl5LGeMN/re3t/7l5eX+oaGhAhkK/cL+nZ2d/vf39ybCm4j+qamp/l1dXQ +; rHO84BPxX+j4+PGTvaCuIi/tXV1cD+fn5+CuX+j2EzNfYF/o9iNBnjKP6RkZEVhogBGcIK+q6ICybD +; IP58fHwKyjvLCv6cnJwrMf5xcXE72wrkHBAg/qKiov5gYGAK4aHy/qxlHDXV/q1XAKKmNeD+tGYXPP +; 5cXFzjtIj+yMjIt4j+kpKS/l9fXxnDCvb+i4uL/u7u7ibCVf68vLwCCss7yhz+xcXF/ujo6P6enp4V +; O9oK5yj+kJCQ/t7e3ouIFArdGcD+eV9F/spoBjXV/pRKAP5wOAD+h0QA/plNAP7MZwA13wURGeMV/p +; +fnyD+uLi4/mtraxnFCvGmiP63t7cXJsKRiBYKzTvJ/oqKiv7k5OSNiP58fHw72grrt4gxvYj+k5OT +; GQrYGcL+nmMoNdX+v2AA/nw+AB3AHsCitv66XQA14Bb+Y11WGeM5/tTU1D/+hYWFGccK7Qz+4+PjJs +; P+zs7OOgrOO8eoiP60tLT+7Ozs/q6urv5mZmY72grtMwYR/rm5uf5oaGgK1BnD/nJeS/67ZxE11f6g +; UAAdwjcewf6nVAAcNd8gLhnjL/6wsLD+5ubm/qioqP5lZWUZyAroKP6lpaX++fn5JsKZiP6ioqIZCs +; 87xr+I/tvb238L/ltbWzvZCvH+fn5+/tHR0amIIgrQf8U4NdUS/oJBAB3BABKipv6VSwAewf6xWQA1 +; 4J8vOP5jXVYZ4wv+3d3djogMGckK5Rj+0tLSJsOBiP53d3cK0VXEo4gz/uvr6/6+vr4YO9oK86yI/q +; +vry/+qamp/mJiYgrLGcYJMDXVDh3C/ppNADXBnloONx4ONeIw/mteUBnjsIj+wcHBJP6ZmZn+YGBg +; GcoK4f6SkpL+8vLyJsJAARUK0jvDtIj+zc3NuIj+mJiY/l9fXzvZCvYZ/ouLi/7a2tqViP51dXUKxx +; nI/ohhOTXVnVv+jkcA/nA4AMGjtf65XQA1xP6+YAD+kEkApNQ14wX+iGE5GeOiiCz+4+Pj/r+/vycZ +; ywrdqYj+wMDA/v///8OOiP6Hh4cK1FXC/pGRkf7o6OiEiP54eHg72gr5J/6/v78CLDcKwhnK/qVkIv +; 7RaQDVMT4dwf6USgA1xxz+o1IANeUG/mNdVhnjuogWq4gaKBnLCtr+f39/DbeIw/7Hx8f+aGhoCtVV +; wKuI/ru7uzr+p6en/mNjYzvZCvwVLP7i4uIE/mxsbAoZyv5yXkv+wmcLNdX+mk0AHcL+rVcANcn+sV +; kA/shkADXl/rtnEf6BYD8Z4wL+qqqq/ubm5v6urq7+Z2dnGcwK1jf+ra2t/vv7+ybClogsCtc7/oCA +; gP7f39+ZiP6EhIQ72gr9wQwpsYj+ioqKGcn+lmIu/tFpANX+v2AA/nw+AJouwQD+xWMANcr+ul0Apc +; M15p8vI/5jXVYZ4zH+2tral4j+fn5+Gc0K07eI/tvb2ybDOyOJiNj+qamp/uzs7P64uLj+ampqkIjZ +; Cv3CGcCpiP6oqKgvNiAZxS7+u2cRNdUOHcIlNcwom0016DAuGeOtiP66urr+5OTk/p+fn/5iYmIZzQ +; rQLBAmwioYNwrZ/uLi4gP+XV1dO9kK/cAZxf6FhYU/ITkZw/6PYjQ11RT+iEQAHcH+gkEAEjXODjXp +; BTgZ4yj+lJSUJIOIIxnOCsytiB4mw4mI/n9/fwrbuIg72Qr9wBnIr4j+ubm5/uXl5QYVGaHh/qVkIj +; XV/q1XAB3CBDXQ/qxWADXrMDwZ4yP+ysrKtIj+kJCQ/l5eXhnOCsn+h4eH/u3t7bKIw/7AwMD+ZWVl +; Ctw72Qr9GcujiP6Tk5P+39/fiIj+hHFd/sJnC/7RaQDV/pRKAB3Bo7UvNdEnNewF/oFgPxnjpYj+oq +; Ki/uXl5f61tbUrGc8KxRX+tLS0CCbCk4gDCt472Ar8Gc+5iP7SmF011f6/YAAfHcEA/sVjADXSDjXu +; /p5jKAkZ4/5/f38/VSIZzwrDvIj+4ODgv4jD/tLS0v5tbW0K3zvWCvwZ0P5yXkv+u2cRNdX+oFAAHc +; IONdT+sVkA/shkADXuIC4Z46uI/rOzs/7m5ub+paWl/mRkZBnPChn+oqKi/vj4+CbCmoggKArgO9UK +; +xnRODXVEgAdwQASNdUPNDXvny84CRnj/o2Njf7e3t6MiP53d3cZzrCI/s/PzybDhIj+eXl5CuI71A +; r6GdGh4f6sZR011Q4dwgQ115kfmR818f60Zhc8GeOyiDG9iP6Wlpb+X19fGcv+j4+P/vDw8CbCCP63 +; t7f+YWFhCuM70wr5GdL+eV9F/spoBjXUFP6USgAdwaO1/rNaADXZ/qxWADXyBf6IYTkZ4wb+nJyc/u +; Tk5P68vLwYj4jIqIg5/v7+/ibCj4j+ioqKCuU70Qr5GdP+nmMoNdUSHx3B/ohEABQ12g419P6lZCIJ +; GeMMA6eI/oiIiP5dXV0Zxf59fX0vJsP+ysrKKxkK5TvQCvgZ0/5yXkv+wmcLNdUEHcIONdz+rFYANf +; X+u2cR/oFgPxnjqIj+rKysLwn+ZmZmGcKiiP6qqqr++vr6JsKYiP6dnZ0ZwgrkO88K+BnT/o9iNDXV +; EgAdwQD+xWMANd3+sVkANfafLzj+Y11WGeP+h4eH/tzc3JOI/nx8fICIwLSI/tfX1ybD/t3d3f50dH +; QZxArjO84K9xnTCTA11f6nVAAdwgQ13/61WwD+yGQANfcwPBnjroj+vb29Av6cnJydiP709PQmwp2I +; /rCwsP5gYGCciMYK4jvNCvYZ1P6IYTk11jM+HcA+/rldADXgDzQ1+J8vOBnjN/6goKD++Pj4JsOLiC +; IZygrgO8sK9xnUBjXYFP6ORwAjNeKbTRs1+jAJGeH+hISE/uvr6ybD/sTExP5nZ2cZzArfO8oK9hnU +; /nJeSws1/cP+sVkANfufL/6BYD8Z3qSI/rGxsTkmwhf+1NTUHBnOCt47yQr1GdX+lmIuNf3FJzX9/p +; 5jKAkZ27mI/t/f3ybD/tbW1v6QkJD+2NjYm4j+gYGBGc4K3TvICvUZ1C4gNf3GDjX9wCAuGdn+n5+f +; /vf39ybCm4j+qampNxmsiP62trb+5eXl/qKiov5jY2MZzQrcO8cK9BnV/m1cS/7MZwA1/cf+sVkAAT +; XbnVv+lksABRw13Z8v/m1ZRRnWr4gpJsMg/nx8fBnCfyX+4ODgh4gQGc0K2zvGCvQZ1v5sUzn+lUsA +; /sNiADX9xv61WwABNdr+v2AA/nw+AJtN/odEAP6ZTQD+yGQANdsSAP5lTDQZ1f6NjY3+7+/vJsIX/r +; u7uwIZxbOI/sfHx7iI/pOTk/5fX18ZzAraO8UK8xnYJ/6HRACitv6xWQA1/cUoNDXZ/ppNAP5wOADA +; Kx7ANxs12Q4dwAwZ0wL+ubm5FybCkYj+jo6OGcikiP6fn58gDP5sbGwZzArZO8QK8xnZJx7B/plNAP +; 7IZAA1/cOeWg411/6/YAAAHcH+hEIANx7B/qNSADXWFP6ORwAdwf5lTDQZ0r6I/uTk5CbD/s7OzgkZ +; y7+I/tPT06SI/oaGhv5dXV0ZywrYO8MK8xnaJx7CN/61WwA1/cP+rFYANdb+p1QAHcIENQH+nlAAHs +; AbNdX+uV0A/nY7AB3CDBnQooj+p6en/vr6+ibCPf6hoaEoGc2piP6vr6/+5ubm/qmpqSAZywrXO8IK +; 8hnc/mxTOR7EOP7MZwA1/cH+rFYANdQUAh3Bo7UxNcEc/rVbAAE11QQdxAwZz7KIISbDgYj+dnZ2Gd +; H+iYmJ/t3d3Sn+enp6GcoK1zvBCvIZ3f5sUzkexafx/r5gADX9wP6sVgA10zE+HcH+lEoANdv+v2AA +; /nw+AB3FDBnO/pOTk/7y8vImwp2IMv5hYWEZ0wn+wMDA/uLi4iwkGckK1jvACvIZ3v5sUzkexqK2/r +; FZAByitvwFATXRBB3C/q1XADXbDh3HDBnMqoj+wsLCJsONiP6GhoYZ1qKI/piYmP7j4+P+wMDA/m9v +; bxnJCtUPGQrwGeAnHsj+mU0A/shkAKXD+/6+YADANc/+v2AAH5ouwQD+xWMANdoU/ohEAB3IDBnL/o +; GBgf7p6ekmw/7GxsYNGdkQ/s/Pz6yI/oyMjP5dXV0ZyFXU/ubm5v6pqan+ZGRkCu4Z4SceyaK2BTX6 +; mR+ZHzXODh3C/qBQADXb/q1XAB3KDBnJpIj+rq6u/vv7+ybCloj+mJiYGdyniP6oqKj+5ubm/rCwsP +; 5oaGgZyArTLf7c3NyQiP55eXkK7H/i/mxTOR7L/plNAP7MZwA1+Q41zBQh/nA4AMEAEjXb/pRKAB3L +; /mVMNBnIt4j+3NzcJsP+2dnZ/nJycoqI3yIsmIj+fn5+GccK1Dr+v7+//uLi4v6ZmZkVCukZ4ycezD +; f+ul0ANfj+rFYANcv+rVcAHcL+mk0ANdsSHx3MDBnH/pubm/729vapiMIq/qysrP5fX18Z4a2I/rm5 +; uf7k5OT+oKCgAhnGVdSiiP6Xl5f+4+Pj/sDAwP5ubm4K5xnk/mxTOR7O/qdUAP7MZwCitvb+rFYANc +; r+lEoAHcE+/rNaADXbJR3O/mVMNBnFrYj+ycnJJsOIiP5/f38Z5Cj+kpKS/uHh4YSIMhnGCtW5iAet +; iP6MjIwoQOMZ5v5sUzkezyv+w2IANfX+rFYANcgS/nw+AB3B/oJBABQ12hIAHc8MGcT+iYmJ/u7u7r +; GIw/6/v78gGee1iP7Jycm1iP6RkZH+Xl5eGcQK1qaI/qenp/7m5uYF/mdnZwrhGef+bFM5HtCitv6x +; WQA19AX+yGQANcYlHcIONdsOHdEMGcKmiP62trb+/f39oojCkog0GeqkiP6hoaH+5eXl/re3t/5qam +; oZxArX/oKCgv7Z2dmZiBcK3xnoJx7S/plNAP7IZAA18v6+YACdezXEEgAdwQASNdqdWyMd0gwZwb2I +; /uLi4ibD/tHR0RgZ7Rf+1dXVwP6EhIQZxArXPi4R/qCgoP5iYmIK3BnpJx7TN/66XQA18QEbNcMOHc +; IENdsS/nw+AB3T/mVMNBl//qSkpP75+fkmwgwRKBnvqoj+sbGx/ubm5v6mpqb+ZGRkGcIK2Bn+kZGR +; /uHh4YSIMgraGeonHtUO/sxnADXw/rBZADXBnVv+jkcAHcGjtRA12wQd1f5lTDQY/tDQ0CbDg4j+eX +; l5GfMp/t7e3oyI/nh4eBnCCtm1iP7IyMg3NP5eXl4K1xnrJx7Wp/H+w2IANe/+sFkANcD+uV0APh3B +; IzXbEv6CQQAd1v6Qd1/+8fHxJsII/ra2tv5iYmIZ9Rj+w8PDJP6Xl5f+YGBgGcAK2gb+oKCgIC7+am +; pqCtUZ7Cce16K2/rFZABw17f6wWAA1BB3C/q1XADXb/qdUAB3Y/sCoj/7////Cj4j+ioqKGfgG/pub +; m/7k5OT+vb29GBnAVdv+fHx8MD/+hISECtMZ7H/+jnVbHtn+mU0A/shkADXsEP58PgAdwQD+xWMANd +; qdW/6ORwAd2f7AqI/+////wTwrGfu7iP7R0dEs/omJiSgK3KqI/rGxsf7m5ub+pqam/mRkZArQGewY +; NP7JsJce2jcbNev+rFYAHcH+oFAANdsQPh3a/sCoj/7///+XiP6cnJz+XFxc/cAR/qurq/7m5ub+ra +; 2t/mZmZgrdGv7e3t6NiC4Kzhns/pGRkf7v7+8a/oNqUB7cOBw16QUd/oJBABI12yMd3P7AqI/+3Nzc +; /nR0dIiI/cP+hYWF/tvb2xY5/ltbW92xiP7CwsIk/peXlwYKyxnrIP6+vr7+8PDw/qampiD+bFM5Ht +; 2n8f66XQA16BIENdsS/nw+AB3No6Udzf6Da1L+YGBgGf3Froj+vLy8/uPj4/6dnZ3+YWFhCtyjiP6a +; mpoCF/5tbW0KyRnr/n5+fv7m5uaSiP6Dg4P+XV1dGSce3qK2/rFZABw15p1bNdsl/nA4AM7+hUIA/p +; 9QAB3N/mVMNBn9xyj+lZWV/uLi4oGI/nFxcQrdu4j+0NDQLAsZCsYZ6gb+rKys/vLy8v62trb+a2tr +; GcEnHuD+mU0A/shkADX9w5ou/oJBAB3O/p9QAP7DYgD+ej0AHc3+ZU00Gf3JMv7MzMyxiP6Ojo7+XV +; 1dCtyoiP6qqqov/q2trf5mZmYKxBnqtYgsqoj+kZGRFRnCJx7hNwU1/cH+rVcAHc7+gEAAEP7DYgD+ +; pFIAHc4R/l1dXcAZ/cgk/qSkpP7l5eX+tLS0/mlpaQrd/oWFhf7a2tol/nx8fArDGen+mJiY/vHx8f +; 7FxcX+dHR0GcQnHuP+mU0A/sxnAKK2+51b/pRKAP5wOADOI/7DYgDB/no9AB3O/mVNNP5dXV3BGf3H +; CsD+gICA/tfX152I/oGBgQrdroj+u7u7/uTk5P6enp4kCsAZ6A3+xsbG/u7u7v6fn5/+Y2NjGcX+bF +; M5HuSitv66XQD+0WkA+RL+fD4AHc7+pFIA/sNiAMH+qVUAHc/+ZU00/l1dXcJV/ccKwKuI/rS0tCD+ +; o6Oj/mNjYwrcKP6UlJT+4uLigYj+cXFxGej+hoaG/uvr6wP+f39/KBnGJx7m/qdUAP7MZwA19v6aTQ +; Adz6Ol/sNiAML+ej0AHc8R/l1dXcMZ/cYKwRkH/t/f3y3+dXV1Ct23iBqyiP6Pj4/+Xl5eGeQk/rKy +; sv7y8vL+r6+v/mlpaRnIJx7np/H+w2IANfMS/oJBAB3Q/qRSACjB/qlVAB3QEf5dXV3EVf3FCsOziP +; 7Gxsa6iCE3Ctwk/qOjoyABHBniuogGwP6Li4s3Gcn+bFM5HuiitgX+zGcANfAOHdGjpf7DYgDC/oBA +; AB3QEf5dXV3FGf3FCsOkiP6dnZ3+5OTk/rq6uv5ra2sK2hnBJj9A/oKCghnff/6hoaEj/r6+vv5wcH +; AZyyce6v6ZTQD+yGQApcPtnVv+jkcAHdL+pFIA/sNiAMH+s1oAHdH+ZU00/l1dXcUZ/cUKxb+I/tPT +; 06SICtoZwquI/rS0tP7m5ub+pKSk/mRkZBncOv7Nzc29iP6ZmZn+YmJiGcwnHus3/rVbADXr/rldAP +; 52OwAd0qOl/sNiAML+gEAAHdH+ZU00/l1dXcYZ/cQKxqmI/q6urgrZGcUH/t/f3y0fGdoH/u3t7Tj+ +; enp6Gc7+bFM5Hu3+o1IAHKK26AQd1P6kUgD+w2IAwRAd0v5lTTT+XV1dxxn9xArhGceyiP7FxcUVMP +; 5fX18Z1qeIKv7x8fH+qKio/mZmZhnP/mxTOR7up/E0NeX+v2AA/nw+AB3Uo6X+w2IAwv6KRQAd0hH+ +; XV1dyBn9wwrgGckG/p2dnSAq/mxsbBnUOSCViP6Ghob+Xl5eGdAnHu+itv6xWQAcNeL+p1QAHdb+n1 +; AA/sNiAMEQHdMR/l1dXcgZ/cMK4BnLGwOmiP6IiIgoGdCiiP6pqan+8/Pz/ri4uP5tbW0Z0ice8f6Z +; TQD+yGQANd+dW/6IRAD+cDgA1qOl/sNiAMIsHdMR/l1dXckZ/cIK3xnNqYj+ra2t/ubm5v6rq6v+Zm +; ZmGc6ziP7V1dWxiP6Tk5P+YWFhGdMnHvKitgU13f6tVwAd2CL+w2IAwTEd1BH+XV1dyhn9wgrdGdD+ +; h4eH/tzc3JKI/nt7e4GIzP6VlZX+8PDw/sfHx/52dnYZ1Sf+h0QA9P6ZTQD+zGcANdr+lEoAHdg7/s +; NiAML+j0gAHdQR/l1dXcoZ/cJV3RnRr4gX/uPj4/6bm5skGciriP7Dw8P+7+/v/qGhof5kZGQZ1ice +; 9Tc0Ndf+v2AA/nw+AB3Z/p9QAP7DYgDBMR3V/mVNNP5dXV3LGf3BVdwZ06KI/peXl/7i4uL+wcHB/n +; BwcBnGE/7p6emMiP6BgYEoGdf+bFM6HtGitv6nVAD+vmAA/qxWADce3w7+zGcANdT+oFAAHdo7/sNi +; AML+j0gAHdUR/l1dXcsZ/cFV2xnWuIj+zc3Nr4j+jY2N/l5eXkDCpIj+sLCw/vLy8v6ysrL+ampqGd +; n+bVQ6Hs/+nlAA/sNiADXCD/6VSwAe3ysoNdES/oJBAB3b/plNACjBnWsd1hH+XV1dzBn9wFXbGdem +; iP6np6f+5ubmI/5oaGgZwLiI/t3d3aSIOAYZ2X82Hs+itv6xWQA1wxwOHt83BTXPDh3cO/7DYgDC/o +; 9IAB3WEf5dXV3MGf3AVdoZ2hP+2NjYm4j+gICAvYj+8vLy/sHBwf5ycnIZ2n/ANh7R/plNAP7IZACl +; w8MP/pBJAB7f/plNAP7IZAA1yxT+jkcA/nA4AN3+mU0A/sNiAMGdax3XEf5dXV3NGf1V2hnbDRf+9/ +; f3Vf6goKD+Y2NjGdoowTYe0jc0NcMc/p5QAB7fNzQ1yTH+djsAHd07/sNiAML+j0gAHdcR/l1dXc0Z +; /cBV2BncGv7s7OyOiKuIgoj+dXV1Gdkowjb+h0QA1A4cNcMFNx7fDhw1xv6aTQAd3wH+w2IAwZ1rHd +; gR/l1dXc0Z/cBV1xnbM/63t7f+8fHx/qurq/5nZ2epiP7Hx8e4iP6Tk5P+X19fGdcowjb+h0QA1Sv+ +; w2IANcMoKx7fKyg1w/6/YAAAHd+jpTEowf6PSAAd2BH+XV1dzhn9VdcZ2r2I/uPj45mI/oeHh/5eXl +; 5AwKSI/qCgoCD+ubm5/mtraxnVKMM2/odEANaitv6xWQAcNcIcDh7fNwUcNcAOHeEB/sNiAMGdax3Z +; Ef5dXV3OGf1V1hnZKP6lpaUy/ru7u/5ubm4ZxDn+1NTUP/6GhoYoGdIoxDYe2AH+yGQANcEx/no9AB +; 7h/plNAP7CYQD+iEQAHdlGHcU7MaXTwf6PSAAd2RH+XV1dzhn9VdYZ2LGIA7WI/pWVlf5hYWEZxqmI +; /rCwsC/+qKioIBnQKMU2Htk3/rVbADUEHaKmHuKaLh3bnUsdxP6PSAD+w2IAwZ1rHdoR/l1dXc4Z/V +; XVGdj+kpKS/u/v7/7Kysr+d3d3Gcr+ioqK/t7e3in+eXl5Gc8oxf5tVDoe25s9HcAzHuIcHdw5Qh3C +; MaXTwf6USgAd2hH+XV1dzxn8CtUZ1iD+wMDA/vDw8P6kpKQgGcwY/sHBwf7i4uL+mZmZ/mBgYBnMKM +; Y2Htv+dzwAHcCiph7iHB3dnlqcbBId/o9IAP7DYgDBnWsd2/5lTTT+XV1dzxn8VdQZ1iY+kIj+g4OD +; KBnOo4gsAv6/v7/+bm5uGcooxzb+h0QA2/53PAAdwDMe4hwd3hKbTRL+s1oA/sNiAMEBHdsR/l1dXc +; 8Z/ArUGdSjiP6tra3+8vLy/rW1tf5qamoZ0rqI/tDQ0KqIGigZyCjHNv6HRADb/nc8AB3AMx7iHB3h +; /p9QAP6+XwCjpQod3BH+XV1dzxn8VdQZ07aIOwIWFRnUp4j+qampL/6vr6/+Z2dnGcYoyP5tVDoe2w +; YdwDMe4pouHeL+hUIAMQEd3BEozxn8CtMZ0yz+8fHx/sPDw/5zc3MZ2P6EhIQ7l4j+fn5+GcQoyTYe +; 2wYdwDMex6fx/rpdAP6MRgAe1hwd46OlwB3cESjPGfwK0xnRrYj+x8fH/u3t7f6enp4CGdoc/rq6uv +; 7k5OT+n5+f/mJiYhnCKMk2/odEANv+dzwAHcCiph7G/rFZAP7RaQDAnlr+nlAAHtWaLh39xf5lTTQo +; zxn8CsQ+CssZ0f6Hh4f+6+vrhogXGd0o/pSUlP7h4eGEiDIZwCjKNv6HRADbBh3AMx7Eorb+vmAANc +; MFNx7THB39xREozxn8CsMT/tzc3ArLf8+liP60tLT+8vLy/q6urv5oaGgZ4LaI/srKyrSI/pCQkP5f +; X18oyjYe2/53PAAdwDMexSv+w2IANcP+rFYAHtMcHf3FEf5dXV3PGfsKwhX+qamp/ufn5xwKyhnPLv +; 7g4OA3/oqKiv5eXl4Z4iT+o6Oj/uXl5f62trb+a2trKMk2HtsGHcAzHsaitgX+zGcANcEc/pBJAB7S +; mi4d/cURKM8Z+wrBuYj+0tLSpYj+hoaGKArKGc0o/qKiov7z8/P+vr6+/m9vbxnm/n9/fz9VMSjI/m +; 1UOh7bBh3AoqYeyP6ZTQD+yGQANcH+ul0AHtIcHd03HeMRKM8Z+wooLCD+ubm5/m1tbQrMGcywiP7P +; z8+7iP6Xl5f+YmJiGeiriP6zs7Mv/qampv5lZWUoxjYe2wYdwKKmHsk3/rVbADXB/plNAB7RHB3cp/ +; H+u14A/qhUAP51OgAd4f5lTTQozxn7roj+wsLCv4j+k5OT/mBgYArMGcz+j4+P/u7u7v7MzMz+eXl5 +; GekowTj+39/fi4j+eHh4KMX+bVQ6Htv+dzwAHcAzHsv+o1IA/sxnADX+vmAANx7Qmi4d3P6kUgD+vF +; 4APJ5a/oNBAB3gESjPGfr+iYmJ/t/f34iI/nZ2dgrOGcqoiP68vLwU/qenp/5mZmYZ6ijCsoj+xMTE +; JA7+YWFhKMP+bVQ6/odEANv+dzwAHcAzHsyn8TQ1/qdUAB7QHB3bKv69XwA/wFr+eT0AHeD+ZU00KM +; 8Z+KiI/rGxsf7m5ub+oqKiEQrPGcn+fHx8/uXl5ZSI/oWFhSgZ6ijEFf6cnJz+5OTkCP5ubm4owjYe +; 2wYdwKKmHs03Bf7IZAA3Hs8cHdv+plMAB8A//qBQAB3Po7XAHc7+ZU00KM9V9wz+1tbWnYj+gYGBGQ +; rPGciiiP6qqqr+8/Pz/re3twkZ7CjGDP7S0tIs/omJiTcowDYe2/53PAAdwDMez/6ZTQAOHs8cHdql +; 0/6/YABWwAcqHc7+lEoANf65XQD+fD4AHc0RKM8Z9aOI/qCgoC/+s7Oz/mpqahlV0BnHtIj+19fXIP +; 6SkpL+YGBgGe0ox6iI/q2trS/+rKys/mdnZyg2Htv+dzwAHcAzHuIcHdo4/sFhAFZGmQ/+cDgAzv6t +; VwA1wZ1b/ppNAB3MESjPGfQn/snJybSI/o6Ojv5fX18ZVdEZxv6Wlpb+8PDw/sbGxv51dXUZ7n/K/o +; eHh/7c3NyTiP59fX02/odEANv+dzwAHcAzHuIcHdml0/7BYQDBWv6ORwD+cDgAzP6CQQD+xWMANcMS +; /nY7AB3LESjPVfP+j4+P/uLi4v7BwcH+cnJyGcEK0RnEq4j+xMTE/u7u7v6goKD+ZGRkGe8oy66I/r +; 29vf7j4+P+noRrHtsGHcCiph7iHB3ZOP7BYQDBnWv+cDgAzP6nVAA1xP6aTQAdzf5oTzcozhnyPi7+ +; 5eXl/pycnP5jY2OZiMIK0BnE/oSEhP7q6uqJiP6AgIAoGfAozKKI/paWlv7Kuqn+jk8PHtr+dzwAHc +; AzHuIcHdijpf7BYQDC/o5HAP5wOADKAP6/YAD+0WkAwxIAHc7+vK6g/oCAgCjNGfE1/tvb25OI/n19 +; fRnEVdAZwqSI/rGxsf7y8vIU/mlpaRnyKM63iP64q57+pnhJHtn+dzwAHcAzHuKaLh3YOP7BYQDBnW +; v+cDgAyx/+s1oA/tFpAMIOHc7+bD8R/m1mX/62trYg/qOjo/5iYmIoyxnvFf6mpqb+5+fnCQ0ZxArR +; GcG5iP7e3t6iiP6MjIz+X19fGfIo0KaI/qCcmP62lXP+jU4PHtf+dzwAHcCiph7iHB3XO/7BYQDC/o +; 5HAP5wOADMo7X+oFAA/stmAMACHc7+ZkouKMCiiP6RkZE3jYj+c3NzKMoZ7raI/s/Pzywt/l5eXhnF +; CtEZwP6enp7+8vLy/sDAwP5xcXEZ9CjS/oGBgf69rJv+m2QtHtb+dzwAHcAzHuIcHdc4/sFhAME//n +; A4AM/+iEQAPh3NK/5hVkwowzL+xMTEvYj+lJSU/l5eXijHGe0o/paWliD+u7u7/m5ubhnHCtE6/svL +; y/7r6+v+mpqa/mJiYhn1KNMN/rCsp/6yjGYe1QYdwKKmHuIcHdajpf7BYQDC/pNKAP5wOADf/mhIKS +; jGpoj+n5+f/uTk5Cr+ampqKMYZ7K2IF/7i4uI//mFhYRnICtAp/u3t7Qf+e3t7Gfco1Df+kJCQ/se2 +; pf6VWh8e0/53PAAdwKKmHuKaLh3WOP7BYQDBnWv+cDgA3qJc/mRPOijJ/n5+fv7R0dGpiP6FhYUoxR +; nr/oaGhv7e3t48PRnJCs+niP64uLj+8fHx/qmpqf5nZ2cZ+CjWs4j+wLy3/qZ4SR7SBh3Aoqb+jEYA +; HuGaLh3Vo6X+wWEAwv6YTAD+cDgA3f5sPxH+X1hRKMsN/q+vr/7m5ub+qqqq/mRkZCjCGeqniP6urq +; 7+5+fn/qWlpf5lZWUZygrOv4j+4+PjmIj+h4eH/l5eXhn5KNck/p+fn/67noD+jk8PlcTQ/nc8AB3+ +; lEoA/tFpAP6+YAD+kEkAHuAcHdX+nk8A/sFhAME//no9AJs93P5lTTQozjf+i4uL/tra2paI/nd3dy +; jBGekuMMD+g4ODKBnLVcwo/qenp/7z8/P+ubm5/m1tbRn7KNm/iP6/sqb+m2QtHs8G/q1XAP7RaQDB +; nlr+p1QAHt+aLh3Uo6X+wWEAwjj+cDgA2/5sPxH+YVZMKNGyiP6/v78C/pqamv5fX18oGec3/p2dnf +; 7m5ub+tbW1/mtraxnMCswY/tPT07SIIf5gYGAKGft/2qmI/qqlof6xi2X+jU4OHs2m4v7MZwA1ww/+ +; jEYAHt2aLh3U/p5PAP7BYQDCFJs92v5oSCko1CT+mZmZ/uHh4YGIGBnmGP7Gxsa4iP6QkJAVGc0Ky/ +; 6Tk5P+7+/v/snJyf52dnYKwRn7KNz+ioqK/sOyof6VWh8ezaK2/rpdADXDnlr+nlAAHtyaLh3To6X+ +; wWEAwjj+cDgA2aJc/mNRQCjXvIj+zMzMsoj+i4uLGeT+jIyM/uLi4jH+dHR0Gc8Kyv7BwcE2/qOjo/ +; 5kZGQKwhn7KN0Y/rq2sv6sglgezv6nVAD+zGcANcP+sVkAHtuaLh3T/phMAP7BYQDCFJs92P5rQRf+ +; XltXKNmoiP6oqKj+5ubm/rCwsP5mZmYZ4C8Q/ubm5v6fn5/+Y2NjGc8Ky/7W1tb+gYGBGQrCGfwo3q +; KILP6/poz+jk8PHs2n8f7DYgA1wige25ouHdI7/rdcAKXTwTj+cDgA2P5lTTT+XV1d2xkoAD8wCBne +; CP7Z2dmXiP5/f38oGdAKy66ICsQZ/CjguYj+uq2g/qZ4SR7Norb+sVkANcGZHx7bmi4d0jn+wWEAwh +; SbPdb+bD8R/l9YUaUf2xnCOj3+5eXl/qCgoP5gYGAZ2hX+o6Oj/ufn5/6vr6/+aWlpGdEK0xn8KOGn +; iP6jn5v+sIpk/o1ODh7N/plNAP7IZAA1mR8e25ouHdE7/rdcAKXTwf6oVAD+cDgA1v5oSCn+XV1d3R +; nDBhL+39/fiYj+cHBwGdi1iP7Nzc2uiBr+Xl5eGdJV0xn8KOP+hISE/r+unf6VWh8ezaK2/rpdAKXD +; HtscHdH+jkcA/sFhAML+f0AA/nA4ANSiXP5hVkz+XV1d3RnGuYj+xsbGu4j+kZGRKBnVIf7k5OT+vr +; 6+BRnUCtMZ/CjkrIj+s6+q/rKNZh7O/plNAB7bmi4d0Sal08H+slkA/nA4ANT+aUYjKN4ZyKeI/qGh +; oSD+t7e3/mhoaBnSDf68vLwR/piYmP5iYmIZ1ArUGfwo5X8S/si3phMe6hyaPtD+jkcA/sFhAML+iU +; UA/nA4ANP+ZE86KN8Zyv6AgID+0tLSpoj+goKCGdAx/t3d3Y6IGxnWCtQZ/H/ntYj+wr66/qZ4SR7p +; HJo+0Cal08Em/nA4ANIr/l9YUSjfGcwN/rKysi/+p6enMxnMM/6rq6v+5+fn/qioqP5mZmYZ1grVGf +; wo6KSI/qKiov68n4H+jU8PHuccHc/+jkcA/sFhAMIC/nA4ANH+ZkouKOEZzTf+jY2N/tvb25OI/nR0 +; dBnKuYj+09PTpIj+hoaGNxnXVdUZ/Sjp/n5+fv7AtKf+m2QtHuYcHc/+wmEAoZefacAm/nA4AND+bD +; 8R/mFWTCjhGdAF/sHBwf7j4+P+l5eXNxnGN/6bm5v+5eXlPf5sbGwZ2FXWGf0o6qqI/qyoo/6yjGYe +; 5Rwdzv6USgD+0WkAVpxMnUv+jkcA/nA4AM/+aEgpKOIZ0iT+m5ub/uPj4/6/v7/+a2trGcQ6/sPDw7 +; 2I/pOTkyQZ2VXWGf0o7P6MjIz+xLSj/pVaHx7jHB3O/sVjADXB/rteAB3Oolz+Y1FAKOIZ1b+IB66I +; /oiIiP5cXFzC/oqKihWGiP52dnYZ2grXGf0o7bGI/r24tP6sglge4hwdzSM1wf6gUAAdzv5rQRf+Xl +; tXKOMZ1i/+qqqq/ubm5v6urq7+ZWVlwP6ysrIv/qKiov5kZGQZ2wrXGf3AKO0JAv7SuZ/+jlAQHuCa +; Lh3N/sVjADX+v2AA/oJBAB3O/mVNNCjkGdgoLf7Y2NgsBgP+gYGBKBnbCtgZ/cAo7P6Ghob+7Ozss4 +; jA/ujcz/6lc0Ae3xyaPsz+jkcANf6nVAAdzv5sPxH+X1hRKOQZ2qOI/q2trf709PTADQYZ3ArZGf0o +; 6iT+srKy/vz8/CbCjcT+soxm/o1ODh7dHB3M/rldAP6USgAdzjQo5Rnas4j+ysrKsoj+j4+PqIgVho +; g2jYjaCtoZ/cAo6LqI/t/f3ybD/tXV1QW2iP7BsJ/+lVofHtwcHcum4sAdzaJc/mJURijlGdr+kZGR +; /uPj4/7AwMD+cnJyGcC6iP7IyMgG/o6OjhnYCtsZ/cAo5/6goKD++Pj4JsKbiP6pqan+X19fKMCuiP +; 62sq3+so1nHtuaLpo+2/5pRiMo5hnZPv65ubn+5eXl/pubm/5jY2MZwqiI/qSkpC/+tLS0PhnWVdsZ +; /cEo5K+I/s3NzSbDhYj+fHx8KMN//paWlv7JuKf+jk8PHtkcHdr+ZE86KOYZ2f6BgYH+3NzcOP58fH +; yAiMb+goKC/tTU1KOI/oCAgBnUCtwZ/cEo4wf+7+/vJsJV/rq6uv5kZGQoxreI/r61rf6meEoe2Bwd +; 2Cv+X1hRKOYZ2CQN/ufn5/6rq6v+Z2dnGcitiP60tLQv/qSkpCQZ0QrdGf3CKOCniBsXJsKRiAcoya +; WI/p+blv69n4L+jU4PHtaaLh3X/mZKLijnGdi4iP7R0dEs/oiIiDcZyjcW/t3d3Y+I/nNzcxnQCt0Z +; /cJ/3zn+5OTkJsM4CSjMBP7Ctqn+m2QtHtUcHdUrGv5dXV3nGdco/piYmCD+urq6/m5ubhnOFP7Dw8 +; O/iP6VlZUoGc0K3xn9wijcBv6pqan++vr6JsI9/qGhoSjPq4j+rqqm/rKMZh7Umi6aPtT+aEgpKOgZ +; 166I/sDAwDMw/mFhYRnQpoj+np6e/uTk5P67u7srGcsK4Bn9wijbs4j+1dXVJsOAiC4o0hb+xrWkDx +; 7SHB3SolwCKOgZ1/6Hh4cGiYguGdT+fX19/tDQ0KuI/oWFhRnKVeAZ/cMo2f6VlZX+8/PzJsKdiP6z +; s7MzKNQF/r+7t/6meEn+h0QA0Rwd0f5sPxH+X1hRKOgZ1qeI/q+vr/7n5+f+pKSk/mVlZRnWq4gYLz +; r+ZGRkGccK4Rn9xCjWPiImw42I/oaGhijXo4g3/rudgP6OTw8ezxwd0P5lTTQo6RnWvIj+1tbWIf6D +; g4MoGdgo/oqKiiwlLhnFCtkwKArGGf3EKNT+goKC/unp6SbD/sbGxv5paWko2r6I/r6xpP6bZC0ezp +; ouHc4r/mFWTCjoGdajiP6enp4v/rW1tf5ra2sZ3Cf+vr6+/uTk5P6bm5sGGcJV2v7j4+P+vLy8/mlp +; aQrFGf3EKNIkNv78/PwmwpWI/piYmCjdqYj+qKSg/rGLZf6NTg4ezJoumj7NNCjpGdYn/sfHx7eIFh +; UZ3hX+l5eX/uHh4YKI/m1tbRnBCtr+fHx8/s/Pz6yIAP5bW1vEGf3FKNC4iP7d3d0mwywji4jg/omJ +; if7CsaD+lVof/odEAMscHcuiXP5jUUAo6RnWB/7i4uIi/nNzcxniPf7Ly8s3/ouLixkK3C/+rKys/u +; bm5v6rq6v+Y2NjCsIZ/cYozv6dnZ3+9vb2JsKciAkVnYjir4j+ubSw/qyCVx7KHB3K/mpEHf5eW1co +; 6BnWqoj+tra2/uXl5f6enp4CGeSpiP6mpqb+5ubm/rGxsSAK3Bk8LJiILgrCGf3GKMuuiP7Kysomwy +; /+fn5+KOUG/piYmP6+pYz+jk8PHsgcmj7J/mVNNCjpGdb+f39//tra2hYXGej+hISEP1UICt0Y/r29 +; vf7k5OT+m5ub/l5eXp2IwBn9xyjJGv7u7u6xiMP+vr6+ICjouIj+uayf/qZ4SR7HHB3HK/5fWFEo6B +; nWpIj+paWl/ufn5/6urq4NGekKr4gf/uXl5f6hoaEGCtwG/paWlv7g4OCEiBgKwBn9xyjGAv63t7f+ +; /f39JsKSiP6RkZEo6wL+op6Z/rCKZAQexRwdxv5mSi4o6RnWI/7Ozs47/oqKijcZ6QrBNzT+3t7ei4 +; gFCt28iP7Kyso3/oyMjAoZ/cgoxLyI/uLi4ibDJRgo7v6Dg4P+vq2cDx7EHB3EpR/+YVZMKOgZ1ij+ +; lZWVEf69vb0FGeoKxDL+xcXFFf6SkpIZCtypiP6lpaUv/rKysv5mZmYZ/cgowX8g/vn5+SbCDP6kpK +; Q3KPCsiP6xran+so1mHsOaLh3D/mlGIyjpGdatiP69vb3+4+Pj/piYmP5iYmIZ6grGM/6goKD+5OTk +; /ri4uA0K3SL+1dXVwP5+fn4Z/cmyiP7S0tImw4KI/nl5eSjzN/6SkpL+x7emEx7Bmi6aPsL+ZE86KO +; gZ1/6FhYX+3t7eCwwZ6wrJF/7R0dEsIgrdKx/+5eXl/qKiov5gYGAZ/cYD/vHx8SbCQB/+YmJiKPa0 +; iP7Cvbn+pnhJHsAcHcD+bD8RASjnGdcz/qysrP7n5+f+p6enLxnrCsusiP6wsLD+5ubm/qioqDMK3K +; OI/pCQkP7e3t48FBn9w6mI/r+/v/7////Djoj+iYmJKPmkiP6goKD+vJ6B/o1PDxwd/mVNNCjnGdi6 +; iCGiiAD+Xl5eGesKzSj+i4uL/tvb2wf+dHR0h4jdMv7FxcW8iP6Tk5MoGf3A/n9/f/7n5+e4iMP+yc +; nJ/mpqaij8/n19ff7As6f+n3VK/oV7cCjmGdg3/pycnC/+t7e3/mxsbJCI7ArQs4j+wMDA/uPj4/6Y +; mJgoCtwzFf7k5OT+ubm5/mlpaRn7o4j+rKysKibCl4j+m5ubGcAo/aqI/rGxsS8N/mZmZijiGdmwiA +; C6iAP+YGBgGexV0hX+mZmZ/uLi4jU6CtsZwBc0qIgiGfm2iP7a2tomw/7b29syGcMo/Rr+3t7ejoj+ +; eXl5KN8Z2hokAP51dXUZ7VXVDCmxiP6IiIgKywAAAAAAAAAB +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 440x240 28616 +; cW9pZgAAAbgAAADwBAD+WVlZ7n/S/oyMjP7p6emxiP6vr6/+YWFhO8Ek/ra2tv78/PyLiP6JiYk79n +; /9/f6ZmZn+9PT0ooj+paWl/l1dXQrBrYj+u7u7G4mI/oGBgf5cXFwK/e479FXsO9KqiP61tbUbPv6G +; hoYKO8MZPP7r6+utiP6zs7MRO/EK/f3ADf7Hx8f+/f39gYj+fHx8CsQZ/pKSkjo9/qqqqiQK/e878i +; zqO9M5N76I/sPDw/5nZ2c7x6+I/sjIyDn+29vb/np6ejvtf/3ZoeEK5P6Hh4f+6urqsIj+uLi4/mNj +; YwrHtYj+zMzMOf7W1tb+dHR0Cv3wO/As6TvSpYj+o6Oj/vT09JyI/pWVlSg7ySj+m5ub/vX19UAC/l +; 5eXjvof/3aOv6sZRyg8/5qXVAK4aWI/rS0tP77+/uQiP6MjIwKyhX+oqKiMkD+l5eXNwr98DvuLOc7 +; 07aI/s7Ozgj+1dXV/nJycjvNu4j+2NjYOf7Ly8v+b29vO+UK/dv+gF8//spoBqHhwAX+h2A5Ct+9iP +; 7d3d2/iP7Jycn+bGxsCs3+fHx8/tvb2/78/Pz+xcXFHJKI/fI76yzlO9MZ/pOTk/7t7e2riP6oqKgG +; O88G/q2trSo6/pGRkTvhCv3d/qVkIjXEBv5iXFUK2xn+oaGh/vf3952I/p2dnRkKz6qI/rS0tP75+f +; mPiP6IiIgZCv3yO+ks4zvUPv69vb0qhoj+f39/CjvRCv6Dg4P+5ubmDP67u7v+ZmZmO90K/d3+cV5K +; /sJnCzXG/rtmEf6AXz8K2bGIB/79/f3+2dnZ/nd3dwrT/ouLi/7n5+e0iP6ysrL+YmJiCv3zO+dV4j +; vU/oKCgv7j4+M5/ry8vBE71SD+wcHB/vz8/IWI/oCAgDvZCv3f/pZiLf7RaQDJBf6PYTM6Ctb+jo6O +; /u/v7z3+sLCwFQrVCQA5/tvb2/56enqBiP30O+VV4DvUp4j+q6ur/vf395aI/o+Pjxk71xkDFKOI/q +; mpqRU71Qr93/5qXVD+u2YRNcz+s2UXNArTqIj+vLy8/vz8/ImI/oSEhArYN/6cnJz+8PDwpYj+oKCg +; /l9fXwr99DvjLN871LqIMDn+zc3N/m1tbTvbtoj+09PTOf7S0tL+c3NzO9EK/eH+h2A5Nc+fL/6PYT +; MK0f59fX3+4+PjKv7Dw8P+aGhoCtu7iDD+/Pz8/szMzBgK/fU74DebiN071KOI/pqamhSliBUGm4jd +; N/6lpaU9loj+mJiY/lxcXDvNCv3hoeH+pWQiNdL+s2UXOgrNooj+qqqqDBT+lZWVGQrdqIj+ra2tH5 +; aIBygK/fQ73r6I/uTk5CzbO9WwiP7ExMQ5/tvb2/56eno74H/+fHx8/uHh4bmI/sLCwv5paWk7ygr9 +; 4v5xXkoLNdQF/oBfPwrLtoj+1dXV/vz8/AP+cnJyCuH+hYWF/uPj4yr+urq6/mRkZAr99Tvb/qSkpC +; qkiCzaO9X+iYmJPir+s7Oz/mFhYTvjqYgbOYqIHjvGCv3k/p1jKDXYEzoKyP6Wlpb+8/PzLv6pqak3 +; CuOuiP6+vr4qhoj+f39/GQr99TvXsogw/v///8Es2DvVAv6zs7MMj4g8GTvlGRr+7OzsLv6xsbECO8 +; IK/eT+cV5K/rtmETXaGyQKxayI/sXFxTmFiP5+fn4K5ij+lZWV/u3t7auI/qenp/5hYWEK/fY71P6Q +; kJAQJsIs1zvVv4j+29vb/v39/f7GxsYcO+mxiCk5/tnZ2f54eHg7f/3l/o9hMzXdBSz+YlxVCsL+g4 +; OD/unp6bGI/ru7u/5kZGQK6baI/s/Pzzn+1NTUIwr99zvQJP7CwsImxP5ZWVnVO9U3Ff7z8/PAHTc7 +; 6yj+np6eH5uIFf5eXl4K/eI6/qxlHDXg/rNlFzQKpYj+srKyGwkHGQrrJCAynIj+lJSUNwr99jvO/o +; CAgBwmxSzUO9WziP7Ly8sI/tfX1/51dXU77go9/tvb2zn+ycnJ/m5ubo2I/eD+eF5EBaHh4gX+mnNM +; /tvb2zkaGI6I7/5/f3/+3t7eORP+Z2dnCv33O8oK/q6urv7+/v5/xYOILNI71Qol/uvr666I/qurqx +; U77QrCFf6xsbH++/v7j4gHCv3e/p1jKDXm/uCfXf6ln5gZCvGriP63t7cMjYj+hYWFGQr99lXIt4j+ +; 29vbJsUX/rm5uSQs0TvVrIj+urq6KomI/oKCgv5bW1s76wrGGf6GhoYNsIj+uLi4Lwr92v5xXkoLNe +; j+u2YR/oBfPwryGQccsYj+rq6u/mJiYgr99zvF/pmZmf74+PgmxY+I/ouLiwo7Vc871v6AgID+4eHh +; Of6/v7/+ZWVlO+sKyj4xOYOI/n5+fgr92P6PYTP+0WkA6wUs/mJcVQryGP7IyMg5/tnZ2f54eHgK/f +; g7wSD+ysrK/v///8b+y8vL/mlpaTvBVc471RX+qKioHyf+kZGR/l1dXTvqCs0oPzJ//qenpxUK/dQ6 +; /rNlFzXuKP5qXVAK8qSI/p+fn/7x8fEB/p2dnf5fX18K/fc7ACcmxZmIKBk7wizMO9a4iBII/tHR0f +; 5vb2876wrQuIj+1dXVOf7Q0NAjCv3S/odgOTXxBf6PYTMK876I/tjY2P78/Pw8/mtrawr99aOILv7+ +; /v4mxf7c3Nz+dnZ2O8RVyzvVooj+l5eX/u/v76iI/qOjowY76grTBv6pqakbk4g/GQr9z/6lZCI19P +; 60Zhf+Y11WGdIK3qmI/rCwsC4r/oyMjP5dXV0K/fIf/uLi4ibFCP6xsbH+X19fO8UsyjvVrogEOYKI +; /nx8fAo76QrWGf5/f3/+5OTkG/6/v7/+aWlpCv3M/nFeSv7CZws19p8v/oFgPxnfCtIe/uXl5Sr+t7 +; e3/mNjYwr98P6hoaEqpIjFKwAKO8YsyDvWD/7m5ua2iC7+YmJiO+kK2qmI/r6+vjmIiAAK/cr+lmIt +; Nfr+nmMoCRnlCsuviAQqN/59fX0ZCv3sKwMmxjH+ZmZmO8gsxzvVM/6vr689koj+i4uLGTvoCt0oBy +; eniP6urq4zCv3G/nFeSv67ZhE1/P67ZxH+cl5LGesKxSj+mJiY/u7u7qmI/qSkpBUK/er+jY2N/vPz +; 8ybFAf6VlZUoCjvILMU71r6I/tnZ2f79/f0t/mpqajvoCuGziCU5/tbW1h8K/cT+j2EzNf3BBf6PYj +; QZ8ArBuIgDOTT+cHBwCv3npoj+v7+//v///8b+19fX/nFxcQrCO8dVxDvVpIj+np6e/vLy8gH+nJyc +; /l5eXjvnCuQ3/qGhoS6ZiP6dnZ3+XV1dCv3AoeH+rGUcNf3E/rRmF/5rXlD+XFxc8jP+qKioEJmI/p +; KSkjcK/eT+fX19/ufn5ybFKg0GCsU7xSzDO9WyiP7Jycn+/f39/tnZ2f53d3c76ArmGb6I/t3d3Sr+ +; xsbGCQr8/nheRP7KaAY1/cYF/oFgPxnz/oGBgf7h4eE5/r+/v/5nZ2cK/eEZ/qqqqgiiiMWGiP5+fn +; 4KyDvEVcE71Qr+jY2N/urq6rCI/q6urv5hYWE75wrqJP61tbUqjYgaCvr+nWMoNf3KFv5jXVYZ8qyI +; /rq6uv75+fmLiCL+XV1dGQr93bWI/tjY2CbFVf69vb0CCso7wyzAO9WriP63t7f++vr6jIgACjvmCu +; 0o/omJiSutiBAgCvb+cV5K/rtmETX9zP67ZxH+cl5LGfIo/pGRkf7r6+sM/qurqzMZwgr92P6Wlpb+ +; 9/f3JsWRiAcZCs07wSw71f59fX3+39/fOf7CwsI+O+YK8a6I/sfHxzmAiP58fHwK9P6PYTM1/c+fL/ +; 6PYjQJny/ytIj+y8vL/vz8/A4fGcQK/dOpiB4mxv7Pz8/+a2trCtA71hX+paWl/vX19ZuI/pOTkyg7 +; 5grzN/6ampoBVf6kpKT+X19fCvCh8jA13pou/qZTAP7MZwA17TD+a15QGfIV/qKioiPA/pmZmf5fX1 +; 8Zxgr9ziL+7OzsJsUM/qGhoTcK0lXUtogl/v39/f7U1NQUO+YK97qIHTn+zc3NBQrsGcD+iGE5Nd/+ +; rVcA/nA4AKfx/pVLAP66XQA17Z8vARnzv4j+29vbOf7Gxsb+ampqGchV/cko/rOzsxcmxf7e3t49Ct +; U70Rn+lJSU/u7u7qqI/qampgY75Qr6Ff6srKz++vr6kogSGQrnGcL+pWQiNd/+lEoAHcCn8aXDorb+ +; sVkAHDXt/qxlHf5jXVYZ8qmIMv74+PiQiP6KioooGckK/cW5iP7f398mxRf+tLS0FQrWO9A+/r6+vj +; mFiP5+fn4KO+UK/Bki/ubm5gz+vLy8/mhoaAriGcT+cl5L/sJnCzXe/r9gAP58PgAdwSoewf6ZTQD+ +; yGQANe3+u2cR/oFgPxnz/ouLi/7n5+e0iP60tLQCGcsK/cH+nZ2dGybFjYj+iIiIGQrXO88i/uTk5L +; iI/rq6uv5kZGQ75Qr9wqqI/sHBwTmGiP6CgoIK3hnG/pZiLjXf/ppNAB3Dp/EewjcFNe2fLyP+Y11W +; GfKwiDH++/v7gYj+e3t7gYjNCvquiCUmxv7Hx8cNCtk7zaiI/qysrP739/eViP6NjY0ZO+UK/cSiiP +; 6RkZH+8PDwpYj+q6ur/mFhYQrZGcf+cl5L/rtnETXeEv6CQQAdwwD+uF0A/pBJAJtNw/6eUAA17jD+ +; cl5LGfKjiP6bm5sFpoj+oaGhFRnNVff+iYmJ/vHx8SbFECwoCto7zLuI/tbW1gj+zMzM/mxsbDvlCv +; 3ItYj+09PTORIQCtUZyf6PYjQ13/6nVAAdxP6gUAA1wJ5a/qNSAB7DNe+fL/6PYjQZ87qI/tTU1Dn+ +; zc3N/m5ubhnPCvIV/ry8vP7+/v4mxf7a2tr+c3NzCtxVyij+m5ubFKSI/p+fn/5eXl475H/9y6SI/q +; SkpD2XiP6ampooCtAZyqHh/qVkIjXenVv+jkcAHcMA/r9gADXD/rpdAP6MRgAewf7IZAA18DD+a15Q +; GfKniDr+9vb2GP6Pj4/+Xl5eGc8K77+I/uXl5SbFnYj+rKys/l9fXwrdVckr/sXFxQj+29vb/nl5eT +; vlf/3NGf58fHz+4ODguogx/mtrawrMGcwuCzXe/rldAP52OwAdwwQ1xgH+nlAAHsA0NfEF/oFgPxnz +; Iv7j4+O5iP68vLz+ZmZmGdAK7P6mpqb+/Pz8JsWIiP6BgYEK3zvIGv7o6OiziP6ysrL+YWFhO+QK/d +; GoiP65ubk5Pv6JiYkKyRnN/p5jKDXf/ppNAB3Do7X+s1oANcgc/qxWAP6MRgD+tVsANfMW/mNdVhny +; rYj+vb29/vr6+oiI/oCAgCgZ0Qrns4g/JsUX/sHBwf5kZGQK4DvGEf60tLQbDS0KO+QK/dMoGv7s7O +; yriP6zs7MRCsQZzv5yXkv+u2cR/tFpAN4S/nw+AB3D/ohEABQ1yzQONfQgLhnyooj+lJSUCT3+qKio +; JBnSCuT+k5OT/vb29ibFk4j+kJCQGQrhO8X+e3t7/tzc3P79/f0A/mhoaDvkCv3XOv7Ly8s5/tra2v +; 56enoKwH/Q/o9iNDXfDh3EDjXO/plNADX1BTj+Y11WGfK1iP7Pz885/tXV1f50dHQZ0wrgAv7Dw8Mm +; xv7T09MnCuNVwwb+oqKi/vT09CP+lpaWKDvkCv3ZN/6dnZ3+9fX1IzMGGc4J/qxlHTXenVv+iEQAHc +; P+gkEA/r9gADXP/p5QADX3/rRmF/5rXlAZ8iT+paWl/vPz852IPwYZ0wrd/oGBgf7q6uomxZuI/qSk +; pDcK5DvCJ/7Nzc0I/tbW1v50dHQ75Qr92xm7iP7a2tr++/v7/srKyv5vb28ZzP55X0X+ymgGNd7+rV +; cAHcT+mk0ANdEfNfgF/ohhORnz/n5+fv7d3d05/sTExBwZ1ArZGf6wsLD+/v7+JsWCiP57e3sK5lXA +; Cv6RkZH+7OzsrYj+qamp/mBgYDvkCv3bGcKliAX++/v7kIglGcr+nmMoNd/+lEoAHcM+/rldADXSDv +; 7IZAA1+f6lZCL+Y11WGfKriP62trYMjoj+h4eH/l1dXRnUCta3iP7c3NwmxRf+uLi4JArnO6yI/ru7 +; u/77+/uIiP6BgYEKO+QK/dp/xSj+hYWFPgz+ubm5/mdnZxnG/nJeS/7CZws13hL+fD4AHcP+lEoANd +; T+sVkApNQ1+v67ZxH+gWA/GfP+jY2N/ujo6Bv+sLCw/mNjYxnVCtP+m5ub/vn5+SbFGP6Li4sZVegE +; /uPj4zn+vr6+/mVlZTvkCv3ZGco+/sTExDmEiP6AgIAZxP6PYjT+0WkA3/6gUAAdxP6tVwA11f6+YA +; D+rFYANfufLzgJGfKxiP7Hx8c5/tra2v55eXkZ1QrQPikmxv7Ly8v+aWlpCur+9vb2l4j+kJCQKJ2I +; 5Ar92BnNN/6VlZX+8vLyooj+qKioJBnACf67ZxE13hL+gkEAHcMA/sVjADXWm03+o1IANf3+tGYXPB +; nyo4j+np6e/vHx8aOIN/5gYGAZ1QrN/oeHh/7v7+8mxZiI/p2dnf5dXV0K6/7Pz8/+bm5uO+UK/dcZ +; 0beI/tXV1Tn+0dHRMv6IYTk13w4dxP6gUAA12Tg1/cAF/o9iNBnzvIj+19fXOf7Kysr+bGxsGdYKyQ +; b+ubm5/v7+/ibF/tvb2/51dXWGiO0GO+QK/dcZ1BUN/vn5+f7ZmFc13p1b/o5HAB3DABI12v6ZTQA1 +; /cL+tGYX/mNdVp8v8qiI/q+vry46B/5eXl4Z1QrHvIj+4+PjJsUINhUK7jvkCv3WGdf+c19M/sRpDj +; Xe/rldAP52OwAdw/6aTQA13P6eUAD+yGQANf3CBf6BYD8Z8x7+5OTkKv65ubn+ZWVlGdYKxP6jo6Mq +; JsWKiP6EhIQK8DvjCv3VGdj+lmIuNd8EHcM+LzXd/rFZAA81/cT+nmMoCRnyroj+wMDAKoSI/n5+fh +; nXCsCxiP7U1NQmxv7ExMT+ZmZmCvE74Qr91RnY/nJeS/67ZxH+0WkA3hIAHcMAMzXe/rVbAKK2Nf3F +; IC4Z8jf+l5eX/u7u7qmI/qWlpSQZ1v6Pj4/+9PT0JsWUiP6UlJQoCvI74Ar91BnZODXf/qdUAB3EDj +; XgmR8ONf3Gny84GfO3iP7R0dE5/tLS0v5xcXEZ06aI/sHBwSbG/tfX1/5wcHAK9DvfCv3TGdmh4f6s +; ZR013p1b/ohEAB3DABI14Z5a/qNSADX9yP60Zhc8GfIz/qenpwGbiP6Tk5P+X19fGdD+f39//ujo6L +; eIxZuIPgYK9TveCv3SGdr+eV9F/spoBjXe/q1XAB3E/ppNADXkHzX9yQX+iGE5GfP+gICA/uDg4Dn+ +; wcHB/mdnZxnO/qysrAgmxRH+fX19Cvc73Ar90hnb/p5jKP7RaQDf/pRKAB3Do7X+s1oANeU4Nf3L/q +; VkIv5jXVYZ8qyIDP75+fmMiP6EhIT+XV1dGcq1iP7Z2dkmxVX+vLy8/mNjYwr4O9sK/dEZ2/5yXkv+ +; wmcLNd7+v2AA/nw+AB3DIf7LZgA15jj+yGQANf3L/rtnER4Z8ij+kJCQ/urq6hv+ra2tMxnI/peXl/ +; 739/cmxTYHKBnACvc72gr90Rnb/o9iNDXf/qBQAB3E/qdUADXo/rFZAKTUNf3Mny84CRnys4j+ysrK +; /vz8/P7Y2Nj+d3d3GcWqiP7IyMgmxv7Ozs7+bGxsGcMK9jvZCv3QGduh4TA13hIA/nA4AMMA/sVjAD +; XpNJ5aNf3OMDwZ8qSI/qGhof7y8vJ//pqamv5fX18Zwv6FhYX+7e3tJsWZiP6fn583GcYK9FXYCv3P +; Gdz+iGE5Nd8OHcT+mk0ANeuZHw41/c8FOBnzvoj+2traOS3+a2trGaOI/rW1tRcmxf7d3d3+eHh4Gc +; kK81XWCv3PGd3+pWQiNd4U/pRKAB3DPv65XQA17f6eUAA1/dEwCRnyqYj+srKy/vj4+JGI/p+fn/7g +; 4OAmxUD+s7Oz/mFhYRnLCvI71Qr9zxnc/nJeS/7CZws13/6tVwA+HcL+lEoANe8fNf3SBf6BYD8Z8/ +; 6Kior+9PT0JsaMiP6Hh4cZzgrxO9QK/c4Z3f6WYi414hL+gkEAHcAvNfAfNf3TBRYJGfCviP7R0dEm +; xv7Hx8cNGdAK8DvTCv3NGd0u/rtnETXkFCUzNfH+o1IA/shkADX91CAuGe7+jIyM/vLy8ibFloj+mJ +; iYNxnTCu470gr9zRnd/o9iNDX93v6xWQCk1DX91Z8vOBnrJP69vb0mxhf+19fX/nBwcBnVCu070Qr9 +; zBndCf6lZCI1/d8PmS81/dcwPBno/nx8fP7m5uYmxSr+ra2tVf729vaXiP6RkZE3GdQK7DvQCv3MGd +; 0u/sJnCzX94AH+p1QANf3Yny8RGeV//qioqP79/f0mxS/+gICAGcD+g4OD/uLi4rqI/r6+vv5mZmYZ +; 1ArrO84K/cwZ3hb+0WkA/eP+nlAANeqeWjXrFv5jXVYZ4jb+19fXJsVV/r+/v/5lZWUZwqyI/ry8vP +; 76+vqJiP6BgYEoGdMK6jvNCv3MGd7+rWARNf3k/plNADXoFCH+mEwA/shkADXr/qldERnh/pWVlf72 +; 9vapiMWSiP6QkJAoGcQo/pOTk/7s7Ow9/qmpqSQZ0wrpO8wK/csZ4P6JSAb+sVkA/sxnADX94v6eUA +; ABNeb+rVcA/nA4AMD+hkMAo7X+tVsANen+p1QAHRnfqIj+xcXFJsYD/m5ubhnItIj+zc3N/vz8/D/+ +; dHR0GdMK6DvLCv3LGeH+hEUGn8P+mU0A/shkADX94f6xWQABNeX+lEoAHcEWHsAfHDXlnVshHcAZ3v +; 6Dg4P+6+vrJsUM/qOjo/5fX18ZyiQC/vPz80D+l5eXBhnSCuc7ygr9yhnjBB7ANwU1/eAborY14/6/ +; YAD+fD4AHcIWHsGlwzQ14y8dwhncooj+srKyFybFFf57e3sZzv59fX3+3NzcOQD+aWlpk4jSCuY7yQ +; r9yhnkBB7C/plNAP7MZwA1/d4P/qdUADXi/qBQAB3E/opGAB7Corb+rFYAHDXg/pRKAB3DGdu4iP7e +; 3t4mxRf+t7e3/mJiYhnQqoj+tbW1DI+I/oiIiCgZ0QrlO8gK/coZ5QQewxI0Nf3dm03+o1IANeD+v2 +; AA/oJBAB3DAP7FYwBG/p5QAB7Cp/EcNd4S/nw+AB3EGdr+nJycDCbFCf6Kior+XV1dVdMpDbOI/rGx +; sf5jY2MZ0ArlO8cK/ckZ5wQexKK2Dhw1/dz+nlAANd8OHcQlNcEcDjceDjXfJR3GGditiAcmxv7Kys +; ocGdaxiP7GxsY5/tvb2/56enoZ0FXkO8YK/ckZ6AQexv6ZTQD+w2IANf3bHzXdnVsjHcM+EjXE/r5g +; AMA13hIAHccZ1y3+8PDwJsWXiP6bm5v+Xl5eGdgG/p2dnQWliP6fn5/+YGBgGc9V4zvFCv3JGekEHs +; eitv6xWQA1/do4/shkADXbEv58PgAdwyM15w4dyRnVFf66uroXJsX+29vb/nV1dRncu4g/ORoYGc9V +; 4v58fHwKO8IK/cgZ6wQeyf6ZTQD+yGQANf3YBTQ12v6aTQAdxP6tVwA15hQjHcoZ1L2I/uTk5CbFnY +; j+rq6uJBneqIj+ra2t/vb29gn+jo6O/l5eXhnOCuE9/qSkpDc7wAr9yBns/oRFBh7KorY0Nf3X/rVb +; AMA12BIAHcMAMzXmEv58PgAdyxnT/qWlpSomxQ0iGeL+hoaG/uTk5Dn+urq6/mVlZRnOCuD+09PTOR +; L+cHBwCv3IGe0EHswO/sxnADX91ZkfDjXXDh3EJTXn/ppNAB3NGdGyiP7V1dUmxRf+w8PD/mZmZhnk +; rYj+v7+/KoWI/n9/f/5dXV0ZzArgpYj+q6ur/vX19ZuI/pGRkSgK/cQZ7/6ERQYezSv+w2IANf3Unl +; r+nlAANdUU/o5HAB3DAP6/YAA15hIAHc4Z0DT+9PT0q4jFMv6Tk5P+XV1dGeaiiP6Wlpb+7e3tqoj+ +; pqamJBnMCt87wP6CgoL+4eHhOf7BwcEgCv3CGfD+hEUGHs6itv6xWQAcNf3THzXU/rldAD4dw/6aTQ +; A15w4d0BnOp4j+wsLCJsb+1tbW/nBwcBnqtoj+0NDQOf7U1NQjGcwK3jsKwK2I/ru7u/75+fmNiP6C +; goIZCv0Z8f6ERQYe0P6ZTQD+yGQANf3SHzXT/ppNAB3DPv6zWgA15hT+jkcAHdEZzf6AgID+6enpJs +; WbiP6mpqb+X19fGeymiC/+9PT0m4j+lJSUBhnLCt07CsL+kpKSCa+I/q2trQYK+xny/oRFBh7RN/61 +; WwD+0WkA/dE4/shkADXQ/r9gAP58PgAdw/6IRAAUNeYxPh3SGcso/q6urhcmxYSICBnw/n9/f/7f39 +; 85/sLCwg0ZygrjBf7MzMw5/tjY2P51dXUK+RnzBB7TOP7MZwA1/c/+sVkApNQ1z/6gUAD+cDgAxA41 +; 5/6aTQAd1BnKI/7a2tr+////xRf+u7u7/mNjYxnyq4j+t7e3DI2I/oWFhSgZyQrjpIj+o6OjMqKI/p +; qamv5eXl4K9Rn1/oRFBh7UKzQ1/c7+vmAAGzXNFP6CQQAdwwD+xWMANeYSHx3VGcn+mZmZPSbFj4j+ +; jY2NKBn0KP6Pj4/+6enpsYj+rq6u/mJiYhnJCuT+e3t7/tzc3Dn+yMjI/mlpaQrzf/YEHtWitgX+zG +; cANf3MmR8ONcz+rVcAHcT+mk0ANeclHdcZxz4LJsb+zc3N/mtraxn4soj+ycnJOf7Z2dn+eHh4GcgK +; 5SD+tbW1/vf395OI/omJif5dXV0K8Bn3/oRFBh7X/plNAP7IZAA1/cz+mU0ANcv+lEoAHcOjtTE15p +; ouAB3YGcb+hoaG/u7u7ibFmYj+n5+f/l5eXhn6Ff6goKD+8vLyMv6cnJz+YGBgnIjHCub+i4uL/ujo +; 6LOI/ra2tv5hYWEK7hn4BB7YorYFNf3LATXJEv58PgAdw/6USgA15y8d2hnEo4j+t7e3FybF/t3d3f +; 54eHgZ/cC9iCw5LToZxwrmsYj+xsbGKoOI/np6egrsGfkEHtoBHDX9yf6eUAAcNcf+mk0AHcQvNecj +; HdsZw7uI/uLi4ibFQP6ysrIkGf3CIP6wsLD+9/f3K/6MjIz+Xl5eGcUK56KI/p2dnf7w8PAu/qGhoT +; cK6Rn6/oRFBh7borY0Nf3IDv7IZAA1xRIAHcMA/sVjADXmEv58PgAd3BnCJP76+vomxSv+h4eH/l1d +; XRn9xf6IiIj+5ubmKv63t7f+Y2NjGcUK6LuIPzn+0NDQ/m5ubgrnGfsEn8PdDhw1/cb+sVkANDXEDh +; 3E/qBQADXn/ppNAB3eGcA6/tLS0ibGD/5oaGgZ/cg6/sHBwSqDiP59fX0ZxArpp4gY/vX19ZmIBygK +; 5Bn8/oRFBh7ep/H+w2IANf3F/r5gAJtNNcIU/o5HAB3DABI15hIAHd8Z/o2NjTImxZWI/paWlv5eXl +; 4Z/co3/piYmDaoiP6kpKQVGcMK6v6EhIT+4+PjOf6+vr4gCuIZ/QQe36K2BRw1/cObTf6sVgA1wf65 +; XQA+nVvD/ppNADXnDh3h/r+/v/7////G/tjY2P5ycnIZ/c64iP7T09M5/tDQ0P5wcHAZwgrrrogX/v +; n5+YuI/oCAgBkK3xn9wP6ERQYe4f6ZTQD+yGQApcP9w/6wWAA1wP6aTQD+cDgAwz7+rVcANeYU/o5H +; AB3iJsUq/qmpqRUZ/dCniBwQmYj+kZGR/l5eXhnBCusZ/pWVlf7t7e2tiBwGCt0Z/cH+hEUGHuKitv +; 61WwA1/cIO/r9gAP58PgAdwwD+xWMANeYxPh3jJsSGiP5/f38Z/dT+goKC/uHh4Tn+v7+//mZmZhnA +; Cu0j/s/Pzzn+1tbW/nNzcwrbGf3BO/6MTQ4e5P6jUgD+zGcANf3A/qRSAB3EDjXn/ppNAB3l/v///8 +; JV/r6+vv5kZGQZ/dYN/rq6uv76+vqKiBP+XV1dGQrtFf6np6f+9PT0Vf6Xl5f+Xl5eCtgZ/cCuiP7I +; yMj+/f39/o5QEB7lp/H+ul0ANf3+nU4AHcIA/r9gADXmEv58PgAd5ibBkYj+j4+PKBn92Cj+kZGR/u +; vr6wz+q6ur/mFhYQrv/n5+fv7e3t45/sXFxf5nZ2cK1hn9wP6IiIj+6+vrDP63t7f+hkcIHuaitv6x +; WQAcNfv+q1YAHcEENef+p1QAHegmwP7R0dH+bW1tGf3ctIj+zMzMOf7X19f+dXV1Cu8v/ri4uP74+P +; iQiP6Hh4cZCtMZ/aaI/ra2tio6/oyMjBn+hEUGn8Po/plNAP7IZAA1+v6vWAAdo7X+s1oANeYUIR3p +; DP6ioqIGGf3epYgz/vLy8sD+mJiYBgrvB/7q6uoq/rKyshUK0Rn9voj+3t7eOf7JyckYGcAEHumitv +; 6xWQA1+TH+jkcAFDXm/q1XAP5wOADVpdMd0xsZ/eK/iP7b29s5/sbGxv5qamqRiO8YPP77+/uBiP55 +; eXkKz3/8KP6jo6P+9/f3nYj+nZ2d/l1dXRnBBB7r/plNABw1951bNef+lEoAHdU7/oBAAB3TGf3kqY +; j+s7OzPQ3+iYmJKAruo4j+oKCg/vHx8aWI/p+fnzcKzBn8soj+zs7OCP7Y2Nj+eHh4GcMEHuyitv66 +; XQD+0WkA/eAS/nw+AJou1P6AQAAxmj4d1CgZ/eUa/ufn5yr+s7Oz/mJiYgrvPf7Y2Ng5/s3NzQkKyh +; n8Fv7w8PCoiP6wsLAkGcQEHu7+p1QAHKK2/d3+oFAA/nA4ANUB/sNiAMD+gEAAHdT+XV1dwBn95Qn+ +; xMTEOYCIGwrvqIj+sbGx/vb29peIKSgKx3/7IP69vb05ioj+hISEGcYEHu+n8f7DYgCn8f3aEv6CQQ +; Ad1P6AQAAQKMAQHdX+XV1dwRn95aKI/pubm/7w8PAf/qGhof5fX1+ciO/+h4eHLzn+u7u7/mRkZArF +; Gfv+f39//uTk5LeI/sPDw/5paWkZxwSfw/Citv6xWQD+0WkA/dgOHdUj/sNiAML+ikUAHdX+XV1dwh +; n95ArAu4j+1dXVOTgnCu+viP7CwsL++vr6iIj+fX19GQrCGfoG/qurq/75+fmXiP6VlZUoGcgEHvIB +; /shkADX91J1b/o5HAB3Uo6X+pFIA/sNiAMIQHdb+XV1dwxn94wrBqIj+rKys/vb29paIFigK7hn+mZ +; mZ/u7u7j3+pqamBgrBGfm2iD85AyMZygQe8zc0Nf3SEv52OwAd1Tv+w2IAw/6KRQAd1v5dXV3EGf3j +; CsIx/uPj4zn+u7u7IArvuYgDOf7U1NT+cXFxChn5/peXl/7z8/Mu/qmpqQYZywQe9f6nVAD+zGcANf +; 3P/ppNAB3WpdP+w2IAw5s9Hdf+XV1dxRn94grDrYj+vb29G4eI/oCAgBkK7qaI/qqqqv709PSdiP6T +; k5P+Xl5eGfYN/sXFxTmEiCYZzf6ERQYe9qfx/sNiAKfx/cz+v2AA/oJBAB3XOyjDLB3X/l1dXcYZ/e +; EKxCj+lJSUGD3+p6en/mBgYArv/oGBgf7h4eE5E/5nZ2cZ9P6FhYX+6enpGyogGc7+hEUGHveitv6x +; WQD+zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV3HGf3hCsW2iP7Pz885/tXV1f5zc3MK7hkNKgyNiAAoGf +; CliP6zs7MbCf6Pj48Z0AQe+f6ZTQD+yGQANf3GnVv+jkcAHdn+pFIA/sNiAMP+j0gAHdj+XV1dyBn9 +; 4FXGJP6lpaX+8/PznYj+lZWV/l5eXgrsGcH+kpKS/uvr67CI/q+vryQZ7ruI/tzc3Dn+y8vL/m5ubo +; 6I0QQe+qK2/rVbADX9xP6tVwAd2qOl/sNiAMMxHdn+XV1dyRn93wrIF/7d3d05Ig0K6hnDtIgaOf7Z +; 2dkuGet//p+fnx9V/qGhoSgZ0gQe/P6jUgAcNf3B/pRKAB3b/qRSAP7DYgDD/o9IAB3Z/l1dXcoZ/d +; 9VyKuI/ra2tgyNiP6GhoYZCucZxRX+o6Oj/vLy8hD+nJyc/l9fX52I6LCI/szMzP79/f3+29vb/np6 +; ehnUBB79KzQ1/BL+fD4AHdujpf7DYgDDnWsd2v5dXV3LGf3eVckZ/o6Ojv7o6OiyiDYK5hnIv4j+29 +; vbOf7Jycn+a2trGeb+jY2N/u7u7gz+s7Oz/mJiYhnVBB79wDf+sVkAHDX5JR3d/qRSAP7DYgDD/o9I +; AB3a/l1dXcsZ/d5Vy7KI/sjIyDkK5hnJqYj+tLS0PZOI/oqKiv5eXl4Z4qeI/rq6ujkN/oeHhxnXBJ +; /D/cL+mU0A/sNiADX2Ev6CQQAd3aOlKMOdax3b/l1dXcwZ/d1VzAb+np6eCuUZzAv+5+fntIj+uLi4 +; Ahng/n19ff7h4eE5/sXFxf5qamoZ2AQe/cOitgU19A4d3/6kUgD+w2IAw/6PSAAd2/5dXV3NGf3dVf +; MZzgkAKoSI/nx8fCgZ3KKI/qenpwwj/piYmCgZ2QQe/cUBHDXwFP6USgD+cDgA36Ol/sNiAMOdax3c +; /l1dXc0Z/d1V8hnQN/6cnJz+8PDwp4j+o6Oj/l9fX52I2rSI/tPT0/79/f3+1dXV/nR0dBnbBB79xq +; K2NDXu/r9gAB8d4P6kUgD+w2IAw/6PSAAd3P5dXV3OGf3cVfIZ0rqIMP78/Pz+0tLSBRnYIf7y8vIu +; /qysrBUZ3AQe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd3+XV1dzxn921XxGdSniP6tra0Qmoj+kJCQ/l +; 5eXhnUq4gTOYaI/oGBgRne/oRFBh79yafx/sNiADXoEv6CQQAd4v6kUgAow/6USgAd3f5dXV3PGf3b +; CvAZ1/6EhIT+4+PjOf7AwMD+ZmZmGdL+goKC/ufn5yr+vr6+/mdnZxnfBB79yqK2/rFZAByituUOHe +; Ojpf7DYgDDnWsd3v5dXV3QGf3bVe8Z2K2IF/76+vqLiBMoGc4V/rCwsBsn/pGRkSgZ4AQe/cz+mU0A +; /shkADXiFP6ORwAd5P6fUAD+w2IAwyMd3v5dXV3QGf3bCu4Z2ij+lZWV/uzs7Bv+q6urFRnMuYj+2t +; raOf7Ozs7+cHBwGeIEHtw3Huw3/rVbADXg/rldAP52OwAd5KOl/sNiAMOdax3f/l1dXdEZ/dpV7Rnd +; togHOf7X19cQGcr+m5ub/vX19cD+pKSk/l5eXhnjBB7Yorb+p1QA/rpdADWZH/6eUAAe7f6jUgD+zG +; cANd3+mk0AHeYiKMP+mU0AHd/+XV1d0Rn92lXtGd4V/qampv7z8/N//pmZmf5eXl4Zxq6IPDmAiP57 +; e3uBiOQo/oRGBh7Vp/H+rFYA/sNiADXDHA7+jEYAHuwrNDXaEv58PgAd5jsow51r/no9AB3f/l1dXd +; IZ/dlV7Bnh/n5+fv7d3d05/sbGxv5paWkZxAv+7OzsDP62trb+Y2NjGeR/wP6ERgYe1Tc0Ncb+vmAA +; /pBJAB7sN/6xWQD+zGcANdcOHegi/sNiAMP+mU0AHeD+XV1d0hn92QrrGeOqiP63t7f++Pj4kYj+iY +; mJ/l5eXhnApogu/vv7+4+ICxnlKMEJHtcO/sxnADXFHA4e7QH+yGQANdSdW/6IRAAd6Dv+w2IAw51r +; /no9AB3g/l1dXdMZ/dlV6hnl/o6Ojv7p6ekq/rS0tDO5iP7f3985Lf5sbGwZ5SjC/oRGBh7Yp/H+w2 +; IANcYP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3TGf3ZCukZ57KILf77+/uLiD0y/pub +; mygZ5ijCCR7ZNwX+zGcANcUc/p5QAB7tARw1z/6USgAd6qOl/sNiAMOdaxQd4f5dXV3TGf3ZVekZ6L +; WI/unp6baIVS4VGeYowwke2wH+yGQANcYFNx7sNzQ1zP6/YAD+fD4AHev+mU0A/sNiAMP+pFIAHeL+ +; XV1d1Bn92AroGej+kZGR/vDw8D3+uLi4/tnZ2Tn+z8/PJxnkKMQJHtw3GzXGmR8rHu0O/sxnADXJ/q +; BQAB3so6UoxP56PQAd4v5dXV3UGf3YCugZ5qmI/r+/vzmJiP6Dg4MZqIj+sLCw/vb29peIB/5eXl4Z +; 4SjFCR7eOBw1xRwOHu0r/sNiADXGEgAd7QEow/6kUgAd4/5dXV3UGf3YCucZ5v6AgIAgKv7BwcH+aG +; hoGcL+h4eHIDn+vb29/mVlZRnfKMYJntTfK/66XQD+0WkAxg/+kEkAHuyitv6xWQA1xA4d7qOlMaXT +; wxQd4/5dXV3VGf3XCuYZ5aOI/q2trQyXiP6UlJQoGcSviAT++vr6iYj+f39/KBndKMYJHuA3/rFZAB +; w1xTT+ikYAHu0B/shkADXAnVv+jkcAHe/+mU0A/sNiAMP+pFIAHeT+XV1d1Rn91wrmGeS3iP7X19f+ +; /f39/tHR0f5ycnIZxyj+mJiY/u7u7gz+qKioFRnbKMcJHuIB/shkADXD/qdUAP5wOAD+hEIAHu43/r +; pdAFo+He+jpTGl08P+ej0AHeT+XV1d1Rn91wrlGeT+mJiY/vT09KOI/qenpwYZyjL+0dHROf7V1dUy +; GdkoyAke4zcFNcCdW/6IRAD+cDgAwAse8B3oRh3G/o9IAP7DYgDD/qRSAB3l/l1dXdYZ/dZV5RnirY +; j+xsbGOYOI/n19fRnNpYj+qampAUD+lZWV/l5eXhnWKMkJHuX+mU0A/q5XAB3CCx7wHeidSxIdxTGl +; 08P+ej0AHeX+XV1d1hn91grkGeL+hoaG/urq6hv+ubm5ERnQ/oGBgQa9iP7ExMT+aGhoGdUoyQke5v +; 52OwAdwgse8B3pmR+hpx3D/o9IAP7DYgDD/qlVAB3m/l1dXdZV/dZV5BngJP60tLQqkYj+jY2NGdMN +; /rq6uv75+fmOiA8oGdIoyv6ERgae1Ob+djsAHcILHvAd6ptNnUsSHcH+uV0ApdPDFB3m/l1dXdYZ/d +; YK4xngvIj+3d3dOf7Kysr+bW1tGdb+kZGR/uvr6yr+sbGxJBnQf8sJHuY+HcL+hEIAHvAd6xIQpcMd +; /oBAAP7DYgDD/qlVAB3n/l1dXdZV/dYK4xneKP6hoaEunYj+n5+fKBnYs4gaKv7a2to9Gc8oywke5j +; 4dwgse8B3tBBL+pFIA/sNiAMM1Hef+XV1d1hn91grjGd0Y/s3Nzf79/f0sPRnbo4j+oqKiIx/+np6e +; BhnMKMwJHuY+HcILHvAd76Ol/qlVAP7DYgDBGR3o/l1dXddV/dUK4hndB/7v7+8M/rKysv5hYWEZ3r +; 6I/tra2jka/mxsbBnKKM3+hEYGHuY+HcILHvAd8f6USgD+vl8Ao6U1Hej+XV1d1xn91VXiGdsR/ru7 +; uzmLiP6FhYUZ4SD+s7OzLpWI/oyMjP5eXl4ZyCjN/oRGBh7m/nY7AB3CCx7LpcMe4h3yNf6kUgAd6S +; jXGf3VCsqoiArUGdv+fX19/uLi4rmI/sTExP5paWkZ5Dz+5+fnOf65ubkRGcYozgke5v52OwAdwgse +; yv6sVgD+0WkA/r5gAP6MRgAe4B394SjXGf3VCsn+fn5+MwrUGdmiiP6pqan++fn5mIj+lpaWKBnmsI +; gxKoWICCgZxCjO/oRGBh7mPh3CCx7IEv7DYgA1wZ5a/p5QAB7fHf3h/l1dXdcZ/dUKxyj+q6urLpuI +; CtMZ2bWI/tTU1P79/f3+09PT/nNzcxnpN/6bm5s2qYj+paWl/mBgYBnCKM8JHuY+HcILHsf+o1IANc +; X+sVkA/oxGAB7dHf3hKNcZ/dVVxhT+1dXVKv7W1tYyCtMZ2P6VlZX+8/PzLv6qqqoGGey5iCEIIRQZ +; wSjPCR7mPh3CCx7HBTXGmR83Htwd/eH+XV1d1xn91QrF/paWliM9/qysrP5eXl4K1BnWq4gxOTMmGe +; +miAn+9fX1m4j+kpKSNyjQ/oRGBh7m/nY7AB3CCx7I/plNABw1xf6xWQAe3B394SjXGf3VCsOsiP7F +; xcUqiIj+gICACtUZ1iL+6Ojosoj+vLy8/mVlZRnyIv7i4uK6iP7BwcE+KM8JHub+djsAHcILHsmitv +; 66XQD+0WkAxBz+lUsAHtsd/eEo1xn91QrC/oSEhA0b/r+/vyAK1hnUFf6xsbEbk4j+kJCQGfWtiP69 +; vb0MjIgi/l5eXijNCR7mPh3CCx7LDv7MZwA1wzQe2x3porYd8yjXGf3VCsAG/rKysgyWiP6RkZEZCt +; YZ1LmI/tra2jn+zc3N/m9vbxn3KP6UlJT+7OzsG/6tra3+YmJiKMz+hEYGHuY+HcILHswr/sNiADXD +; Hx7aHemn8f6oVAD+eT0AHfH+XV1d1xn91ArAvIj+29vb/vv7+xY2jIjYGdP+nZ2d/vb29lX+oqKi/l +; 1dXRn4KMC1iDg5/tjY2P52dnYoywke5j6dW8ILHs2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3wKNcZ +; /dX+n5+fEH/+pKSkGQrZGdGviP7Kysr+/f39/tzc3P56enqCiPoowST+paWlMn/+m5ubFSjJCR7mPh +; 3C/oRCAB7P/plNAP7IZAA1wSce2R3opcP+vF4APMH+mk0A/nU6AB3uKNcZ/dOwiBoqg4j+e3t7gIja +; GdH+ioqK/u3t7Qz+tbW1/mJiYhn7KMP+fX19/tzc3Dkt/mpqaijI/oRGBh7mPh3C/oRCAB7Qorb+ul +; 0ANcAc/pBJAB7YHej+oFAAP8E8wDcd7ijXVf3SKRgM/re3tzMK2xnPp4j+uLi4/vv7+46I/omJiRn8 +; KMWqiP62trb++Pj4koj+ioqK/l5eXijGCR7m/nY7AB3C/oRCAB7SDv7MZwA1NB7YHec2/r1fAMA/wZ +; 5aHe8o1xn90DP+urq6/vr6+is8Ct0Zzr+I/uDg4Dn+x8fHOhn9KMf+jY2N/unp6bKIHwIoxQme1OY+ +; HcILHtOn8f7DYgA1/plNAB7XHef+oVAAB8E/wP6RSQAd7/5dXV3XGf3PCP7g4OAq/snJyToK3RnNKP +; 6mpqY9moj+mpqaKBn9wCjIsYj+x8fHKoOI/nt7eyjECZ7U5j4dwgse1KK2BTQ3HtYd5jb+vl8AwQfA +; nWs2HdX+lEoA/stmACMd1SjWGf3OKP6np6f+9/f3nYj+m5ubKBlV3RnMs4j+0dHR/v39/f7W1tb+dX +; V1Gf3BKMqiiP6enp7+8fHxLv6ioqL+YGBgKMIJHub+djsAHcILHtb+mU0Ao6Ue1h3m/qNSAP7AYQBS +; WsAHJR3V/q1XADXB/rldAD4d0yjWGf3NtIj+0tLS/vv7+/7Y2NgQGcBV3hnL/pKSkhQ9/q2trQYZ/c +; IozLuI/tfX1zn+0NDQBSjBCR7mPp1bwgse8B3lo6X+wWEAwUZWwP56PQCbPdP+gkEA/sVjADXDnVv+ +; jkcAHdIo1hn9zCH+8fHxp4j+sLCwBhnBVd4ZyaqI/sDAwDmIiP6BgYEZ/cQozaeI/q+vr/729vaYiP +; 6Pj48GKAke5j4dwgse8B3lOP7BYQDCVv6hUQD+cDgA0/6gUAD+0WkAxp1bAB3RKNYZ/cov/sLCwhuM +; iP6CgoIZwwrdGcn+gICALyr+wMDA/mdnZxn9xCjQ/oaGhv7k5OQ5/r6+vv5mZmYJHub+djsAHcILHv +; Ad5Dv+wWEAxP5/QAD+cDgA0QAS/tFpAMYxPh3SKNYZ/ckT/ubm5ir+wsLC/mdnZxnECt0Zxwb+rq6u +; G5WIAxn9xijROv7AwMAbEf6FRgce5v52OwAdwgse8B3k/p5PAP7BYQDD/q1XAP5wOADR/ppNAP7RaQ +; DHBB3UBijVGf3HBv6vr68Ml4ghGcUK3hnGt4j+2NjYCP7Pz8/+cHBwGf3HKNJ//peXl/7t7e3+rH5P +; HuY+nVvCCx7wHeM7/sFhAMT+f0AA/nA4AM8+LzXGEv58PgAd1P6LXTD+xsbG/m1tbSjTGf3HEP7Y2N +; gq/tLS0v5xcXEZxgreGcX+mZmZ/vT09KKI/qampv5eXl4Z/cgo1LaI/snFwP66lG7+jlAQHuQ+nVvC +; Cx7wHeP+mEwA/sFhAMP+slkA/nA4ANAC/stmADXF/qdUAB3V/mhIKf63t7f+/Pz8jIj+jIyMKNIZ/c +; b+m5ubAaOI/qenpygZx1XdGcStiP7Hx8f+/f39gYj+fHx8Gf3JKNaliP6oqKj+1sW0/pZbIP6HRADj +; /nY7AB3CCx7wHeI7/sFhAMT+iUUA/nA4ANEAMTXCFP6IRAAd1KJc/mJURih//ouLi/7r6+s9/rW1tf +; 5mZmYo0Bn9xK6I/snJySqFiP59fX0ZyQrdGcP+h4eHOhv+uLi4/mNjYxn9yijY/oCAgP7X08/+upVu +; /odEAOI+HcILHvAd4v6YTAD+wWEAwyb+cDgA0z7+p1QAFKO1/q1XAB3V/mlGIyjDrog8/vz8/P7b29 +; sIKM8Z/cP+iYmJ/uvr6xv+urq6/mNjYxnJVd4ZwSQQKjr+jIyMGf3MKNmriP65ubn+28q5Hx7gPh3C +; Cx7wHeE7JqXTw/6ORwD+cDgA1SEjHdX+ZE86KMWjiP6cnJz+9fX1QP6kpKQkKMxV/cIz/re3t/76+v +; qSiP6NjY0ZywreGcC9iP7e3t45/snJyf5sbGwZ/c0o2/6QkJD+49/b/qx+Tx7fPh3CCx7wHeH+jkcA +; /sFhAMOda/5wOADt/mw/Ef5fWFEoyLqI/tnZ2Tn+zMzM/nFxcSjLGf3Bvog3Kv7Ly8v+bW1tGcwK3i +; j+oqKi/vf3952I/p2dnRn9z3/csog8/sirjR8e3T4dwgse8B3gOyal08MC/nA4AOz+ZkouKMskJyqR +; iP6Tk5Moyhn9KBEuEP6fn58oGc0K3Rj+zs7O/v39/Sz+d3d3Gf3PKN4V/qGhof7bz8L+nWcwHtw+Hc +; ILHvAd4AL+wWEAw51r/nA4AOsr/mFWTCjNf/6FhYX+5ubms4j+vLy8HCjHGf02FiqAiP54eHgZzlXd +; /o6Ojv7v7++piP6xsbH+YWFhGf3QKOC9iP7Szsr+upVuHts+HcILHvAd4Cal08MC/nA4AOo0KNEN/s +; LCwv78/PyFiP6Dg4Moxhn8/pGRkTYM/rOzsxUZzwrbqIj+vLy8/vz8/C/+hISEGf3SKOGoiP6ysrL+ +; 2ci3/pZbIP6HRADZPh3B/pRKAP6nVAD+jEYAHu8d3/6ORwD+wWEAwz/+cDgA6aJc/mNRQCjToogS/v +; Hx8RD+q6ur/mNjYyjEGfog/r+/v/76+vqNiAAZ0Qra/n5+fv7j4+Mq/sPDww0Z/dMo4/6JiYn+39vX +; /qx+Tx7Y/nY7AB3A/q1XADXA/r5gAP6QSQAe7h3f/rdcAKXTw/6TSgD+cDgA6P5rQRf+XltXKNa1iP +; 7U1NQI/tPT0/51dXUowhn6/oCAgP7k5OQq/sXFxf5paWkZ0QrZo4j+qqqqDJeIMBn91Sjkr4gi/seq +; jP6OUBAe1j6m4v7FYwCm4sKeWv6nVAAe7R3e/o5HAP7BYQDDP/5wOADo/mVNNCjZFf6mpqY9Nv6amp +; o3KMAZ+Df+rKysPZqI/peXlxnTCti2iP7V1dUI/tLS0v5ycnIKwBn91CjlNzv+2My//p1nLx7V/p1P +; ADXGD/6MRgAe6x3eJqXTw/6YTAD+cDgA5v5sPxEaKNt//n5+fiQbIv5sbGwZ+LeI/tbW1iohMhnUCt +; f+lpaW/vPz86SI/qmpqTcKwBn91SjnEP7MyMP+upVu/o5PEB7Torb+sVkA/sxnADXFHP6eUAAe6h3d +; Av7BYQDDnWv+ej0Amz3l/mhIKSjfqIj+u7u7/vz8/IqIPBn2/piYmDI9/qurq/5eXl4Z1ArWrIj+xc +; XFOSQXCsIZ/dUo6KaIOv7XxrX+llsg/odEANT+mU0A/shkADXGBf6MRgAe6P5wOADdJqXTw/6jUgD+ +; cDgA5KJc/mJURijhBv6NjY3+7OzsLv6ysrIgGfKtiP7Gxsb++/v7h4j+f39/GdYK1f6Dg4P+6enpG/ +; 67u7v+ZGRkCsMZ/dUo6iL+2tXR/rOJXx7UN/61WwA1xpkf/pVLAB7nHdz+iUUA/sFhAMT+ej0Amz3j +; /mlGI/5dXV3lsIj+zc3NOSz+eXl5GfD+hoaG/ujo6LKI/r29vSAZ1wrTBv6ysrIbk4gHGQrEGf3VKO +; usiP68vLz+zbSb/o5QEB7U/p5QAP7MZwA1xZkfHuf+cDgA3P63XACl08P+o1IA/nA4AOP+ZE86/l1d +; XeUZwAb+n5+f/vb29pyI/qGhof5fX18Z7KWI/rS0tP75+fmViCUZ2ArTu4j+29vb/vz8/P7Ly8v+bm +; 5ujYjGGf3VKO3+k5OT/tXJvP6sfk8e1KXD/rpdADXEmR8e5x3bKf7BYQDEFJs94f5sPxH+X1hRpR/m +; GcK9iP7b29s5/srKyv5vb28Z6ryI/tzc3L+I/s7OzieOiNlV0/729vZV/qCgoBkKxxn91SjutIj+xc +; G9/rqUbv6OUBAe0zf+rFYAHDXCmR8e5x3b/rJZAKjwwzj+cDgA4f5mSi7+XV1d5xnEJP6ysrIqj4j+ +; j4+PGej+oaGhEH/+oqKiKBnZCtT+29vbDIKIyRn91SjvpIj+pKSk/tXEs/6dZzAe1P6ZTQD+w2IANc +; EoHucd2v6EQgD+wWEAxP56PQCbPd/+bD8R/mFWTP5dXV3oGcUo/oeHhw2xiP65ubk+GeSxiP7MzMwq +; goj+enp6GdsK1CQKyhn91X/xv4j+1NDM/rqVbv6HRADUN/6xWQA1wJkfHucd2gio8MP+qFQA/nA4AN +; /+aEgp/l1dXekZyKyIAP78/PyDiP5/f38Z4v6Ojo7+7e3trIj+tra2JBncCuEZ/dUo8qmI/rW1tf7a +; ybj+llsg/odEANQB/shkAJ5aHucd2ajw/sFhAMQy/nA4AN2iXAL+XV1d6hnJo4j+l5eX/vPz83/+qK +; ioJBnep4j+u7u7/vr6+pCI/oiIiBndCuIZ/dUo9P6MjIz+4dzY/qx+Tx7UNyce5x3ZEf7BYQDDCP5w +; OADd/mw/Ef5fWFEo6hnMt4j+1tbWOf7Q0NAyGdz+fX19/uHh4bqI/sjIyP5qamoZ3griGf3VKPWwiP +; 7Gxsb+x6qM/o5QEB79HdgU/sFhAMT+iUUA/nA4ANz+ZU00KOwZzaSI/qmpqRuTiA4oGdiiiBwunIg7 +; KBneCuMZ/dYo9Qb+nZ2d/trNwP6dZy8e/B3Y/qNSAP7BYQDDmz3+cDgA2/5sPxEa/l1dXewZzyj+gI +; CA/uTk5Bv+wMDA/mpqahnWtYj+09PTKv7Y2Nj+dXV1GeAK4xn91ij3uoj+zsrG/rqVbv6OTxAe+v5w +; OADXpdP+wWEAxP6ORwD+cDgA2jQo7RnSIP6+vr45Ef6GhoYZ1P6VlZX+8fHxp4j+r6+vFRngCuQZ/d +; Yo+KeI/q6urv7Yx7b+llsg/odEAPkd1zj+wWEAw51r/nA4ANmiXP5jUUAo7hnTNxb+7u7uqIg2/mNj +; YxnQPv7ExMQqiYj+goKCGeIK5Bn91ij6/oWFhf7c2NT+s4lfHvgd1qbS/sJhAFrD/o5HAP5wOADY/m +; tBF/5eW1co7hnWs4glOf7X19cuGc4i/ufn5xv+wcHBLxniCuUZ/dYo+yv+v7+//s61m/6OUBAe9h3W +; /q1XAP7NZwCbPUbBP/5wOADY/mVNNP5dXV3vGdgG/qKiov739/eaiP6enp7+Xl5eGcoV/rGxsf75+f +; mXiP6Tk5MoGeMK5Rn91yj7N/6Wlpb+18q9/qx+Tx71HdWjtf7RaQDAVpxMnUvAAv5wOADW/mw/Ef5f +; WFEo7xnaKL6I/t7e3r2I/sfHx/5tbW0ZyLqI/tra2ir+0NDQ/nBwcBnkCuYZ/dco/baI/sjEvwn+jl +; AQlNTzHdUvNcKeav69XwAd1jQo8Rncpoj+tbW1Ko6I/o2NjRnG/p2dnf709PQu/qampv5dXV0Z5Qrm +; Gf3XKP3ApIj+p6en/tbFtP6WWyD+h0QA8h3Uo7U1w51b/oJBAB3Uolz+YlRGKPEZ3qKI/omJif7q6u +; o9/ra2tv5mZmYZwq+I/snJySqEiAgZ5grnGf3XKP3C/n9/f/7X0s7+upVuHvEd1P6tVwA1wi8d1QMo +; 8hnhroj+x8fHOYCICP5cXFzA/ouLi/7r6+sM/rm5uTMZ5wrnGf3YKP3Cqoj+zs7O/uHQv/6OUBCU1O +; 8d06O1NcL+lEoAHdX+ZE86KPIZ46OI/pqamgFV/qenpwwbOv6MjIwZ6AroGf3YKP3BBv60tLT+/v7+ +; f5TE/qx+UB7uHdMvNcD+v2AA/nw+AB3U/mw/Ef5fWFEo8xnluoj+39/fF8D+0dHRGBnpCugZ/dgo/c +; AfBibD/sqtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o9BnlKP6lpaUuPTmRiP6UlJQoGecK6Rn9 +; 2Sj8/p6env76+vomxf7o287+nWcwHusd0v6nVAASAB3UK/5hVkwo9Bnls4j+0NDQKv7a2tr+eXl5qo +; j+5ubms4j+vb29/mlpaRnlCuoZ/dko+q6INCbG/sjIyBP+0c3J/rqVbh7qHdE+/qdUAB3V/mhIKf5d +; XV31GeX+kpKS/vDw8Az+sbGxFRnAqogEOYaIIhnkCusZ/dko+P6Li4v+8fHxJsWXiP6ZmZn+X19fKK +; iI/rGxsf7ZyLf+llsg/odEAOgd0T4d1KJc/mNRQCj1GeQv/sDAwBuMiP6EhIQZw6KI/pGRkQWliP6s +; rKz+YmJiGeEK7Bn92Sj2M/68vLwmxjv+dHR0KMP+iIiI/t7a1v6ziV8e5x3n/mtBF/5eW1co9Rnk/o +; GBgf7l5eUq/sTExP5oaGgZxrSI/tPT0/79/f3+1NTU/nV1dRngCuwZ/dko9b+IICbFnYgJJCjFOv7C +; wsL+zrWc/o5QEB7lHeYRKPYZ4wb+rq6uPZmIPygZyBX+pKSkPZeI/pubm/5eXl4Z3VXtGf3aKPP+qK +; ioOSbFiIj+goKCKMg3/pmZmf7YzL/+pHI/HuQd5P5sPxH+X1hRKPYZ47iI/tfX1yoS/nJychnLKP59 +; fX3+4ODgKjH+bGxsGdsK7hn92ijxsoj+1tbWJsUX/sHBwf5mZmYoywH+y8fC/rqVbh8e4h3j/mhIKS +; j3GeMs/vPz8y7+qqqqNxnOp4j+ubm5OT7+iYmJGdoK7hn92yjv/pSUlP729vYmxZOIAzcozTMr/tfG +; tf6WWyD+h0QA4R3holz+YlRGKPcZ4q6I/sfHxyqHiP5/f38Z0Tf+jIyM/uzs7C7+s7Oz/mVlZRnXCu +; 8Z/dwo7CAx/v///8b+1NTUNijRE/7Z1dH+upVu/odEAOAd4P5pRiMo+Bni/oeHh/7q6uob/ry8vP5k +; ZGQZ1LCI/svLy/78/Pw7/np6ehnVCvEZ/dso6yL+6+vrJsWbiP6kpKT+YGBgKNOsiP67u7v+3Mu6/o +; 5QEJTU3h3f/mRPOij4GeEk/rW1tf75+fmViP6Pj48Z1wb+nZ2d/vb29iP+oqKiBhnTCvEZ/dx/6Df+ +; sbGxFybFgoj+fHx8KNf+kpKS/tzUy/6sfk8e3R3d/mw/Ef5fWFEo+BnhvYj+3d3dvoj+zMzM/m5ubh +; navIg7Kho2GdEK8hn93H/nt4j+3d3dJsUX/ri4uP5jY2OaiNkF/sTAvP7Jq43+jlAQHtsd3P5mSi4o +; +RnhM/729vZV/qGhoSgZ3KWI/rCwsCqQiP6QkJAZzwrzGf3dKOX+m5ub/vn5+SbFjoj+jIyMNyjbFf +; 6jo6P+3M/D/p1nMB7aHdor/mFWTCj5GeAn/s7Ozv78/PyAiP56enqCiN8oDz4M/rq6uv5nZ2cZzFXl +; tYgKzRn93SjiHP7Nzc0mxv7Ly8v+a2trKN++iP7Tz8sOHtkd2TQo+RnhFv7u7u4M/rW1tSQZ4j7+w8 +; PDOYSI/oGBgRnLCuX+7Ozs/pOTkygKyxn93SjhLf7v7+8mxS7+nZ2d/l9fX0DhLzL+2sm4/pZbIP6H +; RADXHdeiXP5kTzoo+RngqIj+vb29/vr6+o+I/oeHhxnlNzAjooj+qampJBnICub+4uLiG/68vLz+aG +; hoCsoZ/d4o3iT+urq6FybF/tvb2/53d3co5f6Kior+4NzX/qx+T/6HRADWHdYr/l9YUSj5GeD+fn5+ +; M7mI/sfHx/5qamoZ6LaI/tXV1f78/Pz+0tLS/nNzcxnGVeeqiAg5M/6Dg4MKyRn93yjcvIj+5OTkJs +; VA/rCwsCQo57CI/sXFxf7Iqoz+jlAQlNTUHdX+ZU00KPoZ3zf+qqqq/vf395yILCgZ6qSI/qenp/75 +; +fkn/piYmCgZxAroKCX+7e3tqYj+q6ur/mJiYgrIGf3fKNr+pKSkKibFioj+hYWFKOoG/pycnP7Zzc +; D+nWcvHtP+cDgA0/5sPxH+YVZMKPlV4CMhKj/+dHR0Ge0oF/7j4+O3iP7BwcH+ampqGcIK67OIBzkh +; /nV1dQrHGf3gKNewiCEmxjH+Z2dnKO25iP7NycX+upVu/o5PEB7RHdI0KPoZ4P6Wlpb+8vLypoj+ra +; 2tBhnwqIj+vLy8/vz8/ImI/oeHhxnACu0V/qGhof729vY2/pqamjcKxRn94CjWJf709PQmxTL+lJSU +; NyjvpogY/tfHtv6WWyD+h0QA0B3Qolz+Y1FAKPoZ3w3+xcXFKoiI/oGBgRnzN/6NjY3+7e3tHwX+Y2 +; NjCu/+e3t7/tzc3L+I/sPDw/5ra2sKxX/94CjTAv7BwcEmxv7X19f+cXFxKPP+hISE/tvX0/6ziV8e +; zx3P/mpEHf5eW1co+RngMf7o6Ogb/r+/vy8Z9rGIBzn+2NjY/nd3dwrvAv60tLQqjIgLCsQZ/eEo0f +; 6AgIANt4jFG/6oqKgVKPUr/r6+vv7OtJv+jlAQHs3+cDgAzv5lTTQo+hnfFf6ysrIMNv6SkpIoGfgG +; /qCgoP739/eaiP6fn5/+Xl5eCu4oCxw9I/5lZWUKwhn94ijON/6tra3+/v7+JsWFiP5/f38o+f6VlZ +; X+1sq9/qx+Tx7MHcz+bD8R/l9YUSj5GeC7iP7b29sq/s/Pz/5wcHAZ+lUZvYj+3NzcKv7IyMj+bW1t +; Cu+viP7Hx8c5/tra2v57e3sKwn/94ijMtYg7/v///8UX/ry8vCAo+yP+x8O//rqUbh+U1Mody/5mSi +; 4o+hng/p6env719fWiiP6kpKQoGfoKwqaI/rS0tP77+/scOArvpIj+mpqa/vLy8sD+oaGh/mBgYArA +; Gf3jKMr+mJiYPSbFNv6Ojo7+Xl5eKP2kiP6mpqb+1sW0/pZbIP6HRADJ/nA4AMmlH/5hVkwo+RngsI +; j+y8vLKoOI/nx8fBn7CsQZ/oiIiBw9/re3t/5mZmYK77uI/tjY2P78/Pwa/m9vbwoZ/eUoxqqI/snJ +; ySbGFhgo/cP+fn5+/tXRzf66lW4eyP5wOADI/mlGIyj6GeD+jIyM/uzs7K2I/ri4uP5iYmIZ+wrHrY +; j+xsbGOYGI/n19fQrvpoj+rKysDJGIJf5dXV0Z/eUoxP6FhYX+7e3tJsWZiP6goKAGKP3FPi7+2sq5 +; /o5QEB7GHcf+ZE86KPkZ4KeI/rm5uf76+vqRiAsZ/ArJo4j+mJiYMsD+pqamBgruGf6Dg4P+5OTkGw +; wNGf3lKMGjiP62trb+/v7+JsX+3t7e/nl5eSj9yTj+4t3Z/qx+Tx7F/nA4AMX+bD8R/l9YUSj4GeH+ +; fHx8/uDg4Co8/mxsbBn8Csy5iP7X19f+/Pz8Fv5xcXEK7z7+wMDAORX+gICAGf3lKLqI/uHh4SbFQP +; 6zs7MzKP3LJ/7Hx8f+yKqN/o5QEJTUwx3E/mVNNCj5GeAo/qenp/739/ediP6cnJwoVfwKzgb+q6ur +; /vr6+pOIMBkK7qOI/pKSkv7u7u4BPjMZ/eP+oKCgGybFjIgtKP3OBv6enp7+2s7B/p1nLx7CHcIr/m +; FWTCj4GeEF/tLS0ir+2dnZ/nZ2dhn9wArPGf6BgYH+5OTkG/69vb0NCu8U/tHR0TkD/nNzcxn94K+I +; AybG/sfHxw0o/dG7iP7QzMf+upVu/o5PEJXDwB3B/mhIKSj4GeL+k5OT/vDw8KiI/rCwsP5gYGAZ/c +; AK0iD+v7+/OYeIIgrvFf6kpKQuJ/6YmJj+Xl5eGf3dKSOtiMWWiB0GKP3Tp4gF/tjIt/6WWyD+h0QA +; /nA4AKJc/mNRQCj3GeKqiP7CwsIbjIgiGf3BCtQo/o+Pj/7v7+8Q/q2trf5iYmIK7hn+fn5+/t/f3y +; r+wMDA/mpqahn92jP+vr6+/v///8b+2NjYMij91/6Hh4f+3dnV/rOJX/6MYjj+Z2NgKPYZ4wQvKv7D +; w8MNGf3BCte0iAP+/Pz8/tXV1f51dXUK7hkR/ri4uCqJiB4Z/dj+fX19LybFKv6rq6sVGSj92K6INR +; sR/oCAgDco8hnjo4j+r6+v/vj4+JmI/pWVlSgZ/cEK2Qb+oqKiLpmI/pycnCgK7H/ANzj+6+vrLjb+ +; ZGRkGf3UKP6pqan+/f39JsUvNRnDKP3XN/6Xl5cnq4gc/mFhYSjvGeQQHSoD/nJychn9wgrbGb+I/t +; /f37yI/sXFxf5ra2sK7BnBCf7Kyso5Hf55eXkZ/dKziP7X19cmxVX+wMDAIBnGKP3Xt4j+0dHROf7W +; 1tYBKOwZ5f6bm5v+9PT0Lv6oqKg3Gf3CVd6niP63t7c5i4j+ioqKCtMAAAAAAAAAAQ== +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 480x240 31136 +; cW9pZgAAAeAAAADwBAD+WFhYwn/9wH/S/oyMjP7p6emxiP6vr6/+YWFhO8Ek/ra2tv78/PyLiP6JiY +; k79n/9/f6ZmZn+9PT0ooj+paWl/l1dXQrBrYj+u7u7G4mI/oGBgf5cXFwK/e47/cK0iP7X19f+//// +; xB3Bf/070qqI/rW1tRs+/oaGhgo7wxk8/uvr662I/rOzsxE78Qr9/cAN/sfHx/79/f2BiP58fHwKxB +; n+kpKSOj3+qqqqJAr97zv9/pSUlP739/cmxR0s/TvTOTe+iP7Dw8P+Z2dnO8eviP7IyMg5/tvb2/56 +; eno77X/92aHhCuT+h4eH/urq6rCI/ri4uP5jY2MKx7WI/szMzDn+1tbW/nR0dAr98Dv5Av7FxcUmxv +; 7R0dEs/TvSpYj+o6Oj/vT09JyI/pWVlSg7ySj+m5ub/vX19UAC/l5eXjvof/3aOv6sZRyg8/5qXVAK +; 4aWI/rS0tP77+/uQiP6MjIwKyhX+oqKiMkD+l5eXNwr98Dv2BDomxZqI/qGhof5dXV0s+zvTtoj+zs +; 7OCP7V1dX+cnJyO827iP7Y2Ng5/svLy/5vb2875Qr92/6AXz/+ymgGoeHABf6HYDkK372I/t3d3b+I +; /snJyf5sbGwKzf58fHz+29vb/vz8/AAckoj98jvxGf6xsbH+/v7+JsWAiP55eXk7wCz5O9MZ/pOTk/ +; 7t7e2riP6oqKj+X19fO88G/q2trSo6/pGRkTvhCv3d/qVkIjXEBv5iXFUK2xkkLp2I/p2dnRkKz6qI +; /rS0tP75+fmPiP6IiIgZCv3yO+65iP7e3t4mxRf+tbW1FTvBLPc71D7+vb29KoaI/n9/fwo70Qr+g4 +; OD/ubm5gz+u7u7/mZmZjvdCv3d/nFeSv7CZws1xv67ZhH+gF8/CtmxiAf+/f39/tnZ2f53d3cK0/6L +; i4v+5+fntIj+srKy/mJiYgr98zvr/pycnAymiMWNiP6JiYkKO8JV9jvU/oKCgv7j4+M5/ry8vBE71S +; D+wcHB/vz8/IWI/oCAgDvZCv3f/pZiLf7RaQDJBf6PYTM6Ctb+jo6O/u/v7z3+sLCwFQrVsYgAOf7b +; 29v+enp6gYj99DvnrYj+zs7OJsb+yMjIPjvEVfQ71KeI/qurq/739/eWiP6Pj4/+XFxcO9cZA/7x8f +; GjiP6pqakVO9UK/d/+al1Q/rtmETXM/rNlFzQK06iI/ry8vP78/PyJiP6EhIQK2KOI/pycnP7w8PCl +; iP6goKD+X19fCv30O+QeBSbFH/6ZmZn+XFxcO8VV8zvUuogwOf7Nzc3+bW1tO9u2iP7T09M5/tLS0j +; I70Qr94f6HYDk1z58v/o9hMwrR/n19ff7j4+Mq/sPDw/5oaGgK27uIMP78/Pz+zMzMGAr99TvgN/66 +; uroXJsX+2traMoeIx1XxO9SjiP6ampoUpYgVBpuI3Tf+paWlPZaI/piYmBk7zQr94aHh/qVkIjXS/r +; NlFzoKzaKI/qqqqgwU/pWVlRkK3aiI/q2trR+WiP6Ojo4oCv30O96+iP7k5OQmxTkYNzvILO871bCI +; /sTExDn+29vb/np6ejvgf/58fHz+4eHhuYj+wsLC/mlpaTvKCv3i/nFeSgs11AX+gF8/Csu2iP7V1d +; X+/Pz8A/5ycnIK4f6FhYX+4+PjKv66urr+ZGRkCv31O9v+pKSkKibFiYj+gYGBO8os7jvVPP7n5+cq +; /rOzs/5hYWE746mIGzmKiB47xgr95P6dYyg12BM6Csj+lpaW/vPz8y7+qampNwrjroj+vr6+KoaI/n +; 9/fxkK/fU717KIMP7////FVf7CwsL+ZGRkO8ss7DvVAv6zs7MMDTwZO+UZGv7s7Owu/rGxsQI7wgr9 +; 5P5xXkr+u2YRNdobJArFrIj+xcXFOYWI/n5+fgrmKP6VlZX+7e3tq4j+p6en/mFhYQr99jvU/pCQkB +; AmxZOI/pGRkQo7zCzrO9W/iP7b29v+/f39/sbGxhw76bGIKTn+2dnZ/nh4eDt//eX+j2EzNd0FLP5i +; XFUKwv6Dg4P+6enpsYj+u7u7EQrptoj+z8/POf7U1NT+cnJyCv33O9AkEybGIf5ubm47zlXpO9U3Ff +; 7z8/PAHTc76yj+np6eH5uIFf5eXl4K/eI6/qxlHDXg/rNlF/5qXVAKpYj+srKyGwkHGQrrJCAynIj+ +; lJSUNwr99jvO/oCAgBwmxRv+pKSk/l1dXTvPLOg71bOI/svLywj+19fX/nV1dTvuCj3+29vbOf7Jyc +; knjYj94P54XkQFoeHiBf6ac0z+29vbORoYjojv/n9/f/7e3t45E/5nZ2cK/fc7ygr+rq6u/v7+/n/F +; g4j+e3t7O9Es5jvVCiX+6+vrroj+q6urFTvtCsIV/rGxsf77+/uPiAcK/d7+nWMoNeb+4J9d/qWfmB +; kK8auI/re3twyNiP6FhYUZCv32Vci3iP7b29smxRf+ubm5JDvSLOU71ayI/rq6uiqJiP6CgoL+W1tb +; O+sKxhn+hoaGDbCI/ri4uC8K/dr+cV5KCzXo/rtmEf6AXz8K8hkHHLGI/q6urv5iYmIK/fc7xf6ZmZ +; n++Pj4JsWPiP6Li4sKO9NV4zvW/oCAgP7h4eE5/r+/v/5lZWU76wrKPjE5g4j+fn5+Cv3Y/o9hM/7R +; aQDrBSz+YlxVCvIYLTn+2dnZ/nh4eAr9+DvBIP7Kysr+////xv7Ly8v+aWlpO9VV4jvVFf6oqKgfJ/ +; 6RkZEoO+oKzSg/Mn/+p6enFQr91Dr+s2UXNe4o/mpdUArypIj+n5+f/vHx8QH+nZ2d/l9fXwr99zsA +; JybFmYgoGTvWLOA71riIEgj+0dHR/m9vbzvrCtC4iP7V1dU5/tDQ0CMK/dL+h2A5NfEF/o9hMwrzvo +; j+2NjY/vz8/Dz+a2trCv31o4gu/v7+/ibF/tzc3P52dnY72FXfO9WiiP6Xl5f+7+/vqIj+o6OjBjvq +; CtMG/qmpqRuTiD8ZCv3P/qVkIjX0/rRmF/5jXVYZ0greqYj+sLCwLiv+jIyM/l1dXQr98h/+4uLiJs +; UI/rGxsf5fX1872SzeO9WuiP7BwcE5goj+fHx8CjvpCtYZ/n9/f/7k5OQb/r+/v/5paWkK/cz+cV5K +; /sJnCzX2ny/+gWA/Gd8K0h7+5eXlKv63t7f+Y2NjCv3w/qGhoSqkiMUrAAo72izcO9YP/ubm5raILv +; 5iYmI76QraqYj+vr6+OYiIAAr9yv6WYi01+v6eYygJGeUKy6+IBCo3/n19fRkK/ewrAybGMf5mZmY7 +; 3CzbO9Uz/q+vrz2SiP6Li4sZO+gK3SgHJ6eI/q6urjMK/cb+cV5K/rtmETX8/rtnEf5yXksZ6wrFKP +; 6YmJj+7u7uqYj+pKSkFQr96v6NjY3+8/PzJsUB/pWVlSgKO9ws2TvWvoj+2dnZ/v39/S3+ampqO+gK +; 4bOIJTn+1tbWHwr9xP6PYTM1/cEF/o9iNBnwCsG4iAM5NP5wcHAK/eemiP6/v7/+////xv7X19f+cX +; FxCsI721XYO9WkiP6enp7+8vLyAf6cnJz+Xl5eO+cK5Df+oaGhLpmI/p2dnf5dXV0K/cCh4f6sZRw1 +; /cT+tGYX/mteUP5cXFzyM/6oqKgQmYj+kpKSNwr95P59fX3+5+fnJsUqDQYKxTvZLNc71bKI/snJyf +; 79/f3+2dnZ/nd3dzvoCuYZvoj+3d3dKv7GxsYJCvz+eF5E/spoBjX9xgX+gWA/GfP+gYGB/uHh4Tn+ +; v7+//mdnZwr94Rn+qqqqCKKIxYaI/n5+fgrIO9hV1TvVCv6NjY3+6urqsIj+rq6u/mFhYTvnCuok/r +; W1tSqNiBoK+v6dYyg1/coW/mNdVhnyrIj+urq6/vn5+YuIIv5dXV0ZCv3dtYj+2NjYJsVV/r29vQIK +; yjvXLNQ71auI/re3t/76+vqMiAAKO+YK7Sj+iYmJK62IECAK9v5xXkr+u2YRNf3M/rtnEf5yXksZ8i +; j+kZGR/uvr6wz+q6urMxnCCv3Y/paWlv739/cmxZGIBxkKzTvVLNM71f59fX3+39/fOf7CwsI+O+YK +; 8a6I/sfHxzmAiP58fHwK9P6PYTM1/c+fL/6PYjQJny/ytIj+y8vL/vz8/A4fGcQK/dOpiB4mxv7Pz8 +; /+a2trCtA71FXRO9UV/qWlpf719fWbiP6Tk5MoO+YK8zf+mpqaAVX+pKSk/l9fXwrwofIwNd6aLv6m +; UwD+zGcANe0w/mteUBnyFf6ioqIjwP6ZmZn+X19fGcYK/c4i/uzs7CbFDP6hoaE3CtJV01XQO9W2iC +; X+/f39/tTU1BQ75gr3uogdOf7Nzc0FCuwZwP6IYTk13/6tVwD+cDgAp/H+lUsA/rpdADXtny8BGfO/ +; iP7b29s5/sbGxv5qamoZyFX9ySj+s7OzFybF/t7e3j0K1TvSLM871Bn+lJSU/u7u7qqI/qampgY75Q +; r6Ff6srKz++vr6kogSGQrnGcL+pWQiNd/+lEoAHcCn8aXDorb+sVkAHDXt/qxlHf5jXVYZ8qmIMv74 +; +PiQiP6KioooGckK/cW5iP7f398mxRf+tLS0FQrXO9EszjvUPv6+vr45hYj+fn5+CjvlCvwZIv7m5u +; YM/ry8vP5oaGgK4hnE/nJeS/7CZws13v6/YAD+fD4AHcEqHsH+mU0A/shkADXt/rtnEf6BYD8Z8/6L +; i4v+5+fntIj+tLS0AhnLCv3B/p2dnRsmxY2I/oiIiBkK2TvQLMw71SL+5OTkuIj+urq6/mRkZDvlCv +; 3Cqoj+wcHBOYaI/oKCggreGcb+lmIuNd/+mk0AHcOn8R7CNwU17Z8vI/5jXVYZ8rCIMf77+/uBiP57 +; e3uBiM0K+q6IJSbG/sfHxw0K3DvPLMs71KiI/qysrP739/eViP6NjY0ZO+UK/cSiiP6RkZH+8PDwpY +; j+q6ur/mFhYQrZGcf+cl5L/rtnETXeEv6CQQAdwwD+uF0A/pBJAJtNw/6eUAA17jD+cl5LGfKjiP6b +; m5sFpoj+oaGhFRnNVff+iYmJ/vHx8SbFEP6ZmZkoCt87zVXKO9S7iP7W1tYI/szMzP5sbGw75Qr9yL +; WI/tPT0zkSEArVGcn+j2I0Nd/+p1QAHcT+oFAANcCeWv6jUgAewzXvny/+j2I0GfO6iP7U1NQ5/s3N +; zf5ubm4ZzwryFf68vLz+/v7+JsX+2tra/nNzcwriVcwsyTvTKP6bm5sUpIj+n5+f/l5eXjvkf/3LpI +; j+pKSkPZeI/pqamigK0BnKoeH+pWQiNd6dW/6ORwAdwwD+v2AANcP+ul0A/oxGAB7B/shkADXwMP5r +; XlAZ8qeIOv729vYY/o+Pj/5eXl4Zzwrvv4j+5eXlJsWdiP6srKz+X19fCuRVyyzHO9Qr/sXFxQj+29 +; vb/nl5eTvlf/3NGf58fHz+4ODguogx/mtrawrMGcwuCzXe/rldAP52OwAdwwQ1xgH+nlAAHsA0NfEF +; /oFgPxnzIv7j4+O5iP68vLz+ZmZmGdAK7P6mpqb+/Pz8JsWIiP6BgYEK5zvKLMY71Br+6Ojos4j+sr +; Ky/mFhYTvkCv3RqIj+ubm5OT7+iYmJCskZzf6eYyg13/6aTQAdw6O1/rNaADXIHP6sVgD+jEYA/rVb +; ADXzFv5jXVYZ8q2I/r29vf76+vqIiP6AgIAoGdEK57OIPybFF/7BwcH+ZGRkCuk7ySzFO9MR/rS0tB +; sNLQo75Ar90yga/uzs7KuI/rOzsxEKxBnO/nJeS/67ZxH+0WkA3hL+fD4AHcP+iEQAFDXLNA419CAu +; GfKiiP6UlJQJPf6oqKgkGdIK5P6Tk5P+9vb2JsWTiP6QkJAZCus7yCzEO9P+e3t7/tzc3P79/f0A/m +; hoaDvkCv3XOv7Ly8s5/tra2v56enoKwH/Q/o9iNDXfDh3EDjXO/plNADX1BTj+Y11WGfK1iP7Pz885 +; /tXV1f50dHQZ0wrgAv7Dw8Mmxv7T09MnCu5VxyzDO9IG/qKiov709PQj/paWlig75Ar92Tf+nZ2d/v +; X19SMzBhnOCf6sZR013p1b/ohEAB3D/oJBAP6/YAA1z/6eUAA19/60Zhf+a15QGfIk/qWlpf7z8/Od +; iD8GGdMK3f6BgYH+6urqJsWbiP6kpKQ3CvA7xizCO9In/s3NzQj+1tbW/nR0dDvlCv3bGbuI/tra2v +; 77+/v+ysrK/m9vbxnM/nlfRf7KaAY13v6tVwAdxP6aTQA10R81+AX+iGE5GfP+fn5+/t3d3Tn+xMTE +; HBnUCtkZ/rCwsP7+/v4mxYKI/nt7ewrzVcUswDvSCv6RkZH+7OzsrYj+qamp/mBgYDvkCv3bGcKliA +; X++/v7kIglGcr+nmMoNd/+lEoAHcM+/rldADXSDv7IZAA1+f6lZCL+Y11WGfKriP62trYMjoj+h4eH +; /l1dXRnUCta3iP7c3NwmxRf+uLi4JAr1O8QsO9KsiP67u7v++/v7iIj+gYGBCjvkCv3af8Uo/oWFhT +; 4M/rm5uf5nZ2cZxv5yXkv+wmcLNd4S/nw+AB3D/pRKADXU/rFZAKTUNfr+u2cR/oFgPxnz/o2Njf7o +; 6Ogb/rCwsP5jY2MZ1QrT/pubm/75+fkmxRj+i4uLGVX2O9gE/uPj4zn+vr6+/mVlZTvkCv3ZGco+/s +; TExDmEiP6AgIAZxP6PYjT+0WkA3/6gUAAdxP6tVwA11f6+YAD+rFYANfufLzgJGfKxiP7Hx8c5/tra +; 2v55eXkZ1QrQPikmxv7Ly8v+aWlpCvlV1ST+qamp/vb29peI/pCQkCg75Ar92BnNN/6VlZX+8vLyoo +; j+qKioJBnACf67ZxE13hL+gkEAHcMA/sVjADXWm03+o1IANf3+tGYXPBnyo4j+np6e/vHx8aOIN/5g +; YGAZ1QrN/oeHh/7v7+8mxZiI/p2dnf5dXV0K+zvTuYj+1NTUCP7Pz8/+bm5uO+UK/dcZ0TL+1dXVOf +; 7R0dEy/ohhOTXfDh3E/qBQADXZODX9wAX+j2I0GfO8iP7X19c5/srKyv5sbGwZ1grJBv65ubn+/v7+ +; JsX+29vb/nV1dYaI/cA70Bn+mJiY/vDw8B/+oqKiBjvkCv3XGdQVDf75+fn+2ZhXNd6dW/6ORwD+cD +; gAwwASNdr+mU0ANf3C/rRmF/5jXVafL/KoiP6vr68uOgf+Xl5eGdUKx7yI/uPj4ybFCDYVCv3CO86v +; iP7CwsI5gIj+e3t7O+UK/dZ/1/5zX0z+xGkONd7+uV0A/nY7AB3D/ppNADXc/p5QAP7IZAA1/cKfL/ +; 6BYD8Z8x7+5OTkt4j+ubm5/mVlZRnWCsT+o6OjKibFioj+hISECv3FO8we/ubm5jn+tbW1/mJiYjvl +; Cv3VGdj+lmIuNd8EHcM+/q1XADXd/rFZAA81/cT+nmMoCRnyroj+wMDAKoSI/n5+fhnXCsCxiCEmxv +; 7ExMT+ZmZmCv3HO8mpiP6wsLD++fn5HP6KiooZO+QK/dUZ2P5yXkv+u2cR/tFpAN4SAB3DAP7FYwA1 +; 3v61WwCitjX9xSAuGfI3/peXl/7u7u6piP6lpaUkGdb+j4+P/vT09CbFlIj+lJSUKAr9yDvIvoj+2d +; nZCP7IyMgrO+UK/dQZ2Tg13/6nVAAdxA414JkfDjX9xp8vOBnzt4j+0dHROf7S0tL+cXFxGdOmiP7B +; wcEmxv7X19f+cHBwCv3KO8Y3/p+fn/7z8/PA/pqamjeciOUK/dMZ2aHh/qxlHTXenVv+iEQAHcMAEj +; Xhnlr+o1IANf3I/rRmFzwZ8jP+p6enAZuI/pOTk/5fX18Z0P5/f3/+6Ojot4jFm4g+Bgr9yzvFsoj+ +; ysrKCP7Y2Nj+dnZ2O+YK/dIZ2v55X0X+ymgGNd7+rVcA/nA4AMT+mk0ANeT+nlAANf3JBf6IYTkZ8/ +; 6AgID+4ODgOf7BwcH+Z2dnGc7+rKysCCbFEf59fX0K/c07wwoH/urq6gwJ/mBgYDvlCv3SGdv+nmMo +; /tFpAN/+lEoAHcOjtf6zWgA15Tg1/cv+pWQi/mNdVhnyrIj+ubm5/vn5+YyI/oSEhP5dXV0ZyrWILC +; bFVf68vLwCCv3OO8KriP64uLgbi4j+g4ODCjvlCv3RGdv+cl5L/sJnCzXe/r9gAP58PgAdwyH+y2YA +; NeY4/shkADX9y/67ZxEeGfIo/pCQkCsb/q2trTMZyP6Xl5f+9/f3JsU2BygZwAr9zTvB/n5+fv7g4O +; C8iAT+ZmZmO+YK/dEZ2/6PYjQ13/6gUAAdxP6nVAA16P6xWQCk1DX9zJ8vOAkZ8rOI/srKyjn+2NjY +; /nd3dxnFLy0mxv7Ozs7+bGxsGcMK/cw7poj+pqam/vX19ZqI/pKSkig75gr90BnboeEwNd4SAP5wOA +; DDAP7FYwA16TSeWjX9zjA8GfIV/qGhof7y8vIy/pqamv5fX18Zwv6FhYX+7e3tJsWZiP6fn583GcYK +; /cq1iP7R0dH+/f39/tPT0/5xcXGJiOcK/c8Z3P6IYTk13w4dxP6aTQA165kfDjX9z58vOBnzvoj+2t +; raOS3+a2trGaOI/rW1tf7+/v4mxf7d3d3+eHh4GckK/ck2qIj+paWlBpuI5gr9zxnd/qVkIjXenVv+ +; lEoAHcM+/rldADXt/p5QADX90TAJGfKpiP6ysrL++Pj4kYj+n5+f/uDg4CbFCP6zs7P+YWFhGcsK/c +; gV/n19fQo75gr9zxnc/nJeS/7CZws13/6tVwA+HcL+lEoANe8fNf3SBf6BYD8Z8/6Kior+9PT0JsaM +; iP6Hh4cZzgr9xwI75wr9zhnd/pZiLjXi/r9gAP6CQQAdwC818B81/dMFFgkZ8K+INCbG/sfHxw0Z0A +; r9xv6NjY0ZO+UK/c0Z3S7+u2cRNeQUJTM18f6jUgD+yGQANf3UIC4Z7v6MjIz+8vLyJsWWiP6YmJg3 +; GdMK/cQ5/r29vQI74wr9zRnd/o9iNDX93v6xWQCk1DX91Z8vOBnrJAgmxhf+19fX/nBwcBnVCv3D/s +; DAwP76+vqJiP5+fn4KO+AK/cwZ3Qn+pWQi/tFpAP3fD5kvNf3XMDwZ6P58fHz+5ubmJsUq/q2trVX+ +; 9vb2l4j+kZGRNxnUCv3D/paWlicM/qenpzc73gr9zBndLv7CZws1/eAB/qdUADX92J8vERnlf/6oqK +; j+/f39JsUv/oCAgBnAIv7i4uK6iP6+vr7+ZmZmGdQK/cE7wLeI/tDQ0Dn+1dXV/nJycjvbCv3MGd4W +; /tFpAP3j/p5QADXqnlo16xb+Y11WGeKziP7X19cmxVX+v7+//mVlZRnCrIj+vLy8GwL+gYGBKBnTCv +; 3AO8GliD7+9PT0QP6VlZUoO9gK/cwZ3v6tYBE1/eT+mU0ANeidWyH+mEwA/shkADXr/qldERnhMP72 +; 9vapiMWSiP6QkJAoGcQo/pOTk/7s7Ow9/qmpqSQZ0wr9O8P+fn5+/t/f372I/sPDwy871gr9yxng/o +; lIBv6xWQD+zGcANf3i/p5QAAE15v6tVwD+cDgAwP6GQwCjtf61WwA16f6nVAAdGd+oiP7FxcUmxv7S +; 0tL+bm5uGci0iP7Nzc05/tbW1v50dHQZ0wr8O8SsiP65ubn++fn5joj+hYWFCjvTCv3LGeH+hEUGn8 +; P+mU0A/shkADX94f6xWQABNeX+lEoAHcEWHsAfHDXlnVshHcAZ3v6Dg4P+6+vrJsUM/qOjo/5fX18Z +; yiQC/vPz80D+l5eXBhnSCvs7xv6Ojo4rKv6wsLAGO9EK/coZ4wQewDf+sVkANf3gG6K2NeP+v2AA/n +; w+AB3CFh7BpcM0NeP+rVcAHcIZ3KKI/rKysv7+/v4mxRX+e3t7Gc7+fX19/tzc3Dn+xcXF/mlpaZOI +; 0gr6O8eziP7Jycn++/v7/tra2v52dnaEiM8K/coZ5AQewv6ZTQD+zGcANf3eD/6nVAA14v6gUAAdxP +; 6KRgAewqK2/qxWABw14P6USgAdwxnbuIj+3t7eJsUX/re3t/5iYmIZ0KqI/rW1tQyPiP6IiIgoGdEK +; +TvIKP6goKD+8vLypIj+nZ2d/l1dXTvMCv3KGeUEHsMSNDX93ZtN/qNSADXg/r9gAP6CQQAdwwD+xW +; MARv6eUAAewqfxHDXeEv58PgAdxBna/pycnAwmxQn+ioqK/l1dXVXTKQ0q/rGxsf5jY2MZ0Ar5O8q+ +; iCw5Gv5ra2s7ygr9yRnnBB7EorYOHDX93P6eUAA13w4dxCU1wRwONx4ONd8lHcYZ2K2I/s7OzibG/s +; rKyhwZ1hj+xsbGOf7b29v+enp6GdBV+DvLAv6ysrL+9/f3Cf6Li4sZO8cK/ckZ6AQexv6ZTQD+w2IA +; Nf3bHzXdnVv+lEoAHcOjtRI1xP6+YADANd4SAB3HGdct/vDw8CbFl4j+m5ub/l5eXhnYBv6dnZ0FpY +; j+n5+f/mBgYBnPVfc7zS3+5ubmOf65ubn+YmJiO8UK/ckZ6QQex6K2/rFZADX92jj+yGQANdsS/nw+ +; AB3DIzXnDh3JGdUV/rq6uhcmxf7b29v+dXV1Gdy7iD85/svLyxgZz1X2O86wiP7Dw8MqhYj+fHx8Cj +; vCCv3IGesEHsn+mU0A/shkADX92AU0Ndr+mk0AHcT+rVcANeYUIx3KGdS9iP7k5OQmxZ2I/q6uriQZ +; 3qiI/q2trf729vYJ/o6Ojv5eXl4Zzgr1O88Z/pmZmf7v7++piP6kpKQ3O8AK/cgZ7P6ERQYeyqK2ND +; X91/61WwDANdgSAB3DAP7FYwA15hL+fD4AHcsZ0/6lpaUqJsUN/oODgxni/oaGhv7k5OQ5/rq6uv5l +; ZWUZzgr0O9G6iP7T09M5Ev5wcHAK/cgZ7QQezA7+zGcANf3VmR8ONdcOHcQlNef+mk0AHc0Z0bKI/t +; XV1SbFF/7Dw8P+ZmZmGeStiP6/v78qFf5/f3/+XV1dGcwK9DvSpoj+q6ur/vX19ZuI/pGRkSgK/cQZ +; 7/6ERQYezafx/sNiADX91J5a/p5QADXVFP6ORwAdwwD+v2AANeYSAB3OGdA0/vT09KuIxTL+k5OT/l +; 1dXRnmooj+lpaW/u3t7aqI/qampiQZzArzO9T+goKC/uHh4Tn+wcHBIAr9whnw/oRFBh7Oorb+sVkA +; HDX90x811P65XQA+HcP+mk0ANecOHdAZzqeI/sLCwibG/tbW1v5wcHAZ6raI/tDQ0Dn+1NTUIxnMCv +; I70wrArYj+u7u7/vn5+Y2I/oKCghkK/Rnx/oRFBh7Q/plNAP7IZAA1/dIfNdP+mk0AHcM+/rNaADXm +; FP6ORwAd0RnN/oCAgP7p6ekmxZuI/qampv5fX18Z7KaIL/709PQ2/pSUlAYZywrxO9MKwv6SkpIJr4 +; j+ra2tBgr7GfL+hEUGHtE3/rVbAP7RaQD90Tj+yGQANdD+v2AA/nw+AB3D/ohEABQ15jE+HdIZyyj+ +; rq6uFybFhIgIGfD+f39//t/f3zn+wsLCDRnKCvE70grEBf7MzMw5/tjY2P51dXUK+RnzBB7TOP7MZw +; A1/c/+sVkApNQ1z/6gUAD+cDgAxA415/6aTQAd1BnKI/7a2tr+////xRf+u7u7/mNjYxnyq4j+t7e3 +; DI2I/oWFhSgZyQrwVdEKxqSI/qOjozKiiP6ampr+Xl5eCvUZ9f6ERQYe1Cs0Nf3O/r5gABs1zRT+gk +; EAHcMA/sVjADXmEh8d1RnJLD0mxY+I/o2NjSgZ9Cj+j4+P/unp6bGI/q6urv5iYmIZyQrvVdAKyf57 +; e3v+3NzcOf7IyMj+aWlpCvN/9gQe1aK2Bf7MZwA1/cyZHw41zP6tVwAdxP6aTQA15yUd1xnHPgsmxv +; 7Nzc3+a2trGfiyiDw5/tnZ2f54eHgZyArvO88KyyD+tbW1/vf395OI/omJif5dXV0K8Bn3/oRFBh7X +; /plNAP7IZAA1/cz+mU0ANcv+lEoAHcOjtTE15pouAB3YGcb+hoaG/u7u7ibFmYj+n5+f/l5eXhn6Ff +; 6goKD+8vLyMv6cnJz+YGBgnIjHCu47zgrO/ouLi/7o6OiziP62trb+YWFhCu4Z+AQe2KK2BTX9ywE1 +; yRL+fD4AHcP+lEoANecvHdoZxKOI/re3txcmxf7d3d3+eHh4Gf3AvYgsOS06GccK7TvNCtCxiP7Gxs +; Yqg4j+enp6CuwZ+QQe2gEcNf3J/p5QABw1x/6aTQAdxC815yMd2xnDu4j+4uLiJsVA/rKysiQZ/cIg +; /rCwsP739/cr/oyMjP5eXl4ZxQrtO8wK0qKI/p2dnf7w8PAu/qGhoTcK6Rn6/oRFBh7borY0Nf3IDv +; 7IZAA1xRIAHcMA/sVjADXmEv58PgAd3BnCJP76+vomxSv+h4eH/l1dXRn9xf6IiIj+5ubmKv63t7f+ +; Y2NjGcUK7DvMCtS7iD85/tDQ0P5ubm4K5xn7BJ/D3Q4cNf3G/rFZADQ1xA4dxP6gUAA15/6aTQAd3h +; nAOv7S0tImxg/+aGhoGf3IOv7BwcEqg4j+fX19GcQK7DvLCtaniBj+9fX1mYgHKArkGfz+hEUGHt6n +; 8f7DYgA1/cX+vmAAm001whT+jkcAHcMAEjXmEgAd3xn+jY2NMibFlYj+lpaW/l5eXhn9yjf+mJiYNq +; iI/qSkpBUZwwrrO8oK2f6EhIT+4+PjOf6+vr4gCuIZ/QQe36K2BRw1/cObTf6sVgA1wf65XQA+nVvD +; /ppNADXnDh3h/r+/v/7////G/tjY2P5ycnIZ/c64iP7T09M5/tDQ0P5wcHAZwgrrO8kK266IF/75+f +; mLiP6AgIAZCt8Z/cD+hEUGHuH+mU0A/shkAKXD/cP+sFgANcD+mk0A/nA4AMM+/q1XADXmFP6ORwAd +; 4ibFKv6pqakVGf3Qp4gcEJmI/pGRkf5eXl4ZwQrqO8gK3Rn+lZWV/u3t7a2IHAYK3Rn9wf6ERQYe4q +; K2/rVbADX9wg7+v2AA/nw+AB3DAP7FYwA15jE+HeMmxIaI/n9/fxn91P6CgoL+4eHhOf6/v7/+ZmZm +; GcAK6jvICt8j/s/Pzzn+1tbW/nNzcwrbGf3B/pqamv6MTQ4e5P6jUgD+zGcANf3A/qRSAB3EDjXn/p +; pNAB3l/v///8JV/r6+vv5kZGQZ/dYN/rq6uv76+vqKiBP+XV1dGQrpVccK4RX+p6en/vT09FX+l5eX +; /l5eXgrYGf3Aroj+yMjI/v39/f6OUBAe5afx/rpdADX9/p1OAB3CAP6/YAA15hL+fD4AHeYmwZGI/o +; +PjygZ/dgo/pGRkf7r6+sM/qurq/5hYWEK6TvGCuT+fn5+/t7e3jn+xcXF/mdnZwrWGf3A/oiIiP7r +; 6+sM/re3t/6GRwge5qK2/rFZABw1+/6rVgAdwQQ15/6nVAAd6CbA/tHR0f5tbW0Z/dy0iP7MzMw5/t +; fX1/51dXUK6DvFCuYv/ri4uP74+PiQiP6Hh4cZCtMZ/aaI/ra2tio6/oyMjBn+hEUGn8Po/plNAP7I +; ZAA1+v6vWAAdo7X+s1oANeYUIR3pDP6ioqIGGf3epYgz/vLy8sD+mJiYBgrmO8QK6Qf+6urqKv6ysr +; IVCtEZ/b6I/t7e3jn+ycnJGBnABB7porb+sVkANfkx/o5HABQ15v6tVwD+cDgA1aXTHdMbGf3iv4j+ +; 29vbOf7Gxsb+ampqkYjlO8QK6hg8/vv7+4GI/nl5eQrPf/wo/qOjo/739/ediP6dnZ3+XV1dGcEEHu +; v+mU0AHDX3nVs15/6USgAd1f6kUgD+gEAAHdMZ/eSpiP6zs7M9Df6JiYkoCuNVwwrso4j+oKCg/vHx +; 8aWI/p+fnzcKzBn8soj+zs7OCP7Y2Nj+eHh4GcMEHuyitv66XQD+0WkA/eAS/nw+AJou1P6AQAAxmj +; 4d1CgZ/eUa/ufn5yr+s7Oz/mJiYgriO8IK7z3+2NjYOf7Nzc0JCsoZ/Bb+8PDwqIj+sLCwJBnEBB7u +; /qdUAByitv3d/qBQAP5wOADVAf7DYgDA/oBAAB3U/l1dXcAZ/eUJ/sTExDmAiBsK4TvCCvCoiP6xsb +; H+9vb2l4gpKArHf/sg/r29vTmKiP6EhIQZxgQe76fx/sNiAKfx/doS/oJBAB3U/oBAABAowBAd1f5d +; XV3BGf3looj+m5ub/vDw8B/+oaGh/l9fX5yI3zvBCvP+h4eHLzn+u7u7/mRkZArFGfv+f39//uTk5L +; eI/sPDw/5paWkZxwSfw/Citv6xWQD+0WkA/dgOHdUj/sNiAML+ikUAHdX+XV1dwhn95ArAu4j+1dXV +; OTgnCt47wAr1r4j+wsLC/vr6+oiI/n19fRkKwhn6Bv6rq6v++fn5l4j+lZWVKBnIBB7yAf7IZAA1/d +; SdW/6ORwAd1KOl/qRSAP7DYgDCEB3W/l1dXcMZ/eMKwaiI/qysrP729vaWiBYoCtxVCvcZ/pmZmf7u +; 7u49/qampgYKwRn5tog/OQMjGcoEHvM3NDX90hL+djsAHdX+pFIA/sNiAMP+ikUAHdb+XV1dxBn94w +; rCMf7j4+M5/ru7uyAK21UK+bmIAzn+1NTU/nFxcQoZ+f6Xl5f+8/PzLv6pqakGGcsEHvX+p1QA/sxn +; ADX9z/6aTQAd1qXT/sNiAMObPR3X/l1dXcUZ/eIKw62I/r29vRuHiP6AgIAZCv3Ypoj+qqqq/vT09J +; 2I/pOTk/5eXl4Z9g3+xcXFOYSIJhnN/oRFBh72p/H+w2IAp/H9zP6/YAD+gkEAHdf+pFIAKMMsHdf+ +; XV1dxhn94QrEKP6UlJQYPf6np6f+YGBgCv3Z/oGBgf7h4eE5E/5nZ2cZ9P6FhYX+6enpGyogGc7+hE +; UGHveitv6xWQD+zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV3HGf3hCsW2iP7Pz885/tXV1f5zc3MK/dgZ +; DSoMjYgAKBnwpYj+s7OzGwn+j4+PGdAEHvn+mU0A/shkADX9xp1b/o5HAB3Z/qRSAP7DYgDD/o9IAB +; 3Y/l1dXcgZ/eBVxiT+paWl/vPz852I/pWVlf5eXl4K/dYZwf6SkpL+6+vrsIj+r6+vJBnuu4j+3Nzc +; Of7Ly8v+bm5ujojRBB76orb+tVsANf3E/q1XAB3ao6X+w2IAwzEd2f5dXV3JGf3fCsgX/t3d3TkiDQ +; r91BnDtIgaOf7Z2dkuGet//p+fnx9V/qGhoSgZ0gQe/P6jUgAcNf3B/pRKAB3b/qRSAP7DYgDD/o9I +; AB3Z/l1dXcoZ/d9VyKuI/ra2tgyNiP6GhoYZCv3RGcUV/qOjo/7y8vIQ/pycnP5fX1+diOiwiP7MzM +; z+/f39/tvb2/56enoZ1AQe/Ss0NfwS/nw+AB3bo6X+w2IAw51rHdr+XV1dyxn93lXJGf6Ojo7+6Ojo +; sog2/mJiYgr9zxnIv4j+29vbOf7Jycn+a2trGeb+jY2N/u7u7gz+s7OzMxnVBB79wDf+sVkAHDX5JR +; 3d/qRSAP7DYgDD/o9IAB3a/l1dXcsZ/d5Vy7KI/sjIyDn+2tra/nh4eAr9zhnJqYj+tLS0/vj4+JOI +; /oqKiv5eXl4Z4qeI/rq6ujkN/oeHhxnXBJ/D/cL+mU0A/sNiADX2Ev6CQQAd3aOlKMOdax3b/l1dXc +; wZ/d1VzAb+np6e/vHx8aOI/p2dnQYK/csZzAv+5+fntIj+uLi4Ahng/n19ff7h4eE5/sXFxf5qamoZ +; 2AQe/cOitgU19A4d3/6kUgD+w2IAw/6PSAAd2/5dXV3NGf3dVc29iP7X19c5/srKyv5ra2sK/ckZzg +; kAKoSI/nx8fCgZ3KKI/qenpwwj/piYmCgZ2QQe/cX+mU0AHDXwnVv+lEoA/nA4AN+jpf7DYgDDnWsd +; 3P5dXV3NGf3dVc6piDb+9/f3lIj+jIyMKAr9xhnQN/6cnJz+8PDwLv6jo6P+X19fnYjatIj+09PT/v +; 39/f7V1dX+dHR0GdsEHv3GorY0Ne7+v2AAHx3g/qRSAP7DYgDD/o9IAB3c/l1dXc4Z/dxV0P6Hh4f+ +; 5OTkKv64uLj+Y2NjCv3FGdK6iDD+/Pz8/tLS0gUZ2CH+8vLyLv6srKwVGdwEn8P9yP6nVAAcNev+mk +; 0AHeGjpf7DYgDDnWsd3f5dXV3PGf3bVdGuiP7AwMAqhIj+fX19GQr9whnUAv6tra0Qmoj+kJCQ/l5e +; XhnUq4gTOYaI/oGBgRne/oRFBh79yafx/sNiAKfx6BL+gkEAHeL+pFIAKMP+lEoAHd3+XV1dzxn92w +; rSKP6Xl5cnLv6kpKQVCv3AGdf+hISE/uPj4zn+wMDA/mZmZhnS/oKCgv7n5+cq/r6+vv5nZ2cZ3wQe +; /cqitv6xWQD+zGcAorbl/qdUAB3jo6X+w2IAw51rHd7+XV1d0Bn921XTt4gDOf7R0dH+cHBwCv0Z2K +; 2IF/76+vqLiBMoGc4V/rCwsBsn/pGRkSgZ4AQe/cz+mU0A/shkADXiFP6ORwAd5P6fUAD+w2IAw/6U +; SgAd3v5dXV3QGf3bCtSmiP6np6cQJ/6SkpL+Xl5eCvoZ2ij+lZWV/uzs7Bv+q6urFRnMuYj+2traOf +; 7Ozs7+cHBwGeIEHtyith7sN/61WwA14P65XQD+djsAHeSjpf7DYgDDnWsd3/5dXV3RGf3aVdb+gICA +; /uDg4Dn+wMDA/mdnZwr4Gd22iAc5/tfX1xAZyv6bm5v+9fX1wBH+Xl5eGeMEHtiitv6nVAD+ul0A/t +; FpAJkf/p5QAB7t/qNSAP7MZwA13f6aTQAd5iIow/6ZTQAd3/5dXV3RGf3aVdc+/rm5uf75+fmLiP6D +; g4MZCvYZ3qSI/qampv7z8/N//pmZmf5eXl4Zxq6IPDmAiP57e3uBiOQo/oRGBh7Vp/H+rFYA/sNiAD +; XDHA7+jEYAHuwrNDXaEv58PgAd5jsow51r/no9AB3f/l1dXdIZ/dlV2Bkl/urq6gz+rKysJAr0GeH+ +; fn5+/t3d3Tn+xsbG/mlpaRnE/oqKiv7s7OwM/ra2tv5jY2MZ5H/A/oRGBh7VNzQ1xv6+YAD+kEkAHu +; w3/rFZAP7MZwA11w4d6P6fUAD+w2IAw/6ZTQAd4P5dXV3SGf3ZCtqziBo5/tjY2P52dnYK8hnjqoj+ +; t7e3/vj4+JGI/omJif5eXl4ZwKaILv77+/srCxnlKMEJHtcO/sxnADXFHA4e7QH+yGQANdSdW/6IRA +; D+cDgA6Dv+w2IAw51r/no9AB3g/l1dXdMZ/dlV2qSI/qGhof7y8vIy/pqamgrxGeX+jo6O/unp6Sr+ +; tLS0M7mI/t/f3zkt/mxsbBnlKML+hEYGHtin8f7DYgA1xg/+jEYAHuw3BTXS/q1XAB3q/plNACjD/q +; RSAB3h/l1dXdMZ/dkK3L+I/tra2jkK8BnnJy3++/v7i4g9Mv6bm5soGeYowgke2TcF/sxnADXFHP6e +; UAAe7QEcNc/+lEoAHeqjpf7DYgDDnWsUHeH+XV1d0xn92VXdqoj+srKyCvAZ6LWI/unp6baIVS4VGe +; Yowwke2wH+yGQANcYFNx7sNzQ1zP6/YAD+fD4AHev+mU0A/sNiAMP+pFIAHeL+XV1d1Bn92AretIgK +; 7xno/pGRkf7w8PA9/ri4uP7Z2dk5/s/PzycZ5CjECR7cN/61WwA1xpkfKx7tDv7MZwA1yf6gUAAd7K +; OlKMT+ej0AHeL+XV1d1Bn92ArdAwUK7xnmIP6/v785iYj+g4ODGaiI/rCwsP729vaXiAf+Xl5eGeEo +; xQke3jgcNcUcDh7tK/7DYgA1xhIAHe0BKMP+pFIAHeP+XV1d1Bn92Arbqoj+wMDA/vr6+i8K7hnm/o +; CAgP7l5eUq/sHBwf5oaGgZwv6Hh4cgOf69vb3+ZWVlGd8oxgme1N8r/rpdAP7RaQDGD/6QSQAe7KK2 +; /rFZADXEDh3uo6UxpdPDFB3j/l1dXdUZ/dcK2v6AgID+5eXlKv7Dw8M+Cu0Z5aOI/q2trQyXiP6UlJ +; QoGcSviAQbiYj+f39/KBndKMYJHuA3/rFZAByitsU0/opGAB7tAf7IZAA1wJ1b/o5HAB3v/plNAP7D +; YgDD/qRSAB3k/l1dXdUZ/dcK2KOI/q6urv74+PiZiDAK7xnkt4j+19fX/v39/f7R0dH+cnJyGcco/p +; iYmP7u7u4M/qioqBUZ2yjHCR7iAf7IZAA1w/6nVAD+cDgA/oRCAB7uorb+ul0AWv52OwAd76OlMaXT +; w/56PQAd5P5dXV3VGf3XCtcy/tfX1yr+09PT/nFxcQrvGeT+mJiY/vT09KOI/qenpwYZyjL+0dHROf +; 7V1dUyGdkoyAke4zcFNcCdW/6IRAD+cDgAwAse8B3oRh3G/o9IAP7DYgDD/qRSAB3l/l1dXdYZ/dZV +; 1v6ZmZn+8/PzLg0oCvAZ4q2I/sbGxjmDiP59fX0ZzaWI/qmpqQFA/pWVlf5eXl4Z1ijJCR7l/plNAP +; 6uVwAdwgse8B3onUsSHcUxpdPD/no9AB3l/l1dXdYZ/dYK1K6I/sfHxyqGiP5+fn4K8Rni/oaGhv7q +; 6uob/rm5uREZ0P6BgYEGvYj+xMTE/mhoaBnVKMkJntTm/nY7AB3CCx7wHemZH6GnHcP+j0gA/sNiAM +; P+qVUAHeb+XV1d1lX91lXT/oeHhysb/ru7u/5jY2MK8hngpYj+tLS0/vv7+5GI/o2NjRnTDf66urr+ +; +fn5jogPKBnSKMr+hEYGntTm/nY7AB3CCx7wHeqbTZ1LEh3B/rldAKXTwxQd5v5dXV3WGf3WCtEV/r +; W1tQyUiAcK8xngvIj+3d3dOf7Kysr+bW1tGdb+kZGR/uvr6yr+sbGxJBnQf8sJHuY+HcL+hEIAHvAd +; 6xKaLqXDHf6AQAD+w2IAw/6pVQAd5/5dXV3WVf3WCtC+iP7d3d05/szMzBgK9Bnef/6hoaEunYj+n5 +; +fKBnYNhoq/tra2j0ZzyjLCR7mPh3CCx7wHe0EEv6kUgD+w2IAwzUd5/5dXV3WGf3WCs4Z/qKioh9V +; /qCgoBkK9RndGP7Nzc3+/f39/tnZ2T0Z26OIMyMf/p6engYZzCjMCR7mPh3CCx7wHe+jpf6pVQD+w2 +; IAwRkd6P5dXV3XVf3VCs0Y/s7OziqBiD0K9n/d/o6Ojv7v7++qiP6ysrL+YWFhGd6+iP7a2to5Gv5s +; bGwZyijN/oRGBh7mPh3CCx7wHfH+lEoA/r5fAKOlNR3o/l1dXdcZ/dVVzP6Pj48nDP60tLT+YGBgCv +; cZ2xH+u7u7OYuI/oWFhRnhqYj+s7OzLpWI/oyMjP5eXl4ZyCjN/oRGBh7m/nY7AB3CCx7LpcMe4h3y +; Nf6kUgAd6SjXGf3VCsoC/r29vf76+vqOiA8K+Bnb/n19ff7i4uK5iP7ExMQcGeQ8/ufn5zn+ubm5ER +; nGKM4JHub+djsAHcILHsr+rFYA/tFpAP6+YAD+jEYAHuAd/eEo1xn91QrJFzMq/sbGxhwK+RnZooj+ +; qamp/vn5+ZiI/paWligZ5rCIMSqFiAgoGcQozv6ERgYe5j4dwgseyBL+w2IANcGeWv6eUAAe3x394f +; 5dXV3XGf3VCsco/qurqy6biP6YmJgZCvkZ2bWI/tTU1P79/f3+09PT/nNzcxnpN/6bm5s2qYj+paWl +; /mBgYBnCKM8JHuY+nVvCCx7H/qNSADXF/rFZAP6MRgAe3R394SjXGf3VVcYU/tXV1Sr+1tbWMgr7Gd +; j+lZWV/vPz8y7+qqqqBhnsuYghCCEUGcEozwke5j4dwgsexwU1xpkfNx7cHf3h/l1dXdcZ/dUKxf6W +; lpYjPf6srKz+Xl5eCvwZ1quIMTkzJhnvpogJ/vX19ZuIAzco0P6ERgYe5v52OwAdwgseyP6ZTQAcNc +; X+sVkAHtwd/eEo1xn91QrDrIj+xcXFKoiI/oCAgAr9Gdb+g4ODDRv+vLy8/mVlZRnyIv7i4uK6iP7B +; wcE+KM8JHub+djsAHcILHsmitv66XQD+0WkAxBz+lUsAHtsd/eEo1xn91QrC/oSEhA0b/r+/vyAK/c +; AZ1BX+sbGxG5OI/pCQkBn1rYj+vb29DIyIIv5eXl4ozQke5j4dwgsey/6nVAD+zGcANcM0Htsd6aK2 +; HfMo1xn91QrABv6ysrIMloj+kZGRGQr9wBnUuYj+2traOf7Nzc3+b29vGfco/pSUlP7s7Owb/q2trf +; 5iYmIozP6ERgYe5j4dwgsezCv+w2IANcMfHtod6afx/qhUAP55PQAd8f5dXV3XGf3UCsC8iP7b29v+ +; +/v7/s/PzzaMiP3CGdP+nZ2d/vb29lX+oqKi/l1dXRn4KMC1iDg5/tjY2P52dnYoywke5j6dW8ILHs +; 2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3wKNcZ/dX+n5+fEH/+pKSkGQr9wxnRr4j+ysrK/v39/f7c +; 3Nz+enp6goj6KMEk/qWlpTJ//pubmxUoyQke5j4dwv6EQgAez/6ZTQD+yGQANcEnHtkd6KXD/rxeAD +; zB/ppNAP51OgAd7ijXGf3TsIgaKoOI/nt7e4CI/cQZ0f6Kior+7e3tDP61tbX+YmJiGfsow/59fX3+ +; 3NzcOS3+ampqKMj+hEYGHuY+HcL+hEIAHtCitv66XQA1wBz+kEkAHtgd6P6gUAA/wTzANx3uKNdV/d +; IpGAz+t7e3Mwr9xRnPp4j+uLi4/vv7+46I/omJiRn8KMWqiP62trb++Pj4koj+ioqK/l5eXijGCR7m +; /nY7AB3C/oRCAB7SDv7MZwA1NB7YHec2/r1fAMA/wZ5aHe8o1xn90DP+urq6/vr6+is8Cv3HGc6/iP +; 7g4OA5/sfHxzoZ/SjH/o2Njf7p6emyiB8CKMUJntTmPh3CCx7Tp/H+w2IANf6ZTQAe1x3n/qFQAAfB +; P8D+kUkAHe/+XV1d1xn9zwj+4ODgKv7Jyck6Cv3HGc0o/qampj2aiP6ampooGf3AKMixiP7Hx8cqg4 +; j+e3t7KMQJntTmPh3CCx7UorYFNDce1h3mNv6+XwDBB8CdazYd1f6USgD+y2YAIx3VKNYZ/c4o/qen +; p/739/ediP6bm5soGVX9xxnMs4j+0dHR/v39/f7W1tb+dXV1Gf3BKMqiiP6enp7+8fHxLv6ioqL+YG +; BgKMIJHub+djsAHcILHtb+mU0Ao6Ue1h3m/qNSAP7AYQBSWsAHJR3V/q1XADXB/rldAD4d0yjWGf3N +; tIj+0tLS/vv7+/7Y2NgQGcBV/cgZy/6SkpIUPf6tra0GGf3CKMy7iP7X19c5/tDQ0AUowQke5j6dW8 +; ILHvAd5aOl/sFhAMFGVsD+ej0Amz3T/oJBAP7FYwA1w51b/o5HAB3SKNYZ/cwh/vHx8aeI/rCwsAYZ +; wVX9yBnJqoj+wMDAOYiI/oGBgRn9xCjNp4j+r6+v/vb29piI/o+PjwYoCR7mPh3CCx7wHeU4/sFhAM +; JW/qFRAP5wOADT/qBQAP7RaQDGnVsAHdEo1hn9yi/+wsLCG4yI/oKCghnDCv3HGcn+gICALyr+wMDA +; /mdnZxn9xCjQ/oaGhv7k5OQ5/r6+vv5mZmYJHub+djsAHcILHvAd5Dv+wWEAxP5/QAD+cDgA0QAS/t +; FpAMYxPh3SKNYZ/ckT/ubm5ir+wsLC/mdnZxnECv3HGccG/q6urhuViAMZ/cYo0Tr+wMDAGxH+hUYH +; Hub+djsAHcILHvAd5P6eTwD+wWEAw/6tVwD+cDgA0f6aTQD+0WkAxwQd1AYo1Rn9xwb+r6+vDJeIIR +; nFCv3IGca3iP7Y2NgI/s/Pz/5wcHAZ/cco0n/+l5eX/u3t7f6sfk8e5j6dW8ILHvAd4zv+wWEAxP5/ +; QAD+cDgAzz4vNcYS/nw+AB3U/otdMP7Gxsb+bW1tKNMZ/ccQ/tjY2Cr+0tLS/nFxcRnGCv3IGcX+mZ +; mZ/vT09KKI/qampv5eXl4Z/cgo1LaI/snFwP66lG7+jlAQHuQ+nVvCCx7wHeP+mEwA/sFhAMP+slkA +; /nA4ANAC/stmADXF/qdUAB3V/mhIKf63t7f+/Pz8jIj+jIyMKNIZ/cb+m5ubAaOI/qenpygZx1X9xx +; nErYj+x8fH/v39/YGI/nx8fBn9ySjWpYj+qKio/tbFtP6WWyD+h0QA4/52OwAdwgse8B3iO/7BYQDE +; /olFAP5wOADRADE1whT+iEQAHdSiXP5iVEYof/6Li4v+6+vrPf61tbX+ZmZmKNAZ/cSuiP7JyckqhY +; j+fX19GckK/ccZw/6Hh4c6G/64uLj+Y2NjGf3KKNj+gICA/tfTz/66lW7+h0QA4j4dwgse8B3i/phM +; AP7BYQDDJv5wOADTPv6nVAAUo7X+rVcAHdX+aUYjKMOuiDz+/Pz8/tvb2wgozxn9w/6JiYn+6+vrG/ +; 66urr+Y2NjGclV/cgZwSQQKjr+jIyMGf3MKNmriP65ubn+28q5Hx7gPh3CCx7wHeE7JqXTw/6ORwD+ +; cDgA1SEjHdX+ZE86KMWjiP6cnJz+9fX1QP6kpKQkKMxV/cIz/re3t/76+vqSiP6NjY0Zywr9yBnAvY +; j+3t7eOf7Jycn+bGxsGf3NKNv+kJCQ/uPf2/6sfk8e3z4dwgse8B3h/o5HAP7BYQDDnWv+cDgA7f5s +; PxH+X1hRKMi6iP7Z2dk5/szMzP5xcXEoyxn9wb6INyr+y8vL/m1tbRnMCv3IKP6ioqL+9/f3nYj+nZ +; 2dGf3Pf9yyiDz+yKuNHx7dPh3CCx7wHeA7JqXTwwL+cDgA7P5mSi4oyyQnKpGI/pOTkyjKGf0oES4Q +; /p+fnygZzQr9xxj+zs7O/v39/Sz+d3d3Gf3PKN4V/qGhof7bz8L+nWcwHtw+HcILHvAd4AL+wWEAw5 +; 1r/nA4AOsr/mFWTCjNf/6FhYX+5ubms4j+vLy8HCjHGf02FiqAiP54eHgZzlX9x/6Ojo7+7+/vqYj+ +; sbGx/mFhYRn90CjgvYj+0s7K/rqVbh7bPh3CCx7wHeAmpdPDAv5wOADqNCjRDf7CwsL+/Pz8hYj+g4 +; ODKMYZ/P6RkZE2DP6zs7MVGc8K/cWoiP68vLz+/Pz8L/6EhIQZ/dIo4aiI/rKysv7ZyLf+llsg/odE +; ANk+HcH+lEoA/qdUAP6MRgAe7x3f/o5HAP7BYQDDP/5wOADpolz+Y1FAKNOiiBL+8fHxEP6rq6v+Y2 +; NjKMQZ+iD+v7+//vr6+o2IABnRCv3E/n5+fv7j4+Mq/sPDww0Z/dMo4/6JiYn+39vX/qx+Tx7Y/nY7 +; AB3A/q1XADXA/r5gAP6QSQAe7h3f/rdcAKXTw/6TSgD+cDgA6P5rQRf+XltXKNa1iP7U1NQI/tPT0/ +; 51dXUowhn6/oCAgP7k5OQq/sXFxf5paWkZ0Qr9w6OI/qqqqgyXiDAZ/dUo5K+IIv7Hqoz+jlAQHtY+ +; puL+xWMApuLCnlr+p1QAHu0d3v6ORwD+wWEAwz/+cDgA6P5lTTQo2RX+pqamPTb+mpqaNyjAGfg3/q +; ysrD2aiP6Xl5cZ0wr9wraI/tXV1Qj+0tLS/nJycgrAGf3UKOU3O/7YzL/+nWcvHtX+nU8ANcYP/oxG +; AB7rHd4mpdPD/phMAP5wOADm/mw/ERoo23/+fn5+JBsi/mxsbBn4t4j+1tbWKiEyGdQK/cH+lpaW/v +; Pz86SI/qmpqTcKwBn91SjnEP7MyMP+upVu/o5PEB7Torb+sVkA/sxnADXFHP6eUAAe6h3dAv7BYQDD +; nWv+ej0Amz3l/mhIKSjfqIj+u7u7/vz8/IqIPBn2/piYmDI9/qurq/5eXl4Z1Ar9wKyI/sXFxTkkFw +; rCGf3VKOimiDr+18a1/pZbIP6HRADU/plNAP7IZAA1xgX+jEYAHuj+cDgA3Sal08P+o1IA/nA4AOSi +; XP5iVEYo4Qb+jY2N/uzs7C7+srKyIBnyrYj+xsbG/vv7+4eI/n9/fxnWCv3+g4OD/unp6Rv+u7u7/m +; RkZArDGf3VKOoi/trV0f6ziV8e1Df+tVsANcaZH/6VSwAe5x3c/olFAP7BYQDE/no9AJs94/5pRiP+ +; XV1d5bCI/s3NzTks/nl5eRnw/oaGhv7o6OiyiP69vb0gGdcK+wb+srKyG5OIBxkKxBn91SjrrIj+vL +; y8/s20m/6OUBAe1P6eUAD+zGcANcWZHx7n/nA4ANz+t1wApdPD/qNSAP5wOADj/mRPOv5dXV3lGcAG +; /p+fn/729vaciP6hoaH+X19fGeyliP60tLT++fn5lYglGdgK+7uI/tvb2/78/Pz+y8vL/m5ubo2Ixh +; n91Sjt/pOTk/7Vybz+rH5PHtSlw/66XQA1xJkfHucd2yn+wWEAxBSbPeH+bD8R/l9YUaUf5hnCvYj+ +; 29vbOf7Kysr+b29vGeq8iP7c3Ny/iP7Ozs4njojZVfkZ/p6env729vZV/qCgoBkKxxn91SjutIj+xc +; G9/rqUbv6OUBAe06K2/qxWABw1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXecZxCT+srKyKo+I +; /o+Pjxno/qGhoRB//qKioigZ2Qr5sIga/v39/f7b29sMgojJGf3VKO+kiP6kpKT+1cSz/p1nMB7U/p +; lNAP7DYgA1wSge5x3a/oRCAP7BYQDE/no9AJs93/5sPxH+YVZM/l1dXegZxSj+h4eHDbGI/rm5uT4Z +; 5LGI/szMzCqCiP56enoZ2wr4/oyMjP7u7u6riP6zs7MkCsoZ/dV/8b+I/tTQzP66lW7+h0QA1Df+sV +; kANcCZHx7nHdr+slkAqPDD/qhUAP5wOADf/mhIKf5dXV3pGcisiAD+/Pz8g4j+f39/GeL+jo6O/u3t +; 7Qz+tra2JBncCvaniP65ubkqjYj+h4eHCswZ/dUo8qmI/rW1tf7aybj+llsg/odEANQB/shkAJ5aHu +; cd2ajw/sFhAMQy/nA4AN2iXAL+XV1d6hnJo4j+l5eX/vPz83/+qKioJBnep4j+u7u7/vr6+pCI/oiI +; iBndCvb+e3t7/uHh4TkA/mpqagrNGf3VKPQp/uHc2P6sfk8e1Df+rFYAHucd2RH+wWEAwwj+cDgA3f +; 5sPxH+X1hRKOoZzLeI/tbW1jn+0NDQMhnc/n19fSS6iP7IyMj+ampqGd4K9Cj+pqam/vj4+JqI/piY +; mBkKzhn91Sj1sIj+xsbG/seqjP6OUBAe/f5wOADYFP7BYQDE/olFAP5wOADc/mVNNCjsGc2kiP6pqa +; kbk4gOKBnYoogcLpyIOygZ3gr0Nv7S0tL+/f39/tXV1f50dHQK0Bn91ij1Bv6dnZ3+2s3A/p1nLx78 +; Hdj+o1IA/sFhAMObPf5wOADb/mw/ERr+XV1d7BnPKP6AgID+5OTkG/7AwMD+ampqGda1iP7T09Mq/t +; jY2P51dXUZ4Arz/pOTkyMu/qysrAYK0Rn91ij3uoj+zsrG/rqVbv6OTxAe+v5wOADXpdP+wWEAxP6O +; RwD+cDgA2jQo7RnSIP6+vr45Ef6GhoYZ1P6VlZX+8fHxPf6vr68VGeAK8quI/sHBwTmHiP6AgIAK0x +; n91ij4p4j+rq6u/tjHtv6WWyD+h0QA+R3XOP7BYQDDnWv+cDgA2aJc/mNRQCjuGdM3Fv7u7u6oiDb+ +; Y2NjGdA+/sTExCqJiP6CgoIZ4grx/oGBgf7n5+cq/r+/vy8K1Bn91ij6/oWFhf7c2NT+s4lfHvgd1q +; bS/sJhAFrD/o5HAP5wOADY/mtBF/5eW1co7hnWs4glOf7X19cuGc4iPhv+wcHBLxniCvCjiP6vr68b +; J/6RkZEZCtUZ/dYo+ysm/s61m/6OUBAe9h3W/q1XAP7NZwCbPUbBP/5wOADY/mVNNP5dXV3vGdgG/q +; Kiov739/eaiP6enp7+Xl5eGcoV/rGxsf75+fmXiBIoGeMK77iILDn+zs7O/m9vbwrXGf3XKPs3/paW +; lv7Xyr3+rH5PHvUd1aO1/tFpAMBWnEydS8AC/nA4ANb+bD8R/l9YUSjvGdoovoj+3t7evYj+x8fH/m +; 1tbRnIuoj+2traKv7Q0ND+cHBwGeQK7/6ampr+9fX1f/6kpKQoCtgZ/dco/TL+yMS//rqUbv6OUBCU +; 1PMd1S81wp5q/r1fAB3W/mhIKSjxGdymiP61tbUqjoj+jY2NGcb+nZ2d/vT09C7+pqam/l1dXRnlCu +; 2uiP7IyMg5gYj+e3t7CtoZ/dd//cCkiP6np6f+1sW0/pZbIP6HRADyHdSjtTXDnVv+gkEAHdSiXP5i +; VEYo8Rneooj+iYmJ/urq6j3+tra2/mZmZhnCOv7Jycn++/v7hIj+fX19GeYK7v7s7OwMHzMK2xn91y +; j9wv5/f3/+19LO/rqVbh7xHdT+rVcANcIvHdX+aUYjKPIZ4a6I/sfHxzmAiAj+XFxcwP6Li4v+6+vr +; DP65ubkzGecK7v7q6ur+ioqKCt0Z/dgo/cKqiP7Ozs7+4dC//o5QEJTU7x3To7U1wv6USgAd1f5kTz +; oo8hnjo4g7AVX+p6enDBs6/oyMjBnoCu+wiAreGf3YKP3BBv60tLT+/v7+f5TE/qx+UB7uHdMvNcD+ +; v2AA/nw+AB3U/mw/Ef5fWFEo8xnluoj+39/fF8D+0dHRGBnpCu/+f39/Ct4Z/dgo/cAfBv7////D/s +; qtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o9BnlKP6lpaUuPTmRiP6UlJQoGecK8P709PT+p6en +; JArcGf3ZKPz+np6e/vr6+ibF/ujbzv6dZzAe6x3S/qdUABIAHdQr/mFWTCj0GeWziP7Q0NAq/tra2v +; 55eXmqiP7m5uaziP69vb0cGeUK8f7S0tI5NP5ycnIK2xn92Sj6OjQmxi0T/tHNyf66lW4e6h3Ro7X+ +; p1QAHdX+aEgp/l1dXfUZ5f6SkpL+8PDwDP6xsbEVGcCqiAQ5hogiGeQK8RUgLhg/KAraGf3ZKPj+i4 +; uL/vHx8SbFl4j+mZmZ/l9fXyioiP6xsbH+2ci3/pZbIP6HRADoHdE+HdSiXP5jUUAo9RnkL/7AwMAb +; jIj+hISEGcOiiP6RkZEFpYj+rKys/mJiYhnhCvMZ/n5+fv7g4OAq/r+/vxwK2Rn92Sj2M/68vLz+// +; //xjv+dHR0KMP+iIiI/t7a1v6ziV8e5x3n/mtBF/5eW1co9Rnk/oGBgf7l5eUq/sTExP5oaGgZxrSI +; /tPT0/79/f3+1NTU/nV1dRngCvWpiP65ubkqiYgPCtgZ/dko9b+IICbFnYgJJCjFOv7CwsL+zrWc/o +; 5QEB7lHeb+ZU00KPYZ4wb+rq6uPZmIPygZyKSI/qSkpD2XiP6bm5v+Xl5eGd1V9yg4/uvr6y4n/mNj +; YwrWGf3aKPP+qKioOSbFiIj+goKCKMg3/pmZmf7YzL/+pHI/HuQd5P5sPxH+X1hRKPYZ47iI/tfX1y +; oSIxnLKP59fX3+4ODgKjH+bGxsGdsK+gn+y8vLOf7W1tb+d3d3CtUZ/doo8bKIPybFVf7BwcH+ZmZm +; KMsB/svHwv66lW4fHuId4/5oSCko9xnjLP7z8/OkiP6qqqo3Gc4CDDk+/omJiRnaCvsG/p6env719f +; WbiP6dnZ3+Xl5eCtMZ/dt/7/6UlJT+9vb2JsWTiAM3KM0zK/7XxrX+llsg/odEAOEd4aJc/mJURij3 +; GeKuiP7Hx8cqh4j+f39/GdE3/oyMjP7s7Owu/rOzs/5lZWUZ1wr9wL6IOzn+xsbG/mxsbArSGf3cKO +; wgMf7////G/tTU1DYo0RP+2dXR/rqVbv6HRADgHeD+aUYjKPgZ4v6Hh4f+6urqG/68vLz+ZGRkGdQJ +; Gv78/Pw7/np6ehnVCv3Cp4j+sLCwKo2IKRkK0Rn92yjrIjomxZuI/qSkpP5gYGAo06yI/ru7u/7cy7 +; r+jlAQlNTeHd/+ZE86KPgZ4ST+tbW1/vn5+ZWI/o+PjxnXBv6dnZ3+9vb2I/6ioqIGGdMK/cMZ/oeH +; hz49EP5lZWUK0Bn93H/oN/6xsbEXJsWCiP58fHwo1/6SkpL+3NTL/qx+T/6HRADdHd3+bD8R/l9YUS +; j4GeG9iP7d3d2+iP7MzMz+bm5uGdq8iDsqGjYZ0Qr9xhwx/vz8/CgICs8Z/dx/57eI/t3d3SbFF/64 +; uLj+Y2NjmojZs4j+xMC8/smrjf6OUBAe2x3c/mZKLij5GeEz/vb29lX+oaGhKBncpYj+sLCwKpCI/p +; CQkBnPCv3IN/6Wlpb+8fHxoogRFQrNGf3dKOX+m5ub/vn5+SbFjoj+jIyMNyjbFf6jo6P+3M/D/p1n +; MB7aHdor/mFWTCj5GeAn/s7OzjmAiP56enqCiN8o/oaGhj4M/rq6uv5nZ2cZzFX9y7iI/tbW1v79/f +; 0H/nBwcArNGf3dKOIc/s3NzSbG/svLy/5ra2so376I/tPPyw4e2R3ZNCj5GeEW/u7u7gz+tbW1JBni +; Pv7Dw8M5hIj+gYGBGcsK/cwk/qmpqf74+PiUiP6Tk5MoCssZ/d0o4S3+7+/vJsUu/p2dnf5fX19A4S +; /+s7Oz/trJuP6WWyD+h0QA1x3Xolz+ZE86KPkZ4KiI/r29vf76+vqPiP6Hh4cZ5TcwI6KI/qmpqSQZ +; yAr9zhkE/uLi4hv+vLy8/mhoaArKGf3eKN4k/rq6uhcmxf7b29v+d3d3KOX+ioqK/uDc1/6sfk/+h0 +; QA1h3WK/5fWFEo+Rng/n5+fjO5iP7Hx8f+ampqGei2iP7V1dX+/Pz8/tLS0v5zc3MZxlX90SAIOTP+ +; g4ODCskZ/d8o3LyI/uTk5CbFQP6wsLAkKOewiP7FxcX+yKqM/o5QEJTU1B3V/mVNNCj6Gd83/qqqqv +; 739/eciCwoGeqkiP6np6f++fn5J/6YmJgoGcQK/dIoJf7t7e2piP6rq6v+YmJiCsgZ/d8o2v6kpKQq +; JsWKiP6FhYUo6gb+nJyc/tnNwP6dZy8e0/5wOADT/mw/Ef5hVkwo+VXgIyEqP/50dHQZ7SgX/uPj47 +; eI/sHBwf5qamoZwgr91bOIBzkh/nV1dQrHGf3gKNewiCEmxjH+Z2dnKO25iP7NycX+upVu/o5PEB7R +; HdI0KPoZ4P6Wlpb+8vLyPf6tra0GGfCoiP68vLz+/Pz8iYj+h4eHGcAK/dcV/qGhof729vY2/pqamj +; cKxRn94CjWJf709PQmxTL+lJSUNyjvpogY/tfHtv6WWyD+h0QA0B3Qolz+Y1FAKPoZ3w3+xcXFKoiI +; /oGBgRnzN/6NjY3+7e3tHwX+Y2NjCv3Z/nt7e/7c3Ny/iP7Dw8P+a2trCsV//eAo0wL+wcHBJsb+19 +; fX/nFxcSjz/oSEhP7b19P+s4lfHs8dz/5qRB3+XltXKPkZ4DH+6OjoG/6/v78vGfaxiAc5/tjY2P53 +; d3cK/dkC/rS0tCqMiAsKxBn94SjR/oCAgA23iMUb/qioqBUo9Sv+vr6+/s60m/6OUBAezf5wOADO/m +; VNNCj6Gd8V/rKysgw2/pKSkigZ+Ab+oKCg/vf395qI/p+fn/5eXl4K+VUK2igLHD0j/mVlZQrCGf3i +; KM43/q2trf7+/v4mxYWI/n9/fyj5/pWVlf7Wyr3+rH5PHswdzP5sPxH+X1hRKPkZ4LuI/tvb2yr+z8 +; /P/nBwcBn6VRm9iP7c3Nwq/sjIyP5tbW0K+DsK3K+I/sfHxzn+2tra/nt7ewrCf/3iKMy1iDv+//// +; xRf+vLy8ICj7I/7Hw7/+upRuH5TUyh3L/mZKLij6GeD+np6e/vX19aKI/qSkpCgZ+grCpoj+tLS0/v +; v7+xw4CvZVwArdpIj+mpqa/vLy8sD+oaGh/mBgYArAGf3jKMr+mJiYPSbFNv6Ojo7+Xl5eKP2kiP6m +; pqb+1sW0/pZbIP6HRADJ/nA4AMmlH/5hVkwo+RngsIj+y8vLKoOI/nx8fBn7CsQZ/oiIiBw9/re3t/ +; 5mZmYK81XBCt+7iP7Y2Nj+/Pz8Gv5vb28KGf3lKMaqiP7JyckmxhYYKP3D/n5+fv7V0c3+upVuHsj+ +; cDgAyP5pRiMo+hng/oyMjP7s7OytiP64uLj+YmJiGfsKx62I/sbGxjmBiP59fX0K8jvBCuCmiP6srK +; wMkYgl/l1dXRn95SjE/oWFhf7t7e0mxZmI/qCgoAYo/cU+Lv7ayrn+jlAQHsYdx/5kTzoo+Rngp4j+ +; ubm5/vr6+pGICxn8CsmjiP6YmJgywP6mpqYGCu87wgrhGf6Dg4P+5OTkGwwNGf3lKMGjiP62trb+/v +; 7+JsX+3t7e/nl5eSj9yTj+4t3Z/qx+Tx7F/nA4AMX+bD8R/l9YUSj4GeH+fHx8/uDg4Co8/mxsbBn8 +; Csy5iP7X19f+/Pz8Fv5xcXEK7TvDCuM+/sDAwDkV/oCAgBn95Si6iP7h4eEmxUD+s7OzMyj9yyf+x8 +; fH/siqjf6OUBCU1MMdxP5lTTQo+RngKP6np6f+9/f3nYj+nJycKFX8Cs4G/qurq/76+vqTiDAZCus7 +; wwrko4j+kpKS/u7u7gE+Mxn94/6goKAbJsWMiC0o/c4G/p6env7azsH+nWcvHsIdwiv+YVZMKPgZ4Q +; X+0tLSKv7Z2dn+dnZ2Gf3ACs8Z/oGBgf7k5OQb/r29vQ0K6TvECuYU/tHR0TkD/nNzcxn94K+IAybG +; /sfHxw0o/dG7iP7QzMf+upVu/o5PEJXDwB3B/mhIKSj4GeL+k5OT/vDw8KiI/rCwsP5gYGAZ/cAK0i +; D+v7+/OYeIIgrnO8UK5xX+pKSkLif+mJiY/l5eXhn93SkjrYjFlogdBij906eIBf7YyLf+llsg/odE +; AP5wOACiXP5jUUAo9xniqoj+wsLCG4yIIhn9wQrUKP6Pj4/+7+/vEP6tra3+YmJiCuQ7xgroGf5+fn +; 7+39/fKv7AwMD+ampqGf3aM/6+vr7+////xv7Y2NgyKP3X/oeHh/7d2dX+s4lf/oxiOP5nY2Ao9hnj +; BC8q/sPDww0Z/cEK17SIA/78/Pz+1dXV/nV1dQrjO8YK6RkR/ri4uCqJiB4Z/dj+fX19LybFKv6rq6 +; sVGSj92K6INRsR/oCAgDco8hnjo4j+r6+v/vj4+JmI/pWVlSgZ/cEK2Qb+oqKiLpmI/pycnCgK4DvH +; Cul/wDc4/uvr6y42/mRkZBn91Cj+qamp/v39/SbFLzUZwyj91zf+l5eXJ6uIHP5hYWEo7xnkEB0qA/ +; 5ycnIZ/cIK2xm/iP7f39+8iP7FxcX+a2trCt47yArqGcEJ/srKyjkd/nl5eRn90rOI/tfX1ybFVf7A +; wMAgGcYo/de3iP7R0dE5/tbW1gEo7Bnl/pubm/709PQu/qioqDcZ/cJV3qeI/re3tzmLiP6KiooK3D +; vJAAAAAAAAAAE= +; thumbnail_QOI end +; + +; +; thumbnail begin 640x480 46696 +; iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAACIlUlEQVR4Ae29Z3AeWZaeiV/zY6WIlX +; 5NbIQmQqH5I+2uerUrrZF6Z6Z7prt6WD2trmm1UVd3VbHoQRD0FqABvSdB770FvfcEvQe9t6C3IAn6 +; KlZ1XdV7MQlmwn6Z383MezLfG/FEV1eRH/J+yJv3zXPPOW/Op59+qrKlc+fOavXq1erFixfKGX/605 +; /U3r17VZcuXdQf//jHev/uH/7wB/33T5w4odzj+fPnatOmTfq/mbhGEi/4PRNCkk+fPn3UmTNn1Fdf +; faWf5d99953eGzZv3qzatGnT4N9t2rSpGj58uDp58qT65ptvqveDly9fqvnz56uWLVvq/UTC90CI7e +; Rks6l/8cUXatiwYbUWa2VlpZo5c6Zq0aJFxp/VrFkztXjxYvXmzZvqz3n//r06ffq0GjlypPr888+t +; EjSkcSQtBEJIdnz55Zdq7ty56sGDBzoAgIF9oby8XE2YMEF99tlnGX8WAgdr1qzRe4kzICQPHz6sev +; ToofcDm78LQiQQWADm5eWpBQsWqEePHnkid3jzKywsDCTY8IDA2yMeGO6BB8qyZctUbm6uFcKGNIyk +; BUAIyZ5OnTqpAwcOqNevX1c/t/HPBw8e1P8tyGc2b95cTZw4UV2+fFmLP2c8fvxYjR8/Xv93274HQi +; ThWwA6Ig3Hu06IHwORu5UrV2qRhg/ORjzgM3Bc8P77CKAz3r17px8m/fv3z+rzSThIueEJIebAfgAx +; hpd25xQI0b+HDx+qefPm6ZOdbD+/oKBA7dixQ719+7Z6P/j666/Vli1bVIcOHXxFFgkhH/AlAJF/MW +; XKFHXjxg1PhO7WrVtq6NChOn/DlKBABHHMmDHqyZMnyp1XiAfNrFmz9Ntf1CKHUPQRQqpAPt/GjRv1 +; Ma0ToUNQAKdA/fr1M/6z5syZo+7cuePZe7AXDRo0SB8/x/U9ECKVnEw3+m7duunF7g7x440Pb2YdO3 +; ZssNAjG4GBN7xDhw55Fj0Sirdv365zQaIWPWlH0s1NSGM0++NvRF2vLQwcOFBdvHhRR+Kc/DwU7iFv +; r3Xr1qH8TOScQ+wdOXKk+uc6R80lJSX657JAhJDMaVQAIqo3evRodfbs2erEXoyKigo1efLkSCJxWP +; h4+0MlmFt8XrhwQRUXFxuNPBIKP5J8On3+sdre6l9q8M+Srj1O8LxHsR7y8Jz94P379+ratWt6n4ji +; GhBwWL58uXr69KknMIBOEjguxn4RxXUQIp0GBWD79u3V0qVLay20srIy1bNnT517EZUAwZtdr1691K +; VLlzzXggcRWtC0a9cuFmGUZCTdyIRkytgv/6M6nZtTzdE2f6b/naQ5xAFOgY4dO+bp1PDq1Su1e/du +; vVdEeS3ILRw7dqw6f/68+vbbb6uv59mzZ2ratGm6A0WU10OIROoUgMi/GzBggC65d4fasdjx9odcQP +; zlOAQJfjaOGdwFKPjno0ePqsGDB4dyFJ0mJN28hPgBx72LWvwbj/hzs77ln6u2n30iak5RgIja1KlT +; 1e3bt6vFFv737t27asaMGfoEJo7rwrMeaUAoGKyZmrRr1y7dQ5YFIoTUTy0BiDwK9PCrmWx79epVLQ +; rxMIhbpGBRo+jk/v371deHHBQ8oJxmoXFfozQk3bSE+AXHvIj01Sf+3NHAoi/+RtTcwgTtvpDn7U6/ +; QTUujltxImPDNbZq1UpNnz69VvswCFQ0lc62EpmQpFItAPE2hWPdbdu2ecrtEV2DI0d+fr5V0TVcPK +; 4Jxw/uHlF4E8TbH3oR2nKttiLpRiUkKLOa/9tGhV9N8HfSXiAC8XTlypXqdlzI+UM6EHqyOqdAtoBT +; K1Qe79+/33M6hL0MJ0YQsiwQIcSLFoB4Qxo3bpzOr3OLKfRyQp4F/rutIgYLH8cT7o7xOJ7AgwtFKm +; gPYOu1x4Gkm5OQbHAKPfyKP4e0FohA3K1YsULn0zmFHkgFQkNmnLzYfO0ICiBNqaZBAXIF0b+WBSKE +; fCAHXdrRwLmm5Q66uiPpV0JOHa6xa9eu2jbOPfC2umHDBt2J3vY5hI2km5KQbKlZ6BEUp0AkLdFAnJ +; ycOnVKN9539gK03dq6datq27atiDngpX/EiBF6Hm6LUswDVnW2RS8JiYuc48eP11okaLnix8fXFoGD +; FgXoB1WzYzy8inGckTY/YUk3IiEmgFBb0fJfGRF/aSoQgWhCg33kVbsLPdDkf9KkSSK9dxEUWLdund +; 7TnIGIphPcoJ8wSTs57ojZuXPndJhcslBCgUhRUZEuCHEPPNiWLFmiO8pLmg+FHyGZgeKNTAo9sokG +; Dm36n0V9J5mAZvuw9kSXB2eg1Qu6QHTp0kXUXGqCQAZSgZAS5B5OehP9hEmayXEnymbr42uTAMJxBY +; 4t3NFNzBNJwkgWljQfCj9CGqah9i6mwc9KwpEwUmcggq5fv+7x8UX+3MKFCxMjjhAU6N27ty4OrKvA +; ET0M2S6GpJEcp1Q+icUSiGSiuMXdyBoPOPhHon9VEvyEJd1shJgGRRp7W/+zyMSfu0BEcrsYtPtav3 +; 59LR9fFEv0799f1FwyBUGBefPmqXv37nmigU6Ls7j6GRISFzlolpnk5smYJIpA0CjaPZzEZuSCSJoP +; hR8hVUxu9u8jF35JKBCBwEO6j9MuBQIQQhCCMCwfX1uAyBsyZIh2NHn/T+1tMByTA/QUZLsYkhZyPv +; 30U5UGsPDRJNqd54IHAB6EY8aMsaLBNUUfIY2TbXsX06BAREK7GJx4LFiwQB/xOu1dcPSLExEcBadJ +; +DjdL9Dqxj0gDNHgmgUiJA2kRgAC5HmgzQFC/u6BByIeBmgWauN1S7qhCAkTU+1dwooG2vq9oZgDRR +; 01fXxR/IEiEFuvO0yc/rcXLlyoFsQYFRUVunCEfsIk6aRKADpiysl/cfsc4zjkyJEjauDAgdYciUu6 +; kQgJk8Z8fG2KBtp0JIxIFtq4oJ2Lu70LuiKg7Qtyv2251jjAsx4RPzhgucXx++9Ph2CBh0ghC0RIUk +; mdAHTAgxHNQtEOwBnIhcGDEs1C4+qDKOnmISQK4ir0yCYaaEOBCIoeIGyQ7+wUeqDBMxok4yTEht+t +; LSAoMHPmTHXz5k3P6RD2A7if0E+YJJHUCkBHbKEFwL59+zyLHkcjpaWl+s0wymshhHgJ4uNrC3H6CU +; O0wLrNOeXAESfy3WDxRieMukEeOApkDh486PETRmQQKUIQ1CwQIUki1QLQAQsfbWHcHeORHA1v5AkT +; JoTaIkfSzUJIVNhW6BGUqP2EIe6WLVum89icvLb33x9nohEy2n3Z9Du2FQQFli5dqh4/fuwJDJw5c0 +; b3E6SfMEkKFID/BN7sunfvrquC3QMPUtgJIVHa1M+SdIMQEjW2FnoEJap2MTixgLWnu9nxy5cvdS4b +; Ctxs+z3bDF76R48erUWfkzuJgXY5yJ1kFJUkAQrAGsIMuX84JnHM0DFwjIIHK45VkBCczecTQuomLB +; 9fWwjLTxgtrnCCgab+7kIP2GFOnTqVEassQFBgw4YNWkg7A5FVpA3Ba5gFIkQyFIB1gEWNamA8UN0D +; /x/NQpEwTOFHiDl6fv6TUH18bYoGmvQTxnElLM5q+viinx2a3Nv4u5YGon0Q0teuXfPsB6ikRpSQBS +; JEKhSADYg2HJvg+MR9BICHK3pn9enTh8KPEANILvQIigk/YYgPiJL3/+RogcjUkydP9EtqUnx8bQFB +; ATzz9+zZ4zkdwj8jQpifn89oIBEHBWAjoF3MxIkT1fPnz6sXPQQhDNSnTZum3/4o/Ajxj7T2LqYJ6i +; cMu7I1a9boZ5LT3gVpKhcvXlSDBg2y+ncuHQQF4KaC6J97oGCwqKiIfsJEFBSAGYACEXgmIw/QPfAA +; 3rx5s+6yL+mXTkjcJK3QIyh+C0T69eunCxPcPr7oXrBp0ybVpk0b63/vSQAib9iwYerEiRMeP2HkCS +; 5cuJB+wkQMFIAZgi8L0b5Fixap169fVy96PADwQB41ahSTrQlpBAgdFENIEmlR0FiBCJ498+bN043r +; 3T6+5eXlavz48Tx+jAG8+K9evdpzOoQBR6kePXrQT5hYDwWgT5xcEBiou8eDBw/U8uXL2W6BkHpA8Y +; MkURY19fkJ4/QBzYndL5745wMHDmirMim//ySCXEv0isURsHMcj4FcTPx7+gkTm6EA9Am+NIT3cdyy +; ceNGzxEAEoIPHTqkBgwYwCMAQlxI8PG1BcdPGC+bEBGI8iHa5xR64GUTdpVp9/G1BTzrCwoKdMGguw +; cj9gZY8XXs2JERWmIlFIA+hF9NEOJHJZ67Yzwe0PCTnD17Nt/+SOpJe6FHUBAN3Dmz0OPji7w/pJv0 +; 7dtX1D2QFhAUwHMf/RfdAwJ+8ODBFOzEOigAAwg/N3j7g0sIjmjcw+nA37NnTxE3AiGmmdzs34sSXT +; Zyb3lH9c3rZzrPDPlmKDCQdA+kDeSBo4fs4cOHq32YnSN7WPRBJPJ0iNgCBWBA4VcTLHy8/bk7xuPY +; Bq0Zxo0bx7c/khqS4uNrC+f7/29q9sgeou6BtIOggOPJ7B4nT55UhYWFLBgkVkABmKXwc4M8D0T8IP +; rcAwnB6NuFrv223giEmIDtXcKhvgIRYi+o3B4zZoz2l3ebCSCaO336dPoJk9ihADQg/GqCYxoc1zi9 +; upz8nbKyMjVkyBC2ByCJI+k+vrYQlp8wCQ+0hEGfRrddHwTh7t27dSsZFoiQuEi9AAzri4XIg9ir2T +; H+zp07upM8/IRtuxkICUJafHxtAd91EAcREh8ICiDqV7N92L1799SIESPoJ0xiIbUCMIovF8m+7dq1 +; 02btTvNWJyEYb3+9e/e26mYgxC9p9PG1BXz32foJk+hAUABOLvv27fOcDqF92Nq1a/VewQIREiWpE4 +; BxfMlI+J0yZYqqrKz0HAFcvXpV/3u+/RFpsNDDDvA7wO9C0r2TdiD04CgFVxf3uHDhgm7xwwIREhWp +; EYBxf9F4s+vatas6deqUZ9E/e/ZMN5RGt38bbghCGoOFHnbh10+YxA/8hHH0i/3AafKNgb6PsPyjnz +; CJgsQLQNu+cFgHLV26VL1586Z60aNfFB4EeCDw7Y/YCn187YYFIvJAUADHv+7TITT+Rl/Z7t27s2CQ +; hEpiBaDNXzoWdVFRkbp165YnGoiCkZKSEtW2bVurr5+kD/r4ygDRQPyuJN1baQeOUZMmTVJXrlzx+A +; k/evRI95BF0EDSfIgcEicApXzxCO9D6G3dutVzBAAvSZi8QyDyCIDYAH185bGi5V+o5p/9Vj9DbEHS +; PR81aAWDosDS0lKPnzBOh7Zs2aJ7yLJdDDFNYgSgpC/dDaKBxcXFno7xqBhGu4CZM2fST5jEBn18ZY +; PfXf+mf2OVCKSYbJjc3FydA3j37l3P6dC1a9e0xRwdpYhJxAtASV92feCB1rFjR3XkyBHPokdC8LZt +; 23QuiKT5EPnQxzcZ4Eh4crMfWBcNpGisH+SBDx48WB09elS9f/9euduHIX8cPWQlzYfYi1gBKOlLzh +; S83eHtz90xHg+A8+fP6yghKsckzYfIg+1dksmGVn+uOn/+c1GCLQ6sWoudOqkVK1boThHucfz4cdWr +; Vy8WDJKsEScAJX25QUCeB8zCkRDsHkgIXrVqle4hJWk+RA5s75JsdLuYZv9JlCCzkSjXJHrE4uUfPQ +; LdZgJPnz7VPWSZIkSyQYwAlPSlZgseMgjzr1u3TicBOwPd43EsgOMBJgQTU6C9Cws90gOigXmffyJK +; dEkhjPWJz0XEDwWDOAZ2BooHd+7cqXvIcj8gQRAhACV9oSZBgcjw4cM9HePRJuD27dvVzUIlzYfYBw +; s90gmigUktELGVbNcqggIzZsxQN2/e9JwOYT8YNmwYHaWIb6wWgJK+yLDAgwMtAPbu3evpEYU8QXgM +; FxQUiJoPsQf6+JLZ398DLBCJFz9rFkGB/v3761Zhbj9htI5BihBai5kQmyQdWCkAJX2BUYGE3+nTp+ +; vKYGfAT/jy5cu6iSjf/kimsNCDuNne+l+yQMRCGlrDCAosWbJEPX782BMNPHv2rO4nyAIRkglWCUBJ +; X1wcIM8DLWGwyN0DCcHr16/XrWQkzYdEDws9SF04BSKMBtqNey2ja8SoUaPU6dOna/kJz549W7Vs2V +; LMc4nEgxUCUNIXZgNY2MuXL6/VMf7EiRM6Z5D+kaQmKPRY0fJfiRIlJHpYICKPbt26qQ0bNqiXL19W +; 7weoGN6/f7/+b9wPSH3ELgAlfVk2gUU9YMAAdefOHU808N69e2rx4sW6o7yk+ZDw6Pn5T3SER5IQIf +; GBe2XYl/9ZlAhKOwgKoC3M1atXPfvBgwcP1JgxY+gnTOokNgEo6UuyFSz8vLw8tWPHDp0P6Iw3b96o +; ffv2qX79+omaDzEPCz1IUNAaiEfCckBQoE+fPmr37t3q3bt3yt0+bOPGjfQTJrWIXABK+nKkgITfCR +; MmeDrG4wjg+vXrunCEb3/pg+1diAlQIMJ2MbJAUGD+/Pnq/v37nmggCgZRQUxHKeIQmQCU9KVIBAsf +; DUHLyso8i76yslJt2bJFde3aVdR8SHDo40tMwgIReaBABL0BYRv33uUnjPZhixYt0j1kEQ10/x1Jzz +; hihkgEoKQvRDpoB7Nw4UJPx3g8AFA5jFwQvv0l+Hf/x9+wvQsJDRaIyANBAfQHfP78uScwAEepnj17 +; 6tOj+v6upGcfCUaoAlDSF5EknFwQHAG7BxxFYC6OIwJJ8yGNM7TpfxYlJohM6CcsD6QAjR8/Xl28eN +; HjJ/zkyRPdQxZ+wo19hqRnIcmcUASgpC8gqWDRtmnTRif/uo8AkBx8+PBhNXDgQCYEJwT6+JKoQTSQ +; R8JywLMerlHbt2/XRYLu0yH8O/SQReAg08+T9Hwk9WNUAEqaeFrAokazUHfHeFjKwU9y7ty5bBYqGB +; Z6kDihn7A8EBSYNWuWunXrlud0CPvBkCFDdApRkM+V9NwkHzAmACVNOm1ggXbo0EH7R7oHGofu3LlT +; 9erVS9R8CAs9iD3QT1gWyAPHCdChQ4e0gYAzEBlEihB6yNYsEPGDpOdo2slaAEqabNrBwsfbn7tjPC +; yELl26pNvIoHJM0nzSiPbxbc1CD4cDzXNUWWtZ15xE6CcsDwQFSkpKVEVFhScwcOrUKVVYWNhggUim +; SHq2ppHAAlDSJMkH8GaH6q8LFy54Fj0SgteuXasfCpLmkwachymS7yWJgrCZ+0mO6v7DHDXp5zlqXz +; NZ155EWCAiDxz5ojsEukS4zQTQPmzGjBk6Rcjkz5P03E0DgQSgpAmS2mAhog8U2gO4O8bjOAB9o4YO +; HUr/SAt+Rw44XlvR8i9EiYEwQdRvwI9zVMf/r0oADv1Jjhr1UY5a+3tZ80gqbBcjjx49eqhNmzbpPo +; HOgCDcs2eP7iHrp0AkUyQ9j5NKDoVfesGiHjx4sPYPdg/4C6NZKBKGJc1HMvU9JHt98VP6+LpY+bsq +; 4efgCECHab/IUYdbyJpTEqGfsDwQFJg2bVqt9mHYH0aOHKnbyYT1syU9q5NEDoVfusHiy8/PV6WlpZ +; 4eUWgkvXfvXt1PUNJ8JH3vjTGbPr4eJn7sFX91CUAnGrjxD7LmllToJywL5P3BQx7PfvfpEP553bp1 +; ql27dlkViGSCpOe4dHIo/gjAwp88ebKnYzyOAK5du6amTp1KP2EDZPoARDI927t8YFfTHNX7r2uLv/ +; oEoMOCX7FAxAZQIIJIdtjihZgDQg+OUg8ePPBEA9FMGgIRBYVRXIek57tEcij8iAPe7Lp06aJOnjzp +; WfQQhcgPwX+TNB8b8PvAY6GHl+m/qFv4ZSIAAQpESpvKmnMSoZ+wPNAVYvjw4Xo/QLcIZ6CLxPz58/ +; WRcZTXI+m5L4Ucij9SE0T7lixZUqtj/OnTp3VTaUQLJc0nSoI+3LAxInle0qYeJoj6OYUe2QhAd4EI +; o4HxwwIReaAIZM2aNboy2BkwE0AfQRSPmGgX4wdJ+4Ht5FD4kbpAgQhC/egQ7x44Eli2bJlq27atqP +; mESbYPNCTLS9rEw2bJrxsXfn4EIAtE7ILtYuQBv+CJEyeqy5cva/HnDDhMwWc4Ez/hMJC0T9hIDoUf +; qQ8sMAi9LVu2eI4AkBB88OBB1b9//9QuQlMPMPr4fuBYqxw15meZiz+/ApAFInZBP2FZIEUIDaLhHv +; X27Vvlbh+GPcKvn7BpJO0ftpAj6WJJPCDEP3bsWE/HeFQMl5eXa2cRvP1Jmk9QTD6sWOjhZetnVWLO +; j/gDs35ZlefnRwSC2Z/wSNgG6CcsD1jFwUce7cLcA+1jBg0apHMH47w+SXtK3FAAkozAwurUqZM6fP +; iwZ9G/ePFCbd++XeeCSJqPn3mbZnKzH4japMOmsUKPukB+4K5/Ku6AkMOxMaJ7fkWgpO8pydBPWBao +; AkYP2aNHj3r8hNE+DPZy6CEbdruYTJC018QBBSDxBRY+3v7cHeNxPAxrueLi4kT4CYf1MELUjz6+H2 +; iovUtDQDDW9Xmo9i1ukrkARORQyneVBugnLA8EBZYvX66ePn3qCQycOHFCFRQURF4gUh+S9p8ooQAk +; vkGeBxY3EoLdAwnBq1ev1o2lJc0HhP0AYnsXL34KPdz5fnACaehzEQ1E/79MRaCE7ypNsEBEHugagZ +; f/8+fPe8wEnj17pp1FTPsJZ4ukfSlsKABJILCQWrdurdauXau++uqr6kWPfz527Jg+HsARgO1zCP3h +; +P2xFgs9PoBCj2E/CXbkCw/gTH8OCj0yORJmVbCdsF2MPHr27KmLQXAM7D4d2rVrl+rcuXOsBSJ1Yf +; PeFBUUgCQrsKiHDRvm6RiPNgG3b9+ubhZq0/VG+YBhoYeXmj6+mYJoYZCft69ZVZ5fQwJw22d2f2dp +; hgUi8kBQYMaMGbpA0D1QMIKm0s2aNbPyum3ao6KEApBkDRZQ+/bttX+ku0cU3gTx9te7d28rrjFK6O +; PrpS4fXz+FHkFprECELWHshwUiskDeX1FRkTpw4IDndAitY5AilJeXZ0WBSF3EvU9FDQUgMQYKRJDz +; 4e4YDz/hK1euaJ9hvP1FeT1xPEBY6OHFdKFHUOorEAkaXSTRwgIReSAoAEepR48eeaKB586dU3379o +; 3MTzgIUe5TcUIBSIyCN7vu3burM2fOeBY9qsQ2bNigq8bC/PlxPjRY6OFl7ifBCj3WfxrO9TjRwJru +; IHF/TyQz6CcsD7z0jxw5UtuIus0E0D5szpw5kfsJByGqvTMOKACJcbBo0BwalnE1O8afOnVKjRgxwq +; ifcNwPCGxIK1r+hajNNEwy9fGtCYpDUCQS9vUhGugcCRc3ie97IsFggYg8unXrptavX6+FnzNQMYxj +; YgQMbCsQqYu499UwoAAkoYFFDbs4FIS4x71799TSpUt1R/lsPt+GhwKS1BGZkLSBhkmQ9i7ZFHoEBd +; FAp0CElcDywJqDh7YNzwCSGWgHg1Sgq1evevaDhw8faqcptJORMA9b9lcTUACSUMGCQdLvtm3bdD6g +; OyF4//79ql+/fr4/zxbY3uUDQXx8g7R3MQkigRSAssEa5JGwHBAU6NOnjy4OdJ8OoVhk06ZNOm/Q1g +; KRmti65/qBApBEAo58x48fr5uDuo8Abty4odsGNOYnbNPCZ3sXLzjyDeLjOz3m/DsIP7aCkQ8KRNgu +; RhZt27bVbcJwGuQeKBgcMGBA7H7CfpCw/9YHBSCJDLzZoSEoGkW7B/JCtm7dqnNB3H/exsVOH18vQX +; x8IRa3WiK6ipuwEjgJsEBEHhB5Q4cOVWVlZer9+/fV+wFsRhcvXqx7CkqJBgIp+7AbCkASOagMW7Bg +; gadjPB4AaA+AXBAb3/6QdM72Lh8IWuiBfoA2zaO4SZV1nE3XRILDAhF5ICiwcuVKz+kQBgIFvXr1ss +; ZPOFMk7cUUgCQWkAuCBtHXrl3zLHr0jMLDoF27dtYsaCSbS9oEwyZooUdjPr5xgEKQST+377pIcOgn +; LA8UgCBF6OLFix4/4YqKCl04ghQhSfMBEvZhCkASG1gkbdq00e0B0CLGnRB85MgRNWjQoNjbA7DQ4w +; MSCz0aY+3v2QomqSAayCNhOeC4t6CgQBcMvnnzxnM6tGPHDiv9hDPB5j2YApBETs0FghA/moW6O8bD +; Uu7WrVtq7ty5sTQLZaGHl6A+vmgGbfO8YAXHSuDkQj9heSAoMGvWLP38dw/8f+QM2uon3Bg27sUUgC +; QyGlocePvr0KGDbg3jHkgILi0t1W+GUS1U+vh6icvHNwr2NWMlcBqgn7AsEBRANfChQ4c8fsKIDCJF +; CD1kJRWIuLFpT6YAJKHjZ3HAH3LmzJmejvGwELp06ZKaOHFiqG9/9PH1YouPb5g4rWAQCZRyzSQY9B +; OWB4ICJSUl6smTJ57AAKxGkUNus59wY9iwN1MAklDIZmEgz6Nnz57q/PnznkWPhOB169apjh07Gl+M +; 9PH1YpuPb5gUN2ErmLTAdjHywEv/6NGjtehzmwlUVlbqo2IJfsINEec+TQFIjGJyYWBhI9z/7t276k +; WPYpHjx4+rYcOGGWkPQB9fL7b7+IYBKoFnW56rSMzCdjHyQJ/YjRs3qpcvX1bvBxCEe/fuVV27dhVZ +; IOIQ135NAUiMENbCgMhDNfDdu3c90UD8fzQLRS5I0M/u9cVP6ePrQoqPr2nQB5CtYNIH/YTlgaDA1K +; lT1fXr1z37wf3799WoUaPE+AnXR9T7NgUgCUxUiwLJvugLuHPnTs8RABKC8fbXt29f35/JQo8PIHIX +; tNDD1vYufkArGOQBSrpmYg76CcsCkT488/Hsd58O4Z/RUiw/P19sgYhDVHs4BSDxTVyLAgm/kyZNUs +; +fP/ccAaCZ9LRp0zJqFsr2Ll7SUOjRGKgAZiuYdEM/YXkgKABHqQcPHniigSgY7N+/v+gCEYew93IK +; QJIxNiwIvNl16dJFnThxwrPoIQo3b96sc0Hq+7ss9PAi3cfXFGwFQwALROQB29Dhw4fr/cDtJ4w8QY +; hDHBkzGlg/FICkUWxcFMj1QA5gzY7xqBRDxZj77Q8PdCR9S9qMwiQpPr4mYSsY4sACEXkgKLBmzRpd +; GewMmAnAUQodJaT5CddFGHs7BSCpF9sXBHJB+vXrp8rLyz3RQBwJLF++XOXl5dHHtwZJ8vE1SXETto +; IhH6CfsDyQAjRhwgR9BAzx54zHjx/rfy/RT7guTO7xFICkFpIWA8L7qATetGmT5wgACcGHhvxE1KYT +; Jkn08TUJ2sBMS1BeIzED/YRlgf2gsLBQewe/fftWuduHwWMYPWQlt4txMLXXUwASjaSbvy4Q4h8zZo +; zuGP/2zkl1sfe/FrXRhAly9pC751f82e7jaxK2giH1QT9heSAoMHv2bHXnzh3P6dCNGzfU4MGDxfoJ +; 1yTbfZ8CMOVIutkbA29/izr9/6I2l7AJUughxcfXJGwFQxqDfsKyQB44esgiDxARQGe8fv1aLVu2TL +; SfsJuge//QoUMpANOKpBs8E+jj64XtXfxR2pStYEjj0E9YHjj2heB7+vSpJxp48uRJfVychAIRkOne +; jwJKWKoiZYoCMGVIuqEzhe1dvAQp9MARcdILPRqCrWBIprBARB448h07dqw6d+6cx0zg2bNnavr06a +; ply5ai5tMQDe3/PXr00H1znUEBmBIk3cCZQh9fLyj0gCcvCz2CAQG44reyrpnEB9vFyAMtYdAvFsfA +; zvjmm2/U7t27dSuZJBSIgJr7P466586d65k3BWAKkHTT+oE+vl4QvQvS3oWtTz5Q3KSqGETSNZN4YY +; GIPFq3bq2jfigIcQ/4y48YMUK8n7AbaAA4phw7dkzVNSgAE4qkm9Qv9PH1EtTHN22FHo3BVjAkKCwQ +; kQXy/oqKitT+/fvVV199VS2I0Dpm7dq1uodsEgpEcOyNY+76BgVgwpB0c/qFhR5eWOhhFhz/FjeRdc +; 3EHlggIo/8/Hy1aNEi9fDhQ48wOn/+vOrbt69YP2FEObdu3erJd6QATDCSbs4gsNDDC3r0BSn0WP+p +; rHlGCazgWAlMsoF+wvKAn/DIkSPVqVOndD6gM168eKHz5uAnLGk+ffr0Ubdv31aZDApA4Ui6MYNAH1 +; 8vQX18URyCIhFJc40apxVMKY/GSZawQEQe3bp10+1RIPycAUu5gwcPqu7du1tfIAIhu3TpUo8DCgVg +; grH5ZjQBfXy9BPXxZaFHZiDyx1YwxBSIBuIZJumZm3bQDmby5MnqypUrHj9hHBEXFxdbWyDSqVMnde +; bMGeV3UAAKxMYb0DSLWvwbUQ/7MKGPb3QUN6FgJmbBs4xHwnJApK93796qtLS0lp8wWsh06NDBqmgg +; BGtlZaUKMigABWHLDRcmSKLe2/qfiXrAhwl9fKMFVcBsBUNMgwIRtouRRdu2bdW8efN0exj3uHr1qh +; o4cKA+co3z+mBlh/6F2QwKQAHEeZNFyeRmPxD1UA+bID6+qApme5fgoBXMpJ/LumYiAxaIyANVwEOG +; DNF99GCd5oxXr16pJUuW6GrbONrFwOP4wYMHKttBAWgxUd9UccH2Ll6CtndBP0BJ87SRtb/PUaM+kn +; XNRBYoEGG7GGF7VOfOasWKFbV66pWVlalevXpF5ieMHMRVq1bp4+hsx7t37ygAbSSKG8kW2N7FS9BC +; jzT7+JqErWBIFNBPWB4QX+PGjVMXLlxQf/rTn6qF1NOnT9WUKVNC9xNGJfLly5eViXHv3j3d+oYC0D +; KiupljX0zfH4Ow0OMD9PG1g33NWAlMogPRQB4JywHHvYj4ocnymzdvqgUV+gfu3LlTRwpNF4jgZ86e +; PVsfO2c70Bh6z5491b7HFICWEOdNHTUs9PBCH197KGtdJQARCZR03UQu9BOWR5s2bdTMmTPVzZs3PQ +; ILDZiHDRummjVrZuTnwJLu0KFDysR4/vy59kB2RyopAGMm7hs5aujj64U+vvZR3ITimkQP/YRlgby/ +; /v3760bRNf2EkaeHKuJsCkRGjRqlKioqlIkBl5PCwsJauYoUgDFh040cBSz08EIfX3tBJfBsttEhMU +; A/YXm0b99eO3A8fvzYI7rQmBm2bH79hGE9t2nTJo8tXdCBY+rly5frljF1iVEKwBiw8SYOExZ6eKGP +; r92gDyBbwZC4YLsYeeDId/To0Vr0Ic/OGWjQjPy9TP2ECwoKah0rBx3l5eW6hU1Dx9EUgBFi680bFn +; iArWj5F6IevmFCH18ZoBUM8gAlXTNJHvQTlgcqdTds2KBevnxZLcRQMbxv3z7tNVxfgQiihIsWLfIU +; lgQd6Fe4bds21bFjx0YLUigAI8DmGzYsen3xU/0mK+mBGyb08ZUDKoDZCobYAP2E5YEiC7SFuXbtmk +; eYoXHzmDFjavkJw1ru5MmTysR48uSJmjhxomrRokVG10oBGCI236RhwkKPDyByF7TQg+1d4oGtYIht +; 0E9YFoi89e3bV7dcQcNlZ6BYZOPGjSo/P1/n5E2YMEFX55oYR44cUT179vTVlJoCMCRsv0HDgO1dvL +; DQQy4QgDgKlnTNJNnQT1geaOOyYMECdf/+fY9Yu3LlihZs3333ncp2oD/gwoULdZ6h36pjCkAKPyOw +; 0MPL1s+qCjf8FnpsZdTJCoqb8Pid2AcLROTx5Zdf6t6Ax48f9/gJmxhwBkErGr+Vxg4UgBR+WYEHEZ +; KVJT1EowJiLtMIIH187QJtYKYxEksshQUi8sDxLCJ/JgaOklFs4hwlB70mCkCKv8AgOVnSQzMOMskB +; pI+vfSD6V9xE1jWTdEE/YTl07dpVewibGCgmQcuZmsUkQaAApPALBH18/YEefjWPhFnoYS9OKxhWAh +; PboZ+w3cyYMcPTFiboyKSdjF8oACn+fMFCj+BA7I35WZX4m0unCaspbUoBSORAP2H7gBXcgQMHlInh +; t6F0plAAUvhlzORmPxD1ULQRHAmzqbP9QPixFQyRBv2E7WD48OG1rOGCjrNnz6revXsHLvRoCApACr +; 9GoY8vSSPFTVgJTORBP+H4QBPo9evXG6v2raio0GISlcRhXC8FIMVfg7C9C0krxU2qfIElXTMhgAUi +; 0dOrVy9148YNZWJ85+oPiEbRyCOEuDR9zRSAFH51Qh9fknbQCmbSz2VdMyFu2C4mfOC8MX/+fPX69W +; uV7fjmm2903uCuXbt0g2dnfPvtt9pVBNXEpgpAAAUgxV8t6ONLSFUl8KiPZF0zITWhn3B4oA9fWVmZ +; MjGePn2qPYQR6UOxx7Rp02pFFO/du6dGjhxppAUMoACk8PNAH19Cqtj4B1YCk+RAP2GzFBcXq2fPni +; kTAyISR8huH1/8c79+/XTrF7efMP553bp1ql27dlk1gQYUgBR/GrZ3IcTLvmasBCbJAgUiOOGRtDfZ +; RuvWrdW2bdt0X75sB455lyxZoj+zPjEHobdo0SL18OFDz99FY2kIxGyqgykAUy78AAs9CKlNWesqAY +; hIoKTrJqQh6CccHAiuO3fuKBPj6tWrauDAgRlV+OLPjBgxQp08eVLnCToDDaaRfxi0P2DqBaAtN1Yc +; 0MeXkIYpbsJWMFEAsS3pepMAC0QyBwKspKTEcxQbdMDHd/PmzapDhw6+CzpQBLJ27VrdGNoZqBg+dO +; iQ6tGjh+cIORNSKwBturnigD6+hDQOKoFn07UlNHY1rbJEnPBx1ZG7pGtPAmwX0zidO3fWzZhNDBzj +; IncwmyKOFi1aqEmTJqnLly972sU8evRIjR8/Xv/3TD8rlQLQppsrLqb88s/41k1II6APIFvBhAMiq4 +; 4v9pC/q6q4RuU1n0vRAVeiid+Lb0l7V6T75JQp6sWLFyrbAaF28OBB1b17dyNtXPAZcAcpLS1Vb9++ +; rf45X3/9tdqyZUvG0cVUCUDbbq44GfTTP9MPXCa4E1I/ECTIA5R0zbYD0eF4YrsFIL5nMO0XrLyOAk +; Rfe/911fcvae+KgtzcXLV3715lYkBAzp0717iPr3Od+OyaeYnXr19XgwYNajS/MDUC0KabywYgAJ0H +; Lt7E+dZNSG3wgsRWMObY+v332f2HXvFXUwACvJyy+CY8pv/C+/1L2rvCZsiQIbUqboOO8+fPq759+4 +; bi4+uAzx48eLA6evSojgA6A42ply5dqtq0aVNvhXHiBaBNN5ZNuAUgwDEXc3AI8cJWMOaoKToaEoAO +; OILny6k5nJzLmt+/pL0rLJA7t2bNGo+ICjpwLIvPysvLy7pXX6Z06tRJrVixolZvwhMnTqiCgoI6C0 +; QSLQBtuKlspaYAdN66mYNDiBe2gskO91GjXwHovJxSgGePO+eSAtALKmjRlsXEuHv3rm7ZYsqtww/4 +; mSgyQeTR3acQLiNTp06t5SecSAEYxw0kjboEIHNwCKlNcRO2gglKQ6IjUwHIl9PsqCvnkgKwChRKzJ +; kzx+O7G3SgP9/u3btVly5djPr1+gURx549e+piELc/Ma4PRSOoanauL3ECMK4vXRoNCUDm4BDyAbSB +; wUuRpGuOG4iOYT/JTPxlIgD5chqM+nIuKQCrHDaOHDmiTAwcu06fPr1WhC1O4C4yY8YMVV5e7rlWFI +; wMHz5cNWvWLFkC0JYvXgKNCUDm4BBSBdZAcRNZ1xwnK3+XufDzKwDd0UBJ30kcNJRzmXYBOHr0aH0s +; amLAnaOwsNB3E+YowDX1799fHThwQDegdgZyFFevXp0MAWjbly6BTAUgc3BI2nFawfBFqHHQU86v+H +; MaQWf6PHJHA/k7qU0mOZdpFYBoxQIXjm+//VZlO3C8umzZMt2KJapCj6C0b99eew6jWbR7iBeANn/p +; NuNHADIHh6SZ0qZsBdMYQUQHmP5PR+t4riBfEM8ZP8+lFb+V9T2FTaY5l2kUgIjS3bp1S5kYN27c0K +; 1XcIwqZf641pEjR6rTp09X+wmLFYBSvnRb8SsAmYND0grud7aCqZ+5n/gXHMhLW/9p7c+C2C5uktkL +; KX8fH/Cbc5kmAYg+eYsXL/Y4ZgQdaBGzdetW1bFjx1gLPbKhW7duav369bpBtUgBKOnLtpWgApA5OC +; SN4J5nJbCX+nrKNQaECgRLfZ+LaCDyLnn0mxlBci7TIgAh1E6dOqVMjMePH6sJEyb48tq1FRSrwOZO +; nACU9CXbTDYCkA9ikjaKm1SJEknXHCZBjxr9iGh0Iah5JMwXTy9Bci7TIgAnTpyoKisrVbYDPr6HDx +; /WrVVsLPQICiKYYgSgpC9WAiYEII9iSFpgK5gqMukpVxeIFB5o7v/nwYkF3z2dirwEzblMgwCE9Rn6 +; 3UG4ZTtevnypFixYoFuq2F7oEQQRAlDSFyoFUwLQgX7CJMkg8lTcRNY1mwaiI5OecjWZnqVw5nPFS5 +; Ccy7QIwIEDB6r79+8rE+PSpUuqqKgoVB/fuLFeAEr6MiVhWgACvqWTpILjSNzjaS2A8tNTzgFicStP +; B4wRNOcyDQIQFmgrV6709LoLOt69e6eLJPLz8xMZ9XNjrQCU9CVKJAwByAIRklScVjD4X0nXnS1BRQ +; dy0yTN03aC5lymQQB27dpVR+tMDEQPR40aFYuPbxxYKQAlfYFSCUsAugtE2C6GJAWnFUya7BGDig5U +; pUqap80EzblMgwBEdG7mzJk6Ty/bgcbQe/fu1WJSanuXIFgnACV9eZIJWwCyQIQkjeIm6WgFE7SnXN +; BCD1I3mfr4plEA5uXlqYMHDyoTA5XCs2bN0i4hkr4DE1gjACV9aUkgCgHoQD9hkgQQ1U56K5igPeVQ +; mCBpnrYTJOcyLQJwxIgR6smTJ8rEgCtG7969E13o0RBWCEBJX1hSiFIAskCEJAGIP9zHkq7ZD0F9fH +; elLC8yTMIs9JAuANG8eMOGDdU2ZtmMN2/e6KKRtm3bJr7QoyFiF4CSvqwkEbUAdI6E6SdMpIJ7F/ew +; pGvOhGx9fIkZwi70kCwACwoKVHl5uTIx4Ac8dOhQUT6+YRGrAJT0RSWNOASgAwtEiESS2ArGpI8vCU +; a2Pr5JFoBw3li4cKGO2GU73r9/r3bs2KE6deqUqkKPhohFAEr6gpJKnALQiQamqaKSyAcpDLh3k1DY +; FJaPL/GHCR/fpArA9u3bq+PHjysTo6KiQk2ePFkfI9s417iIXABK+nKSTNwC0AE2TzwSJhLAfYp7Vv +; qLSxQ+vqRxTPn4JlEAjh8/Xj1//lyZGMeOHVO9evVKlI+vKSIVgJK+mKRjiwAExU3YLobIoLiJXCGE +; yF3QQg+2dzGHaR/fJAlA+PjimPZPf/qTyna8evVKLV68OLE+viaITABK+lLSgE0C0DkSXvFbRgOJ3S +; BiPU1g8QMLPewgDB/fpAhA+O7evXtXmRhXrlzRvsBffvmlFXOzlUgEoKQvJC3YJgAdsLmyXQyxFYmt +; YOjjGz9xtXeRIABRjbt8+XLtwZvtgBfwpk2bdP4gCz0aJ1QBKOmLSBu2CkAnGkg/YWIjuC9xj0q4Vv +; r42kGc7V1sF4BdunRR586dUybGw4cP1dixY1Pj42uC0ASgpC8hjdgsAN3RQLaLITZR2rTq3rT9vqSP +; b/wEzblMiwCcNm2aevHihcp2IF/wwIEDqnv37oz6+SQUASjpC0grEgSgEw1kgQixBdtbwUB0jPmZfw +; HAQg+z2FjoYYsAhPvGvn37lIkBATlnzpxU+viawLgAlDT5NCNFADogosECEWIDuB9tTFFAzh5y9/xu +; /vTxNUvUPr6SBCAcOB49eqRMDBwd9+3bN7U+viYwKgAlTTztSBOAgH7CxAaKm9jXCiaI6KCPr1lsL/ +; SIUwC2aNFCrV27Vn399dcq2/H27Vu1evVqlZeXx/YuWWJEAEqaMKlCogAE9BMmcWNTKxi2d7EDCYUe +; cQnAnj17qmvXrikT486dO2rEiBEs9DBE1gJQ0mTJB6QKQAcWiJC4wGZf3MSO6/C70eOImIUe5giac5 +; kGAYiCjLlz56rXr1+rbMc333yjdu3apauGWehhjqwEoKSJEi/SBaATDaSfMIka3HO4/+J6AYHogCev +; 302ehR5mCZpzmQYB2K5dO3X06FFlYjx79kxXDNPH1zyBBaCkSZLaJEEAOqA5L4+ESVQ4rWDiyEdF9C +; 7IBk8fX7NILPSISgCiFx9Em4lx4sQJVVhYSB/fkAgkACVNkNRNkgQgQIEI28WQKEDkD/dc1PdbUB9f +; FnqYQ1p7lygFIFqxbN26VX377bcq24Fj45KSEu0NzEKP8PAtACVNjtRP0gQgYIEIiYriJtFF1VjoYQ +; e2+vjaIAD79Omjbt++rUyM69evq8GDB2uLOFv3z6SQQ/GXTpIoAB1YIELCBvcYUg/C/jlBRAfy0tZ/ +; Gv61pQXkXEpt7xK2APzyyy/V0qVLdWuWbAdaxCCC2LFjRxZ6REQOxV86SbIAdEcDJW00RA5oBYO0g7 +; A+P2hPORSHQLCEOfc0ETTnMg0CsFOnTurMmTPKxEBz6PHjx+t+gTbvm0kjh8IvnSRdALqjgTwSJqbB +; ywVeMsL47KA95VjoYRYpPr5xCMDJkyeryspKle347rvv1OHDh3WvQBZ6RE8OxV86SYsAdKKBLBAhJg +; mjFQx9fO0gqYUeJgRgbm6u2r17txZu2Y6XL1+q+fPnq9atW7PQIyZyKP7SSZoEoAP9hIkp0AIG95Sp +; Fwv6+NpB0tq7mBSAgwYNUg8ePFAmxsWLF1VRUZHOIbR9r0wyORR/6SSNAhDQT5iYAC8SuJ9MNCIPIj +; oQoWJ7F3NI9vENWwDCdm3VqlVGfHzfvXun1q1bp/Lz8xn1s4Acir90klYBCFggQkxQ3CRHrfht8L8f +; 9KgRuWlxzTmJSPfxDVMAduvWTV2+fFmZGPfu3VMjR46kj69F5FD8pZM0C0AHtosh2YBK4KCFF0FFB3 +; 18zZEUH98wBCCic7NmzVKvXr1S2Q40ht6zZ4/q2rUr27tYRg7FXzqhAPwQDaSfMAkC+gDO99kLkD6+ +; dpAkH1/TAjAvL08dOnRImRjPnz9XM2bM0C4hUvbGNJFD8ZdOKAC90E+Y+AVpBH6OY+njawdpLfTIRA +; COGjVKVVRUKBPj1KlTqnfv3qpp06ai9sY0kSPpYok5KABrQz9h4gfcK8V/n9mfpY9v/KSxvUumtGzZ +; Um3atEl98803Ktvx5s0btWLFCtW2bVsWelgOBWBKoQCsG/oJk0xBNfmQv2v4z9DH1w5Y6NEwN2/eVC +; YGPmfo0KH08RUCBWBKoQBsGBaIkEyAAKzvPqGPb/wEzblMG9mO9+/fq+3bt2t7OBZ6yIECMKVQADYO +; 28WQxsARcM2+kvTxtYO0+PjGLQCfPHmiJk2apI+RJe2BhAIwtVAAZg79hEl9zPqlN2+UPr52kCYf3z +; gF4NGjR1WvXr3o4ysUCsCUQgHoD/oJk7qY/49VbYQQuQta6MH2LuZgoUc0AhD9ARctWkQfX+FQAKYQ +; eDAO//h/EiXAbIF+wsTNmv+eo2b8goUeNhAk55L4F4BwBhkwYAB9fBMABWCKgAXP/Pnz1cOHD9WYX/ +; 7PooSXTdBPmDgg+udX/KHQYyujycZIu49vVALwq6++Uhs3blTt27dnoUdCoABMCV26dNHd3V+/fq0X +; 85hP/oUo0WUbLBAhoPQLfwKQPr5mYXuXaATggwcP1JgxY1SLFi1E7XukYSgAEw7e1CZOnKj7MzlNPv +; /0pz99HwGkADQB28WkG0SCMxWA9PE1R9CcS+JPAGKv2L9/v+revTsLPRIIBWCCyc3NVZs3b1YvXrxQ +; 3333XXUY/8yZM2rEz/+ZKKFlMywQSS8Q/40JQBZ6mIWFHtEIQOwbs2fPpo9vgqEATCiDBw9Wly5dUl +; 9//bVezBCAMOZes2aNrtxiFbB5WCCSThoSIyhMkDQX26GPbzQC8OzZs6pPnz708U04FIAJA804ly5d +; qptzInyPgS7t165d00bfzp+jAAwHFoikD7iB1NxQ6eNrFhZ6RCMA3759q1atWqXy8vLY3iUFUAAmiB +; 49eqiysjK9iJ3x8uVLtWvXLl255f6zFIDhQT/hdDH2772bKdu7mIWFHtEIwNu3b6vhw4frbhFS9jyS +; HRSACQBh+mnTpqk7d+6ob7/9Vi9m/O/du3fVjBkz6gzjUwCGDwtE0oFTjID2Liz0MAcKPcb8TJaQkk +; ppaanuFMH2LumCAlA4+fn5evEi0ucO4x8/flz17Nmz3r9HARgNiAaiV5ykjZf4Y+o/VB1P0sfXHOs/ +; rRLUkkSUm8F/m6O6Cbp++vimEwpAwSCn7+rVqzrHzynZr6ioUCUlJY0uaArAaFnwKx4JJxVW+JpFen +; uXOZ/kqIW/knXNEvY7Yh4KQIGgiheJuqjqddq7oNoXVb9Dhw7N6DMoAKMHBSJsF0NI3Uhv71L0oyqH +; FxSBFfyVrGu3fc8j4UABKAyU5p8+fVq9e/euur0L+jVt3bpVtW3bNuPPoQCMBxaIEFIb6T6+yPeF8A +; Mlv5F17YAVv+mEAlAIzZo1U3PmzFH379/3FHrA4WPSpEm+k3cpAOOFBSKEVB2fS27vgkgf8hUd8QcQ +; CZQ0B0cAUgSmDwpAAXTq1Enb8Tg+vhhv3rxRhw8f1pVbQT6TAjB+6CdM0gwqpiWJpJqg/Y9b+EmN/r +; kFIEVguqAAtBgsxnHjxqkbN254fHwfPXqkFixYkFW/JgpAe0A0kEfCJC0kwccXRR41xZ/U6F9NAUgR +; mB4oAC0FPr4bN25UlZWVHh/fc+fOqaKioqw/nwLQLugnTNJAkgo9aoKjYElzaUgAUgimAwpACxk4cK +; C6cOGCx8cXQnDdunWqTZs2Rn4GBaCd0E+YJBXpPr5o71KX8JMe/WtIAFIEJhsKQIto0aKFWrRokXr8 +; +HG1jy+Ofq9fv67Gjh1r9GdRANoL/YRJkpDu4wthV7PQI0nRv8YEIEVgcqEAtIRu3bqpo0eP6uIOZ7 +; x69Urt3btXdezY0fjPowC0G7aLIUlAuo8vchUbEn4OI34qa15+BSBFYDKhAIyZL774Qk2ZMkUbcbvb +; u9y7d0/NmjVLffnll6H8XApAGbBdDJGIdB9f2LihojcT8Sc9+pepAKQITB4UgDHSrl07tX37do+PLx +; o8nzx5UhUWFob6sykA5UA/YSIJFElI9/Et/SIz8ZeE6J8fAUgRmCwoAGNi+PDh6sqVKx4f36dPn6oV +; K1aoVq1ahf7zKQDlQT9hYjvSCz0Q+ctU+AGIXUnzMyEAKQKTAwVgxEDcQeRB7DmFHhCBEIMQhVFdBw +; WgTOgnTGxEensXh/r6+9UHmkFLmp8pAUgRmAwoACMEx7o43nV8fDFw/ItjYBwHR3ktFIByYYEIsQnp +; hR4OaY3+BRWAFIHyoQCMABRyoKADhR3uQg8UfqAABIUgUV8TBaB8WCBC4gSFHsN+IkvoNERjff6SGv +; 3LRgBSBMqGAjBkOnTooFu5oKWL28cXLV/Q+iWu66IATAb0EyZxIN3HtyZ+o38oEpE0vzAFIEWgXCgA +; QwTNm9HE2e3jiybPaPaMps9xXhsFYLKgnzCJCuk+vnWR5uifCQFIESgTCsAQgF0bbNvcPr6wdYO9G2 +; zebLhGCsDkQT9hEiZJKfSoSdqjf6YEIEWgPCgADVNUVKTOnTunvvrqK4+P78aNG1Vubq4110kBmFzo +; J0xMM/cTWYLGD2mP/pkUgBSBsqAANETz5s3VggUL1KNHjzw+vjdu3FDjxo2zbmFQACYb+gkTE0j38W +; 0MRv/MC0CKQDlQABqgS5cu6vDhwx4f39evX6v9+/erTp06WXnNFIDJhwUiJBuS0t6lIZA7m/boXxgC +; kCJQBhSAWfD555+rSZMmqZs3b3rauzx48EDNmTNHNWvWzNprpwBMD2wXQ/wg3cc3Uxj9owBMOxSAAW +; nbtq3aunWrevHiRXWhBxo8nz59WvXp08f666cATBcsECGZIN3H1w9+o39JrH4OUwBSBNoPBWAAhgwZ +; oi5duqQre51Cj+fPn6tVq1ap1q1bi5gDBWA6YYEIqQ/pPr5+YPQvGgFIEWg3FIA+aNmypSopKVEVFR +; UeH9+rV6+qUaNGiZoLBWB6YYEIcZP0Qo+6YPQvOgFIEWgvFIAZ0rNnT3X8+HH19u1bj49vaWmpys/P +; FzUXQAGYbugnTEAaCj1qgugfjroZ/YtOAFIE2gkFYCM0bdpUzZgxQ929e9dT6HHnzh01ffp0/d8lzc +; eBApAAFoikk6T5+PrBb/RvWgqOxqMQgBSB9kEB2ADt27fXET5E+pyBCGBZWZnq0aOHqLnUhAKQOCAa +; uPEPsgQMCU7SfHz9wOgfBSD5AAVgPSCn79q1azrHz/HxRe7f0qVLdS6gpLnUBQUgqcmCX/FIOOkkPZ +; etMRj9y4lVAFIE2gUFYA1Qxbt69Wpd1ev28UXVL6p/Jc2lISgASV2gQITtYpJHUn18/RAk+lfwV7Lm +; GJQoBSBFoD1QALro27evOnPmjMfHF33+Nm/erPv+SZpLY1AAkvrAkfCK3zIamBSS7OPrB7h4+In+zU +; nR90YBmE4oAL8Hjh1z587VDh5uH184fEycOFE7fkiaTyZQAJLGcI7LJIkd8oE0tnepD0b/GiZqAUgR +; aAepF4CdO3dWBw4c0N69bh/fQ4cOaY9fSXPxAwUgyQT6Ccskje1dGsJv9G/hr2TNL1viEIAUgfGTWg +; GIqN748eNVeXm5jvY5hR4PHz5U8+fPV82bNxc1H79QABI/sF2MDNDeJe2FHjXxG/0DaYr+xSkAKQLj +; JZUCMDc3V23atElVVlZWF3og7+/s2bOqqKhI1FyCQgFI/EI/YbthoUfdMPrXOBSA6SR1AnDQoEHq4s +; WLtXx8165dK8bH18j3QAFIAkI/YftIk4+vXxj9a5w4BSBFYHzkQPSk4RfQokULtXjxYvXkyROPjy96 +; /Y0ZM0bUXExAAUiygX7CdsBCj4Zh9C8zKADTSc6FCxe0z20SK10dunfvro4dO6bevHlTXejx6tUrtX +; v3btWhQwdRczEFBSDJFvoJxwsLPRqH0b/MiFsAUgTGQw7EEKzOZs2aJdbXtj4wn6lTp6rbt297fHzv +; 3bunZs6cqb788ktR8zEJBSAxBQtEogWFHmN+JktgxAGjf5ljgwCkCIyeHOUaaIfSsWPHRPwi2rVrp3 +; bs2FHLx/fEiROqoKBA1FzCgAKQmIR+wtGAiFb3H8oSF3HB6F/mUACmkxw0O3YqYTEeP36sfXC/+OIL +; URNxM2LECHXlyhWPj+/Tp0/V8uXLE+HjawIKQBIG9BMODxZ6ZA6jf/6wRQBSBEZLzsCBA9Xhw4er7c +; +c4oiNGzfqdimSfiGtWrVSK1euVM+ePfP4+F6+fFkNGzZMzDyigAKQhAX9hM3C9i7+YfTPHxSA6US3 +; gcFxKYQTmiC7x/Xr17U/roRoYO/evdWpU6fUu3fvqq8fPr7btm1TeXl51l9/1FAAkjBhgYgZWOjhH7 +; /Rv5LfyJpfGNgkACkCo6O6DyAKItAO5ezZs9VHp44t2sKFC611xsB1z549W92/f99T6HHr1i01efJk +; 0UfZYUIBSKKABSLBQKHHsJ/IEhG2wOiff2wTgBSB0VCrEXS3bt3Uli1btEuGe5SVlWlvXJt+MShY2b +; t3r27p4gy0ejly5Ijq2rWrNddpIxSAJCroJ+yPlb+TJR5sgtG/YFAAppM6nUDQNHnGjBn6CNhpmoyB +; 3LoJEybE3i4GN0dxcbG6ceOGx8cXBSyIVuL647w+CVAAkqhBNJBHwg1DH9/s8Bv9K/qRrPmFhY0CkC +; IwfBq0goMv7v79+z0NlHG8ivYqyBuM4xfUpk0btX79eo+PLwo9zp8/rwYMGBD59UglrQIQBQqSrjdp +; 0E+4bljokT1+o3+rGGmthgIwnTTqBYxK4CVLlujmye5x584dLbiizLHDz4PQcyqWIQAhBDds2KCFYZ +; RfnHTSIgARdVrx2xxV2rRqo0UECon1kuaQROgn/IG5n8gSC7bC6F9wbBWAFIHhkpPJH4LIGz58uDp5 +; 8qSOtrkbK69YsSL03nooQMHR7qNHj6qPpHH0iyNgHAXzJvHPoI+SKwBnf9J4BSoEYXETWfNKGmn3E6 +; aPrzn8Rv/WfyprfmFDAZhOcvz84U6dOuloG5oquwcqh3v06BGKnzCKOdCnsKaP7759+3QRSFRfVJLA +; okqSAISQQETJifJlCgQixKKkuSaNtBaIsL2LWfxG/0b8VNb8wsZmAUgRGB45fv8ConGTJk3SThtO2x +; Wn5x4KR0wViCDqiJ+Ddi7u9i5o94K2L2n28c0GZ0FJF4DFTaqEg4kWI/gcCBFJ808aaWkXQx9f80DM +; MfqXHRSA6SQn6F8sLCxUu3bt8rRgQU4eInMdOnTI6peGxs1o4AxR6RR6oMEzGj2j4XOcX5hk3AsqCR +; FAk7Zj2BhYIBIvSfcTpo9vODD6lz0UgOkkJ5u/3Lp1azV//nx1+/Ztj58wHEXgxxukQGTo0KHaus3J +; NcTnov0MnEpg9Rb3FyaVmgsqKUfAEG1+j37rwykQYTQwXpLoJ0wf33DwG/2DWJQ0v6iwXQBSBIZDTr +; YfgLy/wYMHq6NHj3r8hCHg1q1bp6tzM/nloZBk2bJlOr/QKfSAIwmOmiEmbfviJFHXYkpSDqBp2zEW +; iMSPSWEfJ2zvEi6M/pmBAjCd5Jj6oPz8fLV69WrdjNk9IOBwbNtQgUivXr3UiRMndFWxM16+fKl27t +; ypP9emL0wa9S2mJFYBm8wjg5hEJErS/JOGdD9hFnqEC6N/5pAgACkCzZNj8sNQmIG2LOjV5zh0OFW7 +; OCpu1qxZrT+PwpG7d+96Cj3QY3Dq1KmxO45Ip6GFlNQ2MKarSpGTxiPheJFWIEIf32hAMYcfAYhWMZ +; LmFyUSxB8FoHlywvhQtIRxijjcA8fEaCWDX2T79u3V7t27PUUkiAAeO3ZM/33bvihpNLaQktwH0BEN +; JgtE2C4mXqS0i6GPbzQw+mcW24UfRWA45IT1wcjpmzVrliovL/f4CVdUVOij4mvXrukcPwz89ydPnm +; jHEfr4miHtAhAUNzFnO8YCETuw2U+YPr7RweifWSQIPwpA8+SE+eH4ZfXv318dOHDAk9+Hyl63j+/F +; ixfVoEGDrPtypJLJIkqDAAQQbCZtx1CYwHYx8WJS2JuAhR7R4jf6V/qFrPnFge2ijwIwHHKi+CHo64 +; eegU7Ezy0Ejx8/rtq1a2fVlyKZTBdRWgSgg0nbMfoJx49pYR8U+vhGD6N/5rFd9FEEhkPoAhDOIfPm +; zdO9Ad1Hwc6AxVtJSQmPfg3gZwGlTQA6osFkHhmigTwSjhccCZsS9n6jfvTxjZ6iHzH6Fwa2Cz4KwH +; AIVQB27txZHTx4UL1+/bpa8MHR4+bNm7q5s3ucPn1adevWLRQ/4TTgdwGlUQC6RYPJdjEsEImXqAtE +; 2N4lPhj9CwebxR4FYHiEIgAh4iZMmKALQJx2MIj+PXr0SC1atEi1bdtWt3lBIYjbT7iyspLtXwIQZA +; GlWQA6osFkHhn9hOMn7HYxaO/CQo/4YPQvPGwWexSB4WFcAObm5qrNmzd7fHzhEHLmzBnVr18/z5/t +; 06eP2rNnjydCCKGI9jBoE8NfcmZQAAbHZB4ZNh0WiMSLaWHvPvJloUe8rPodo39hYbPQowAMD6MCEJ +; ZwqOh1+/g+f/5crVmzRvsG1/V3YBW3cOFC3fzZPe7fv699gYP4CaeJoIuHAvADYRSIMBoYLyaFPX18 +; 44fRv3CxWehRAIaHEQGInn9Lly7VvfzcPr444h09enSjfx8iD2KvrKzM4yeMf3bEI3/htclm8VAAeq +; GfcPLIVtiz0MMeSn7jTwDyqN4ftoo8isBwyVoAwrUDws3d5w/uHmj7gmNcP5/VoUMHtXbtWi0k3QNR +; xYKCAhaIuMh24VAA1g39hJNFUGHPQg97KPgrRv/CxlaBRwEYLoEFIAo1pk2bpo9u3T6+8PWFv2/QQg +; 74BY8fP16LPref8MuXL9WcOXO0f3DcX1rcmFg4FIANiwZ4AJsQgQCfNeTvZH0HSSNTYY9CjzE/k7V5 +; Jx1G/8LHVoFHARgugQRgfn6+Ki0t1aLMGYgAoqlzr169jFwYPmfnzp2en4Fx6NAh1bFjx1T/8ikAow +; HROxNHwtiQBv6trLknkcaEPfxiu/9Q1saddBj9iwYbxR0FYPj4FoAjR45UV69e9fj4wt8XzZyRC2jy +; 4vB5iPqhb6BTUYyBI+IxY8aksl2MqYVDAZgZyCMLWlXqrhylALSHuoQ9Cz3sZOGv/AnAafw9BsJGcU +; cRGD4ZC8BWrVqpVatW6apet4/vpUuXdAFHWBeIX/DAgQN15A9NpJ0BAbpp0ybdUzAtN4HJRUMBmDlB +; 8shqCgoKQLtwhD3bu9hLtx/6j/4hYihpjrZgm6ijAIyGjAQg+vWdOnWqWoBBAKLP39atW7UAi+JC4R +; e8YsUKbSnnHjdu3FB9+/ZNfLsY04uGAtA/meSR1Vc5SgFoHxD2UjZom8n7f6ow/bmM/kWHjcKOAjB8 +; GhSAKMiYPXu27snnLvS4deuWmjRpUuRVuSgAwdHv2bNnq4+gMdBIevHixYn2E6YAtEc01Gc71lDlKA +; WgnUjYnCXQ/v/NUbn/d9X/mvg8Rv+ixUZhRwEYPvUKwE6dOql9+/Z5XDrevHmjDh8+rLp27RrrRcMz +; eMuWLfo42j1OnDihunTpkrh2MWEsGgrA7EC0wTkSzqRylALQTiRszpJo/R+rhGC2nzPnE38CENHCOO +; abFGwTdRSB0ZBTl9gYN26cPlqt6eO7YMEC1bx5cysuHNG+6dOnq+vXr3v8hCEKEZ1MSoFIWAuGAjB7 +; EA1c/N8yqxylALQTCZuzNJr9n1UEjQYy+hc9tgk6CsBo8AhA2LJt2LBBVVZWenx8z507p/r372/lBO +; AvjEglopPOgCBECxm0q5F+c1AA2k1hhgUEFIB2YvvGLJWW/1eO+t3/Hiw3ENF1Rv+ixTZBRwEYDdUC +; EJW258+f9/j4QgiuW7dOC0ObJ5Gbm6uWLFmi7t275zkSRlPqQYMGiS0QCXPBUACagQJQNrZvzJLBcf +; An/7YqGujn76Efox8ByOhf9tgm6CgAoyEHR6mLFi1Sjx8/rvbxxdEvjlbHjh0r5suFyBs+fLjOA3RE +; LAYql1euXKnb2Ei6UcJeMBSAZqAAlI3tG7N0kA/4m/81R/3xB5kdCY/9e0b/4sAmMUcBGB05R48e9R +; yfwsd379692m1D0kQcULyyfv163ZzaPRDd7Nmzp5gCEQpAGVAAysb2jTkJQPhBAEIINnYkzOhfPNgk +; 5igCoyPH3d4FR6izZs0S77eLQhUUgly+fNnjJ4zehTNnzrR+flEsFgpAM1AAysb2jTlJ4Ej4v/27qv +; zAuv774L9l9C8ubBJyFIDRkeMck548eVIVFhaKuvjGwHzgWYyopnvs37/fWj/hqBYLBaAZKABlY/vG +; nDQQDfz8/8hRTf9D7f9W8htG/+LCFhFHARgtOc+ePdMOG8iRk3ThmYJ5zZs3T92+fdvjJwxHEfga29 +; YuhgJQFhSAsrF9Y04qiAZCCDr/32/rF0b/zGKTkKMAjI4cFE5IuuAgIO9v8ODBCvmOaGvjDBSLIF8Q +; VcQ23EBRLhYKQDNQAMrG9o05ySAaCCGIf/bb+oXRP7PYIuIoAKMl549//KNKC+gLuGrVKt3U2j2uXr +; 2q/Y7jbBcT9WKhADQDBaBsbN+Y0wIigBCBaOrM6F/02CLiKACjJVUCEKAABO1t0Ny6pp8wnE7gfxzH +; dUW9WCgAzUABKBvbN+a0gUKQ9Z8y+hc1tog4isBoSZ0AdOjRo4fatm2brgx2j2PHjqnOnTtH2i4mjo +; VCAWgGCkDZ2L4xpxFEA+vzAkahiKS5SMEWAUcBGC2pFYCgZcuWui0MfI+dJtgYT58+VePHj4+sQCSO +; hUIBaAYKQNnYvjGnGUQDa/YFLPqRrDlIwRYBRwEYLakWgAA3TVFRkTpw4IB6+/ZttQhE/0BECPPy8k +; K9seJaKBSAZqAAlI3tG3PaQTTQaQ/D6F94xC3cKADjIfUC0KFt27aqpKRE3b9/33MkjPYxAwYMCKVA +; JM6FQgFoBlsF4KiPctSCX0X7MyVi+8ZMqpj4MaN/YWKDeKMAjB4KQBc48kVvwFOnTnn8hBEZXLZsmT +; 4yNnmTxblQKADNYKMARDVlWescdTo3R5U2zVHFTaL72dKwfWMmJAriFm4UgPFAAVgHKALZuHGjQpNs +; 9zhz5ozq3r27kQKRuBcKBaAZbBOAa39fJfzcQAzO/iS670QStm/MhESBDeKNAjB6KADrAX7CU6ZM0T +; 0CHb9kjMrKSjVt2rSsC0TiXigUgGawRQBO+nmOOtyitvhzA3GIo+GovyObsX1jJiQKbBBvFIDRQwHY +; CGgQvXv3bt0n0BmwlNuzZ49q3759oJvOhoVCAWgGGwTgit82LPzcIJkeYjGO78pGbN+YCYkCG/YkCs +; DooQDMgDZt2qiFCxeqO3fuePyEHzx4oIYNG+a7QMSGhUIBaIY4BSCieRB0mYo/95Hwkl8zGghs35gJ +; iQIb9iQKwOihAMwQ5P0NGTJElZWVefyE8c9r167VIjGTG9CWhUIBaIa4BCAqfP0Kv5qwQMT+jZmQKL +; BlX6IAjBYKQJ/g2HfNmjXq8ePHngKRy5cvq8LCwkajgbYsFApAM8QhABG5ayzfz080MM3tYmzfmAmJ +; Alv2JQrAaKEADAD8guEUcuHCBd0w2hkvX75Uc+fO1X7Ddf09mxYKBaAZ4ooAQgRu/IMZEQjQOibu7z +; IObN+YCYkCm/YmCsDooADMgp49e6odO3Zo4ecehw8fVp06dap1Q9q0UCgAzRB3EQiid07Pv2yPg+P6 +; DuPE9o2ZkCiwaW+iAIwOCsAsQXPoOXPmqPLyco+f8JMnT9TYsWOr28XYtlAoAM1gQxUwqnq3fZa9CE +; zjUbDtGzMhUWDb/kQBGA0UgAbAjTdw4EB16NAh9e7du2oR+P79e7VlyxZtM4ciEpsWCgWgGWzpA4gj +; YfT5yyYaiLzCtFUG274xExIFNu1NFIDRQQFokLy8PLV8+XLdHsY9bt68qYqKinSBiC0LhQLQDLY5gS +; CXL0hrGAf0FIzqu7MB2zdmQqLAln2JAjBaKAANgyPf0aNHa9s4RACd8ebNG7VkyRLVokULKxYKBaAZ +; bPQCdqKBQauC09Qo2vaNmZAosGFPogCMHgrAkOjatavavHmzev78uScaePLkSf3f4j4SpgA0g40C0A +; HRwCBHwsgnjPpa48L2jVkyA38s63rTjA3ijQIweigAQwTRPvgGX7t2zeMnDFE4efJk3S4mroVCAWgG +; mwUgQDQwSIFIWtrC2L4xS6bXX+Wopb+u+l9J151GbBBvFIDRQwEYIs5N2bdvX7V37159DOwMVAyXlp +; bqxtJxRAMpAM1guwB0gPWbn2hgWgpCbN+YJTPnkxy1/fMctemP/K5tJ27hRgEYDxSAIeK+MXNzc9Xi +; xYvV3bt3PUfC9+7dU4MHD9a5g1EuFApAM0gRgAC5fX4KRNJQEGL7xiyVbv+lSvy5mf6Lqn8vaR5pIU +; 7RRgEYHxSAIVHXzQmRN3z4cHX8+HH19ddfV4tAtI5ZtWqVat26dWQLhQLQDJIEIPDTLgZ/JulRQNs3 +; Zqk40b+arPxdjur7N7LmkgbiEmwUgPFCARgSDd2kHTt2VOvWrVMVFRWeaCCs5Xr16hVJuxgKQDNIE4 +; AOyPHLxE8YYtGm6zaN7RuzRBDl2/F5FXWJQBwJj2vCaKBNRC3UKADtgAIwJBq7UZs3b64mTpyoLl26 +; VMtPeNasWaEXiFAAmkGqAASZ+gknuSDE9o1ZIoj+OQKwISHIAhF7iFqoUQDaAQVgCPi5YQsKCnQxyK +; tXrzzRwAMHDuhIYVgFIhSAZpAsAB0a8xNOsk+w7RuzNNzRv0xEII6EJc0vqUQhzqJAkk6wAQrAEPB7 +; 0yL3b+7cuerWrVvqu+++qxaBjx490k2lwygQoQA0QxIEIGjMTxhVxDZff1Bs35ilMeO/5qidX2QuAh +; GFljS/pBKFOIsCSTrBBigAQyDIjYtI36BBg9SRI0fUV199VS0CUSyyYcMG437CFIBmSIoABA0ViCS1 +; IMT2jVkSiP5B/DnUJwJ3uHIBJc0vyUQt1CgA7YAC0DDZ3sD5+flq5cqV6uHDh54j4evXr+t+gqYKRC +; gAzZAkAehQX4FIEgtCbN+YJTH5514B2JgQRFsYSfNLMlGKNApAe6AANIyJm7hZs2Zq7Nix6ty5cx4/ +; 4devX6uFCxfqAhIKQDtIogAEdfkJJ9En2PaNWQqI/pV+UbcArE8EsgDEHqIWahR/dkABaBDTN3T37t +; 3V1q1bVWVlpScaWFZWprp06ZLVkTAFoBmSKgAdavoJJ80n2PaNWQpTfl4lABsSgW4hiEphSfNLOnEI +; NgrA+KEANEgYN3XLli3VzJkz1Y0bN7R9nDOePXumJkyYELhdDAWgGZIuAEFNP+HZn8i6/oawfWOWAK +; J/Gz79IAAbE4IQgEloBt39hzlq/ffz3vr92uj918E/xwbiEGwUgPFDAWiQMG/uoqIitX//fvX27dtq +; Efjtt9+qHTt2qHbt2vmOBlIAmiENAtDB8RNOkk+w7RuzBCZ8XFv8NSQCIZokza8uhn1/7xxr9eGlCP +; 885mey5uAmTuFGARgfFICGiOIGRyXw0qVLtX+we9y5c0cNGDDAV7sYCkAzpEkAAsdPOCk+wbZvzLbT +; 7fso2OY/5qg9X+aoXU0zE4KjBQslgBeh+toloa8hIoOS5gPiFG4UgPFBAWiIqG5yiLwRI0aokydPev +; yEERlcvny5atWqFQVghKRNAAJE/yAAi5vIuu66sH1jth1EvSD+HHY3IgK3fCZrfm4G/DhHHWjeuHMO +; hLC0aGDc4o0CMB4oAA0R9c3euXNn3R/w6dOnnmjg2bNnVY8ePRo9EqYANEMaBaBDEo6Bbd+YbceJ/n +; lEIKKB9YjA8U1kzc8BLWsaE351NU+XEg2MW7xRAMYDBaAB4rrhW7RooSZPnqyuXLmi8wGd8eLFCzVj +; xowGC0QoAM2QZgGYBGzfmG2mZvSvJjWPhFEsgYIRSXOEgNv6mX/x544GInJo+zzjFG4Uf/FBAWiAuG +; /+3r17q927d3v8hGEpt3fvXtWhQ4c6o4EUgGagAJSN7RuzzdQV/asVDXSJwHnCWr9M/Di48HODAhHb +; m17HvYdRAMYDBaABbFgAbdq0UQsWLFC3b9/2+AnDUWT48OG1CkQoAM1AASgbmzdlm2ks+ldXNLBAUO +; NnFHOYEH9OYYjt87VhD6MAjB4KQAPYsghgEzdkyBB17Ngxj58w/nndunUqNze3OhpIAWgGCkDZ2L4x +; 20om0T83C34lY16ZFnpkCqKIEuZtyx5GARgtFIBZYuNiaN++vVq9erV6/Pixp0AEuYKFhYVaKFIAmo +; ECUDa2b8w24jf6ByRE/+Z+Yk74IeIpqTm0jfsYBWD4UABmia0LAn7C48aNU+fPn1fffPNNtQhEnuD8 +; +fPVYApAI1AAysb2jdlG/Eb/lv7a7vkg6gfBZkr8zRVoc2frPkbxFy4UgFli++Lo2bOn2r59u64Mdo +; +R//DPRW3UtkIBKBvbN2bbGP5T/9G/fj+ydz5B2rtIr/itC6QH2b6XUQCahwIwC6QsEDSHnj17tiov +; L6/2Ex7zyb8QtVHbCgWgbGzfmG3Db/Rv3e/tnAfau5gs9FhieZSzMdAxol+/fmL2NApAM1AAZoGkRY +; LiD9jFHTx4ULuG3Dq1S03+w78WtVnbCAWgbGzfmG0iSPRv0N/aNw9E6dw+vtm2eJHsAdzrJ/9Cndmz +; WveRvXbtmpo+fbpq2bKlqL1NkmawDQrALJC0SBzatWunli1bph48eKDevXquVvT9RNSGbRsUgLKxfY +; O2CUTzpEf/hv3EnPhDg2iJvr8O+X/3v6jD+/d4UoOeP3+uNm/erLp16yZiP5OkF2yEAjALJCyQuoBD +; yKhRo9Tp06fV+/fv1dGVY3kkHBAKQNnYvknbAiJ5fqN/iBjaOBcTRR+2N3ZujJa/+E96L4Cb1JIlS9 +; SbN2+qRSD2hDNnzqjRo0c36CZlA5L0go1QAGaBzQsjE7p27ao2bdqknj17ph5eO6lmtf4PojZvG6AA +; lI3tG7Ut+I3+IVfQ5vkgcoe8vSCFHpLau9Sk3d/8S/Xlb//Bsw+gLVhRUZHOEXcPmAgsX75cnxrZuo +; dJ0gs2QgEYEFsXhF/wBjht2jSd//HmRYXaPrkjo4E+oACUje0btg2gitdv9E9KXpyfI2HphR5t/v7f +; 1bsPIEc8Ly9PH/8iAuiMd+/eqUOHDqmBAwfWaSkaN5I0g41QAAbEtoWQLX379tWVYK9fv2aBiA8oAG +; Vj+6ZtA+jj50f87fhc1vwQDVz/acOFHsME3ysd/sufqWa//iijfQCWoWPHjlVPnjypFoHoHHHz5k01 +; Z84c3VHClj1Lkl6wFQrAgNiyCEyCXlCLFi1Sd+/e1QUiG0Y0FbWZxwEFoGxs37zjJsnRv5ogr69mNF +; CCj29D5H70l773AUT6OnfurA4fPuw5En758qXasWOH6tWrlxX7lSS9YCsUgAGxYQGEAd4Ahw0bpo4f +; P649hC8fWM0j4QagAJSN7Rt43CQ9+lcTd4GIFB/f+mjxy/+S1V6AApC5c+dq4ecMuEpduHBBjR8/Xj +; Vv3jzWvUqSXrAVCsCAxHnjR0HHjh3V2rVr9VFA5cNytoupBwpA2di+iccJ/Hv9Rv8mCBdNAEfC0gs9 +; vvjv/2hkH0CBCPzjL1265IkGYl9Ys2aN6tChQyz7kyStYDMUgAGI44aPA7zhTZgwQS/+15VP1L55/R +; gNrAEFoGxs38zjZMGv0hX9SwKtPv6B8X0AR8Jt2rTRAQGcCjkD/1xWVqaGDh2qhWKUe5MkvWAzFIAB +; iPJGt4GCggK1c+dOfRSAAhG2i/kABaBsbN/Q4yJI9G/qP8iaY5Koq72LaZz0oPv371eLwO+++07duX +; NHLVy4UIvEqPYkSXrBZigAAxDVTW4TqP5CPgiqwd6+fKbbxUja6MOCAlA2tm/scREk+tdNsCuGZBpq +; 72IaRANx7Ltnzx4t/pyB7hH4d3369InkOiTpBZuhAAxAFDe4jWDxDxo0SFeHoT8U28VQAErH9s09Di +; DkGP2zH7R3ybbQIygoEEH/2MrKymoRCD/hq1evqilTpuj+smH9bElawXYoAAMQ5UKzkfz8fLVixQrd +; KT7tfsIUgLKxfZOPgyDRPxwZS5qjdEwWegQFAYHu3btr2zj3gLPUxo0bVZcuXUL5uZK0gu1QAAYg6o +; VmI82aNVNjxoxRZ8+eTbWfMAWgbGzf6KMmSPQPglHSHKXj+PjaQsuWLdWyZcvU27dvq0Xg119/rU6d +; OqVGjhxp3E9YklawHQrAAMS10GwEb4BbtmzRRwFp9BOmAJSN7Zt91PiN/gFG/6IhikKPoKBAZMCAAe +; r27dueaCAKRkpKSrTNnImfI0knSIAC0CdxLzQbwRvgjBkz1PXr19WbF09T5SdMASgb2zf9KGH0z16i +; LPQICo6E27Vrp7Zt26YbRjsDkcEDBw6o/v37Z/0zJGkFCVAA+sSGhWYrRUVFat++ferNmzepKRDJRA +; CiseyM/yprXmlBwuYfFYz+2QcKPZr/449E7QOIBsIp5OnTpx4/4Rs3bqhZs2bpgEGQz5WkE6RAAegT +; 2xabbbRt21YtWbJE3bt3LxV+wo0JQNhJOb6iG/+Qo0Z9JGt+SUeCCIgCRv/sI+/Hf64+/8PvRD3/HR +; w/4aNHj3qOhF+8eKEjhD169PD9mZJ0ghQoAH1i42KzDbwBjhgxQp08eVInA5/ZOiexR8INCUAYybuN +; 5cG+Zjlq9iey5phkJAiBKJj9S0b/bMK2Qo+gwE1qwYIF6tWrV9UiEEWD58+fV8XFxbqYMNPPkqQTpE +; AB6BObF5tt4A1w/fr1qqKiQheIJLFdTF0CEIbyB5rXFn8OZa1z1JJfMxpoA1IEQZgEif4t/bWsOUrB +; hvYupoFNXO/evXWPQPd49OiRWrVqlWrfvn2jnyFJI0iCAtAnEhacTaAh6OTJk9WVK1fU68qKxBWI1B +; SAEHb1Cb+alDbNUcVNZM03aUgRBmESJPrX70ey5igBCYUeQcGRcG5urg4I4FTI7SeMY+LBgwc36Ccs +; SSNIggLQJxIWm40UFhaqXbt26aOAJBWIOAIQUb9dTTMXfzWjgZLmnCQkCYQwCBL9W/d7WXO0HRR62N +; rexTRID0JvQJgIuP2E0T5m/vz5qnXr1rX+jiR9IA0KQJ9IWWg2gsWNRY7F/u5VMvyEIQCn/8K/8Ksr +; Gsgj4eiRJBTCABZufgXg8J/KmqPN5H70l6Ke4SZw/IT379/v8RNGcABBAgQL3H9ekj6QBgWgT6QsMl +; tBmH/IkCE67I/wP6KBko+Ex/59VRQvWwHoRANZIBItksSCaRD92/xHf+IPf17SHG0mLh9fW4BDyMyZ +; M3VlsDPQP/Dy5ctq0qRJuoAEf06SPpAGBaBPJC0wm0Hi7+rVq3UisHQ/YUTutn1mRgSCtb9nNDAqJA +; kG0wSJ/o35maw52kgSCz2Cgmhgz5491blz5zwFIigcRL5gp06dROkDaVAA+kTS4rIdtABAKwC0BEiC +; nzBy+UxFA9EuZtLPZc1fIpKEg0mCRP92fC5rjjbS6uMfiHpGR0WrVq3UypUr1bt37zx+widOnFDDhw +; /XJ0eSdIIUKAB9ImlRSQFvgGgOiqMA6X7CEG0Qb6aOhFkgEi6SxINJEMlj9C86bPbxtQUUiAwaNEjd +; vXvXEw2EqQDMBVBFLEkrSIAC0CeSFpQkYA8Em6Dy8nL19uVT0QUiOL7FMa6paCDbxYSHJBFhEkb/oi +; PJ7V1MgyPh/Px8tXPnTvXtt99Wi0DYi8JmtF+/fqL0gu1QAPpE0mKSCAzDYRwOA3Hp7WKm/SJHHW7B +; AhGbkSQkTMHoXzRI9PG1BUQDUQjy/Plzj5/w9evX1fTp03V/WUm6wVYoAH0iaRFJJS8vT5WUlKj79+ +; +L9xNGNBAewCwQsRNJgsIUjP6Fj2QfXxvAXotoYJcuXXQeoHtAFG7ZskV169ZNlHawEQpAn0haRJJB +; iwA0DD116lQi/IQX/MrckTBEoKS524wkUWGCING/CR/LmmPcJMXHNy5q7rdoB7N48WL1+vVrj5/w2b +; Nn1ZgxY/ReIUU/2AYFoE8kLaQkgDfATZs2qWfPnukCkUVdfiRKYLhBgYiJdjEUgOaQJCxMwOhfeLC9 +; ixnq2ndRBYz8vxs3bniigXAUWbFihWrXrp0YDWETFIA+kbSQkgLyPaZOnarNxKX7CZsoEKEANIckgZ +; EtQaJ/6BUoaY5xwUIPMzS297Zt21YHBBABdAZaxxw6dEgNHDiw0c8gXigAfSJpMSWNPn36qD179uij +; gDQXiFAAmkOSyMiWING/gr+SNceoQaFHs19/JOo5ajOZ7MEoEMHR7+PHjz1+wjdv3lRz5szRHSVs1x +; G2QAHoE0mLKYm0adNGLVy4UN25c0cXiCShXQwFYHxIEhvZECT6h7xVSXOMmjT6+IaJ330YLiGI/LnH +; y5cvdQuZXr16Wa8lbIAC0CeSFlRSQT7I0KFDVVlZWSL8hBEN9HMkTAFoDkmCIxv8Rv8Ao3/1k3Yf3z +; AIsh+jAARRPwg/t5/wxYsX1YQJE7TblM16Im4oAH0iaUElnQ4dOqi1a9eqJ0+epMpPmALQHJJER1CG +; /5TRP1Ow0CMcstmTERAoKCjQos89sC9gf8A+YaueiBsKQJ9IWlRpAC0C8KaHxf/q+ZNU+AlTAJpDkv +; gIyrrfM/pnAvr4hoeJfbl169ZqzZo1+lTIGfhnnBThxIh+wrWhAPSJpEWVJpDzsWPHDn0UkHQ/YQpA +; c0gSIEFg9C976OMbLib3Zyc9CCYC7oGc8UWLFukccht1RVxQAPpE0sJKG61atdL5IKgGe/vyWWILRC +; gAzSFJiASB0b/sYHuXcAlrj27fvr3avXu3to9zBrpHoIsEuknYpiviggLQJ5IWVxqBfRD6QaE6DP2h +; ktguhgLQHJLEiF/6/ci/+Fv6a1lzDAu0d2GhR/iEuVejXQz6x1ZWVlaLwG+//VZdu3ZN/3ukD9mmL6 +; KGAtAnkhZXmkFneHSIR6f4pBWIUACaQ5Io8UuQ6B9Eo6Q5hgELPaIhiv0aAQF4BsNS1D3gLIWG0nCa +; sklfRA0FoE8kLbC0gxYBo0ePVmfOnNGd45NSIEIBaA5JwsQPQaJ/EIyS5hgG9PGNjij3bDSHLikpUW +; /evKkWgfCYP336tBo1apSOFtqkM6KCAtAnkhYYqQJvgJs3b1bPnz9PRIEIkvQlXbPNSBInfsBRrl8B +; iIIRSXM0CQs9oiWOvRsFIv3791e3bt3yRAMfPHigli1bpvLy8qzRGVFBARgASQuNVIE3wOnTp+v8jz +; cvnor2EybmkCRSMgVFHIz+ZQ4LPaIl7r0bQm/r1q26YbQz3r59qw4ePKgFYtzXGAU4Gp8yZQoFYBAk +; LTbipV+/fmrfvn36KEB6gQjJHklCJVMQIfYrAGEVJ2mOJkChR/N//JGo51cSsGEPx5FvcXGxqqioqB +; aBqBguLy9Xs2bNSrSfcNu2bbVdHvZACsAASFpspDZYAIsXL1Z3797VBSIbRjQVJVqIOSQJlkzo9kP/ +; 4g82cZLmaIK2P/kL9fkffifquZUEbNvHO3furI4cOeI5En7x4oXavn276tGjh1XXa4Jhw4apGzduVE +; c/KQADIGnBkbrBG+Dw4cPViRMndDLwma1zeCScQiSJlkxg9K9xWOgRHzbu5/ALnjdvnnr16pXHT/j8 +; +fM6SohiQhuv2w+IaK5cuVK3xPnuu++qW+JQAAZA0oIjDdOpUye1fv16fRSAAhHJ7WKIfyQJl8YIEv +; 3b8bmsOWYD27vEi817OgpEevfura5cueKJBj5+/FitXr1aN5a2+fobAi5ZZ8+e9VjkIefxwIEDFIBB +; kLToSOOgIeikSZPU5cuX1evKChaIpAhJAqYxGP2rH/r4xouUfR1WcevWrdOnQm4/4WPHjqkhQ4bo4g +; kJcwE45Zo9e7Z69OhRtSMK/hf/f+7cuTqySQGYxc1CkkVhYaEqLS3VRwEsEEkHkkRMQzD6Vzft/+qf +; s72LBUja2xENRHoQTAScgWPT27dvq/nz56vWrVtbPwdELPfv368jfe6+h+fOndP7nPPnKAADImnxkc +; zB4kY+CHpFSfcTJo0jScw0xOxf+heAEz6WNUe/5H70l6KePUlF0r7u3t87dOig9u7dW50zh4HgwK5d +; u/Rxsa3XPnbsWL1/OYUeuH4UtuAou1WrVp4/SwGYxQ1CkgneAAcPHqyOHj2qw/+IBvJIOJlIEjT1we +; hfbejjaweS9vS6wDHqjBkztIBy+wkjV3Dy5MlW+QkjeIF8dohUR7RCBKK1zYgRI+r8OxSAAZG0CEkw +; 8vPz1apVq3TOhHQ/YZJcAYjcv70+BeDUf5A1x0xhoYddSNrT6wN5f927d9eFFO7x9OlTtWHDBl1IGP +; c1or/txYsXteWpM9DnD9HKhhxOKAADImkRkuCgRQBC6sidSIKfMEmWAOz+ffTvdG6OOtE6Rx1onnn0 +; D24hkuaZCWzvYheS9vNM9nscn65YsaJWXt3Jkyd1hC0OP2HsTwsWLNBdLNyFHrC3mzZtmj7NaujvUw +; BmeVOQdICmoLAPwlGAdD9hkhwBuOTXVQIQnGqTow63aDwaiHxBSXNsDPr42oekfdwPEFQDBgxQd+7c +; 8UQD7927p5YuXapNBqK6FjSxRprSu3fvPBXLEKTdunXL6DMoALNA0oIk2YNmmjNnztSd1OknnAwkCZ +; 2aONG/mhz/Phq4r1k6on/08bUPSXt40H2/Xbt2aseOHTof0N1bD5W3RUVFof98tC2Dk5Xz8xH1e/bs +; mRahLVq0yPizKACz/EWQ9AHDcDTRxIJnuxjZSBI7NXFH/2qCaOChFrUFIPIFJc2xPujjay+S9vBswJ +; HvxIkTtfByBoTY9evXdeGIHyGWKbm5uWrbtm3q9evX1YUeSE26evWqGjRokO/PowDMAkmLkpgFibUl +; JSXq/v379BMWjCTR46a+6F9Nylp5j4STEP3L+/Gf08fXUiTt36Y0QJcuXVRZWZnnSBiWa0gZQvGIqZ +; +FRtTXrl2rLvSAAIQQ3LJlixaGQT6TAtDADUDSCTqpI/kXORf0E5aJJOHjZu4nmQlAcPL7aODB5smI +; /rHQw14k7dumQTHGokWLtCBzBoQaKodRRJiNnzAiicuWLVPPnz+vLvTA0S/yECdMmJDVd08BmCWSFi +; gJB7wBbty4UbcFoJ+wLCSJH4dMo39uSr+o+nuS5umG7V3sRtKeHRYoEOnTp48+AnYPOIqsXLlS5w36 +; /UwUH546dcrj44uij8OHDxtpP0MBmCWSFikJD7ylTZkyRedi0E9YDpJEkIOf6B9ArqCk+dWEhR72I2 +; nPDlsP4DgWAYGafsIQbcjTy8RPGGISeYRo5+Ju7/LkyRNtR4eIo4nrpQA08AsnxAFvgLt379ZHASwQ +; sR9JQgggiod+f5kIv2OtctQwgXN0QKFHs19/JGr9pxFJ+3VUQMCNGjVKmwg4Azl7sGibO3duLUs2N4 +; gU7tmzRzdydvcbvHDhgt5fTF4nBaABJC1WEj5t2rTRzTlhHo4CEfoJ24skQQQmfpyZ+Fv5O1nzqgl9 +; fGUgaZ+OQxd07NhRd4xwj5cvX6qdO3eqgoKCWn8HovHmzZseH1/8+bVr12qrN9PXSAFo6BdNiBu8AQ +; 4dOlQdO3aMfsIWI0kUZRr9g0iUNK+a0MdXBpL26DhBAcisWbO0kHMGBN6lS5d0EQeOcxERhMjDn3H7 +; +EIMjh49OrRrowA0gKRFS6IFb3TOwqefsH1IEkaNRf92Nc1Rvf9a1pzcsNBDDpL2ZxtA3l+vXr30Ma +; 57IKcPDaXx7905g+gxu3fvXu1HH+Z1UQAaQtLiJeGDJqE4BnbncTiDfsL2IEkgNRT9Q2GIpLnUpNXH +; PxC1vtOOpL3ZFvC9IdK3atUqj30bijtq+vjCcaoxH18TUAAa/OUSAjp06KCOHz+uGhr0E7YDKQKpvu +; gfon4DfixnHjWhj688JO3LNgJhN3Xq1OqGzm4hiN5+QRw9gkIBaAhJC5iEx/jx43XDzkwGC0TiR4pQ +; qiv6x/YuJGok7cm2gj0CQs/tI+wMnBjh6Ldv376RXAsFoEEkLWRiFlT+IpfDCeX7GWwXEx8ShFLN6B +; /au4z5mYxrrwv6+MpE0l5sI+gPuHnzZo+PLwo9YCf64sWL6v0AwhCWb9OmTQvFT9gNBaBBJC1mYo6i +; oiJ19+5dlc1ggUg8SBBM7ujf1s9kO3rQx1cmkvZhGxk4cKC6cuWKx8cX0b7t27drX/muXbtqS1H3wE +; kSBCP+W1jXRQFoGEmLmmQHyveXL1/uSegNOvDWB1ggEi22CyZ39G/6L+y/3oagj69MJO2/ttG8eXO1 +; ZMkS9ezZM4+P771799SkSZM83y+iffiz7sJBCMYzZ87oVjAoLDR9fRSAhpG0sElw4P977tw5ZWLAKx +; J9ovC2hwcFC0Siw3bRhOgfCz1IXEjae22jW7du6sSJE54AAf4ZvWGxf9T1d1Ag0q9fP93/zz1QGYxg +; A6KFJq+RAtAwkhY3CQZyM9w5G0EH3gjRJR6G31j4eAPEZyP/480L+glHgc3CCXl+LPQgcSJp77UF9P +; zDcxy5fU6hB571FRUVauHChY36+OJ7h9BDQMBdKQzxePDgQTVgwABjvxsKwBCQtMBJ5mBRokLLxICA +; hCck7H1q/hxUgOHn4CiABSLhYrN4ktzUmT6+8pG059oC9ojS0lL97HYKPSDi4PqBXHE/n4Uj37Fjx+ +; pm0e6gQXl5uZo9e7Zq2bJl1tdLARgCkhY5yYxhw4Z5jL2zGTg6hsiDRVB9Pw8VY4sWLdLFJSgQ2TCi +; qShhJQVJokoK9PGVj6T91haGDx+ubty44fHxRcXvhg0bdJeIIJ+J30WnTp3U4cOHPXsI3KXQdaJnz5 +; 5ZXTMFYEhIWuykfvCWBY9Gt01P0AF7nzVr1qh27drpY4LGfjbeACE80VQaP//M1jk8EqYAtBr6+MpH +; 0j5rA467R2Vlpae9y+3bt3UEz8TPQLAAJ0avXr2q3k/wM2AhN27cuEaPleuDAjAkJC14Ujd4u0I+no +; mBxp8jRozQeX5+r6Njx45q3bp1+igABSJsF0MBaBv08U0GkvZYGygoKFBnz56t5eO7f/9+7Qhl8mch +; aFBYWKguX77s2VseP36sAwvt27f3/ZkUgCEiaeGTD6AgY968eZ63raADb2m7du3SVV/43KDXhHYCEy +; dO1LkkryufsECEAtAa6OObDCTtrXGDiNycOXN0WpDbxxf/H/l5YbRscTQF8sZxKvXVV19V7zP4Z1QX +; DxkyRAvFTD+PAjBEJC1+UkV+fr46evSoMjHQ0mX69On6iMDU9eGNE0nGyAFBgQjbxVAAxgXbuyQHSf +; tq3CCyhwgfIn1uAYZIIJ7PUVwDgglID0J7GGfg+BknTQsWLNAiMZPPoQAMGUkPgbSDfA2INhMD/Z8Q +; rseboOnrhKBEPsitW7fU25fP6CdMARg5bO+SHCTtp3GDPQK5fe5CD+T+IQfQRFWuX22BY989e/ZU5x +; 5ioPBk9+7dqnfv3o1+BgVgBL8kYjd4W9q6dWud5tx+BxZfSUmJruLNpNAjKPjsQYMG6eow9IdCNJBH +; whSAYYP2Liz0SA6S9tI4QRXvxo0ba/n4ouoX1b9xXhuCDOg7CCHqDOxlV69eVZMnT9bpQ/X9XQrACJ +; D0QEgbffr00W90Jsb169fV4MGDdUVWVNePI+uVK1dqNxH6CVMAhgkLPZKFpD00TtC/D7nXNX18kYpj +; 2pkjKAgIdO/eXdvGuQdOtCBcO3fuXOffowCMAEkPhbSAJN6lS5d68jiCDlSAIYKIfk3ZFHoEBYITRx +; PIQcFDin7CFICmoY9vspC0f8YFImdw7nj69KnHxxcOH1OnTvVVbBEVOIZetmyZZ1/D/nTq1CndhaJm +; cQoFYERIejgkHQi106dPKxMDJfjjx4/XCy/ueeENEEIURwH0E6YANAELPZKHpH0zLtC1AVW1NX18kd +; sNj1+brx1BiP79+9c62YJwRXpS27Ztq/8sBWBESHpAJBnkRLhzJYIOHAMg/w69AsMo9AgKhOiMGTP0 +; cfSbF0/ZLoYCMDAs9EgekvbMOEBUD3vEvXv3PD6+OEpdsmRJg/l0NoHfNY6nt23bVl2wgoHIIPznHV +; s6CsCIfykkHvDWg8ood7VU0IEWLE6pva3zxQJHqwL6CVMA+gWFHs3/8UfW3tskGJL2yjjAHrF9+/Za +; Pr5XrlxRAwcOFDUXBwQncEKFY2xnQNCieGXmzJkUgFEi6WGRJFCY4e6XlM24ePGiFlcN+fjaAh5oyH +; PE2yz9hCkAMyHvx3+uPv/D76y/t4k/JO2TcYAGyjg1qenju3nz5sA+vraA3z+KQHCk7R4vXrygAIzj +; l0GiAbZr6M9kwscX+R+wY0PVbZjtXUyDN0Ak/548eZJ+whSADcJCj2QiaX+MGqTMLF++3OPji6NfNF +; RG5EzSXBoDxYI4uXI7XFEARoykB4dkUBBR0zMx6EAEbdSoUYF8fG0Bb4AbNmzQRwH0E6YAdMP2LslF +; 0t4YNT169NDFgG5LNbzoHzp0SPuvS5pLpiB4gQbRjsc9BWAMSHqASAM3+KxZs4z4+OJNcO/evbrqK4 +; 72LqaBgEWCM3JaXldWsECEApCFHglF0n4YNTgVQf4beqe6fXzR0QEe8EjvkTQfv+D+wLH2+vXrKQDj +; /CUQs7Rr106/vZkYz58/1w8Jkz6+toA3wF27dmmRnPYCEUlizSQo9Gj2649E3bckMyTtg1GDFB681N +; fsk3f+/PmMrNOSBIIaFIAxIemBIgEc0VZUVCgTA8cCeBhIKPQICiqYkQ+CXlHvXqXXT1iSaDNF7kd/ +; KepeJZkjaQ+MmtGjR6ubN296Cj3Q0WHNmjX6RV/SXExBARgjkh4stoKFu2nTJk+vo6AD5f8rVqzQ1b +; OSCj2CgjdAVL8dPXpU58Gk0U9YknAzAX18k4ukvS9K8LKLAj6IPbePL8TgyJEjRc3FNBSAMSPpAWMb +; hYWFehGbGPicoUOH6kafkr4DE7Rv316tXr1a58CkzU9YknjLBhZ6JBtJe16U9O3bV124cMHTCQIv+u +; gJi5QhSXMJAwrAmJH0kLEFJPEuWrRIL+RsBxp9ovknqmSTUOgRFLQIGDdunM6FwdtxWvyEJYm4oLT6 +; +Aei7kXiD0n7XVTgeTZ//nz15MkTT6EH+sHCKQnPeknzCQsKQAuQ9LCJmw4dOmg/RhMDDwdUxdrg42 +; sLsLaDIEaT0DT4CUsSckGifvTxTTaS9rmogNf7kSNHPD6+SHE5deqUbg8maS5hQwFoCZIeOnExYcIE +; XZ1rYqAreq9evazy8bUF5FWilU55ebl6+/JpogtEJAk6P7C9S7KRtLdFuYdij7h7967Hxxd7xrJly3 +; QbLEnziQIKQEuQ9PCJmtzcXLVz587qUH42A61PcHyMPkhpKPQICr6bAQMGqIMHD+qWCUltFyNJ1GUC +; fXyTj6R9LSqwR2zZskXbt7l9fNHwGFagkuYSJRSAFiHpIRQV/fv3104cJgYaIEPUID9E0ncQJ3l5ef +; rt+f79+4n0E5Yk7hqDPr7JR9J+FhWDBg1SV69e1YIPw/Hx3bZtmxaGkuYSNRSAliHpYRQmqMZFSxZ3 +; HkfQgfyPjRs36vxBRv38g36I6LOI/ohJ8xOWJPAagj6+yUfSPhYFONJdunSpevbsWfXpEI5+cQQ8ce +; JEfmcZQAFoIZIeSmHQtWtXXbpvYqDqa+zYsaJ9fG36vaDnIh64KBBZ1OVHosReEgUg27skH0l7V1TA +; nvPkyZO1fHzR0xQdHSTNJU4oAC1E0sPJNNOnT9cNO7MdeCPcv3+/rvpKc3sX00BIT506VefWJMFPWJ +; LYqwkLPZKPpH0rCvAsxx6BF3t3exe4QMHZCOk9kuYTNxSAliLpIWUC5JpBsJkYaGEyZ84c3QFe0ncg +; CTRY3bNnj861kVwgIknwOdDHNx1I2q+iAHsEPMzd/V+R93fx4kXVr18/UXOxBQpAi5H0sMqGYcOGaR +; cKE+Ps2bNanCTZx9cWkGC9cOFCdefOHbEFIpKEH6CPbzqQtE9FwYgRI3RbKrePLzo6rF+/Xr/oS5qL +; TVAAWo6kh5Zf0IAZHo1O9VY2A61KYGeGt0QWekQH+ijCQq+srEzn41w+sFrUkbAk8Ucf3+QjaW+KAv +; QkxXMdpzpuH99bt26pMWPGiJqLjVAACkDSAyxT0IT5+vXrysRABGr48OGp9PG1BVRYr127VrurVD4s +; F+MnLEH4sdAjHUjak6IAXu/nzp3z+PjiRX/fvn3av1zSXGyFAlAIkh5kDYEkXng0Incs24E3wdLSUt +; WlSxcWelgABDg68SMn59XzJ2rfvH7WRwNtF3/08U0HkvaisEH6zty5c3VakLvQ49GjR9qhCKcOkuZj +; MxSAQpD0MKuP/Px8bcFmYjx9+lRXo+KIQNJ3kAYQ3YVzC6q5USBis5+wrcKPPr7pQNIeFAUdO3asdh +; 9yBlJLzpw5o58rkuYiAQpAQUh6sNWkuLhY948zMY4fP64KCgro42sxEOaoxL5586Z6+/KZtX7CNoo/ +; tndJB5L2nigYN26cun37drWPL3L+Kisr1cqVK3W+uKS5SIECUBiSHnAAnruw5HEWdTYDx8bo/E4fXx +; ngdzRw4EB16NAh3aTVxnYxNgk/tHdhoUfykbTfRAGe52gw7/bxRXrPjRs3dIcISXORBgWgQKQ86NCO +; BQUaJgYaD8PzkT6+8sDRP2z9Hj58qNvF2FQgYov4Y6FHOpC0z0QBvN4vX77s8fFFnz+kkLRt21bUXC +; RCASgUmx9yEGklJSWePI6gAxVgW7Zs0bkhLPSQCxK70bYBuTx42B9dOdaKAhEbxB99fNOBpP0lbFAw +; tnjxYp3L7fbxvXfvnpoyZYo+PZA0H6lQAArGxoccfBjRjNnEQNXX+PHj6eObIODhCUH//Plz7Sccd4 +; FI3FE/FnokH0l7ShTAUxx9Q5EW4gz8M3K78d8kzUU6FIDCselBhzc3NOzMduAYAHljPXr0YNQvgSCh +; G36e6AP55sXTWP2E4xJ/LPRIB5L2krBBVA97BKJ8Tk44on+IAiIaiKigpPkkAQrABBD3Qw65GvCFNT +; HQOmTevHn08U0B8O9EU1fk/MRVIBK18EOhR/N//JGo3xPxj6T9IwqwR+zYsUOvdafQA6kgyP9DHqCk +; uSQJCsCEENeDbsiQITq538S4cOGCFgX08U0P2BiWLFmiowJx+AlHKf7yfvzn6vM//E7U74f4R9K+EQ +; Wo5EVFr9vHFxW/qPxFBbCkuSQNCsAEEeVDDnl58Gh02/QEHcj/gI0YqkXZ3iV9oJ8jrPxOnDih76cz +; W+dEdiQclfhjoUfykbRXRAFSPdDDD738nKgfjn7R6w89/yTNJalQACaMKB50yM27cuWKMjEQ+Rk5ci +; QLPYjq1KmTWr9+vaqoqNAFIlG0iwlb+LG9S/KRtD9ERc+ePXXFP1w8nIGuEHD5QEcHSXNJMhSACSSs +; Bx2ic3B3ePXqlcp24E1w9+7duuqLhR7EAYngkyZN0rlBrysrQi8QCVP80cc3+UjaF6IA0Xz49aKDg9 +; vHF76+8PdFeo+k+SQdCsCEYvpBh+PZw4cPKxMDLUBmzJhBH19SL4WFhWrXrl36ZSPMApEwhF/7v/rn +; bO+ScCTtBVHRvn17XdTl7v+KlI5z587p9SxpLmmBAjDhmHjYjR49Wh/LmRinTp1SvXv3po8vaRRUgq +; Mi/NatW6H5CZsWf7kf/aWo75j4R9LzPyrQ5B3r1F3ogY4OyBPHi76kuaQJCsAUEPRBhw148+bN1Ys6 +; m4Hy/+XLl+uqTxZ6kExBesDgwYPV0aNHdT4RooEmj4RNij/6+CYbSc/8qMAegbxdROrdPr7l5eVqxI +; gRouaSRigAU4Lfhx2idHijMzHwMEC7GPr4kqDgeGnVqlU6t8ikn7AJ4cdCj2Qj6TkfJWjZdfHixWof +; X+dFH6kb7dq1EzWXtEIBmCIyedghSRdd2bGQsx14MGzbtk1Xd7LQg2QLXiCKi4t1TpEpP+FsxR/buy +; QbSc/3qMA6XLBggXry5Imn0OPBgwfa4QfPeknzSTMUgCmjoYcdyvORo2di4OEwceJE3QtKwoOeyAFt +; iPBiAdvBbP2Es4n6sdAjuUh6pkcJvN6PHDni8fFFasbJkydV9+7dRc2FUACmlpoPPIg1VOeaGHhAoA +; 8UCz1IWODFAu0m4DCQjZ9wEPFHH9/kIukZHvV+gfZMd+/e9fj4Pnv2TJWUlOg+rpLmQ6qgAEwxWNS5 +; ubmqtLS0OoE3m4Gqr0WLFunEYBZ6kCiAj+iBAwd064kg7WL8CD/6+CYXSc/tqMEegYg77NvcPr5Xr1 +; 5VgwYNEjUX4oUCMMUMGDBA3b9/X5kYjqk3fXxJ1OTl5ekoBO5lv37CmYo/+vgmF0nP7KhBBf61a9eq +; Cz0cH9+tW7dqYShpLqQ2FIApBEm88Gh02/QEHfiMDRs26CpNRv1IXODFA5aCyGH14yfMQo/0IumZHT +; U40l22bJlOC3IKPXD0iyPgCRMm8PtLCBSAKQPWayjdNzEQcUEDUPr4Elvo0qWL2rhxo85NysRPmO1d +; 0oek53UcoJgDL1LuAAGKPpDbjSIQSXMhDUMBmCJgv4Y8vWwH3ghh+YMHBdu7ENvAC8mUKVN0jlJjfs +; Is9EgPkp7VcYBnOfYItHNxt3dBR4f58+frkyNJ8yGNQwGYApAjhUR5E6OyslJXX9LHl9hOnz591O7d +; u3XOUn0FInUVejT79Uei5kkaRtKzOi7QuBlrxd3/FakUFy5cUH379hU1F5I5FIAJB3Y8jx8/VibGmT +; Nn9KbKQg8ihTZt2qiFCxeqO3fu6AKRmn7CHenjm1gkPafjZNSoUermzZseH19Yu61bt053dJA0F+IP +; CsCEgj5pKM5w2/QEHXgrhA0XIoks9CDSwNHW0KFD1bFjx2r5CVcXenzyQ1FzIvUj6TkdJzjFWbNmjU +; 4Lcvv4QgyOHj1a1FxIMCgAE0ivXr10g1wTA37Aw4YNU82bNxe1CRBSkw4dOugNDxFxx0945Gf/QZUd +; KFWFhYXMZxWOpGd03MDr/fz58/qY1xnopbl3716Vn58vai4kOBSACQIbGJJ1kfOU7UDkcOfOnbrqC1 +; E/52dI2hAIqQleZMaPH69zm5wjLwwcec2bN08nukuaD6Hw8wPSd3Cf4yXIXejx8OFDNXPmTO3eJGk+ +; JDsoABMC3tqOHz+uTIyKigpdRYlj5Lp+lqTNgZC6QJR8x44dtariDx8+XP3SI2k+aUTKs9kW4PV+6N +; ChWj6+p0+f1tadkuZCzEABmADGjRtnzMe3rKxMFRQU6GhiYz9X0mZBSE2QAzV79mxVXl5eHQ3BQNuL +; 4uJiellbioRnsm0g6o1CKMfHFzl/6OiwfPnyel/0SfKhABQMKrS2b9/u2byCDhyBLVmyRFdN+n3ISt +; o8CHGDSN/AgQNrRUaQArFlyxYWPlmE7c9jG8HzfPPmzR4fX6Q+XL9+XRdGSZoLMQ8FoFD69eunbXlM +; DDTMxSaYTaNPSRsJITVBHzREQ9AE1z1QEVlUVMQCkRix+TlsM3imX7lyxePji44OCBq0bdtW1FxIOF +; AACgNJvPBodEcrgg7kf+DtENWR7kKPbJC0sRDiBmsL7S/Q79LdPgmb5tKlS2l5GCG2Pn8lgEInnObA +; DtHt43vv3j01efJkY896Ip//AYs5mPzCJ5rXAAAAAElFTkSuQmCC +; thumbnail end +; +; + +; external perimeters extrusion width = 0.45mm +; perimeters extrusion width = 0.45mm +; infill extrusion width = 0.45mm +; solid infill extrusion width = 0.45mm +; top infill extrusion width = 0.42mm +; first layer extrusion width = 0.50mm + +M73 P0 R7 +M73 Q0 S7 +M201 X2500 Y2500 Z200 E2500 ; sets maximum accelerations, mm/sec^2 +M203 X200 Y200 Z40 E100 ; sets maximum feedrates, mm / sec +M204 P2000 R1200 T2000 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2 +M205 X8.00 Y8.00 Z2.00 E10.00 ; sets the jerk limits, mm/sec +M205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec + +M486 S0 +M486 Axyz-cali-cube-by-r3d_v1.stl +M486 S-1 + +;TYPE:Custom +M17 ; enable steppers +M862.3 P "MK4" ; printer model check +M862.1 P0.4 A0 F0 ; nozzle check +M115 U6.1.2+7894 +M555 X114.916 Y91.1 W32 H23.8005 + +G90 ; use absolute coordinates +M83 ; extruder relative mode + +M140 S60 ; set bed temp +M104 T0 S170 ; set extruder temp for bed leveling +M109 T0 R170 ; wait for temp + +M84 E ; turn off E motor + +G28 ; home all without mesh bed level + +G1 X42 Y-4 Z5 F4800 + +M302 S160 ; lower cold extrusion limit to 160C + + +G1 E-2 F2400 ; retraction + + +M84 E ; turn off E motor + +G29 P9 X10 Y-4 W32 H4 + +M106 S100 + +G0 Z40 F10000 + +M190 S60 ; wait for bed temp + +M107 + +; +; MBL +; +M84 E ; turn off E motor +G29 P1 ; invalidate mbl & probe print area +G29 P1 X0 Y0 W50 H20 C ; probe near purge place +G29 P3.2 ; interpolate mbl probes +G29 P3.13 ; extrapolate mbl outside probe area +G29 A ; activate mbl + +; prepare for purge +M104 S215 +G0 X0 Y-4 Z15 F4800 ; move away and ready for the purge +M109 S215 + +G92 E0 +M569 S0 E ; set spreadcycle mode for extruder + +; +; Extrude purge line +; +G92 E0 ; reset extruder position +G1 E2 F2400 ; deretraction after the initial one before nozzle cleaning +G0 E7 X15 Z0.2 F500 ; purge +G0 X25 E4 F500 ; purge +M73 P1 R7 +M73 Q1 S7 +G0 X35 E4 F650 ; purge +G0 X45 E4 F800 ; purge +G0 X48 Z0.05 F8000 ; wipe, move close to the bed +G0 X51 Z0.2 F8000 ; wipe, move quickly away from the bed + +G92 E0 +M221 S100 ; set flow to 100% +G21 ; set units to millimeters +G90 ; use absolute coordinates +M83 ; use relative distances for extrusion +M900 K0.05 ; Filament gcode + + + +M142 S36 ; set heatbreak target temp +M107 +;LAYER_CHANGE +;Z:0.2 +;HEIGHT:0.2 +G1 E-.8 F2100 +G1 Z.8 F720 +G1 X116.784 Y97.356 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Skirt/Brim +;WIDTH:0.5 +G1 F1200 +G3 X119.696 Y95.774 I3.447 J2.874 E.12901 +G1 X130.29 Y95.773 E.40264 +G1 X131.3 Y96.002 E.03936 +M73 P2 R7 +M73 Q2 S7 +G3 X134.21 Y99.561 I-1.572 J4.255 E.18323 +G1 X134.247 Y105.382 E.22124 +G3 X134.25 Y109.274 I-12.554 J1.955 E.14851 +G3 X131.923 Y113.721 I-4.577 J.438 E.20178 +G3 X130.13 Y114.242 I-2.361 J-4.78 E.07133 +G1 X119.874 Y114.242 E.38979 +G3 X115.788 Y110.427 I.363 J-4.485 E.22924 +G2 X115.626 Y107.993 I-16.032 J-.157 E.0928 +G3 X115.752 Y104.875 I13.042 J-1.033 E.11888 +G1 X115.772 Y99.722 E.19585 +G3 X116.746 Y97.403 I4.459 J.508 E.09689 +M204 P1000 +G1 X116.746 Y97.403 F12000 +G1 X117.1 Y97.69 +M204 P600 +G1 F1200 +G3 X119.764 Y96.227 I3.127 J2.536 E.11845 +G1 X130.216 Y96.224 E.39724 +G3 X133.759 Y99.639 I-.446 J4.009 E.20115 +G1 X133.79 Y105.389 E.21854 +G3 X133.794 Y109.236 I-12.569 J1.936 E.14677 +G3 X132.988 Y112.214 I-4.311 J.431 E.11989 +G3 X130.109 Y113.785 I-3.255 J-2.54 E.12819 +G1 X119.893 Y113.785 E.38827 +M73 Q3 S7 +G3 X116.244 Y110.39 I.34 J-4.024 E.20415 +M73 P3 R7 +G2 X116.08 Y107.943 I-16.063 J-.156 E.0933 +G3 X116.209 Y104.882 I13.556 J-.962 E.11669 +G1 X116.224 Y99.793 E.19342 +G3 X117.063 Y97.737 I4.003 J.433 E.08551 +M204 P1000 +G1 X117.063 Y97.737 F12000 +G1 X117.421 Y98.019 +M204 P600 +G1 F1200 +G3 X119.83 Y96.68 I2.802 J2.203 E.10755 +G1 X130.144 Y96.676 E.392 +G1 X130.964 Y96.853 E.03188 +G3 X133.308 Y99.714 I-1.219 J3.389 E.14762 +G1 X133.333 Y105.403 E.21622 +G3 X133.338 Y109.199 I-12.343 J1.914 E.14483 +G3 X132.614 Y111.949 I-3.901 J.444 E.11059 +G3 X130.089 Y113.328 I-2.871 J-2.257 E.11239 +G1 X119.913 Y113.328 E.38675 +G3 X116.7 Y110.351 I.316 J-3.563 E.17916 +G2 X116.535 Y107.892 I-16.088 J-.154 E.09376 +G3 X116.666 Y104.896 I13.528 J-.906 E.11421 +G1 X116.677 Y99.863 E.19129 +G3 X117.384 Y98.066 I3.546 J.359 E.07432 +M204 P1000 +G1 X117.384 Y98.066 F12000 +G1 X117.748 Y98.342 +M204 P600 +G1 F1200 +G3 X119.894 Y97.133 I2.471 J1.877 E.09626 +G1 X130.119 Y97.133 E.38861 +G3 X132.856 Y99.79 I-.341 J3.09 E.15609 +M73 P4 R7 +M73 Q4 S7 +G1 X132.876 Y105.416 E.21382 +G3 X132.882 Y109.163 I-12.124 J1.893 E.14297 +G3 X132.243 Y111.682 I-3.497 J.454 E.10115 +G3 X130.067 Y112.871 I-2.488 J-1.967 E.09683 +G1 X119.933 Y112.871 E.38516 +G3 X117.156 Y110.311 I.292 J-3.103 E.15421 +G2 X116.989 Y107.842 I-16.105 J-.152 E.09414 +G3 X117.123 Y104.909 I13.535 J-.849 E.11181 +G1 X117.129 Y99.93 E.18923 +G3 X117.712 Y98.39 I3.09 J.289 E.06334 +M204 P1000 +G1 X117.712 Y98.39 F12000 +G1 X118.08 Y98.659 +M204 P600 +G1 F1200 +G3 X119.955 Y97.586 I2.136 J1.557 E.08458 +G1 X130.095 Y97.59 E.38538 +G3 X132.404 Y99.863 I-.313 J2.628 E.13252 +G1 X132.419 Y105.428 E.21151 +G3 X132.426 Y109.128 I-11.91 J1.872 E.14118 +G3 X132.136 Y111.009 I-3.6 J.409 E.07319 +G3 X130.045 Y112.414 I-2.371 J-1.27 E.09964 +G1 X119.954 Y112.414 E.38352 +G3 X117.612 Y110.269 I.268 J-2.644 E.12936 +M73 P5 R7 +M73 Q5 S7 +G2 X117.443 Y107.791 I-16.111 J-.149 E.09449 +G3 X117.581 Y104.922 I13.578 J-.788 E.10937 +G1 X117.586 Y99.953 E.18885 +G3 X118.046 Y98.708 I2.63 J.263 E.05099 +M204 P1000 +G1 X118.046 Y98.708 F12000 +G1 X118.42 Y98.968 +M204 P600 +G1 F1200 +G3 X119.977 Y98.043 I1.792 J1.243 E.07098 +G1 X130.07 Y98.048 E.3836 +G3 X131.952 Y99.933 I-.285 J2.166 E.10887 +G1 X131.962 Y105.441 E.20934 +G3 X131.969 Y109.107 I-11.965 J1.857 E.13987 +G3 X131.724 Y110.81 I-3.203 J.408 E.06619 +G3 X130.024 Y111.957 I-1.953 J-1.062 E.08101 +G1 X119.977 Y111.957 E.38185 +G3 X118.061 Y110.182 I.241 J-2.181 E.10632 +G2 X117.898 Y107.741 I-15.183 J-.209 E.09308 +G3 X118.038 Y104.936 I13.665 J-.725 E.10693 +G1 X118.043 Y99.976 E.18851 +G3 X118.386 Y99.017 I2.169 J.235 E.03907 +M204 P1000 +G1 X118.386 Y99.017 F12000 +G1 X118.768 Y99.267 +M204 P600 +G1 F1200 +G3 X120.001 Y98.5 I1.44 J.941 E.05697 +G1 X130.045 Y98.505 E.38174 +G3 X131.5 Y99.999 I-.257 J1.705 E.08512 +G1 X131.505 Y105.453 E.20729 +G3 X131.512 Y109.084 I-11.818 J1.84 E.13854 +G3 X131.312 Y110.61 I-2.829 J.405 E.05922 +G3 X130 Y111.5 I-1.533 J-.849 E.06252 +G1 X120 Y111.5 E.38006 +G3 X118.511 Y110.1 I.213 J-1.718 E.08309 +M73 P6 R7 +M73 Q6 S7 +G2 X118.352 Y107.691 I-14.376 J-.261 E.09186 +G3 X118.495 Y104.949 I13.805 J-.657 E.10453 +G1 X118.5 Y99.999 E.18813 +G3 X118.736 Y99.318 I1.708 J.209 E.0276 +M204 P1000 +G1 X118.736 Y99.318 F12000 +G1 X119.126 Y99.553 +M204 P600 +G1 F1200 +G3 X120.023 Y98.957 I1.081 J.654 E.04229 +G1 X130.019 Y98.962 E.37991 +G3 X131.043 Y100.022 I-.233 J1.249 E.05976 +G1 X131.048 Y105.466 E.20691 +G3 X131.056 Y109.061 I-11.672 J1.823 E.13717 +G3 X130.9 Y110.411 I-2.48 J.399 E.0523 +G3 X129.978 Y111.043 I-1.118 J-.644 E.04394 +G1 X120.023 Y111.043 E.37835 +G3 X118.962 Y110.023 I.188 J-1.257 E.05966 +G2 X118.807 Y107.641 I-13.704 J-.301 E.09084 +G3 X118.952 Y104.963 I13.941 J-.587 E.10209 +G1 X118.957 Y100.022 E.18779 +G3 X119.096 Y99.605 I1.25 J.185 E.01679 +M204 P1000 +G1 X119.096 Y99.605 F12000 +G1 X119.496 Y99.823 +M204 P600 +G1 F1200 +G3 X120.045 Y99.414 I.718 J.391 E.02685 +G1 X129.999 Y99.423 E.37832 +G3 X130.586 Y100.045 I-.22 J.795 E.03417 +G1 X130.591 Y105.478 E.20649 +G3 X130.599 Y109.039 I-11.527 J1.807 E.13587 +G3 X130.463 Y110.249 I-1.92 J.397 E.04705 +G3 X129.955 Y110.586 I-.688 J-.487 E.02371 +G1 X120.045 Y110.586 E.37664 +G3 X119.415 Y109.958 I.169 J-.8 E.03575 +M73 P7 R7 +M73 Q7 S7 +G1 X119.41 Y109.021 E.03561 +G3 X119.409 Y104.976 I13.174 J-2.025 E.15433 +G1 X119.415 Y100.045 E.18741 +G3 X119.47 Y99.877 I.799 J.169 E.00673 +M204 P1000 +G1 X119.47 Y99.877 F12000 +G1 X119.872 Y100.113 +M204 P600 +G1 F1200 +G1 X120.029 Y99.906 E.00987 +G1 X129.887 Y99.871 E.37467 +G1 X130.128 Y100.113 E.01298 +G1 X130.134 Y105.491 E.2044 +G3 X130.142 Y109.017 I-11.38 J1.79 E.13454 +G1 X130.128 Y109.89 E.03318 +G1 X129.887 Y110.129 E.0129 +G1 X120.113 Y110.129 E.37147 +G1 X119.872 Y109.888 E.01295 +G2 X119.689 Y107.01 I-23.652 J.059 E.10967 +G1 X119.866 Y104.99 E.07707 +G1 X119.871 Y100.173 E.18308 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X119.872 Y100.113 E-.01247 +G1 X120.029 Y99.906 E-.05399 +G1 X120.479 Y99.904 E-.09354 +;WIPE_END +M486 S0 +G1 X123.342 Y106.458 Z.325 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:External perimeter +;WIDTH:0.4501 +G1 F1200 +G1 X123.359 Y106.435 E.00097 +;WIDTH:0.462156 +G1 X123.338 Y106.667 E.00812 +;WIDTH:0.505208 +G2 X123.341 Y107.156 I1.649 J.234 E.01887 +;WIDTH:0.460671 +G1 X123.365 Y107.413 E.00897 +;WIDTH:0.448347 +G1 X123.305 Y107.309 E.00405 +;WIDTH:0.480559 +G1 X123.244 Y107.205 E.00439 +;WIDTH:0.505208 +G2 X122.862 Y107.094 I-.558 J1.204 E.01535 +;WIDTH:0.462045 +G1 X122.707 Y107.067 E.00548 +;WIDTH:0.466484 +G1 X122.625 Y107.069 E.00289 +;WIDTH:0.514086 +G1 X122.542 Y107.07 E.00325 +;WIDTH:0.561688 +G1 X122.459 Y107.072 E.00358 +G1 X122.36 Y107.281 E.00998 +G3 X122.101 Y107.333 I-.199 J-.324 E.01164 +G1 X122.004 Y107.151 E.0089 +G1 X122.038 Y106.636 E.02226 +G3 X122.329 Y106.522 I.22 J.133 E.01449 +G1 X122.451 Y106.715 E.00985 +G1 X122.541 Y106.712 E.00388 +;WIDTH:0.514086 +G1 X122.631 Y106.708 E.00353 +;WIDTH:0.466484 +G1 X122.721 Y106.705 E.00317 +;WIDTH:0.449989 +G1 X122.972 Y106.661 E.00863 +;WIDTH:0.481095 +G1 X123.224 Y106.617 E.00932 +G1 X123.292 Y106.526 E.00414 +;WIDTH:0.4501 +G1 X123.307 Y106.506 E.00085 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.342 Y106.458 E-.01235 +G1 X123.359 Y106.435 E-.00594 +G1 X123.338 Y106.667 E-.04841 +G2 X123.336 Y107.115 I1.649 J.233 E-.0933 +;WIPE_END +G1 X124.447 Y103.681 Z.263 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.38292 +G1 F1200 +G1 X124.479 Y103.598 E.00251 +M204 P1000 +G1 X124.479 Y103.598 F12000 +G1 X125.515 Y103.085 +M204 P600 +G1 F1200 +G1 X125.317 Y103.059 E.00565 +G1 X125.124 Y103.179 E.00643 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.515 Y103.085 E-.08357 +G1 X125.317 Y103.059 E-.0415 +G1 X125.174 Y103.148 E-.03493 +;WIPE_END +G1 X126.482 Y104.348 Z.231 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.423559 +G1 F1200 +G1 X126.457 Y104.151 E.00629 +;WIDTH:0.464198 +G1 X126.431 Y103.954 E.00696 +;WIDTH:0.473532 +G1 X126.427 Y103.002 E.03409 +;WIDTH:0.47904 +G1 X126.424 Y102.955 E.00171 +M204 P1000 +G1 X126.424 Y102.955 F12000 +G1 X127.307 Y102.397 +M204 P600 +;WIDTH:0.38292 +G1 F1200 +G1 X127.936 Y102.397 E.01778 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.307 Y102.397 E-.13071 +G1 X127.448 Y102.397 E-.02929 +;WIPE_END +G1 X127.287 Y104.345 Z.234 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +G1 F1200 +G1 X127.287 Y101.814 E.07155 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.287 Y102.584 E-.16 +;WIPE_END +G1 X128.122 Y107.637 Z.289 F12000 +G1 Z.2 F720 +M73 Q8 S7 +G1 E.8 F1500 +M73 P8 R7 +M204 P600 +;WIDTH:0.426044 +G1 F1200 +G1 X128.251 Y107.625 E.00413 +;WIDTH:0.401638 +G1 X128.474 Y107.518 E.00738 +;WIDTH:0.38292 +G1 X128.984 Y107.56 E.01447 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X128.217 Y107.628 E-.16 +;WIPE_END +G1 X125.344 Y108.217 Z.251 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +G1 F1200 +G1 X125.344 Y106.367 E.0523 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.344 Y107.137 E-.16 +;WIPE_END +G1 X123.805 Y102.838 Z.28 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.447755 +G1 F1200 +G1 X124.04 Y102.807 E.00798 +;WIDTH:0.494158 +G1 X124.299 Y102.773 E.0098 +;WIDTH:0.507249 +G1 X124.518 Y102.671 E.00933 +G1 X124.406 Y103.016 E.014 +;WIDTH:0.489033 +G2 X124.65 Y103.236 I.272 J-.057 E.01304 +G2 X124.551 Y103.374 I.125 J.194 E.00645 +;WIDTH:0.451962 +G1 X124.5 Y103.479 E.00397 +G1 X124.472 Y103.414 E.00241 +;WIDTH:0.489033 +G1 X124.445 Y103.349 E.00261 +G1 X124.116 Y103.24 E.01286 +;WIDTH:0.525984 +G1 X123.865 Y103.221 E.01011 +;WIDTH:0.562935 +G1 X123.615 Y103.202 E.01084 +G1 X123.514 Y103.417 E.01027 +G3 X123.259 Y103.462 I-.16 J-.164 E.0119 +G1 X123.183 Y103.306 E.0075 +G3 X123.189 Y102.774 I1.815 J-.247 E.02309 +G3 X123.486 Y102.653 I.243 J.171 E.01465 +G1 X123.607 Y102.845 E.00981 +G1 X123.65 Y102.844 E.00186 +;WIDTH:0.522539 +G1 X123.693 Y102.843 E.00172 +;WIDTH:0.482143 +G1 X123.737 Y102.842 E.00161 +;WIDTH:0.441747 +G1 X123.745 Y102.842 E.00027 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.805 Y102.838 E-.0125 +G1 X124.04 Y102.807 E-.04926 +G1 X124.299 Y102.773 E-.05429 +G1 X124.491 Y102.683 E-.04395 +;WIPE_END +G1 X125.272 Y102.225 Z.216 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.499999 +G1 F1200 +G1 X125.33 Y102.207 E.00231 +G1 X125.447 Y102.201 E.00445 +G1 X125.967 Y102.31 E.02019 +G1 X126.3 Y102.563 E.01589 +G1 X126.369 Y102.684 E.00529 +;WIDTH:0.493626 +G1 X126.424 Y102.955 E.01036 +;WIDTH:0.499999 +G1 X126.399 Y101.805 E.04372 +G1 X126.42 Y101.703 E.00396 +G1 X126.634 Y101.57 E.00958 +G1 X127.231 Y101.598 E.02271 +;WIDTH:0.498678 +G1 X127.25 Y101.67 E.00282 +;WIDTH:0.460092 +G1 X127.268 Y101.742 E.00257 +;WIDTH:0.421506 +G1 X127.287 Y101.814 E.00234 +G1 X127.307 Y101.743 E.00232 +;WIDTH:0.460092 +G1 X127.326 Y101.671 E.00258 +;WIDTH:0.498678 +G1 X127.346 Y101.599 E.00283 +;WIDTH:0.499999 +G1 X127.975 Y101.515 E.02412 +G1 X128.193 Y101.613 E.00908 +G1 X128.23 Y101.772 E.0062 +G1 X128.155 Y102.327 E.02129 +G1 X128.082 Y102.35 E.00291 +;WIDTH:0.460973 +G1 X128.009 Y102.374 E.00267 +;WIDTH:0.421947 +G1 X127.936 Y102.397 E.00241 +;WIDTH:0.428617 +G1 X128.027 Y102.418 E.003 +;WIDTH:0.474313 +G1 X128.118 Y102.44 E.00336 +;WIDTH:0.499999 +G3 X128.349 Y103.415 I-2.52 J1.11 E.0383 +G1 X128.29 Y104.496 E.04115 +G3 X128.05 Y104.703 I-.26 J-.059 E.0129 +G1 X127.468 Y104.578 E.02262 +G3 X127.339 Y104.485 I-.021 J-.107 E.00677 +;WIDTH:0.460973 +G1 X127.313 Y104.415 E.0026 +;WIDTH:0.421947 +G1 X127.287 Y104.345 E.00235 +G1 X127.264 Y104.416 E.00235 +;WIDTH:0.460973 +G1 X127.24 Y104.487 E.00261 +;WIDTH:0.499999 +G1 X127.217 Y104.558 E.00284 +G1 X126.642 Y104.565 E.02186 +G1 X126.548 Y104.532 E.00379 +;WIDTH:0.480044 +G1 X126.515 Y104.44 E.00355 +;WIDTH:0.431482 +G1 X126.482 Y104.348 E.00316 +;WIDTH:0.421947 +G1 X126.445 Y104.417 E.00247 +;WIDTH:0.460973 +G1 X126.408 Y104.486 E.00272 +;WIDTH:0.499999 +G1 X126.371 Y104.555 E.00298 +;WIDTH:0.548095 +G1 X126.182 Y104.61 E.00827 +;WIDTH:0.59619 +G1 X125.992 Y104.665 E.0091 +;WIDTH:0.644285 +G1 X125.803 Y104.72 E.00984 +;WIDTH:0.649263 +G1 X125.298 Y104.754 E.02552 +;WIDTH:0.64952 +G3 X125.056 Y104.691 I-.073 J-.214 E.01336 +;WIDTH:0.59968 +G1 X124.93 Y104.611 E.00691 +;WIDTH:0.54984 +G1 X124.804 Y104.53 E.00631 +;WIDTH:0.499999 +G3 X124.413 Y104.092 I.476 J-.817 E.02269 +G1 X124.41 Y103.924 E.00639 +G1 X124.346 Y104.071 E.00609 +G3 X123.609 Y104.679 I-1.936 J-1.596 E.03654 +G1 X123.055 Y104.764 E.0213 +;WIDTH:0.541261 +G1 X122.871 Y104.745 E.00766 +;WIDTH:0.582523 +G3 X122.544 Y104.649 I-.019 J-.542 E.01555 +;WIDTH:0.540532 +G1 X122.103 Y104.285 E.02366 +;WIDTH:0.499999 +G1 X121.808 Y103.775 E.02239 +G1 X121.699 Y103.13 E.02486 +G1 X121.777 Y102.486 E.02465 +G1 X122.102 Y101.873 E.02637 +G1 X122.567 Y101.512 E.02237 +G1 X123.116 Y101.366 E.02159 +G1 X123.486 Y101.369 E.01406 +G3 X124.104 Y101.667 I-.459 J1.742 E.02624 +G1 X124.414 Y102.136 E.02137 +;WIDTH:0.509868 +G1 X124.547 Y102.449 E.0132 +;WIDTH:0.518886 +G1 X124.558 Y102.599 E.00595 +G1 X124.83 Y102.367 E.01415 +;WIDTH:0.499999 +G1 X125.215 Y102.244 E.01536 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.272 Y102.225 E-.01249 +G1 X125.33 Y102.207 E-.01262 +G1 X125.447 Y102.201 E-.02435 +G1 X125.967 Y102.31 E-.11041 +G1 X125.967 Y102.31 E-.00013 +;WIPE_END +G1 X124.382 Y107.568 Z.296 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.360125 +G1 F1200 +G3 X124.538 Y107.407 I.234 J.071 E.00614 +;WIDTH:0.363177 +G1 X124.545 Y106.367 E.0277 +;WIDTH:0.408785 +G1 X124.559 Y106.302 E.00202 +;WIDTH:0.454392 +G1 X124.573 Y106.237 E.00227 +;WIDTH:0.499999 +G1 X124.586 Y106.172 E.00252 +G3 X125.224 Y106.174 I.315 J1.702 E.02439 +;WIDTH:0.462123 +G1 X125.288 Y106.214 E.00263 +;WIDTH:0.432249 +G1 X125.344 Y106.367 E.00527 +;WIDTH:0.422522 +G1 X125.305 Y106.248 E.00395 +;WIDTH:0.462123 +G1 X125.266 Y106.128 E.0044 +;WIDTH:0.499999 +G1 X125.207 Y105.61 E.01981 +;WIDTH:0.538464 +G1 X125.239 Y105.548 E.00287 +;WIDTH:0.576928 +G1 X125.271 Y105.487 E.00306 +;WIDTH:0.615393 +G1 X125.303 Y105.425 E.00332 +;WIDTH:0.653857 +G1 X125.335 Y105.363 E.00354 +G1 X125.874 Y105.316 E.02748 +;WIDTH:0.650923 +G3 X126.091 Y105.338 I.09 J.199 E.01155 +;WIDTH:0.613192 +G1 X126.14 Y105.376 E.00294 +;WIDTH:0.575461 +G1 X126.189 Y105.414 E.00275 +;WIDTH:0.53773 +G1 X126.238 Y105.452 E.00255 +;WIDTH:0.499999 +G1 X126.292 Y105.637 E.00732 +G1 X126.227 Y106.07 E.01664 +G1 X126.547 Y106.036 E.01223 +G1 X127.022 Y106.258 E.01993 +G3 X127.281 Y106.534 I-.689 J.907 E.01445 +G1 X127.499 Y106.301 E.01213 +G1 X127.996 Y106 E.02208 +G1 X128.145 Y105.97 E.00578 +G1 X128.684 Y106.056 E.02074 +G1 X129.021 Y106.351 E.01702 +;WIDTH:0.505356 +G1 X129.221 Y106.851 E.02071 +G1 X129.249 Y107.361 E.01964 +;WIDTH:0.519318 +G1 X129.217 Y107.52 E.00642 +G1 X129.155 Y107.546 E.00266 +;WIDTH:0.489229 +G1 X129.094 Y107.573 E.00248 +;WIDTH:0.487675 +G1 X129.159 Y107.632 E.00325 +;WIDTH:0.516211 +G1 X129.223 Y107.692 E.00345 +;WIDTH:0.520383 +G1 X129.19 Y107.991 E.01194 +G1 X128.877 Y108.417 E.02099 +;WIDTH:0.499999 +G1 X128.652 Y108.535 E.00966 +G1 X128.099 Y108.594 E.02114 +G3 X127.263 Y108.051 I1.131 J-2.656 E.03808 +G1 X127.182 Y108.168 E.00541 +G3 X126.493 Y108.547 I-1.19 J-1.35 E.03013 +G1 X126.086 Y108.378 E.01675 +G1 X125.944 Y108.435 E.00582 +G1 X125.43 Y108.432 E.01954 +G1 X125.401 Y108.361 E.00291 +;WIDTH:0.460973 +G1 X125.373 Y108.289 E.00269 +;WIDTH:0.421947 +G1 X125.344 Y108.217 E.00245 +G1 X125.316 Y108.289 E.00243 +;WIDTH:0.460973 +G1 X125.287 Y108.361 E.0027 +;WIDTH:0.499999 +G1 X125.258 Y108.432 E.00291 +G1 X124.739 Y108.435 E.01973 +G1 X124.563 Y108.381 E.007 +G3 X124.215 Y108.52 I-.343 J-.353 E.01461 +G3 X123.942 Y108.5 I-.109 J-.391 E.01061 +G1 X123.511 Y108.193 E.02011 +G3 X123.34 Y107.681 I.796 J-.55 E.02079 +;WIDTH:0.467744 +G1 X123.356 Y107.494 E.00663 +;WIDTH:0.464522 +G1 X123.25 Y107.751 E.00975 +;WIDTH:0.499999 +G1 X123.143 Y108.008 E.01058 +G1 X122.713 Y108.436 E.02306 +G1 X122.462 Y108.571 E.01083 +G3 X121.647 Y108.596 I-.46 J-1.679 E.03128 +G1 X121.385 Y108.48 E.01089 +;WIDTH:0.502168 +G1 X120.975 Y108.103 E.02127 +;WIDTH:0.52159 +G3 X120.757 Y107.454 I1.317 J-.805 E.02748 +;WIDTH:0.50575 +G1 X120.731 Y106.845 E.02346 +;WIDTH:0.50628 +G1 X120.787 Y106.273 E.02214 +G1 X120.97 Y105.806 E.01932 +;WIDTH:0.499999 +G3 X121.57 Y105.322 I1.337 J1.042 E.02956 +G1 X122.086 Y105.23 E.01992 +;WIDTH:0.540219 +G1 X122.296 Y105.232 E.00868 +;WIDTH:0.580438 +G3 X122.716 Y105.361 I.073 J.508 E.02029 +;WIDTH:0.540219 +G1 X122.927 Y105.488 E.01018 +;WIDTH:0.499999 +G3 X123.051 Y105.763 I-.305 J.304 E.01171 +G3 X123.298 Y106.156 I-.491 J.583 E.01793 +;WIDTH:0.464031 +G1 X123.354 Y106.326 E.00627 +G1 X123.45 Y106.232 E.0047 +;WIDTH:0.499999 +G1 X123.547 Y106.139 E.00511 +G1 X124.065 Y106.139 E.01969 +G1 X124.184 Y106.172 E.00469 +G1 X124.198 Y106.238 E.00256 +;WIDTH:0.454392 +G1 X124.211 Y106.303 E.00227 +;WIDTH:0.408785 +G1 X124.225 Y106.368 E.00202 +;WIDTH:0.363177 +G1 X124.228 Y107.258 E.0237 +;WIDTH:0.361437 +G1 X124.237 Y107.564 E.00811 +;WIDTH:0.360125 +G1 X124.322 Y107.566 E.00224 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X124.382 Y107.568 E-.01248 +G3 X124.538 Y107.407 I.234 J.071 E-.04839 +G1 X124.541 Y106.93 E-.09913 +;WIPE_END +G1 X121.809 Y104.735 Z.261 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Perimeter +;WIDTH:0.650769 +G1 F1200 +G2 X120.979 Y105.045 I.184 J1.757 E.04526 +G2 X120.957 Y104.348 I-4.782 J-.196 E.03528 +;WIDTH:0.609275 +G1 X120.936 Y104.161 E.00886 +;WIDTH:0.567781 +G1 X120.916 Y103.975 E.00816 +;WIDTH:0.526287 +G1 X120.895 Y103.788 E.00756 +;WIDTH:0.484793 +G1 X120.875 Y102.984 E.02955 +;WIDTH:0.491105 +G1 X120.9 Y102.517 E.01743 +;WIDTH:0.538909 +G1 X120.924 Y102.05 E.01929 +;WIDTH:0.578634 +G1 X120.944 Y101.898 E.00683 +;WIDTH:0.618359 +G1 X120.964 Y101.745 E.00738 +;WIDTH:0.658084 +G1 X120.984 Y101.593 E.00784 +;WIDTH:0.697809 +G1 X121.005 Y101.441 E.00836 +;WIDTH:0.721386 +G1 X121.017 Y101.018 E.02387 +G1 X122.309 Y101.018 E.07289 +M73 Q9 S7 +G1 X121.836 Y101.339 E.03225 +M73 P9 R7 +G1 X121.618 Y101.601 E.01923 +;WIDTH:0.697809 +G1 X121.56 Y101.744 E.0084 +;WIDTH:0.658084 +G1 X121.503 Y101.886 E.00783 +;WIDTH:0.618359 +G1 X121.446 Y102.029 E.00737 +;WIDTH:0.578634 +G1 X121.389 Y102.171 E.00682 +;WIDTH:0.538909 +G1 X121.361 Y102.299 E.0054 +;WIDTH:0.510529 +G1 X121.333 Y102.426 E.00506 +;WIDTH:0.482148 +G1 X121.275 Y103.191 E.02802 +;WIDTH:0.48696 +G1 X121.337 Y103.777 E.02176 +;WIDTH:0.527913 +G1 X121.389 Y103.929 E.00648 +;WIDTH:0.568865 +G1 X121.441 Y104.08 E.00698 +;WIDTH:0.609817 +G1 X121.493 Y104.231 E.00753 +;WIDTH:0.650769 +G1 X121.544 Y104.382 E.00806 +G1 X121.773 Y104.687 E.01928 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.809 Y104.735 E-.01247 +G2 X121.136 Y104.947 I.183 J1.757 E-.14753 +;WIPE_END +G1 X123.049 Y100.91 Z.278 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.519434 +G1 F1200 +G1 X123.602 Y100.917 E.02191 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.049 Y100.91 E-.11493 +G1 X123.266 Y100.913 E-.04507 +;WIPE_END +G1 X126.96 Y105.112 Z.298 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.677889 +G1 F1200 +G1 X127.331 Y105.113 E.01959 +;WIDTH:0.636015 +G1 X127.502 Y105.116 E.00843 +;WIDTH:0.594141 +G1 X127.673 Y105.118 E.00784 +;WIDTH:0.552266 +G1 X127.933 Y105.122 E.01101 +;WIDTH:0.505874 +G1 X128.194 Y105.127 E.01005 +;WIDTH:0.499121 +G1 X128.285 Y105.103 E.00357 +;WIDTH:0.53876 +G1 X128.377 Y105.078 E.00393 +;WIDTH:0.578399 +G1 X128.468 Y105.054 E.00419 +;WIDTH:0.618038 +G2 X128.6 Y104.906 I-.004 J-.136 E.01063 +;WIDTH:0.575453 +G1 X128.639 Y104.783 E.00571 +;WIDTH:0.532868 +G1 X128.679 Y104.66 E.00527 +;WIDTH:0.490283 +G1 X128.719 Y104.537 E.00481 +;WIDTH:0.447698 +G1 X128.762 Y103.927 E.02058 +;WIDTH:0.418698 +G1 X128.764 Y103.415 E.016 +;WIDTH:0.447147 +G1 X128.719 Y102.883 E.01795 +;WIDTH:0.479719 +G1 X128.67 Y102.61 E.01007 +;WIDTH:0.51229 +G3 X128.672 Y101.832 I1.786 J-.385 E.0306 +;WIDTH:0.518145 +G1 X128.633 Y101.586 E.00984 +;WIDTH:0.558255 +G1 X128.594 Y101.339 E.01072 +G1 X128.476 Y101.298 E.00535 +;WIDTH:0.515813 +G1 X128.358 Y101.257 E.00491 +;WIDTH:0.473371 +G1 X128.24 Y101.216 E.00447 +;WIDTH:0.430929 +G1 X128.122 Y101.174 E.00404 +;WIDTH:0.388487 +G1 X128.004 Y101.133 E.00359 +;WIDTH:0.349043 +G1 X127.927 Y101.137 E.00196 +;WIDTH:0.382482 +G3 X126.519 Y101.182 I-1.068 J-11.158 E.0398 +;WIDTH:0.425889 +G1 X126.433 Y101.224 E.00305 +;WIDTH:0.471254 +G1 X126.346 Y101.265 E.00343 +;WIDTH:0.516619 +G1 X126.26 Y101.306 E.00375 +;WIDTH:0.561984 +G1 X126.174 Y101.347 E.00411 +;WIDTH:0.60735 +G1 X126.087 Y101.388 E.00451 +;WIDTH:0.652715 +G1 X126.001 Y101.43 E.00485 +;WIDTH:0.69808 +G1 X125.914 Y101.471 E.00524 +G1 X125.863 Y101.72 E.01385 +G2 X125.264 Y101.653 I-.507 J1.823 E.03298 +;WIDTH:0.724229 +G1 X124.85 Y101.763 E.02427 +G2 X124.273 Y101.081 I-1.865 J.992 E.05099 +G1 X124.15 Y101.019 E.0078 +G1 X125.056 Y101.014 E.05133 +;WIDTH:0.714178 +G1 X125.617 Y101.006 E.03132 +;WIDTH:0.69808 +G1 X125.765 Y100.983 E.00816 +;WIDTH:0.651885 +G1 X125.912 Y100.96 E.00753 +;WIDTH:0.60569 +G1 X126.06 Y100.937 E.00701 +;WIDTH:0.559494 +G1 X126.207 Y100.914 E.00639 +;WIDTH:0.513299 +G1 X126.355 Y100.891 E.00586 +;WIDTH:0.467104 +G1 X126.503 Y100.868 E.00528 +;WIDTH:0.420909 +G1 X126.65 Y100.844 E.00468 +;WIDTH:0.386419 +G2 X128.004 Y100.83 I.448 J-21.602 E.03868 +;WIDTH:0.388487 +G1 X128.164 Y100.851 E.00464 +;WIDTH:0.430929 +G1 X128.325 Y100.873 E.00524 +;WIDTH:0.473371 +G1 X128.486 Y100.894 E.00581 +;WIDTH:0.515813 +G1 X128.646 Y100.915 E.00635 +;WIDTH:0.558255 +G2 X129.064 Y100.936 I.311 J-1.985 E.01797 +G1 X129.085 Y101.527 E.02534 +;WIDTH:0.518145 +G1 X129.106 Y101.861 E.01322 +;WIDTH:0.478034 +G1 X129.122 Y102.859 E.03611 +;WIDTH:0.447147 +G1 X129.138 Y103.415 E.0187 +;WIDTH:0.418698 +G1 X129.137 Y103.945 E.01656 +;WIDTH:0.452272 +G1 X129.121 Y104.623 E.02308 +;WIDTH:0.493714 +G1 X129.101 Y104.778 E.00586 +;WIDTH:0.535155 +G1 X129.08 Y104.933 E.0064 +;WIDTH:0.576597 +G1 X129.059 Y105.089 E.00699 +;WIDTH:0.618038 +G2 X129.048 Y105.684 I2.867 J.353 E.02851 +G2 X128.581 Y105.53 I-.428 J.512 E.02408 +;WIDTH:0.578399 +G1 X128.456 Y105.533 E.00557 +;WIDTH:0.53876 +G1 X128.331 Y105.535 E.00515 +;WIDTH:0.499121 +G1 X128.206 Y105.538 E.00474 +;WIDTH:0.505874 +G1 X127.977 Y105.561 E.00886 +;WIDTH:0.552266 +G1 X127.749 Y105.585 E.00971 +;WIDTH:0.594141 +G1 X127.596 Y105.652 E.00766 +;WIDTH:0.636015 +G1 X127.443 Y105.719 E.00824 +;WIDTH:0.677889 +G1 X127.291 Y105.786 E.00877 +G1 X126.837 Y105.566 E.02664 +G2 X126.659 Y105.111 I-1.017 J.135 E.02605 +G1 X126.9 Y105.112 E.01272 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.96 Y105.112 E-.01247 +G1 X127.331 Y105.113 E-.0771 +G1 X127.502 Y105.116 E-.03554 +G1 X127.67 Y105.118 E-.03489 +;WIPE_END +G1 X129.006 Y108.992 Z.272 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.701232 +G1 F1200 +G1 X128.821 Y109.013 E.01019 +;WIDTH:0.660837 +G1 X128.636 Y109.033 E.00956 +;WIDTH:0.620441 +G1 X128.451 Y109.053 E.00894 +;WIDTH:0.580046 +G1 X128.266 Y109.073 E.00831 +;WIDTH:0.56184 +G1 X127.96 Y109.062 E.01321 +M204 P1000 +G1 X127.96 Y109.062 F12000 +G1 X127.246 Y108.679 +M204 P600 +;WIDTH:0.45808 +G1 F1200 +G1 X127.536 Y108.79 E.01072 +;WIDTH:0.492667 +G1 X127.677 Y108.88 E.00626 +;WIDTH:0.527254 +G1 X127.818 Y108.971 E.00676 +;WIDTH:0.56184 +G1 X127.96 Y109.062 E.00728 +G1 X127.839 Y109.078 E.00527 +;WIDTH:0.530278 +G1 X127.512 Y109.096 E.01327 +;WIDTH:0.494179 +G1 X127.185 Y109.114 E.01229 +;WIDTH:0.505369 +G1 X126.997 Y109.09 E.00729 +;WIDTH:0.552658 +G1 X126.809 Y109.067 E.00803 +;WIDTH:0.599946 +G1 X126.621 Y109.043 E.00878 +G1 X126.705 Y108.998 E.00441 +;WIDTH:0.564712 +G1 X126.789 Y108.953 E.00413 +;WIDTH:0.529477 +G1 X127.018 Y108.816 E.0108 +;WIDTH:0.493779 +G1 X127.195 Y108.709 E.00775 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.246 Y108.679 E-.0123 +G1 X127.536 Y108.79 E-.06453 +G1 X127.677 Y108.88 E-.03476 +G1 X127.818 Y108.971 E-.03487 +G1 X127.873 Y109.006 E-.01354 +;WIPE_END +G1 X121.677 Y109.08 Z.308 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.526258 +G1 F1200 +G1 X121.573 Y109.096 E.00423 +;WIDTH:0.494638 +G1 X121.468 Y109.111 E.00398 +;WIDTH:0.463018 +G1 X121.162 Y109.126 E.0107 +;WIDTH:0.434539 +G1 X120.856 Y109.14 E.00997 +;WIDTH:0.406059 +G1 X120.834 Y108.547 E.01792 +G1 X121.146 Y108.814 E.0124 +;WIDTH:0.446126 +G1 X121.323 Y108.903 E.00664 +;WIDTH:0.486192 +G1 X121.5 Y108.991 E.00729 +;WIDTH:0.526258 +G1 X121.623 Y109.053 E.00554 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.677 Y109.08 E-.01255 +G1 X121.573 Y109.096 E-.02187 +G1 X121.468 Y109.111 E-.02204 +G1 X121.162 Y109.126 E-.06367 +G1 X120.97 Y109.135 E-.03987 +;WIPE_END +G1 X124.069 Y109.044 Z.254 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.597824 +G1 F1200 +G1 X123.847 Y109.069 E.01031 +;WIDTH:0.548582 +G1 X123.626 Y109.093 E.00935 +;WIDTH:0.499339 +G1 X122.765 Y109.093 E.03268 +;WIDTH:0.505098 +G1 X122.239 Y109.09 E.02021 +G1 X122.561 Y109.017 E.01269 +;WIDTH:0.500313 +G1 X122.87 Y108.881 E.01284 +;WIDTH:0.499339 +G1 X123.241 Y108.555 E.01874 +G1 X123.69 Y108.881 E.02106 +;WIDTH:0.548582 +G1 X123.879 Y108.962 E.00865 +;WIDTH:0.597824 +G1 X124.014 Y109.02 E.00678 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X124.069 Y109.044 E-.01247 +G1 X123.847 Y109.069 E-.04643 +G1 X123.626 Y109.093 E-.0462 +G1 X123.362 Y109.093 E-.0549 +;WIPE_END +G1 X122.239 Y109.09 Z.22 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.526258 +G1 F1200 +G1 X121.677 Y109.08 E.02259 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.239 Y109.09 E-.11681 +G1 X122.031 Y109.086 E-.04319 +;WIPE_END +G1 X125.278 Y108.828 Z.257 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.371873 +G1 F1200 +G1 X125.465 Y108.828 E.00511 +;WIDTH:0.376213 +G1 X126.069 Y108.807 E.01675 +;WIDTH:0.418747 +G1 X126.154 Y108.856 E.00307 +;WIDTH:0.461281 +G1 X126.239 Y108.904 E.0034 +;WIDTH:0.503815 +G1 X126.324 Y108.952 E.00374 +;WIDTH:0.546349 +G1 X126.409 Y109 E.00409 +;WIDTH:0.588882 +G1 X126.493 Y109.049 E.00441 +G1 X126.411 Y109.07 E.00384 +;WIDTH:0.544953 +G1 X126.329 Y109.092 E.00354 +;WIDTH:0.501024 +G1 X126.247 Y109.114 E.00323 +;WIDTH:0.457095 +G1 X126.165 Y109.136 E.00292 +;WIDTH:0.413166 +G1 X125.944 Y109.157 E.00683 +;WIDTH:0.3765 +G1 X124.739 Y109.157 E.03342 +;WIDTH:0.42175 +G1 X124.649 Y109.132 E.00294 +;WIDTH:0.471629 +G1 X124.558 Y109.107 E.00336 +;WIDTH:0.521508 +G1 X124.468 Y109.082 E.00372 +;WIDTH:0.571387 +G1 X124.378 Y109.057 E.0041 +;WIDTH:0.621266 +G1 X124.287 Y109.032 E.00454 +G1 X124.352 Y108.994 E.00362 +;WIDTH:0.573254 +G1 X124.417 Y108.957 E.0033 +;WIDTH:0.525241 +G1 X124.482 Y108.919 E.00302 +;WIDTH:0.477228 +G1 X124.547 Y108.881 E.00272 +;WIDTH:0.429215 +G1 X124.611 Y108.843 E.00239 +;WIDTH:0.381202 +G1 X124.739 Y108.828 E.00363 +;WIDTH:0.371873 +G1 X125.218 Y108.828 E.0131 +M204 P1000 +G1 X125.218 Y108.828 F12000 +G1 X124.287 Y109.032 +M204 P600 +;WIDTH:0.621266 +G1 F1200 +G1 X124.069 Y109.044 E.0105 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X124.287 Y109.032 E-.04537 +G1 X124.069 Y109.044 E-.04537 +;WIPE_END +G1 E-.06926 F2100 +G1 X124.54 Y105.038 Z.27 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.696293 +G1 F1200 +G2 X124.808 Y105.183 I1.045 J-1.612 E.01657 +G2 X124.653 Y105.588 I.978 J.606 E.0237 +G1 X124.385 Y105.659 E.01506 +G1 X124.236 Y105.602 E.00867 +G1 X123.637 Y105.583 E.03256 +G1 X123.418 Y105.27 E.02075 +G2 X123.95 Y105.139 I-.178 J-1.869 E.02987 +G1 X124.317 Y104.87 E.02472 +G2 X124.49 Y105.005 I1.268 J-1.444 E.01193 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X124.54 Y105.038 E-.01245 +G2 X124.808 Y105.183 I1.045 J-1.612 E-.06339 +G2 X124.659 Y105.557 I.978 J.606 E-.08416 +;WIPE_END +G1 X129.553 Y109.55 Z.31 F12000 +G1 Z.2 F720 +M73 P10 R7 +M73 Q10 S7 +G1 E.8 F1500 +M204 P600 +;TYPE:External perimeter +;WIDTH:0.499999 +G1 F1200 +G1 X120.448 Y109.55 E.34605 +G1 X120.421 Y108.487 E.04041 +;WIDTH:0.519268 +G1 X120.294 Y107.468 E.04067 +;WIDTH:0.50575 +G1 X120.283 Y106.784 E.02633 +;WIDTH:0.513833 +G1 X120.415 Y105.515 E.04996 +;WIDTH:0.499999 +G1 X120.444 Y105.001 E.01957 +G1 X120.45 Y100.45 E.17297 +G1 X129.55 Y100.45 E.34586 +G1 X129.554 Y104.623 E.1586 +G2 X129.62 Y106.326 I14.186 J.304 E.06481 +;WIDTH:0.504854 +G1 X129.702 Y107.03 E.02722 +G1 X129.694 Y107.449 E.0161 +;WIDTH:0.513692 +G3 X129.606 Y108.479 I-6.395 J-.031 E.04051 +;WIDTH:0.499999 +G2 X129.555 Y109.492 I9.977 J1.013 E.03857 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.553 Y109.55 E-.01206 +G1 X128.841 Y109.55 E-.14794 +;WIPE_END +G1 X126.241 Y106.98 Z.264 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.399722 +G1 F1200 +G3 X126.345 Y106.922 I.153 J.153 E.00358 +G1 X126.478 Y106.973 E.00423 +G1 X126.534 Y107.14 E.00523 +;WIDTH:0.401346 +G1 X126.533 Y107.47 E.00984 +G1 X126.432 Y107.63 E.00564 +G3 X126.198 Y107.492 I-.04 J-.199 E.00887 +G1 X126.178 Y107.137 E.0106 +;WIDTH:0.399722 +G3 X126.205 Y107.028 I.216 J-.004 E.00337 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.241 Y106.98 E-.01247 +G3 X126.345 Y106.922 I.153 J.153 E-.02507 +G1 X126.478 Y106.973 E-.0296 +G1 X126.534 Y107.14 E-.0366 +G1 X126.533 Y107.411 E-.05626 +;WIPE_END +G1 X125.329 Y103.787 Z.267 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.4417 +G1 F1200 +G1 X125.435 Y103.764 E.0036 +M204 P1000 +M106 S127.5 +;LAYER_CHANGE +;Z:0.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.329 Y103.787 E-.02254 +G1 X125.435 Y103.764 E-.02254 +;WIPE_END +G1 E-.11492 F2100 +G1 X125.435 Y103.764 Z.2 F12000 +G1 X129.368 Y109.368 Z.4 +;AFTER_LAYER_CHANGE +;0.4 +M104 S210 ; set temperature +G1 X129.368 Y109.368 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2036.965 +G1 X120.632 Y109.368 E.2957 +G1 F2039.355 +G1 X120.632 Y100.632 E.2957 +G1 F2036.968 +G1 X129.368 Y100.632 E.2957 +G1 F2039.165 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2017.066 +G1 X129.1 Y109.775 E.02285 +G1 F2023.803 +G1 X120.9 Y109.775 E.27756 +G1 F2019.035 +G1 X120.225 Y109.775 E.02285 +G1 F2017.172 +G1 X120.225 Y109.1 E.02285 +G1 F2026.691 +G1 X120.225 Y100.9 E.27756 +G1 F2018.991 +G1 X120.225 Y100.225 E.02285 +G1 F2016.553 +G1 X120.9 Y100.225 E.02285 +G1 F2023.806 +G1 X129.1 Y100.225 E.27756 +G1 F2018.89 +M73 P11 R7 +M73 Q11 S7 +G1 X129.775 Y100.225 E.02285 +G1 F2016.617 +G1 X129.775 Y100.9 E.02285 +G1 F2031.576 +G1 X129.775 Y109.1 E.27756 +G1 F2020.299 +G1 X129.775 Y109.715 E.02082 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.1 Y109.775 E-.14027 +G1 X129.065 Y109.775 E-.00726 +;WIPE_END +G1 X122.183 Y100.815 Z.597 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.465052 +G2 X121.129 Y101.9 I.915 J1.944 E.05426 +G1 X122.715 Y100.998 E.06404 +G1 X123.569 Y100.998 E.02998 +G1 X120.998 Y102.46 E.10381 +M73 Q11 S6 +G1 X120.998 Y102.946 E.01706 +G1 X124.423 Y100.998 E.1383 +G1 X124.738 Y100.998 E.01106 +M73 P11 R6 +G1 X124.958 Y101.18 E.01002 +G1 X120.998 Y103.431 E.15988 +G1 X120.998 Y103.917 E.01706 +G1 X126.131 Y100.998 E.20727 +G1 X126.985 Y100.998 E.02998 +G1 X121.151 Y104.316 E.23558 +G1 X121.25 Y104.531 E.00831 +G1 X120.998 Y104.674 E.01017 +G1 X120.998 Y104.888 E.00751 +G1 X127.839 Y100.998 E.27623 +G1 X128.693 Y100.998 E.02998 +G1 X120.998 Y105.374 E.31072 +G1 X120.998 Y105.859 E.01702 +G1 X129.002 Y101.308 E.32318 +G1 X129.002 Y101.794 E.01706 +G1 X120.998 Y106.345 E.32318 +G1 X120.998 Y106.83 E.01702 +G1 X129.002 Y102.28 E.32317 +G1 X129.002 Y102.765 E.01702 +G1 X120.998 Y107.316 E.32318 +G1 X120.998 Y107.802 E.01706 +G1 X129.002 Y103.251 E.32318 +G1 X129.002 Y103.736 E.01702 +M73 P12 R6 +M73 Q12 S6 +G1 X120.998 Y108.287 E.32318 +G1 X120.998 Y108.773 E.01706 +G1 X129.002 Y104.222 E.32318 +G1 X129.002 Y104.708 E.01706 +G1 X121.45 Y109.002 E.30493 +G1 X122.304 Y109.002 E.02998 +G1 X128.945 Y105.225 E.26816 +G1 X129.002 Y105.256 E.00228 +G1 X129.002 Y105.679 E.01485 +G1 X123.158 Y109.002 E.23597 +G1 X124.012 Y109.002 E.02998 +G1 X129.002 Y106.164 E.2015 +G1 X129.002 Y106.65 E.01706 +G1 X124.866 Y109.002 E.16701 +G1 X125.72 Y109.001 E.02998 +G1 X129.002 Y107.136 E.1325 +G1 X129.002 Y107.621 E.01702 +G1 X126.574 Y109.002 E.09804 +G1 X127.428 Y109.002 E.02998 +G1 X129.002 Y108.107 E.06356 +G1 X129.002 Y108.592 E.01702 +G1 X127.96 Y109.185 E.04208 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X128.629 Y108.804 E-.16 +;WIPE_END +G1 X121.145 Y101.059 Z.588 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.607526 +G1 F7970.16 +G2 X121.127 Y101.127 I-.065 J.019 E.01649 +M204 P1000 +M106 S255 +;LAYER_CHANGE +;Z:0.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X121.145 Y101.059 I-.047 J-.049 E-.07298 +;WIPE_END +G1 E-.08702 F2100 +G1 X121.145 Y101.059 Z.4 F12000 +G1 X129.368 Y109.368 Z.604 +;AFTER_LAYER_CHANGE +;0.6 +G1 X129.368 Y109.368 +G1 Z.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1947 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M73 P13 R6 +M73 Q13 S6 +M204 P800 +;TYPE:External perimeter +G1 F1947 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X121.031 Y108.891 Z.741 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F1947 +G2 X121.093 Y108.907 I.018 J.058 E.01318 +M204 P1000 +G1 X121.093 Y108.907 F12000 +G1 X121.888 Y109.134 +M204 P1500 +;WIDTH:0.38292 +G1 F1947 +G1 X121.869 Y109.205 E.00208 +G2 X121.929 Y109.145 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X121.836 Y109.073 E.00365 +;WIDTH:0.449999 +G1 X121.743 Y109.002 E.00396 +G1 X120.998 Y108.257 E.03566 +G1 X120.927 Y108.164 E.00396 +;WIDTH:0.41646 +G1 X120.855 Y108.071 E.00365 +;WIDTH:0.38292 +G3 X120.795 Y108.131 I-.04 J.02 E.00315 +G1 X120.814 Y108.06 E.00208 +;WIDTH:0.449999 +G1 X120.998 Y107.682 E.01423 +G1 X122.318 Y109.002 E.06319 +G1 X122.894 Y109.002 E.0195 +G1 X120.998 Y107.106 E.09076 +G1 X120.998 Y106.53 E.0195 +G1 X123.47 Y109.002 E.11833 +G1 X124.046 Y109.002 E.0195 +G1 X120.998 Y105.954 E.14591 +G1 X120.998 Y105.379 E.01946 +G1 X124.621 Y109.002 E.17343 +G1 X125.197 Y109.002 E.0195 +G1 X120.998 Y104.803 E.201 +G1 X120.998 Y104.227 E.0195 +G1 X125.773 Y109.002 E.22858 +G1 X126.348 Y109.002 E.01946 +G1 X120.998 Y103.652 E.2561 +G1 X120.998 Y103.076 E.0195 +G1 X126.924 Y109.002 E.28367 +G1 X127.5 Y109.002 E.0195 +G1 X120.998 Y102.5 E.31125 +G1 X120.998 Y101.925 E.01946 +G1 X128.075 Y109.002 E.33877 +G1 X128.651 Y109.002 E.0195 +G1 X120.998 Y101.349 E.36634 +G1 X120.898 Y100.868 E.01663 +;WIDTH:0.38292 +G1 X120.879 Y100.939 E.00208 +G2 X120.939 Y100.879 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X121.081 Y100.939 E.00479 +;WIDTH:0.449999 +G1 X121.224 Y100.998 E.00524 +G1 X129.002 Y108.776 E.37233 +G1 X129.061 Y108.919 E.00524 +M73 P14 R6 +M73 Q14 S6 +;WIDTH:0.41646 +G1 X129.121 Y109.061 E.00479 +;WIDTH:0.38292 +G3 X129.061 Y109.121 I-.04 J.02 E.00315 +G1 X129.08 Y109.05 E.00208 +M204 P1000 +G1 X129.002 Y108.201 F12000 +M204 P1500 +;WIDTH:0.449999 +G1 F1947 +G1 X121.799 Y100.998 E.3448 +G1 X122.375 Y100.998 E.0195 +G1 X129.002 Y107.625 E.31723 +G1 X129.002 Y107.049 E.0195 +G1 X122.951 Y100.998 E.28966 +G1 X123.527 Y100.998 E.0195 +G1 X129.002 Y106.473 E.26208 +G1 X129.002 Y105.898 E.01946 +G1 X124.102 Y100.998 E.23456 +G1 X124.678 Y100.998 E.0195 +G1 X129.002 Y105.322 E.20699 +G1 X129.002 Y104.746 E.0195 +G1 X125.254 Y100.998 E.17941 +G1 X125.829 Y100.998 E.01946 +G1 X129.002 Y104.171 E.15189 +G1 X129.002 Y103.595 E.0195 +G1 X126.405 Y100.998 E.12432 +G1 X126.813 Y100.784 E.01559 +;WIDTH:0.38292 +G1 X126.794 Y100.855 E.00208 +G2 X126.854 Y100.795 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X126.918 Y100.897 E.00374 +;WIDTH:0.449999 +G1 X126.981 Y100.998 E.00403 +G1 X129.002 Y103.019 E.09674 +G1 X129.083 Y103.077 E.00337 +;WIDTH:0.41646 +G1 X129.164 Y103.135 E.00309 +;WIDTH:0.38292 +G1 X129.145 Y103.206 E.00208 +G2 X129.205 Y103.146 I.02 J-.04 E.00315 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X129.145 Y103.206 I-.04 J.02 E-.02315 +G1 X129.164 Y103.135 E-.01527 +G1 X129.083 Y103.077 E-.0207 +G1 X129.002 Y103.019 E-.0207 +G1 X128.729 Y102.746 E-.08018 +;WIPE_END +G1 X127.7 Y101.142 Z.633 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1947 +G1 X129.002 Y102.444 E.06233 +G1 X129.002 Y100.998 E.04895 +G1 X127.556 Y100.998 E.04895 +M204 P1000 +G1 X127.556 Y100.998 F12000 +G1 X128.564 Y101.369 +M204 P1500 +;WIDTH:0.48239 +G1 F1947 +G2 X128.617 Y101.383 I.015 J.05 E.00999 +M204 P1000 +;LAYER_CHANGE +;Z:0.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X128.564 Y101.369 I-.038 J.036 E-.05679 +;WIPE_END +G1 E-.10321 F2100 +G1 X128.564 Y101.369 Z.6 F12000 +G1 X129.368 Y109.368 Z.8 +;AFTER_LAYER_CHANGE +;0.8 +G1 X129.368 Y109.368 +G1 Z.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1951 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1951 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +M73 P15 R6 +M73 Q15 S6 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X121.109 Y101.031 Z1.006 F12000 +G1 Z.8 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F1951 +G2 X121.093 Y101.093 I-.058 J.018 E.01318 +M204 P1000 +G1 X121.093 Y101.093 F12000 +G1 X120.866 Y101.888 +M204 P1500 +;WIDTH:0.38292 +G1 F1951 +G1 X120.795 Y101.869 E.00208 +G2 X120.855 Y101.929 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X120.927 Y101.836 E.00365 +;WIDTH:0.449999 +G1 X120.998 Y101.743 E.00396 +G1 X121.743 Y100.998 E.03566 +G1 X121.836 Y100.927 E.00396 +;WIDTH:0.41646 +G1 X121.929 Y100.855 E.00365 +;WIDTH:0.38292 +G3 X121.869 Y100.795 I-.02 J-.04 E.00315 +G1 X121.94 Y100.814 E.00208 +;WIDTH:0.449999 +G1 X122.318 Y100.998 E.01423 +G1 X120.998 Y102.318 E.06319 +G1 X120.998 Y102.894 E.0195 +G1 X122.894 Y100.998 E.09076 +G1 X123.47 Y100.998 E.0195 +G1 X120.998 Y103.47 E.11833 +G1 X120.998 Y104.046 E.0195 +G1 X124.046 Y100.998 E.14591 +G1 X124.621 Y100.998 E.01946 +G1 X120.998 Y104.621 E.17343 +G1 X120.998 Y105.197 E.0195 +G1 X125.197 Y100.998 E.201 +G1 X125.773 Y100.998 E.0195 +G1 X120.998 Y105.773 E.22858 +G1 X120.998 Y106.348 E.01946 +G1 X126.348 Y100.998 E.2561 +G1 X126.924 Y100.998 E.0195 +G1 X120.998 Y106.924 E.28367 +G1 X120.998 Y107.5 E.0195 +G1 X127.5 Y100.998 E.31125 +G1 X128.075 Y100.998 E.01946 +G1 X120.998 Y108.075 E.33877 +G1 X120.998 Y108.651 E.0195 +G1 X128.651 Y100.998 E.36634 +G1 X129.132 Y100.898 E.01663 +;WIDTH:0.38292 +G1 X129.061 Y100.879 E.00208 +G2 X129.121 Y100.939 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X129.061 Y101.081 E.00479 +;WIDTH:0.449999 +G1 X129.002 Y101.224 E.00524 +G1 X121.224 Y109.002 E.37233 +G1 X121.081 Y109.061 E.00524 +M73 Q16 S6 +;WIDTH:0.41646 +G1 X120.939 Y109.121 E.00479 +;WIDTH:0.38292 +G3 X120.879 Y109.061 I-.02 J-.04 E.00315 +M73 P16 R6 +G1 X120.95 Y109.08 E.00208 +M204 P1000 +G1 X120.95 Y109.08 F12000 +G1 X121.799 Y109.002 +M204 P1500 +;WIDTH:0.449999 +G1 F1951 +G1 X129.002 Y101.799 E.3448 +G1 X129.002 Y102.375 E.0195 +G1 X122.375 Y109.002 E.31723 +G1 X122.951 Y109.002 E.0195 +G1 X129.002 Y102.951 E.28966 +G1 X129.002 Y103.527 E.0195 +G1 X123.527 Y109.002 E.26208 +G1 X124.102 Y109.002 E.01946 +G1 X129.002 Y104.102 E.23456 +G1 X129.002 Y104.678 E.0195 +G1 X124.678 Y109.002 E.20699 +G1 X125.254 Y109.002 E.0195 +G1 X129.002 Y105.254 E.17941 +G1 X129.002 Y105.829 E.01946 +G1 X125.829 Y109.002 E.15189 +G1 X126.405 Y109.002 E.0195 +G1 X129.002 Y106.405 E.12432 +G1 X129.216 Y106.813 E.01559 +;WIDTH:0.38292 +G1 X129.145 Y106.794 E.00208 +G2 X129.205 Y106.854 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X129.103 Y106.918 E.00374 +;WIDTH:0.449999 +G1 X129.002 Y106.981 E.00403 +G1 X126.981 Y109.002 E.09674 +G1 X126.918 Y109.103 E.00403 +;WIDTH:0.41646 +G1 X126.854 Y109.205 E.00374 +;WIDTH:0.38292 +G3 X126.794 Y109.145 I-.02 J-.04 E.00315 +G1 X126.865 Y109.164 E.00208 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.794 Y109.145 E-.01527 +G2 X126.854 Y109.205 I.04 J.02 E-.02315 +G1 X126.918 Y109.103 E-.02502 +G1 X126.981 Y109.002 E-.02474 +G1 X127.225 Y108.758 E-.07182 +;WIPE_END +G1 X128.858 Y107.7 Z.834 F12000 +G1 Z.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1951 +G1 X127.556 Y109.002 E.06233 +G1 X129.002 Y109.002 E.04895 +G1 X129.002 Y107.556 E.04895 +M204 P1000 +G1 X129.002 Y107.556 F12000 +G1 X128.631 Y108.564 +M204 P1500 +;WIDTH:0.48239 +G1 F1951 +G2 X128.617 Y108.617 I-.05 J.015 E.00999 +M204 P1000 +;LAYER_CHANGE +;Z:1 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1 + + +G1 X128.617 Y108.617 Z.8 F12000 +G1 X129.368 Y109.368 Z1 F4313.133 +;AFTER_LAYER_CHANGE +;1 +G1 X129.368 Y109.368 F12000 +G1 Z1 F720 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1545 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1545 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +M73 P17 R6 +M73 Q17 S6 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.687 Y108.425 Z1.024 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1545 +G1 X129.002 Y108.197 E.01316 +G3 X128.674 Y109.002 I-.692 J.188 E.03162 +G1 X129.002 Y109.002 E.0111 +G1 X128.496 Y108.496 E.02422 +G3 X127.273 Y108.156 I-.332 J-1.174 E.04519 +G3 X126.19 Y108.485 I-.78 J-.622 E.04073 +G2 X125.986 Y108.494 I-.098 J.09 E.00789 +G1 X125.428 Y108.494 E.01889 +G2 X125.261 Y108.494 I-.084 J.02 E.00779 +G1 X124.702 Y108.494 E.01892 +G1 X124.632 Y108.424 E.00335 +G3 X123.298 Y107.887 I-.458 J-.787 E.05611 +G3 X121.566 Y108.434 I-1.182 J-.727 E.06702 +G1 X120.998 Y109.002 E.02719 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X123.326 Y109.002 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y109.002 E-.16 +;WIPE_END +G1 X123.259 Y106.741 Z1.041 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +G1 F1545 +G1 X122.958 Y107.042 E.01441 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.259 Y106.741 E-.08846 +;WIPE_END +G1 E-.07154 F2100 +G1 X121.094 Y106.888 Z1.038 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.398614 +G1 F1545 +G1 X121.099 Y107.236 E.01029 +;WIDTH:0.405198 +G2 X121.394 Y107.873 I1.397 J-.26 E.02137 +;WIDTH:0.380517 +G1 X121.723 Y108.09 E.01106 +;WIDTH:0.369376 +G1 X122.03 Y108.158 E.00854 +;WIDTH:0.368209 +G1 X122.411 Y108.129 E.01034 +;WIDTH:0.382998 +G1 X122.752 Y107.943 E.01098 +;WIDTH:0.397648 +G1 X122.891 Y107.798 E.00592 +;WIDTH:0.401323 +G1 X123.071 Y107.506 E.01022 +G1 X122.742 Y107.375 E.01055 +G1 X122.479 Y107.725 E.01305 +;WIDTH:0.38754 +G1 X122.229 Y107.836 E.00784 +;WIDTH:0.369376 +G1 X121.857 Y107.8 E.01015 +;WIDTH:0.38205 +G1 X121.596 Y107.584 E.00955 +;WIDTH:0.397184 +G1 X121.45 Y107.201 E.01207 +;WIDTH:0.399448 +G1 X121.447 Y106.692 E.01509 +G1 X121.622 Y106.265 E.01368 +;WIDTH:0.379306 +G1 X121.899 Y106.08 E.00932 +;WIDTH:0.367112 +G1 X122.266 Y106.055 E.00992 +;WIDTH:0.390378 +G1 X122.553 Y106.202 E.00932 +;WIDTH:0.401831 +G1 X122.721 Y106.414 E.00807 +G1 X123.03 Y106.324 E.0096 +G1 X122.88 Y106.037 E.00966 +G1 X122.598 Y105.826 E.01051 +;WIDTH:0.379693 +G1 X122.265 Y105.738 E.00965 +;WIDTH:0.359584 +G1 X121.84 Y105.77 E.01122 +;WIDTH:0.367182 +G1 X121.623 Y105.852 E.00625 +;WIDTH:0.378878 +G1 X121.335 Y106.088 E.0104 +;WIDTH:0.394604 +G1 X121.132 Y106.505 E.01356 +;WIDTH:0.399448 +G1 X121.091 Y106.684 E.00544 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.132 Y106.505 E-.03816 +G1 X121.335 Y106.088 E-.09638 +G1 X121.43 Y106.01 E-.02546 +;WIPE_END +G1 X123.537 Y107.488 Z1.045 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.406743 +G1 F1545 +G2 X123.635 Y107.338 I-.054 J-.142 E.0058 +;WIDTH:0.38861 +G1 X123.634 Y106.535 E.02308 +;WIDTH:0.386917 +G1 X123.676 Y106.455 E.00258 +G1 X123.947 Y106.455 E.00775 +G1 X123.978 Y106.626 E.00497 +;WIDTH:0.391198 +G1 X124.01 Y107.723 E.03178 +;WIDTH:0.439652 +G1 X124.238 Y107.949 E.01059 +;WIDTH:0.475502 +G1 X124.323 Y107.959 E.00308 +;WIDTH:0.511351 +G1 X124.407 Y107.97 E.0033 +;WIDTH:0.5472 +G1 X124.492 Y107.98 E.00359 +G1 X124.413 Y108.026 E.00383 +;WIDTH:0.511351 +G1 X124.333 Y108.072 E.00359 +;WIDTH:0.475502 +G1 X124.254 Y108.117 E.00327 +;WIDTH:0.439652 +G1 X124.144 Y108.124 E.00364 +;WIDTH:0.414236 +G1 X124.034 Y108.131 E.0034 +;WIDTH:0.388819 +G3 X123.728 Y107.928 I.173 J-.594 E.01072 +;WIDTH:0.397129 +G1 X123.647 Y107.655 E.00839 +M204 P1000 +G1 X123.647 Y107.655 F12000 +G1 X124.557 Y107.962 +M204 P1500 +;WIDTH:0.543758 +G1 F1545 +G1 X124.625 Y107.889 E.00415 +;WIDTH:0.51752 +G1 X124.693 Y107.817 E.00391 +;WIDTH:0.491281 +G1 X124.787 Y107.626 E.00794 +;WIDTH:0.449999 +G1 X124.845 Y106.486 E.03864 +G1 X125.359 Y106.47 E.01741 +G1 X125.519 Y106.322 E.00738 +;WIDTH:0.435487 +G1 X125.568 Y105.787 E.01754 +;WIDTH:0.386412 +G1 X125.843 Y105.771 E.00787 +G1 X125.896 Y105.824 E.00214 +;WIDTH:0.435487 +G1 X125.926 Y106.326 E.01642 +;WIDTH:0.484289 +G1 X126.02 Y106.424 E.00498 +;WIDTH:0.53309 +G1 X126.113 Y106.523 E.00554 +;WIDTH:0.562049 +G1 X126.183 Y106.542 E.00313 +;WIDTH:0.591008 +G1 X126.253 Y106.562 E.00332 +G1 X126.183 Y106.602 E.00367 +;WIDTH:0.562049 +G1 X126.114 Y106.643 E.00346 +;WIDTH:0.53309 +G1 X126.001 Y106.839 E.00922 +;WIDTH:0.491545 +G1 X125.889 Y107.035 E.00842 +;WIDTH:0.449999 +G1 X125.89 Y107.485 E.01523 +;WIDTH:0.495972 +G1 X126.021 Y107.805 E.01303 +;WIDTH:0.535951 +G1 X126.087 Y107.88 E.0041 +;WIDTH:0.57593 +G1 X126.153 Y107.955 E.00443 +G1 X126.04 Y107.966 E.00503 +;WIDTH:0.536626 +G1 X125.952 Y108.009 E.00402 +;WIDTH:0.493313 +G1 X125.864 Y108.051 E.00365 +;WIDTH:0.449999 +G1 X125.707 Y108.087 E.00545 +G2 X125.224 Y107.996 I-.395 J.77 E.01687 +G3 X124.881 Y108.087 I-.256 J-.274 E.01251 +;WIDTH:0.472947 +G1 X124.792 Y108.022 E.00394 +;WIDTH:0.484476 +G1 X124.754 Y108.012 E.00144 +M204 P1000 +G1 X124.754 Y108.012 F12000 +G1 X125.185 Y107.055 +M204 P1500 +;WIDTH:0.381231 +G1 F1545 +G1 X125.16 Y107.632 E.01625 +G3 X125.558 Y107.655 I.155 J.776 E.01134 +G3 X125.51 Y107.035 I2.016 J-.468 E.01756 +;WIDTH:0.365588 +G1 X125.642 Y106.712 E.00936 +G1 X125.501 Y106.807 E.00456 +G1 X125.189 Y106.851 E.00845 +M204 P1000 +G1 X125.189 Y106.851 F12000 +G1 X126.153 Y107.955 +M204 P1500 +;WIDTH:0.614142 +G1 F1545 +G1 X126.384 Y108.027 E.01149 +M204 P1000 +G1 X126.384 Y108.027 F12000 +G1 X126.6 Y106.66 +M204 P1500 +;WIDTH:0.501621 +G1 F1545 +G1 X126.61 Y106.667 E.00047 +;WIDTH:0.495589 +G1 X126.717 Y106.873 E.00874 +;WIDTH:0.463925 +G1 X126.824 Y107.079 E.00813 +;WIDTH:0.449999 +G1 X126.835 Y107.483 E.01368 +;WIDTH:0.491267 +G1 X126.559 Y107.914 E.01908 +;WIDTH:0.526824 +G1 X126.503 Y107.954 E.00277 +;WIDTH:0.56238 +G1 X126.446 Y107.995 E.00303 +;WIDTH:0.597936 +G1 X126.389 Y108.035 E.00321 +G1 X126.488 Y108.049 E.00461 +;WIDTH:0.54824 +G1 X126.586 Y108.063 E.00416 +;WIDTH:0.498543 +G1 X126.684 Y108.078 E.00376 +;WIDTH:0.448846 +G1 X126.764 Y108.066 E.00273 +;WIDTH:0.408151 +G1 X126.843 Y108.054 E.00243 +;WIDTH:0.408728 +G1 X126.991 Y107.896 E.00658 +;WIDTH:0.449999 +G1 X127.138 Y107.737 E.00733 +G1 X127.449 Y107.752 E.01054 +G1 X127.818 Y108.062 E.01631 +G2 X128.455 Y108.078 I.346 J-1.11 E.02185 +G2 X128.794 Y107.771 I-.428 J-.812 E.01565 +G3 X128.87 Y107.318 I.36 J-.172 E.01657 +G2 X128.68 Y106.69 I-1.517 J.115 E.02238 +G1 X128.466 Y106.522 E.00921 +G2 X127.939 Y106.472 I-.414 J1.591 E.018 +G2 X127.464 Y106.825 I.384 J1.015 E.02029 +G1 X127.187 Y106.845 E.0094 +G1 X127.115 Y106.792 E.00303 +;WIDTH:0.422388 +G1 X127.042 Y106.739 E.00285 +;WIDTH:0.394777 +G1 X126.906 Y106.575 E.00623 +;WIDTH:0.416701 +G1 X126.827 Y106.555 E.00253 +;WIDTH:0.456145 +G1 X126.748 Y106.534 E.00281 +;WIDTH:0.495589 +G1 X126.669 Y106.514 E.00307 +;WIDTH:0.53248 +G1 X126.589 Y106.526 E.00329 +;WIDTH:0.56937 +G1 X126.509 Y106.538 E.00354 +;WIDTH:0.60626 +G1 X126.253 Y106.562 E.01204 +M204 P1000 +G1 X126.253 Y106.562 F12000 +G1 X127.275 Y107.288 +M204 P1500 +;WIDTH:0.519924 +G1 F1545 +G1 X127.417 Y107.288 E.00563 +;WIDTH:0.561175 +G1 X127.676 Y107.236 E.01138 +;WIDTH:0.577127 +G1 X127.958 Y107.005 E.01619 +G1 X128.091 Y106.934 E.0067 +G1 X128.277 Y106.972 E.00843 +G1 X128.379 Y107.152 E.00919 +G1 X128.342 Y107.254 E.00482 +G1 X128.274 Y107.63 E.01697 +G1 X128.057 Y107.627 E.00964 +G1 X127.674 Y107.348 E.02105 +;WIDTH:0.561175 +G1 X127.615 Y107.334 E.00261 +;WIDTH:0.551674 +G1 X127.335 Y107.296 E.01195 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.615 Y107.334 E-.05872 +G1 X127.674 Y107.348 E-.0126 +G1 X128.019 Y107.6 E-.08868 +;WIPE_END +G1 X129.002 Y103.326 Z1.077 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1545 +G1 X129.002 Y101.326 E.0677 +G1 X128.674 Y100.998 E.0157 +M73 P18 R6 +M73 Q18 S6 +G1 X129.002 Y100.998 E.0111 +G1 X128.22 Y101.78 E.03743 +G1 X128.22 Y102.334 E.01875 +G2 X128.22 Y102.46 I.015 J.063 E.00584 +G1 X128.22 Y104.374 E.06479 +G1 X127.969 Y104.624 E.01199 +G1 X127.376 Y104.624 E.02007 +G2 X127.198 Y104.624 I-.089 J.022 E.00822 +G1 X126.605 Y104.624 E.02007 +G2 X126.338 Y104.624 I-.133 J.014 E.01348 +G1 X125.77 Y104.624 E.01923 +G2 X125.426 Y104.643 I-.158 J.27 E.01235 +G1 X125.344 Y104.656 E.00281 +G1 X123.921 Y106.079 E.06812 +G3 X124.353 Y106.33 I.033 J.441 E.01797 +G1 X124.357 Y107.481 E.03896 +G2 X124.405 Y107.466 I.032 J.019 E.00608 +G1 X124.417 Y106.33 E.03845 +G3 X125.177 Y106.079 I.55 J.388 E.02902 +G3 X125.412 Y105.412 I.562 J-.177 E.02567 +G1 X124.267 Y104.267 E.05481 +G3 X122.937 Y104.627 I-.977 J-.971 E.04884 +G3 X122.11 Y102.11 I.514 J-1.563 E.10423 +G1 X120.998 Y100.998 E.05323 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +G1 X120.998 Y103.326 F12000 +G1 X122.251 Y103.018 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.398614 +G1 F1545 +G1 X122.256 Y103.366 E.01029 +;WIDTH:0.405202 +G2 X122.551 Y104.003 I1.397 J-.26 E.02137 +;WIDTH:0.380517 +G1 X122.88 Y104.22 E.01106 +;WIDTH:0.369375 +G2 X123.595 Y104.252 I.422 J-1.415 E.01962 +;WIDTH:0.388585 +G1 X123.908 Y104.082 E.01024 +;WIDTH:0.396235 +G1 X124.045 Y103.919 E.00626 +;WIDTH:0.433367 +G1 X124.069 Y103.862 E.00201 +;WIDTH:0.470498 +G1 X124.094 Y103.805 E.00221 +;WIDTH:0.507629 +G3 X124.19 Y103.727 I.092 J.015 E.00523 +;WIDTH:0.471771 +G1 X124.26 Y103.707 E.0026 +;WIDTH:0.467055 +G1 X124.078 Y103.634 E.00692 +;WIDTH:0.498197 +G1 X123.896 Y103.56 E.00744 +G1 X123.819 Y103.652 E.00454 +;WIDTH:0.461032 +G1 X123.743 Y103.743 E.00412 +;WIDTH:0.423867 +G1 X123.666 Y103.834 E.00378 +;WIDTH:0.386702 +G1 X123.386 Y103.965 E.00884 +;WIDTH:0.369375 +G1 X123.014 Y103.929 E.01015 +;WIDTH:0.38205 +G1 X122.753 Y103.714 E.00954 +;WIDTH:0.397185 +G1 X122.607 Y103.331 E.01207 +;WIDTH:0.399447 +G1 X122.604 Y102.822 E.01509 +G1 X122.778 Y102.395 E.01367 +;WIDTH:0.379233 +G1 X123.056 Y102.211 E.00932 +;WIDTH:0.364931 +G1 X123.422 Y102.186 E.00982 +;WIDTH:0.383071 +G1 X123.695 Y102.317 E.00856 +;WIDTH:0.40338 +G1 X123.878 Y102.543 E.00872 +G1 X124.191 Y102.445 E.00983 +;WIDTH:0.404146 +G2 X123.814 Y101.986 I-1.012 J.446 E.01806 +;WIDTH:0.380491 +G1 X123.497 Y101.878 E.0094 +;WIDTH:0.367809 +G2 X122.799 Y101.971 I-.171 J1.375 E.01923 +;WIDTH:0.378877 +G1 X122.492 Y102.218 E.01101 +;WIDTH:0.394602 +G1 X122.289 Y102.635 E.01356 +;WIDTH:0.399447 +G1 X122.248 Y102.814 E.00544 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.289 Y102.635 E-.03816 +G1 X122.492 Y102.218 E-.09638 +G1 X122.588 Y102.141 E-.02546 +;WIPE_END +G1 X124.26 Y103.707 Z1.04 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.435912 +G1 F1545 +G1 X124.545 Y103.746 E.0094 +;WIDTH:0.449889 +G1 X124.702 Y103.876 E.0069 +;WIDTH:0.449999 +G1 X124.703 Y103.876 E.00003 +G2 X124.993 Y104.22 I.524 J-.148 E.0157 +G1 X125.272 Y104.25 E.0095 +G1 X125.721 Y104.148 E.01559 +G2 X126.132 Y104.217 I.415 J-1.215 E.01417 +G1 X126.25 Y104.082 E.00607 +G3 X126.657 Y104.101 I.183 J.463 E.01421 +G1 X126.773 Y104.217 E.00555 +G2 X127.197 Y104.115 I.105 J-.496 E.01526 +G3 X127.623 Y104.217 I.044 J.757 E.01504 +G1 X127.812 Y104.196 E.00644 +G1 X127.812 Y102.629 E.05304 +G1 X127.736 Y102.468 E.00603 +G3 X127.812 Y102.1 I1.553 J.132 E.01275 +G1 X127.806 Y101.939 E.00545 +G1 X127.545 Y101.933 E.00884 +G3 X127.216 Y102.036 I-.267 J-.278 E.0121 +G2 X126.761 Y101.954 I-.328 J.519 E.01604 +G1 X126.761 Y102.777 E.02786 +G1 X126.651 Y102.958 E.00717 +G1 X126.298 Y103.044 E.0123 +G1 X126.149 Y102.981 E.00548 +G1 X125.969 Y102.713 E.01093 +G2 X125.022 Y102.672 I-.52 J1.049 E.03303 +G2 X124.812 Y102.9 I.358 J.541 E.01059 +G3 X124.953 Y103.383 I-.702 J.468 E.01729 +G1 X124.701 Y103.684 E.01329 +;WIDTH:0.441881 +G1 X124.545 Y103.746 E.00557 +;WIDTH:0.449999 +G1 X125.236 Y103.795 E.02345 +;WIDTH:0.531888 +G2 X125.366 Y103.037 I-.653 J-.502 E.03253 +G1 X125.641 Y103.053 E.0112 +G1 X125.751 Y103.197 E.00737 +;WIDTH:0.541305 +G1 X126.004 Y103.428 E.0142 +;WIDTH:0.576568 +G1 X126.103 Y103.468 E.00474 +;WIDTH:0.61183 +G1 X126.203 Y103.509 E.00511 +;WIDTH:0.647092 +G1 X126.303 Y103.549 E.00541 +G1 X126.189 Y103.613 E.00657 +;WIDTH:0.61183 +G1 X126.076 Y103.676 E.00612 +;WIDTH:0.576568 +G1 X125.963 Y103.74 E.00576 +;WIDTH:0.541305 +G1 X125.674 Y103.703 E.01207 +;WIDTH:0.531888 +G1 X125.436 Y103.753 E.00989 +M204 P1000 +G1 X125.436 Y103.753 F12000 +G1 X126.303 Y103.549 +M204 P1500 +;WIDTH:0.655746 +G1 F1545 +G1 X126.469 Y103.532 E.0085 +G1 X126.639 Y103.442 E.0098 +;WIDTH:0.609244 +G1 X126.81 Y103.353 E.00908 +;WIDTH:0.562742 +G1 X126.98 Y103.263 E.00831 +;WIDTH:0.541405 +G1 X127.103 Y103.027 E.01103 +;WIDTH:0.56657 +G1 X127.226 Y102.792 E.01155 +;WIDTH:0.593756 +G1 X127.24 Y102.69 E.00472 +;WIDTH:0.620942 +G1 X127.254 Y102.588 E.00495 +G1 X127.3 Y102.69 E.00538 +;WIDTH:0.593756 +G1 X127.347 Y102.793 E.00519 +;WIDTH:0.56657 +G1 X127.36 Y103.226 E.01886 +;WIDTH:0.541405 +G1 X127.372 Y103.66 E.018 +;WIDTH:0.516239 +G1 X127.216 Y103.674 E.00616 +G1 X126.917 Y103.775 E.01242 +;WIDTH:0.475934 +G1 X126.833 Y103.709 E.00385 +;WIDTH:0.520634 +G1 X126.742 Y103.665 E.00402 +;WIDTH:0.565334 +G1 X126.652 Y103.621 E.00435 +;WIDTH:0.606213 +G1 X126.477 Y103.585 E.00837 +;WIDTH:0.647092 +G1 X126.361 Y103.561 E.00595 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.477 Y103.585 E-.02462 +G1 X126.652 Y103.621 E-.03713 +G1 X126.742 Y103.665 E-.02082 +G1 X126.833 Y103.709 E-.02101 +G1 X126.917 Y103.775 E-.0222 +G1 X127.073 Y103.722 E-.03422 +;WIPE_END +G1 X124.209 Y102.859 Z1.052 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1545 +G1 X124.388 Y102.816 E.00623 +G2 X124.525 Y103.255 I.437 J.104 E.01634 +G1 X124.475 Y103.313 E.00259 +G1 X123.867 Y103.093 E.02189 +G2 X123.475 Y103.475 I.074 J.468 E.01975 +G1 X122.975 Y102.975 E.02393 +G3 X123.479 Y102.638 I.289 J-.114 E.02847 +G2 X124.209 Y102.859 I.45 J-.17 E.02982 +M204 P1000 +;LAYER_CHANGE +;Z:1.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X123.53 Y102.737 I-.28 J-.39 E-.16 +;WIPE_END +G1 X123.53 Y102.737 Z1 F12000 +G1 X129.368 Y109.368 Z1.2 +;AFTER_LAYER_CHANGE +;1.2 +G1 X129.368 Y109.368 +G1 Z1.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +M73 P19 R6 +M73 Q19 S6 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z1.301 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P20 R6 +M73 Q20 S6 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y108.674 E.24872 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:1.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z1.2 F12000 +G1 X129.368 Y109.368 Z1.4 +;AFTER_LAYER_CHANGE +;1.4 +G1 X129.368 Y109.368 +G1 Z1.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +M73 P21 R6 +M73 Q21 S6 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z1.501 F12000 +G1 Z1.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y108.674 E.24872 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:1.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.6 + + +G1 E-.64 F2100 +M73 P22 R6 +M73 Q22 S6 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z1.4 F12000 +G1 X129.368 Y109.368 Z1.6 +;AFTER_LAYER_CHANGE +;1.6 +G1 X129.368 Y109.368 +G1 Z1.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +M73 P23 R6 +M73 Q23 S6 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z1.701 F12000 +G1 Z1.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y108.674 E.24872 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:1.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z1.6 F12000 +G1 X129.368 Y109.368 Z1.8 +;AFTER_LAYER_CHANGE +;1.8 +G1 X129.368 Y109.368 +G1 Z1.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +M73 P23 R5 +M73 Q23 S5 +G1 X129.368 Y109.308 E.29367 +M73 P24 R5 +M73 Q24 S5 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z1.901 F12000 +G1 Z1.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P25 R5 +M73 Q25 S5 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y108.674 E.24872 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z1.8 F12000 +G1 X129.368 Y109.368 Z2 +;AFTER_LAYER_CHANGE +;2 +G1 X129.368 Y109.368 +G1 Z2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F908 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.123 Y105.879 E.00916 +G1 X121.132 Y105.407 E.01598 +G1 X121.126 Y104.146 E.04268 +G1 X120.998 Y103.847 E.01101 +G1 X120.632 Y103.671 E.01375 +G1 X120.632 Y100.632 E.10287 +G1 X121.534 Y100.632 E.03053 +G1 X121.606 Y100.865 E.00825 +G1 X121.866 Y101.081 E.01144 +G1 X122.11 Y101.132 E.00844 +G1 X123.183 Y101.132 E.03632 +G1 X123.378 Y101.1 E.00669 +G1 X123.629 Y100.938 E.01011 +G1 X123.75 Y100.632 E.01114 +G1 X126.221 Y100.632 E.08364 +G1 X126.342 Y100.938 E.01114 +G1 X126.511 Y101.066 E.00718 +G1 X126.788 Y101.132 E.00964 +G1 X127.894 Y101.132 E.03744 +G1 X128.139 Y101.081 E.00847 +G1 X128.399 Y100.865 E.01144 +G1 X128.47 Y100.632 E.00824 +G1 X129.368 Y100.632 E.0304 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.912 Y104.276 E.00541 +G1 X128.868 Y104.628 E.01201 +G1 X128.878 Y105.519 E.03016 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F908 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 P26 R5 +M73 Q26 S5 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.68 Y104.101 E.00459 +G1 X120.521 Y104.025 E.00597 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X121.907 Y100.225 E.05693 +G1 X121.907 Y100.521 E.01002 +G1 X121.942 Y100.636 E.00407 +G1 X122.11 Y100.725 E.00644 +G1 X123.183 Y100.725 E.03632 +G1 X123.288 Y100.696 E.00369 +G1 X123.387 Y100.521 E.00681 +G1 X123.387 Y100.225 E.01002 +G1 X126.585 Y100.225 E.10825 +G1 X126.585 Y100.521 E.01002 +G1 X126.639 Y100.66 E.00505 +G1 X126.788 Y100.725 E.0055 +G1 X127.894 Y100.725 E.03744 +G1 X128.062 Y100.636 E.00644 +G1 X128.097 Y100.521 E.00407 +G1 X128.097 Y100.225 E.01002 +G1 X129.775 Y100.225 E.0568 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.29 Y104.428 E.00771 +G1 X129.275 Y104.628 E.00679 +G1 X129.289 Y105.482 E.02891 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X129.002 Y103.326 Z2.113 F12000 +G1 Z2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F908 +G1 X129.002 Y101.326 E.0677 +G1 X128.718 Y101.042 E.01359 +G1 X128.731 Y100.998 E.00155 +G1 X129.002 Y100.998 E.00917 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X128.674 Y109.002 E.24872 +G1 X129.002 Y108.674 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.273 Y100.998 E.00931 +M73 P27 R5 +M73 Q27 S5 +G1 X120.998 Y101.326 E.01449 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z2 F12000 +G1 X129.368 Y109.368 Z2.2 +;AFTER_LAYER_CHANGE +;2.2 +G1 X129.368 Y109.368 +G1 Z2.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.156 E.05467 +G1 X120.998 Y103.847 E.01135 +G1 X120.632 Y103.671 E.01375 +G1 X120.632 Y100.632 E.10287 +G1 X121.685 Y100.632 E.03564 +G1 X121.769 Y100.886 E.00906 +G1 X122.023 Y101.085 E.01092 +G1 X122.259 Y101.132 E.00815 +G1 X123.327 Y101.132 E.03615 +G1 X123.422 Y101.125 E.00322 +G1 X123.664 Y101.03 E.0088 +G1 X123.878 Y100.632 E.0153 +G1 X126.096 Y100.632 E.07508 +G1 X126.31 Y101.03 E.0153 +G1 X126.374 Y101.068 E.00252 +G1 X126.647 Y101.132 E.00949 +G1 X127.492 Y101.132 E.0286 +G1 X127.986 Y101.085 E.0168 +G1 X128.241 Y100.885 E.01097 +G1 X128.324 Y100.632 E.00901 +G1 X129.368 Y100.632 E.03534 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.902 Y104.304 E.00641 +G2 X128.878 Y105.519 I8.102 J.764 E.04117 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G2 X120.521 Y104.025 I-.237 J.033 E.01048 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.055 Y100.225 E.06194 +G1 X122.055 Y100.521 E.01002 +G1 X122.096 Y100.643 E.00436 +G1 X122.259 Y100.725 E.00618 +G1 X123.327 Y100.725 E.03615 +M73 Q28 S5 +G1 X123.403 Y100.71 E.00262 +M73 P28 R5 +G1 X123.531 Y100.521 E.00773 +G1 X123.531 Y100.225 E.01002 +G1 X126.443 Y100.225 E.09857 +G1 X126.443 Y100.521 E.01002 +G1 X126.535 Y100.691 E.00654 +G1 X126.647 Y100.725 E.00396 +G1 X127.75 Y100.725 E.03734 +G1 X127.914 Y100.643 E.00621 +G1 X127.954 Y100.521 E.00435 +G1 X127.954 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06164 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.286 Y104.437 E.00212 +G1 X129.289 Y105.482 E.03537 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z2.301 F12000 +G1 Z2.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.746 E.08191 +G2 X128.502 Y104.49 I.236 J.699 E.03261 +G2 X128.569 Y105.766 I3.835 J.437 E.04345 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z2.2 F12000 +G1 X129.368 Y109.368 Z2.4 +;AFTER_LAYER_CHANGE +;2.4 +G1 X129.368 Y109.368 +G1 Z2.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 P29 R5 +M73 Q29 S5 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.129 Y104.167 E.05429 +G1 X121.015 Y103.869 E.0108 +G1 X120.632 Y103.671 E.01459 +G1 X120.632 Y100.632 E.10287 +G1 X121.836 Y100.632 E.04075 +G1 X121.932 Y100.904 E.00976 +G1 X122.163 Y101.081 E.00985 +G1 X122.407 Y101.132 E.00844 +G1 X123.471 Y101.132 E.03602 +G1 X123.608 Y101.116 E.00467 +G1 X123.834 Y101.012 E.00842 +G1 X124.026 Y100.632 E.01441 +G1 X125.951 Y100.632 E.06516 +G1 X126.143 Y101.012 E.01441 +G1 X126.239 Y101.071 E.00381 +G1 X126.506 Y101.132 E.00927 +G1 X127.492 Y101.132 E.03337 +G1 X127.835 Y101.088 E.01171 +G1 X128.084 Y100.903 E.0105 +G1 X128.179 Y100.632 E.00972 +G1 X129.368 Y100.632 E.04025 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.893 Y104.33 E.00733 +G2 X128.878 Y105.519 I10.324 J.724 E.04027 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G2 X120.521 Y104.025 I-.24 J.036 E.01046 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.204 Y100.225 E.06699 +G1 X122.204 Y100.521 E.01002 +G1 X122.249 Y100.649 E.00459 +G1 X122.407 Y100.725 E.00593 +G1 X123.471 Y100.725 E.03602 +G1 X123.549 Y100.71 E.00269 +G1 X123.675 Y100.521 E.00769 +G1 X123.675 Y100.225 E.01002 +G1 X126.302 Y100.225 E.08892 +G1 X126.302 Y100.521 E.01002 +G1 X126.385 Y100.685 E.00622 +G1 X126.506 Y100.725 E.00431 +G1 X127.607 Y100.725 E.03727 +G1 X127.766 Y100.649 E.00597 +G1 X127.811 Y100.521 E.00459 +G1 X127.811 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06648 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.283 Y104.446 E.00244 +G1 X129.289 Y105.482 E.03507 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +M73 Q30 S5 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +M73 P30 R5 +;WIPE_END +G1 X123.326 Y109.002 Z2.501 F12000 +G1 Z2.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.746 E.08191 +G2 X128.502 Y104.492 I.238 J.701 E.03266 +G2 X128.569 Y105.766 I3.827 J.437 E.04338 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z2.4 F12000 +G1 X129.368 Y109.368 Z2.6 +;AFTER_LAYER_CHANGE +;2.6 +G1 X129.368 Y109.368 +G1 Z2.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.122 Y104.119 E.05592 +G1 X120.999 Y103.848 E.01007 +G1 X120.632 Y103.671 E.01379 +G1 X120.632 Y100.632 E.10287 +G1 X121.986 Y100.632 E.04583 +G1 X122.093 Y100.92 E.0104 +G1 X122.296 Y101.074 E.00862 +G1 X122.556 Y101.132 E.00902 +G1 X123.615 Y101.132 E.03585 +G1 X123.803 Y101.102 E.00644 +G1 X124.007 Y100.989 E.00789 +G1 X124.174 Y100.632 E.01334 +G1 X125.806 Y100.632 E.05524 +G1 X125.973 Y100.989 E.01334 +G1 X126.107 Y101.075 E.00539 +G1 X126.365 Y101.132 E.00894 +G1 X127.464 Y101.132 E.0372 +G1 X127.689 Y101.089 E.00775 +G1 X127.928 Y100.918 E.00995 +G1 X128.034 Y100.632 E.01032 +G1 X129.368 Y100.632 E.04515 +G1 X129.368 Y103.949 E.11228 +M73 P31 R5 +M73 Q31 S5 +G1 X128.991 Y104.137 E.01426 +G1 X128.886 Y104.357 E.00825 +G2 X128.878 Y105.519 I13.187 J.668 E.03935 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G1 X120.693 Y104.119 E.00388 +G1 X120.521 Y104.025 E.00663 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.352 Y100.225 E.072 +G1 X122.352 Y100.521 E.01002 +G1 X122.402 Y100.654 E.00481 +G1 X122.556 Y100.725 E.00574 +G1 X123.615 Y100.725 E.03585 +G1 X123.701 Y100.706 E.00298 +G1 X123.819 Y100.521 E.00743 +G1 X123.819 Y100.225 E.01002 +G1 X126.161 Y100.225 E.07927 +G1 X126.161 Y100.521 E.01002 +G1 X126.234 Y100.677 E.00583 +G1 X126.365 Y100.725 E.00472 +G1 X127.464 Y100.725 E.0372 +G1 X127.619 Y100.654 E.00577 +G1 X127.667 Y100.521 E.00479 +G1 X127.667 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07135 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.281 Y104.455 E.00274 +G1 X129.289 Y105.482 E.03476 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z2.701 F12000 +G1 Z2.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.746 E.08191 +G2 X128.502 Y104.494 I.242 J.703 E.03271 +M73 P32 R5 +M73 Q32 S5 +G2 X128.569 Y105.766 I3.819 J.436 E.04332 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z2.6 F12000 +G1 X129.368 Y109.368 Z2.8 +;AFTER_LAYER_CHANGE +;2.8 +G1 X129.368 Y109.368 +G1 Z2.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F920 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.131 Y104.19 E.05351 +G1 X121.03 Y103.891 E.01068 +G1 X120.632 Y103.671 E.01539 +G1 X120.632 Y100.632 E.10287 +G1 X122.137 Y100.632 E.05094 +G1 X122.254 Y100.934 E.01096 +G1 X122.431 Y101.068 E.00751 +G1 X122.704 Y101.132 E.00949 +G1 X123.759 Y101.132 E.03571 +G1 X123.927 Y101.108 E.00574 +G1 X124.185 Y100.959 E.01008 +G1 X124.323 Y100.632 E.01201 +G1 X125.66 Y100.632 E.04526 +G1 X125.798 Y100.959 E.01201 +G1 X125.976 Y101.08 E.00729 +G1 X126.224 Y101.132 E.00858 +G1 X127.321 Y101.132 E.03713 +G1 X127.56 Y101.083 E.00826 +G1 X127.772 Y100.932 E.00881 +G1 X127.889 Y100.632 E.0109 +G1 X129.368 Y100.632 E.05006 +G1 X129.368 Y103.949 E.11228 +G1 X128.99 Y104.137 E.01429 +G1 X128.88 Y104.383 E.00912 +G1 X128.878 Y105.519 E.03845 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F920 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 P33 R5 +M73 Q33 S5 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G2 X120.699 Y104.13 I-27.335 J-.395 E.05556 +G1 X120.521 Y104.025 E.007 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.501 Y100.225 E.07704 +G1 X122.501 Y100.521 E.01002 +G1 X122.554 Y100.659 E.005 +G1 X122.704 Y100.725 E.00555 +G1 X123.759 Y100.725 E.03571 +G1 X123.865 Y100.695 E.00373 +G1 X123.963 Y100.521 E.00676 +G1 X123.963 Y100.225 E.01002 +G1 X126.02 Y100.225 E.06963 +G1 X126.02 Y100.521 E.01002 +G1 X126.082 Y100.667 E.00537 +G1 X126.224 Y100.725 E.00519 +G1 X127.321 Y100.725 E.03713 +G1 X127.471 Y100.658 E.00556 +G1 X127.524 Y100.521 E.00497 +G1 X127.524 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07619 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.279 Y104.464 E.00305 +G1 X129.289 Y105.482 E.03446 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z2.901 F12000 +G1 Z2.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F920 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.746 E.08191 +G2 X128.502 Y104.496 I.245 J.705 E.03276 +G2 X128.569 Y105.766 I3.811 J.435 E.04325 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +M73 Q34 S5 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M73 P34 R5 +M204 P1000 +;LAYER_CHANGE +;Z:3 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z2.8 F12000 +G1 X129.368 Y109.368 Z3 +;AFTER_LAYER_CHANGE +;3 +G1 X129.368 Y109.368 +G1 Z3 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.072 Y103.964 E.0092 +G1 X121.001 Y103.85 E.00455 +G1 X120.632 Y103.672 E.01387 +G1 X120.632 Y100.632 E.1029 +G1 X122.287 Y100.632 E.05602 +G1 X122.414 Y100.947 E.0115 +G1 X122.719 Y101.117 E.01182 +G1 X122.852 Y101.132 E.00453 +G1 X123.903 Y101.132 E.03558 +G1 X124.136 Y101.086 E.00804 +G1 X124.366 Y100.92 E.0096 +G1 X124.473 Y100.632 E.0104 +G1 X125.513 Y100.632 E.0352 +G1 X125.62 Y100.92 E.0104 +G1 X125.849 Y101.086 E.00957 +G1 X126.083 Y101.132 E.00807 +G1 X127.177 Y101.132 E.03703 +G1 X127.43 Y101.078 E.00876 +G1 X127.618 Y100.944 E.00781 +G1 X127.744 Y100.632 E.01139 +G1 X129.368 Y100.632 E.05497 +G1 X129.368 Y103.949 E.11228 +G1 X128.99 Y104.138 E.01431 +G1 X128.876 Y104.408 E.00992 +G1 X128.878 Y105.519 E.03761 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.705 Y104.14 E.05521 +G1 X120.521 Y104.025 E.00734 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.649 Y100.225 E.08205 +G1 X122.649 Y100.521 E.01002 +G1 X122.706 Y100.663 E.00518 +G1 X122.852 Y100.725 E.00537 +G1 X123.903 Y100.725 E.03558 +G1 X124.009 Y100.695 E.00373 +G1 X124.107 Y100.521 E.00676 +G1 X124.107 Y100.225 E.01002 +G1 X125.879 Y100.225 E.05998 +G1 X125.879 Y100.521 E.01002 +G1 X125.928 Y100.654 E.0048 +G1 X126.083 Y100.725 E.00577 +G1 X127.177 Y100.725 E.03703 +G1 X127.324 Y100.662 E.00541 +M73 P35 R5 +M73 Q35 S5 +G1 X127.381 Y100.521 E.00515 +G1 X127.381 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08103 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.302 Y104.402 E.0069 +G2 X129.289 Y105.482 I8.131 J.639 E.03659 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z3.101 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.746 E.08191 +G2 X128.502 Y104.498 I.227 J.694 E.03296 +G2 X128.569 Y105.766 I3.803 J.434 E.04318 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:3.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z3 F12000 +G1 X129.368 Y109.368 Z3.2 +;AFTER_LAYER_CHANGE +;3.2 +G1 X129.368 Y109.368 +G1 Z3.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +M73 Q36 S5 +G1 X121.132 Y104.229 E.05219 +G1 X121.086 Y103.995 E.00807 +M73 P36 R5 +G1 X121.002 Y103.852 E.00561 +G1 X120.632 Y103.672 E.01393 +G1 X120.632 Y100.632 E.1029 +G1 X122.437 Y100.632 E.0611 +G1 X122.574 Y100.958 E.01197 +G1 X122.836 Y101.109 E.01024 +G1 X123.001 Y101.132 E.00564 +G1 X124.047 Y101.132 E.03541 +G1 X124.321 Y101.067 E.00953 +G1 X124.551 Y100.866 E.01034 +G1 X124.624 Y100.632 E.0083 +G1 X125.365 Y100.632 E.02508 +G1 X125.437 Y100.866 E.00829 +G1 X125.668 Y101.067 E.01036 +G1 X125.941 Y101.132 E.0095 +G1 X127.034 Y101.132 E.037 +G1 X127.298 Y101.072 E.00916 +G1 X127.464 Y100.955 E.00687 +G1 X127.599 Y100.632 E.01185 +G1 X129.368 Y100.632 E.05988 +G1 X129.368 Y103.949 E.11228 +G1 X128.989 Y104.139 E.01435 +G1 X128.872 Y104.432 E.01068 +G1 X128.878 Y105.519 E.03679 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.71 Y104.151 E.05484 +G1 X120.521 Y104.025 E.00769 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.797 Y100.225 E.08706 +M73 P36 R4 +M73 Q36 S4 +G1 X122.797 Y100.521 E.01002 +G1 X122.859 Y100.667 E.00537 +G1 X123.001 Y100.725 E.00519 +G1 X124.047 Y100.725 E.03541 +G1 X124.169 Y100.685 E.00435 +G1 X124.251 Y100.521 E.00621 +G1 X124.251 Y100.225 E.01002 +G1 X125.738 Y100.225 E.05033 +G1 X125.738 Y100.521 E.01002 +G1 X125.82 Y100.685 E.00621 +G1 X125.941 Y100.725 E.00431 +G1 X127.034 Y100.725 E.037 +G1 X127.177 Y100.666 E.00524 +G1 X127.238 Y100.521 E.00532 +G1 X127.238 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08587 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.297 Y104.412 E.00722 +G2 X129.289 Y105.482 I9.388 J.605 E.03624 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z3.301 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +M73 P37 R4 +M73 Q37 S4 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.747 E.08195 +G2 X128.502 Y104.499 I.23 J.695 E.03294 +G2 X128.569 Y105.766 I3.796 J.434 E.04315 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:3.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z3.2 F12000 +G1 X129.368 Y109.368 Z3.4 +;AFTER_LAYER_CHANGE +;3.4 +G1 X129.368 Y109.368 +G1 Z3.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.098 Y104.026 E.00697 +G1 X121.003 Y103.854 E.00665 +G1 X120.632 Y103.672 E.01399 +G1 X120.632 Y100.632 E.1029 +G1 X122.587 Y100.632 E.06617 +G1 X122.733 Y100.968 E.0124 +G1 X122.955 Y101.1 E.00874 +G1 X123.149 Y101.132 E.00666 +G1 X124.191 Y101.132 E.03527 +G1 X124.301 Y101.122 E.00374 +G1 X124.559 Y101.009 E.00953 +G1 X124.739 Y100.792 E.00954 +G1 X124.776 Y100.632 E.00556 +G1 X125.216 Y100.632 E.01489 +G1 X125.253 Y100.792 E.00556 +G1 X125.432 Y101.009 E.00952 +G1 X125.543 Y101.075 E.00437 +G1 X125.8 Y101.132 E.00891 +G1 X126.891 Y101.132 E.03693 +G1 X126.954 Y101.129 E.00213 +G1 X127.31 Y100.965 E.01327 +G1 X127.454 Y100.632 E.01228 +G1 X129.368 Y100.632 E.06479 +G1 X129.368 Y103.949 E.11228 +G1 X128.988 Y104.14 E.0144 +G1 X128.87 Y104.456 E.01142 +M73 Q38 S4 +G1 X128.878 Y105.519 E.03598 +G1 X128.972 Y105.748 E.00838 +M73 P38 R4 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.701 Y104.133 E.05545 +G1 X120.521 Y104.025 E.00711 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.946 Y100.225 E.0921 +G1 X122.946 Y100.521 E.01002 +G1 X123.01 Y100.67 E.00549 +G1 X123.149 Y100.725 E.00506 +G1 X124.191 Y100.725 E.03527 +G1 X124.234 Y100.72 E.00147 +G1 X124.374 Y100.612 E.00599 +G1 X124.395 Y100.225 E.01312 +G1 X125.597 Y100.225 E.04069 +G1 X125.597 Y100.521 E.01002 +G1 X125.678 Y100.684 E.00616 +G1 X125.8 Y100.725 E.00436 +G1 X126.891 Y100.725 E.03693 +G1 X127.031 Y100.669 E.0051 +G1 X127.095 Y100.521 E.00546 +G1 X127.095 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09071 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.293 Y104.421 E.00749 +G2 X129.289 Y105.482 I10.707 J.565 E.03593 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z3.501 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.748 E.08198 +G2 X128.502 Y104.487 I.229 J.693 E.03249 +G2 X128.569 Y105.766 I3.85 J.438 E.04355 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +M73 P39 R4 +M73 Q39 S4 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:3.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z3.4 F12000 +G1 X129.368 Y109.368 Z3.6 +;AFTER_LAYER_CHANGE +;3.6 +G1 X129.368 Y109.368 +G1 Z3.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F917 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.107 Y104.057 E.05802 +G1 X121.005 Y103.856 E.00763 +G1 X120.632 Y103.672 E.01408 +G1 X120.632 Y100.632 E.1029 +G1 X122.737 Y100.632 E.07125 +G1 X122.891 Y100.977 E.01279 +G1 X123.076 Y101.09 E.00734 +G1 X123.298 Y101.132 E.00765 +G1 X124.426 Y101.125 E.03818 +G1 X124.698 Y101.012 E.00997 +G1 X124.93 Y100.632 E.01507 +G1 X125.064 Y100.632 E.00454 +G1 X125.296 Y101.012 E.01507 +G1 X125.426 Y101.086 E.00506 +G1 X125.659 Y101.132 E.00804 +G1 X126.748 Y101.132 E.03686 +G1 X126.824 Y101.127 E.00258 +G1 X127.158 Y100.974 E.01244 +G1 X127.309 Y100.632 E.01265 +G1 X129.368 Y100.632 E.06969 +G1 X129.368 Y103.949 E.11228 +G1 X128.987 Y104.141 E.01444 +G1 X128.941 Y104.215 E.00295 +G1 X128.868 Y104.479 E.00927 +G1 X128.878 Y105.519 E.0352 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F917 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +M73 Q40 S4 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +M73 P40 R4 +G1 X120.704 Y104.139 E.05525 +G1 X120.521 Y104.025 E.0073 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.094 Y100.225 E.09711 +G1 X123.094 Y100.521 E.01002 +G1 X123.162 Y100.673 E.00564 +G1 X123.298 Y100.725 E.00493 +G1 X124.391 Y100.717 E.037 +G1 X124.531 Y100.577 E.0067 +G1 X124.539 Y100.225 E.01192 +G1 X125.456 Y100.225 E.03104 +G1 X125.456 Y100.521 E.01002 +G1 X125.538 Y100.685 E.00621 +G1 X125.659 Y100.725 E.00431 +G1 X126.748 Y100.725 E.03686 +G1 X126.884 Y100.672 E.00494 +G1 X126.951 Y100.521 E.00559 +G1 X126.951 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09559 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.289 Y104.431 E.00779 +G2 X129.289 Y105.482 I12.061 J.52 E.03559 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z3.701 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F917 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.748 E.08198 +G2 X128.502 Y104.489 I.23 J.695 E.03255 +G2 X128.569 Y105.766 I3.841 J.438 E.04348 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:3.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z3.6 F12000 +G1 X129.368 Y109.368 Z3.8 +;AFTER_LAYER_CHANGE +;3.8 +G1 X129.368 Y109.368 +M73 P41 R4 +M73 Q41 S4 +G1 Z3.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F915 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.115 Y104.086 E.05704 +G1 X121.007 Y103.858 E.00854 +G1 X120.632 Y103.673 E.01415 +G1 X120.632 Y100.632 E.10293 +G1 X122.886 Y100.632 E.0763 +G1 X123.049 Y100.985 E.01316 +G1 X123.199 Y101.08 E.00601 +G1 X123.446 Y101.132 E.00854 +G1 X124.55 Y101.128 E.03737 +G1 X124.825 Y101.025 E.00994 +G1 X124.999 Y100.727 E.01168 +G1 X125.173 Y101.025 E.01168 +G1 X125.318 Y101.098 E.00549 +G1 X125.515 Y101.132 E.00677 +G1 X126.605 Y101.132 E.0369 +G1 X126.692 Y101.126 E.00295 +G1 X127.005 Y100.982 E.01166 +G1 X127.165 Y100.632 E.01303 +G1 X129.368 Y100.632 E.07457 +G1 X129.368 Y103.95 E.11231 +G1 X128.986 Y104.143 E.01449 +G1 X128.899 Y104.312 E.00643 +G2 X128.878 Y105.519 I11.126 J.795 E.04088 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F915 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.707 Y104.145 E.05504 +G1 X120.521 Y104.025 E.00749 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.243 Y100.225 E.10216 +G1 X123.243 Y100.521 E.01002 +G1 X123.314 Y100.676 E.00577 +G1 X123.446 Y100.725 E.00477 +G1 X124.525 Y100.72 E.03652 +;WIDTH:0.488106 +G1 X124.597 Y100.661 E.00345 +;WIDTH:0.526213 +G1 X124.668 Y100.603 E.00368 +;WIDTH:0.564319 +G1 X124.739 Y100.544 E.004 +G1 X124.739 Y100.281 E.0114 +;WIDTH:0.562712 +G1 X125.258 Y100.281 E.02243 +G1 X125.258 Y100.521 E.01037 +G1 X125.307 Y100.577 E.00322 +;WIDTH:0.525141 +G1 X125.355 Y100.633 E.00296 +;WIDTH:0.48757 +G1 X125.403 Y100.689 E.00273 +;WIDTH:0.449999 +G1 X125.517 Y100.725 E.00405 +G1 X126.605 Y100.725 E.03683 +G1 X126.738 Y100.675 E.00481 +G1 X126.808 Y100.521 E.00573 +G1 X126.808 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10043 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +M73 P42 R4 +M73 Q42 S4 +G1 X129.314 Y104.384 E.00625 +G1 X129.285 Y104.44 E.00213 +G1 X129.289 Y105.482 E.03527 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z3.901 F12000 +G1 Z3.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F915 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.749 E.08202 +G2 X128.502 Y104.491 I.232 J.696 E.03257 +G2 X128.569 Y105.766 I3.833 J.437 E.04342 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z3.8 F12000 +G1 X129.368 Y109.368 Z4 +;AFTER_LAYER_CHANGE +;4 +G1 X129.368 Y109.368 +G1 Z4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F915 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.121 Y104.115 E.05605 +G1 X121.009 Y103.86 E.00943 +G1 X120.632 Y103.673 E.01424 +G1 X120.632 Y100.632 E.10293 +G1 X123.036 Y100.632 E.08137 +M73 P43 R4 +M73 Q43 S4 +G1 X123.206 Y100.992 E.01348 +G1 X123.323 Y101.068 E.00472 +G1 X123.595 Y101.132 E.00946 +G1 X124.623 Y101.132 E.0348 +G1 X124.899 Y101.066 E.00961 +G1 X125 Y100.917 E.00609 +G1 X125.101 Y101.066 E.00609 +G1 X125.227 Y101.113 E.00455 +G1 X125.496 Y101.132 E.00913 +G1 X126.559 Y101.124 E.03598 +G1 X126.853 Y100.99 E.01094 +G1 X127.02 Y100.632 E.01337 +G1 X129.368 Y100.632 E.07948 +G1 X129.368 Y103.95 E.11231 +G1 X128.985 Y104.145 E.01455 +G1 X128.891 Y104.339 E.0073 +G2 X128.878 Y105.519 I13.161 J.729 E.03996 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F915 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.71 Y104.151 E.05484 +G1 X120.521 Y104.025 E.00769 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.391 Y100.225 E.10717 +G1 X123.391 Y100.521 E.01002 +G1 X123.465 Y100.678 E.00587 +G1 X123.595 Y100.725 E.00468 +G1 X124.673 Y100.719 E.03649 +G1 X124.812 Y100.538 E.00772 +;WIDTH:0.421205 +G1 X124.812 Y100.21 E.01032 +;WIDTH:0.419765 +G1 X125.189 Y100.21 E.01181 +G1 X125.189 Y100.521 E.00975 +;WIDTH:0.449999 +G1 X125.285 Y100.703 E.00696 +G1 X125.377 Y100.725 E.0032 +G1 X126.461 Y100.725 E.03669 +G1 X126.592 Y100.677 E.00472 +G1 X126.665 Y100.521 E.00583 +G1 X126.665 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10527 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.294 Y104.418 E.00741 +G2 X129.289 Y105.482 I8.775 J.572 E.03604 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z4.101 F12000 +G1 Z4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F915 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P44 R4 +M73 Q44 S4 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.75 E.08205 +G2 X128.502 Y104.493 I.228 J.694 E.03263 +G2 X128.569 Y105.766 I3.824 J.436 E.04335 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z4 F12000 +G1 X125 Y100.259 Z4.2 +G1 X125 Y100.259 Z4.2 +;AFTER_LAYER_CHANGE +;4.2 +G1 X125 Y100.259 +G1 Z4.2 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.519004 +G1 F927 +G1 X125 Y100.542 E.0112 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125 Y100.259 E-.05881 +G1 X125 Y100.542 E-.05881 +;WIPE_END +G1 E-.04238 F2100 +G1 X129.368 Y109.368 Z4.372 F12000 +G1 Z4.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F927 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.126 Y104.143 E.05511 +G1 X121.011 Y103.863 E.01025 +G1 X120.632 Y103.673 E.01435 +G1 X120.632 Y100.632 E.10293 +G1 X123.186 Y100.632 E.08645 +G1 X123.362 Y100.999 E.01378 +G1 X123.611 Y101.118 E.00934 +G1 X124.229 Y101.132 E.02092 +G1 X124.948 Y101.103 E.02436 +G1 X125 Y101.038 E.00282 +G1 X125.052 Y101.103 E.00282 +G1 X125.496 Y101.132 E.01506 +G1 X126.426 Y101.122 E.03148 +G1 X126.702 Y100.996 E.01027 +G1 X126.876 Y100.632 E.01366 +G1 X129.368 Y100.632 E.08435 +G1 X129.368 Y103.95 E.11231 +G1 X128.984 Y104.147 E.01461 +G1 X128.884 Y104.365 E.00812 +G2 X128.878 Y105.519 I15.586 J.653 E.03907 +M73 Q45 S4 +G1 X128.972 Y105.748 E.00838 +M73 P45 R4 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F927 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G2 X120.521 Y104.025 I-.239 J.035 E.01046 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.54 Y100.225 E.11221 +G1 X123.54 Y100.521 E.01002 +G1 X123.616 Y100.681 E.006 +G1 X123.699 Y100.72 E.0031 +G1 X124.229 Y100.725 E.01794 +G1 X124.775 Y100.725 E.01848 +;WIDTH:0.484502 +G1 X124.888 Y100.633 E.00535 +;WIDTH:0.519004 +G1 X125 Y100.542 E.00571 +G1 X125.087 Y100.629 E.00487 +;WIDTH:0.484502 +G1 X125.175 Y100.715 E.00452 +;WIDTH:0.449999 +G1 X125.496 Y100.725 E.01087 +G1 X126.397 Y100.709 E.0305 +G1 X126.522 Y100.521 E.00764 +G1 X126.522 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11011 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.292 Y104.424 E.00758 +G2 X129.289 Y105.482 I10.324 J.553 E.03583 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z4.301 F12000 +G1 Z4.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F927 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.751 E.08208 +G2 X128.502 Y104.495 I.229 J.694 E.03266 +G2 X128.569 Y105.766 I3.816 J.435 E.04328 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +M73 Q46 S4 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +M73 P46 R4 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z4.2 F12000 +G1 X124.997 Y100.128 Z4.4 +;AFTER_LAYER_CHANGE +;4.4 +G1 X124.997 Y100.128 +G1 Z4.4 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F928 +G1 X124.997 Y100.531 E.01139 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X124.997 Y100.128 E-.08375 +G1 X124.997 Y100.495 E-.07625 +;WIPE_END +G1 X129.368 Y109.368 Z4.573 F12000 +G1 Z4.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F928 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.129 Y104.171 E.05416 +G1 X121.013 Y103.866 E.01105 +G1 X120.632 Y103.674 E.01444 +G1 X120.632 Y100.632 E.10297 +G1 X123.335 Y100.632 E.09149 +G1 X123.519 Y101.005 E.01408 +G1 X123.751 Y101.116 E.00871 +G1 X124.229 Y101.132 E.01619 +G1 X125.496 Y101.132 E.04289 +G1 X126.292 Y101.121 E.02695 +G1 X126.551 Y101.002 E.00965 +G1 X126.732 Y100.632 E.01394 +G1 X129.368 Y100.632 E.08923 +G1 X129.368 Y103.95 E.11231 +G1 X128.982 Y104.149 E.0147 +G1 X128.878 Y104.391 E.00892 +G1 X128.878 Y105.519 E.03818 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F928 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +M73 P47 R4 +M73 Q47 S4 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G2 X120.521 Y104.025 I-.239 J.035 E.01046 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.688 Y100.225 E.11722 +G1 X123.688 Y100.521 E.01002 +G1 X123.767 Y100.683 E.0061 +G1 X123.891 Y100.725 E.00443 +G1 X124.891 Y100.725 E.03385 +G1 X124.944 Y100.628 E.00374 +;WIDTH:0.41646 +G1 X124.997 Y100.531 E.00343 +G1 X125.036 Y100.627 E.00322 +;WIDTH:0.449999 +G1 X125.075 Y100.723 E.00351 +G1 X125.496 Y100.725 E.01425 +G1 X126.261 Y100.706 E.0259 +G1 X126.378 Y100.521 E.00741 +G1 X126.378 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11498 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.289 Y104.429 E.00775 +G2 X129.289 Y105.482 I11.732 J.53 E.03565 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z4.501 F12000 +G1 Z4.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F928 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.752 E.08212 +G2 X128.502 Y104.497 I.229 J.694 E.0327 +G2 X128.569 Y105.766 I3.808 J.434 E.04321 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z4.4 F12000 +G1 X129.368 Y109.368 Z4.6 +;AFTER_LAYER_CHANGE +;4.6 +G1 X129.368 Y109.368 +M73 P48 R4 +M73 Q48 S4 +G1 Z4.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F909 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.131 Y104.197 E.05328 +G1 X121.015 Y103.868 E.01181 +G1 X120.632 Y103.674 E.01453 +G1 X120.632 Y100.632 E.10297 +G1 X123.485 Y100.632 E.09657 +G1 X123.706 Y101.033 E.0155 +G1 X124.04 Y101.132 E.01179 +G1 X125.496 Y101.132 E.04928 +G1 X126.157 Y101.119 E.02238 +G1 X126.401 Y101.008 E.00907 +G1 X126.587 Y100.632 E.0142 +G1 X129.368 Y100.632 E.09413 +G1 X129.368 Y103.951 E.11234 +G1 X128.981 Y104.151 E.01475 +G1 X128.874 Y104.417 E.0097 +G1 X128.878 Y105.519 E.0373 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F909 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.725 Y104.229 E.05219 +G2 X120.521 Y104.025 I-.24 J.036 E.01046 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.836 Y100.225 E.12223 +G1 X123.836 Y100.521 E.01002 +G1 X123.918 Y100.685 E.00621 +G1 X124.04 Y100.725 E.00435 +G1 X125.496 Y100.725 E.04928 +G1 X126.124 Y100.703 E.02127 +G1 X126.235 Y100.521 E.00722 +G1 X126.235 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11982 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y104.301 E.11512 +G1 X129.479 Y104.301 E.01002 +M73 P49 R4 +M73 Q49 S4 +G1 X129.313 Y104.386 E.00631 +G1 X129.275 Y104.504 E.0042 +G1 X129.289 Y105.482 E.03311 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z4.701 F12000 +G1 Z4.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F909 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P49 R3 +M73 Q49 S3 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.753 E.08215 +G2 X128.502 Y104.49 I.224 J.69 E.03245 +G2 X128.569 Y105.766 I3.838 J.437 E.04345 +G2 X129.002 Y106.152 I.585 J-.221 E.02042 +G1 X129.002 Y108.674 E.08537 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z4.6 F12000 +G1 X129.368 Y109.368 Z4.8 +;AFTER_LAYER_CHANGE +;4.8 +G1 X129.368 Y109.368 +G1 Z4.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F906 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.127 Y104.152 E.0548 +G1 X121.017 Y103.871 E.01021 +G1 X120.632 Y103.675 E.01462 +G1 X120.632 Y100.632 E.103 +G1 X123.634 Y100.632 E.10161 +M73 Q50 S3 +G1 X123.83 Y101.016 E.01459 +M73 P50 R3 +G1 X123.938 Y101.078 E.00422 +G1 X124.188 Y101.132 E.00866 +G1 X125.888 Y101.132 E.05754 +G1 X126.208 Y101.042 E.01125 +G1 X126.443 Y100.632 E.016 +G1 X129.368 Y100.632 E.09901 +G1 X129.368 Y103.819 E.10788 +G1 X128.982 Y104.018 E.0147 +G1 X128.896 Y104.19 E.00651 +G1 X128.868 Y104.373 E.00627 +G1 X128.877 Y105.647 E.04312 +G1 X128.976 Y105.89 E.00888 +G1 X129.368 Y106.096 E.01499 +G1 X129.368 Y109.308 E.10872 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F906 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.706 Y104.143 E.05511 +G1 X120.521 Y104.025 E.00743 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.985 Y100.225 E.12727 +G1 X123.985 Y100.521 E.01002 +G1 X124.069 Y100.686 E.00627 +G1 X124.188 Y100.725 E.00424 +G1 X125.627 Y100.725 E.04871 +G1 X125.995 Y100.695 E.0125 +G1 X126.092 Y100.521 E.00674 +G1 X126.092 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12466 +G1 X129.775 Y104.169 E.1335 +G1 X129.479 Y104.169 E.01002 +G1 X129.313 Y104.254 E.00631 +G1 X129.275 Y104.373 E.00423 +G1 X129.289 Y105.617 E.04211 +G1 X129.479 Y105.747 E.00779 +G1 X129.775 Y105.747 E.01002 +G1 X129.775 Y109.715 E.13431 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z4.901 F12000 +G1 Z4.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F906 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P51 R3 +M73 Q51 S3 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.621 E.07768 +G2 X128.501 Y104.361 I.228 J.693 E.03255 +G1 X128.514 Y105.705 E.04549 +G2 X129.002 Y106.291 I.676 J-.067 E.02741 +G1 X129.002 Y108.674 E.08066 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:5 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z4.8 F12000 +G1 X129.368 Y109.368 Z5 +;AFTER_LAYER_CHANGE +;5 +G1 X129.368 Y109.368 +G1 Z5 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F909 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.16 E.05453 +G1 X121.029 Y103.889 E.00977 +G1 X120.632 Y103.675 E.01527 +G1 X120.632 Y100.632 E.103 +G1 X123.783 Y100.632 E.10666 +G1 X123.983 Y101.019 E.01475 +G1 X124.079 Y101.075 E.00376 +G1 X124.337 Y101.132 E.00894 +G1 X125 Y101.132 E.02244 +G1 X125.818 Y101.128 E.02769 +G1 X126.101 Y101.018 E.01028 +G1 X126.299 Y100.632 E.01468 +G1 X129.368 Y100.632 E.10388 +G1 X129.368 Y103.685 E.10334 +G1 X128.983 Y103.883 E.01465 +G1 X128.922 Y103.987 E.00408 +G1 X128.868 Y104.239 E.00872 +G1 X128.877 Y105.788 E.05243 +G1 X128.98 Y106.034 E.00903 +G1 X129.368 Y106.235 E.01479 +G1 X129.368 Y109.308 E.10402 +G1 X129.775 Y109.775 F12000 +M73 Q52 S3 +M204 P800 +;TYPE:External perimeter +G1 F909 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 P52 R3 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.708 Y104.148 E.05494 +G1 X120.521 Y104.025 E.00758 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.133 Y100.225 E.13228 +G1 X124.133 Y100.521 E.01002 +G1 X124.219 Y100.687 E.00633 +G1 X124.337 Y100.725 E.0042 +G1 X125 Y100.725 E.02244 +G1 X125.83 Y100.707 E.0281 +G1 X125.949 Y100.521 E.00747 +G1 X125.949 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12951 +G1 X129.775 Y104.035 E.12896 +G1 X129.479 Y104.035 E.01002 +G1 X129.313 Y104.12 E.00631 +G1 X129.275 Y104.239 E.00423 +G1 X129.289 Y105.757 E.05138 +G1 X129.479 Y105.885 E.00775 +G1 X129.775 Y105.885 E.01002 +G1 X129.775 Y109.715 E.12964 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z5.101 F12000 +G1 Z5 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F909 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.486 E.07311 +G2 X128.501 Y104.229 I.227 J.693 E.03267 +G1 X128.514 Y105.846 E.05474 +G2 X129.002 Y106.432 I.678 J-.068 E.02739 +G1 X129.002 Y108.674 E.07589 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +M73 P53 R3 +M73 Q53 S3 +G1 X121.326 Y100.998 E.0157 +G1 X123.326 Y100.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:5.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y100.998 E-.16 +;WIPE_END +G1 X122.556 Y100.998 Z5 F12000 +G1 X129.368 Y109.368 Z5.2 +;AFTER_LAYER_CHANGE +;5.2 +G1 X129.368 Y109.368 +G1 Z5.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F906 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.044 Y103.913 E.0111 +G1 X120.632 Y103.675 E.01611 +G1 X120.632 Y100.632 E.103 +G1 X123.932 Y100.632 E.1117 +G1 X124.135 Y101.022 E.01488 +G1 X124.306 Y101.105 E.00643 +G1 X124.485 Y101.132 E.00613 +G1 X125 Y101.132 E.01743 +G1 X125.65 Y101.13 E.022 +G1 X125.956 Y101.019 E.01102 +G1 X126.156 Y100.632 E.01475 +G1 X129.368 Y100.632 E.10872 +G1 X129.368 Y103.55 E.09877 +G1 X128.987 Y103.743 E.01446 +G1 X128.955 Y103.791 E.00195 +G1 X128.868 Y104.105 E.01103 +G1 X128.878 Y105.928 E.06171 +G1 X128.984 Y106.179 E.00922 +G1 X129.368 Y106.374 E.01458 +G1 X129.368 Y109.308 E.09931 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F906 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G2 X120.696 Y104.123 I-16.747 J-.526 E.05581 +G1 X120.521 Y104.025 E.00679 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.458 Y100.225 E.10943 +M73 P54 R3 +M73 Q54 S3 +G1 X124.282 Y100.225 E.02789 +G1 X124.282 Y100.521 E.01002 +G1 X124.369 Y100.688 E.00637 +G1 X124.485 Y100.725 E.00412 +G1 X125.602 Y100.725 E.03781 +G1 X125.72 Y100.687 E.0042 +G1 X125.806 Y100.521 E.00633 +G1 X125.806 Y100.225 E.01002 +G1 X129.775 Y100.225 E.13435 +G1 X129.775 Y103.902 E.12446 +G1 X129.479 Y103.902 E.01002 +G1 X129.315 Y103.984 E.00621 +G1 X129.275 Y104.105 E.00431 +G1 X129.29 Y105.897 E.06066 +G1 X129.479 Y106.024 E.00771 +G1 X129.775 Y106.024 E.01002 +G1 X129.775 Y109.715 E.12494 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z5.301 F12000 +G1 Z5.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F906 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.35 E.06851 +G2 X128.501 Y104.096 I.239 J.7 E.03268 +G1 X128.515 Y105.988 E.06404 +G2 X129.002 Y106.574 I.68 J-.07 E.02735 +G1 X129.002 Y108.674 E.07108 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +G1 X121.326 Y100.998 E.0157 +G1 X123.326 Y100.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:5.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y100.998 E-.16 +;WIPE_END +G1 X122.556 Y100.998 Z5.2 F12000 +G1 X129.368 Y109.368 Z5.4 +;AFTER_LAYER_CHANGE +;5.4 +G1 X129.368 Y109.368 +G1 Z5.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F904 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 P55 R3 +M73 Q55 S3 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.162 E.05446 +G1 X121.036 Y103.899 E.00943 +G1 X120.632 Y103.675 E.01564 +G1 X120.632 Y100.632 E.103 +G1 X123.844 Y100.632 E.10872 +G1 X124.045 Y101.02 E.01479 +G1 X124.137 Y101.074 E.00361 +G1 X124.397 Y101.132 E.00902 +G1 X125.692 Y101.128 E.04383 +G1 X125.982 Y101.017 E.01051 +G1 X126.179 Y100.632 E.01464 +G1 X129.368 Y100.632 E.10794 +G1 X129.368 Y103.416 E.09423 +G1 X128.992 Y103.603 E.01421 +G1 X128.883 Y103.837 E.00874 +G2 X128.878 Y106.069 I42.451 J1.211 E.07556 +G1 X128.989 Y106.324 E.00941 +G1 X129.368 Y106.514 E.01435 +G1 X129.368 Y109.308 E.09457 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F904 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.709 Y104.15 E.05487 +G1 X120.521 Y104.025 E.00764 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.194 Y100.225 E.13435 +G1 X124.194 Y100.521 E.01002 +G1 X124.28 Y100.688 E.00636 +G1 X124.397 Y100.725 E.00415 +G1 X125.625 Y100.725 E.04157 +G1 X125.744 Y100.687 E.00423 +G1 X125.829 Y100.521 E.00631 +G1 X125.829 Y100.225 E.01002 +G1 X129.775 Y100.225 E.13357 +G1 X129.775 Y103.768 E.11993 +G1 X129.479 Y103.768 E.01002 +G1 X129.316 Y103.849 E.00616 +G1 X129.275 Y103.971 E.00436 +G1 X129.275 Y104.968 E.03375 +G3 X129.275 Y105.041 I-.015 J.036 E.00315 +G1 X129.291 Y106.037 E.03372 +G1 X129.479 Y106.162 E.00764 +G1 X129.775 Y106.162 E.01002 +G1 X129.775 Y109.715 E.12026 +M73 Q56 S3 +M204 P1000 +G1 E-.64 F2100 +M73 P56 R3 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z5.501 F12000 +G1 Z5.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F904 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.212 E.06384 +G2 X128.501 Y103.963 I.229 J.695 E.03293 +G1 X128.515 Y106.129 E.07332 +G2 X129.002 Y106.716 I.684 J-.072 E.02737 +G1 X129.002 Y108.674 E.06628 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +G1 X121.326 Y100.998 E.0157 +G1 X123.326 Y100.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:5.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y100.998 E-.16 +;WIPE_END +G1 X122.556 Y100.998 Z5.4 F12000 +G1 X129.465 Y105.012 Z5.6 +;AFTER_LAYER_CHANGE +;5.6 +G1 X129.465 Y105.012 +G1 Z5.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F909 +G1 X129.82 Y105.012 E.01004 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.465 Y105.012 E-.07377 +G1 X129.82 Y105.012 E-.07377 +;WIPE_END +G1 E-.01246 F2100 +G1 X129.368 Y109.368 Z5.676 F12000 +G1 Z5.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F909 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.158 E.0546 +G1 X121.02 Y103.876 E.01022 +G1 X120.632 Y103.675 E.01479 +G1 X120.632 Y100.632 E.103 +G1 X123.701 Y100.632 E.10388 +G1 X123.899 Y101.018 E.01468 +M73 P57 R3 +M73 Q57 S3 +G1 X124 Y101.076 E.00394 +G1 X124.25 Y101.132 E.00867 +G1 X125.841 Y101.128 E.05385 +G1 X126.138 Y101.012 E.01079 +G1 X126.329 Y100.632 E.0144 +G1 X129.368 Y100.632 E.10287 +G1 X129.368 Y103.281 E.08967 +G1 X128.997 Y103.462 E.01397 +G1 X128.884 Y103.7 E.00892 +G1 X128.868 Y104.875 E.03978 +G1 X128.917 Y105.012 E.00492 +G1 X128.881 Y105.044 E.00163 +G1 X128.868 Y105.5 E.01544 +G1 X128.893 Y106.271 E.02611 +G1 X128.995 Y106.469 E.00754 +G1 X129.368 Y106.653 E.01408 +G1 X129.368 Y109.308 E.08987 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F909 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.707 Y104.145 E.05504 +G1 X120.521 Y104.025 E.00749 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.051 Y100.225 E.12951 +G1 X124.051 Y100.521 E.01002 +G1 X124.136 Y100.687 E.00631 +G1 X124.253 Y100.725 E.00416 +G1 X125.774 Y100.725 E.05148 +G1 X125.895 Y100.685 E.00431 +G1 X125.978 Y100.521 E.00622 +G1 X125.978 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12852 +G1 X129.775 Y103.634 E.11539 +G1 X129.479 Y103.634 E.01002 +G1 X129.303 Y103.735 E.00687 +G2 X129.275 Y104.861 I12.17 J.862 E.03814 +G1 X129.37 Y104.936 E.0041 +;WIDTH:0.41646 +G1 X129.465 Y105.012 E.00378 +G1 X129.372 Y105.07 E.0034 +;WIDTH:0.449999 +G1 X129.279 Y105.128 E.00371 +G1 X129.275 Y105.17 E.00143 +G1 X129.275 Y106.097 E.03138 +G1 X129.317 Y106.221 E.00443 +G1 X129.479 Y106.301 E.00612 +G1 X129.775 Y106.301 E.01002 +G1 X129.775 Y109.715 E.11556 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.837 Y106.777 Z5.652 F12000 +G1 Z5.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F909 +G1 X129.002 Y106.858 E.00622 +G1 X129.002 Y108.674 E.06147 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +M73 P58 R3 +M73 Q58 S3 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X126.674 Y100.998 Z5.703 F12000 +G1 Z5.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F909 +G1 X128.674 Y100.998 E.0677 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y100.998 E.0111 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X123.326 Y109.002 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:5.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y109.002 E-.16 +;WIPE_END +G1 X122.556 Y109.002 Z5.6 F12000 +G1 X129.701 Y105.014 Z5.8 +;AFTER_LAYER_CHANGE +;5.8 +G1 X129.701 Y105.014 +G1 Z5.8 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.600836 +G1 F909 +G1 X129.454 Y105.014 E.01146 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.701 Y105.014 E-.05133 +G1 X129.454 Y105.014 E-.05133 +;WIPE_END +G1 E-.05734 F2100 +G1 X129.368 Y109.368 Z5.876 F12000 +G1 Z5.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F909 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.126 Y104.14 E.05521 +G1 X121.016 Y103.87 E.00987 +G1 X120.632 Y103.674 E.01459 +G1 X120.632 Y100.632 E.10297 +G1 X123.558 Y100.632 E.09904 +G1 X123.75 Y101.013 E.01444 +G1 X123.868 Y101.081 E.00461 +G1 X124.112 Y101.132 E.00844 +G1 X125.991 Y101.128 E.0636 +G1 X126.294 Y101.006 E.01106 +G1 X126.479 Y100.632 E.01412 +G1 X129.368 Y100.632 E.09779 +G1 X129.368 Y103.146 E.0851 +G1 X129.003 Y103.321 E.0137 +G1 X128.956 Y103.389 E.0028 +G1 X128.868 Y103.704 E.01107 +G1 X128.868 Y104.736 E.03493 +G1 X128.906 Y104.949 E.00732 +G1 X128.991 Y105.014 E.00362 +G1 X128.906 Y105.078 E.0036 +G1 X128.868 Y105.5 E.01434 +G1 X128.894 Y106.413 E.03092 +G1 X129 Y106.615 E.00772 +G1 X129.368 Y106.793 E.01384 +G1 X129.368 Y109.308 E.08513 +G1 X129.775 Y109.775 F12000 +M73 P59 R3 +M73 Q59 S3 +M204 P800 +;TYPE:External perimeter +G1 F909 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G2 X120.725 Y105.771 I-.036 J-.24 E.01045 +G1 X120.705 Y104.141 E.05518 +G1 X120.521 Y104.025 E.00736 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.909 Y100.225 E.1247 +G1 X123.909 Y100.521 E.01002 +G1 X123.992 Y100.685 E.00622 +G1 X124.112 Y100.725 E.00428 +G1 X125.923 Y100.725 E.0613 +G1 X126.047 Y100.683 E.00443 +G1 X126.127 Y100.521 E.00612 +G1 X126.127 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12348 +G1 X129.775 Y103.5 E.11085 +G1 X129.479 Y103.5 E.01002 +G1 X129.304 Y103.599 E.00681 +G2 X129.275 Y104.748 I11.508 J.864 E.03892 +;WIDTH:0.487709 +G1 X129.32 Y104.814 E.00295 +;WIDTH:0.525418 +G1 X129.365 Y104.881 E.00324 +;WIDTH:0.563127 +G1 X129.41 Y104.947 E.00346 +;WIDTH:0.600836 +G1 X129.454 Y105.014 E.00372 +G1 X129.413 Y105.065 E.00304 +;WIDTH:0.563127 +G1 X129.371 Y105.117 E.00289 +;WIDTH:0.525418 +G1 X129.329 Y105.169 E.00268 +;WIDTH:0.487709 +G1 X129.288 Y105.221 E.00245 +;WIDTH:0.449999 +G1 X129.275 Y105.292 E.00244 +G1 X129.275 Y106.236 E.03195 +G1 X129.319 Y106.362 E.00452 +G1 X129.479 Y106.439 E.00601 +G1 X129.775 Y106.439 E.01002 +G1 X129.775 Y109.715 E.11089 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.715 Y106.852 Z5.851 F12000 +G1 Z5.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F909 +G1 X129.002 Y107 E.01093 +G1 X129.002 Y108.674 E.05666 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X123.326 Y109.002 Z5.92 F12000 +G1 Z5.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F909 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X129.002 Y101.326 E.0111 +M73 P60 R3 +M73 Q60 S3 +G1 X128.674 Y100.998 E.0157 +G1 X126.679 Y101.006 E.06753 +M204 P1000 +;LAYER_CHANGE +;Z:6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.449 Y101.003 E-.16 +;WIPE_END +G1 X127.449 Y101.003 Z5.8 F12000 +G1 X125.046 Y100.526 Z6 F9009.12 +;AFTER_LAYER_CHANGE +;6 +G1 X125.046 Y100.526 F12000 +G1 Z6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F926 +G1 X125.046 Y100.5 E.00074 +G1 F900 +G1 X125.046 Y100.3 E.00565 +M204 P1000 +;TYPE:Overhang perimeter +G1 X125.046 Y100.072 E.00645 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.046 Y100.526 E-.09435 +G1 X125.046 Y100.5 E-.0054 +G1 X125.046 Y100.3 E-.04156 +;WIPE_END +G1 E-.01869 F2100 +G1 X129.368 Y109.368 Z6.175 F12000 +G1 Z6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F926 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.94 E.04834 +G1 X120.928 Y107.828 E.01071 +G1 X121.092 Y107.588 E.00984 +G1 X121.132 Y107.371 E.00747 +G1 X121.132 Y103.299 E.13783 +G1 X121.129 Y102.563 E.02491 +G1 X120.928 Y102.172 E.01488 +G1 X120.632 Y102.06 E.01071 +G1 X120.632 Y100.632 E.04834 +G1 X123.411 Y100.632 E.09407 +G1 X123.577 Y100.989 E.01333 +G1 X123.691 Y101.065 E.00464 +G1 X123.97 Y101.132 E.00971 +G1 X124.996 Y101.132 E.03473 +G1 X125.386 Y101.132 E.0132 +G1 X126.14 Y101.128 E.02552 +G1 X126.451 Y101 E.01138 +G1 X126.629 Y100.632 E.01384 +G1 X129.368 Y100.632 E.09271 +G1 X129.368 Y103.012 E.08056 +G1 X129.009 Y103.18 E.01342 +G1 X128.961 Y103.247 E.00279 +G1 X128.868 Y103.57 E.01138 +G1 X128.868 Y104.614 E.03534 +G1 X128.941 Y104.904 E.01012 +G1 X129.11 Y105.015 E.00684 +G1 X128.941 Y105.127 E.00686 +G1 X128.889 Y105.257 E.00474 +G1 X128.868 Y105.5 E.00826 +G1 X128.895 Y106.555 E.03572 +G1 X129.006 Y106.761 E.00792 +G1 X129.368 Y106.932 E.01355 +G1 X129.368 Y109.308 E.08042 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F926 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y107.575 E.07447 +G1 X120.521 Y107.575 E.01002 +G1 X120.657 Y107.523 E.00493 +G1 X120.725 Y107.371 E.00564 +G1 X120.725 Y102.629 E.16051 +G1 X120.657 Y102.477 E.00564 +M73 P61 R3 +M73 Q61 S3 +G1 X120.521 Y102.425 E.00493 +G1 X120.225 Y102.425 E.01002 +G1 X120.225 Y100.225 E.07447 +G1 X123.766 Y100.225 E.11986 +G1 X123.766 Y100.521 E.01002 +G1 X123.839 Y100.677 E.00583 +G1 X123.97 Y100.725 E.00472 +G1 X124.996 Y100.725 E.03473 +G1 X125.021 Y100.625 E.00349 +;WIDTH:0.41646 +G1 X125.046 Y100.526 E.00317 +G1 X125.07 Y100.625 E.00316 +;WIDTH:0.449999 +G1 X125.093 Y100.725 E.00347 +G1 X126.072 Y100.725 E.03314 +G1 X126.198 Y100.681 E.00452 +G1 X126.276 Y100.521 E.00603 +G1 X126.276 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11844 +G1 X129.775 Y103.367 E.10635 +G1 X129.479 Y103.367 E.01002 +G1 X129.322 Y103.44 E.00586 +G1 X129.281 Y103.522 E.0031 +G1 X129.275 Y103.928 E.01374 +G1 X129.282 Y104.667 E.02502 +G1 X129.461 Y104.815 E.00786 +;WIDTH:0.445393 +G1 X129.778 Y104.815 E.01061 +;WIDTH:0.443861 +G1 X129.778 Y105.216 E.01337 +G1 X129.479 Y105.216 E.00997 +;WIDTH:0.449999 +G1 X129.299 Y105.32 E.00704 +G1 X129.275 Y105.416 E.00335 +G1 X129.275 Y106.374 E.03243 +G1 X129.321 Y106.503 E.00464 +G1 X129.479 Y106.578 E.00592 +G1 X129.775 Y106.578 E.01002 +G1 X129.775 Y109.715 E.10618 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.651 Y106.866 Z6.051 F12000 +G1 Z6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F926 +G2 X129.002 Y107.143 I.431 J-.186 E.01577 +G1 X129.002 Y108.674 E.05182 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G2 X120.998 Y101.822 I.376 J.627 E.03226 +G1 X121.174 Y101.889 E.00637 +G3 X121.495 Y103.121 I-1.127 J.951 E.04456 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G2 X121.442 Y102.362 I-1.448 J-.281 E-.16 +;WIPE_END +G1 X126.766 Y101.144 Z6.095 F12000 +G1 Z6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F926 +G1 X126.836 Y100.998 E.00548 +G1 X128.674 Y100.998 E.06221 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y100.998 E.0111 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X123.326 Y109.002 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:6.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y109.002 E-.16 +;WIPE_END +G1 X122.556 Y109.002 Z6 F12000 +G1 X125.05 Y100.199 Z6.2 +;AFTER_LAYER_CHANGE +;6.2 +G1 X125.05 Y100.199 +M73 Q62 S2 +G1 Z6.2 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.39964 +G1 F944 +G1 X125.05 Y100.537 E.01003 +M204 P1000 +G1 E-.64 F2100 +M73 P62 R3 +;WIPE_START +G1 F9600 +G1 X125.05 Y100.199 E-.07024 +G1 X125.05 Y100.537 E-.07024 +M73 P62 R2 +;WIPE_END +G1 E-.01952 F2100 +G1 X129.368 Y109.368 Z6.372 F12000 +G1 Z6.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F944 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.737 E.05521 +G1 X120.945 Y107.611 E.01142 +G1 X121.096 Y107.379 E.00937 +G1 X121.132 Y107.171 E.00715 +G1 X121.132 Y102.829 E.14697 +G1 X121.094 Y102.616 E.00732 +G1 X120.945 Y102.389 E.00919 +G1 X120.632 Y102.263 E.01142 +G1 X120.632 Y100.632 E.05521 +G1 X123.268 Y100.632 E.08923 +G1 X123.43 Y100.985 E.01315 +G1 X123.527 Y101.053 E.00401 +G1 X123.827 Y101.132 E.0105 +G1 X124.873 Y101.132 E.03541 +G1 X124.921 Y101.13 E.00163 +G1 X125.05 Y101.074 E.00476 +G1 X125.086 Y101.115 E.00185 +G1 X125.51 Y101.132 E.01436 +G1 X126.289 Y101.128 E.02637 +G1 X126.608 Y100.994 E.01171 +G1 X126.779 Y100.632 E.01355 +G1 X129.368 Y100.632 E.08763 +G1 X129.368 Y102.877 E.07599 +G1 X129.016 Y103.038 E.0131 +G1 X128.886 Y103.288 E.00954 +G1 X128.868 Y103.779 E.01663 +G1 X128.869 Y104.532 E.02549 +G1 X128.975 Y104.836 E.0109 +G1 X129.285 Y105.016 E.01213 +G1 X128.975 Y105.197 E.01215 +G1 X128.902 Y105.341 E.00546 +G1 X128.868 Y105.543 E.00693 +G1 X128.897 Y106.698 E.03911 +G1 X129.013 Y106.908 E.00812 +G1 X129.368 Y107.072 E.01324 +G1 X129.368 Y109.308 E.07569 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F944 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y107.375 E.08124 +G1 X120.521 Y107.375 E.01002 +G1 X120.663 Y107.318 E.00518 +G1 X120.725 Y107.171 E.0054 +G1 X120.725 Y106.496 E.02285 +G1 X120.725 Y103.504 E.10128 +G1 X120.725 Y102.829 E.02285 +G1 X120.663 Y102.682 E.0054 +G1 X120.521 Y102.625 E.00518 +G1 X120.225 Y102.625 E.01002 +G1 X120.225 Y100.225 E.08124 +G1 X123.624 Y100.225 E.11505 +G1 X123.624 Y100.521 E.01002 +G1 X123.695 Y100.676 E.00577 +G1 X123.827 Y100.725 E.00477 +G1 X124.882 Y100.725 E.03571 +G1 X124.966 Y100.631 E.00427 +;WIDTH:0.42482 +G1 X125.05 Y100.537 E.004 +G1 X125.115 Y100.628 E.00355 +;WIDTH:0.449999 +G1 X125.181 Y100.719 E.00381 +G1 X125.228 Y100.725 E.0016 +G1 X126.221 Y100.725 E.03361 +G1 X126.35 Y100.679 E.00464 +G1 X126.425 Y100.521 E.00592 +G1 X126.425 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11339 +G1 X129.775 Y103.233 E.10182 +M73 P63 R2 +M73 Q63 S2 +G1 X129.479 Y103.233 E.01002 +G1 X129.324 Y103.304 E.00577 +G1 X129.281 Y103.387 E.00316 +G1 X129.275 Y103.779 E.01327 +G1 X129.278 Y104.525 E.02525 +;WIDTH:0.490464 +G1 X129.337 Y104.601 E.00358 +;WIDTH:0.530929 +G1 X129.396 Y104.677 E.0039 +;WIDTH:0.571394 +G1 X129.456 Y104.753 E.00425 +G1 X129.715 Y104.753 E.01138 +;WIDTH:0.570084 +G1 X129.715 Y105.279 E.02306 +G1 X129.479 Y105.279 E.01034 +G1 X129.423 Y105.329 E.00329 +;WIDTH:0.530056 +G1 X129.367 Y105.378 E.00301 +;WIDTH:0.490028 +G1 X129.311 Y105.428 E.00279 +;WIDTH:0.449999 +G1 X129.275 Y105.541 E.00401 +G1 X129.275 Y106.513 E.0329 +G1 X129.323 Y106.644 E.00472 +G1 X129.479 Y106.716 E.00582 +G1 X129.775 Y106.716 E.01002 +G1 X129.775 Y109.715 E.10151 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z6.301 F12000 +G1 Z6.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F944 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y102.662 E.04522 +G2 X128.501 Y103.427 I.253 J.712 E.03325 +G1 X128.557 Y104.817 E.04709 +G3 X128.557 Y105.216 I-.202 J.199 E.01498 +G2 X128.546 Y106.813 I5.203 J.833 E.05427 +G2 X129.002 Y107.285 I.648 J-.169 E.02322 +G1 X129.002 Y108.674 E.04702 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +G1 X121.326 Y100.998 E.0157 +G1 X123.054 Y100.998 E.05849 +G1 X123.167 Y101.245 E.00919 +M204 P1000 +;LAYER_CHANGE +;Z:6.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.054 Y100.998 E-.05645 +G1 X122.556 Y100.998 E-.10355 +;WIPE_END +G1 X122.556 Y100.998 Z6.2 F12000 +G1 X129.368 Y109.368 Z6.4 +;AFTER_LAYER_CHANGE +;6.4 +G1 X129.368 Y109.368 +G1 Z6.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F934 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.535 E.06204 +G1 X120.96 Y107.396 E.01206 +G1 X121.098 Y107.172 E.00891 +G1 X121.132 Y106.971 E.0069 +G1 X121.132 Y103.685 E.11123 +G1 X121.118 Y102.899 E.02661 +G1 X120.96 Y102.604 E.01133 +G1 X120.632 Y102.465 E.01206 +G1 X120.632 Y100.632 E.06204 +M73 Q64 S2 +G1 X123.125 Y100.632 E.08438 +M73 P64 R2 +G1 X123.286 Y100.983 E.01307 +G1 X123.423 Y101.073 E.00555 +G1 X123.685 Y101.132 E.00909 +G1 X124.74 Y101.132 E.03571 +G1 X124.978 Y101.084 E.00822 +G1 X125.055 Y100.978 E.00443 +G1 X125.131 Y101.084 E.00441 +G1 X125.37 Y101.132 E.00825 +G1 X126.37 Y101.132 E.03385 +G1 X126.588 Y101.092 E.0075 +G1 X126.765 Y100.987 E.00697 +G1 X126.93 Y100.632 E.01325 +G1 X129.368 Y100.632 E.08252 +G1 X129.368 Y102.742 E.07142 +G1 X129.023 Y102.896 E.01279 +G1 X128.887 Y103.15 E.00975 +G1 X128.868 Y103.63 E.01626 +G1 X128.874 Y104.447 E.02766 +G1 X128.986 Y104.725 E.01014 +G1 X129.368 Y104.959 E.01516 +G1 X129.368 Y105.076 E.00396 +G1 X128.986 Y105.31 E.01516 +G1 X128.913 Y105.441 E.00508 +G1 X128.868 Y105.672 E.00797 +G1 X128.868 Y106.651 E.03314 +G1 X128.898 Y106.84 E.00648 +G1 X129.02 Y107.055 E.00837 +G1 X129.368 Y107.212 E.01292 +G1 X129.368 Y109.308 E.07095 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F934 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y107.175 E.08801 +G1 X120.521 Y107.175 E.01002 +G1 X120.668 Y107.113 E.0054 +G1 X120.725 Y106.971 E.00518 +G1 X120.725 Y103.685 E.11123 +G1 X120.702 Y102.934 E.02543 +G1 X120.521 Y102.825 E.00715 +G1 X120.225 Y102.825 E.01002 +G1 X120.225 Y100.225 E.08801 +G1 X123.481 Y100.225 E.11021 +G1 X123.481 Y100.521 E.01002 +G1 X123.552 Y100.675 E.00574 +G1 X123.685 Y100.725 E.00481 +G1 X124.74 Y100.725 E.03571 +G1 X124.819 Y100.709 E.00273 +G1 X124.858 Y100.622 E.00323 +;WIDTH:0.404589 +G1 X124.897 Y100.535 E.00287 +;WIDTH:0.359179 +G1 X124.897 Y100.179 E.00936 +;WIDTH:0.357976 +G1 X125.212 Y100.179 E.00825 +G1 X125.212 Y100.521 E.00896 +;WIDTH:0.403988 +G1 X125.251 Y100.615 E.00306 +;WIDTH:0.449999 +G1 X125.29 Y100.709 E.00344 +G1 X125.37 Y100.725 E.00276 +G1 X126.37 Y100.725 E.03385 +G1 X126.502 Y100.677 E.00475 +G1 X126.574 Y100.521 E.00582 +G1 X126.574 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10835 +G1 X129.775 Y103.099 E.09728 +G1 X129.479 Y103.099 E.01002 +G1 X129.327 Y103.167 E.00564 +G1 X129.281 Y103.252 E.00327 +G1 X129.275 Y103.63 E.0128 +G1 X129.283 Y104.419 E.02671 +G1 X129.425 Y104.56 E.00677 +G1 X129.775 Y104.567 E.01185 +G1 X129.775 Y105.468 E.0305 +G1 X129.479 Y105.468 E.01002 +G1 X129.314 Y105.551 E.00625 +G1 X129.275 Y105.672 E.0043 +G1 X129.275 Y106.651 E.03314 +G1 X129.326 Y106.786 E.00488 +G1 X129.479 Y106.855 E.00568 +G1 X129.775 Y106.855 E.01002 +G1 X129.775 Y109.715 E.09681 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z6.501 F12000 +G1 Z6.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F934 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P65 R2 +M73 Q65 S2 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y102.524 E.04055 +G2 X128.502 Y103.293 I.26 J.717 E.03331 +G2 X128.574 Y104.735 I4.476 J.497 E.04908 +G1 X128.699 Y104.958 E.00865 +G3 X128.695 Y105.08 I.005 J.061 E.0069 +G2 X128.502 Y105.627 I1.036 J.672 E.01982 +G1 X128.502 Y106.664 E.0351 +G2 X129.002 Y107.428 I.775 J.039 E.03304 +G1 X129.002 Y108.674 E.04218 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +G1 X121.326 Y100.998 E.0157 +G1 X122.91 Y100.998 E.05362 +G2 X123.142 Y101.325 I.472 J-.089 E.014 +M204 P1000 +;LAYER_CHANGE +;Z:6.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X122.91 Y100.998 I.24 J-.416 E-.08595 +G1 X122.554 Y100.998 E-.07405 +;WIPE_END +G1 X122.554 Y100.998 Z6.4 F12000 +G1 X129.368 Y109.368 Z6.6 +;AFTER_LAYER_CHANGE +;6.6 +G1 X129.368 Y109.368 +G1 Z6.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F931 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.333 E.06888 +G1 X120.973 Y107.183 E.01261 +G1 X121.101 Y106.965 E.00856 +G1 X121.132 Y106.771 E.00665 +G1 X121.132 Y103.543 E.10926 +G1 X121.112 Y103.073 E.01592 +G1 X120.973 Y102.817 E.00986 +G1 X120.632 Y102.667 E.01261 +G1 X120.632 Y100.632 E.06888 +G1 X122.982 Y100.632 E.07954 +G1 X123.142 Y100.983 E.01306 +G1 X123.323 Y101.091 E.00713 +G1 X123.543 Y101.132 E.00757 +G1 X124.601 Y101.132 E.03581 +G1 X124.66 Y101.129 E.002 +G1 X124.922 Y101.041 E.00936 +G1 X125.06 Y100.817 E.00891 +G1 X125.198 Y101.041 E.00891 +G1 X125.338 Y101.105 E.00521 +G1 X125.518 Y101.132 E.00616 +G1 X126.519 Y101.132 E.03388 +G1 X126.741 Y101.09 E.00765 +G1 X126.923 Y100.979 E.00722 +G1 X127.08 Y100.632 E.01289 +G1 X129.368 Y100.632 E.07745 +G1 X129.368 Y102.607 E.06685 +G1 X129.031 Y102.754 E.01245 +G1 X128.889 Y103.012 E.00997 +G1 X128.868 Y103.481 E.01589 +G1 X128.876 Y104.334 E.02887 +G1 X128.99 Y104.599 E.00976 +G1 X129.22 Y104.786 E.01003 +G1 X129.368 Y104.819 E.00513 +G1 X129.368 Y105.217 E.01347 +G1 X129.22 Y105.25 E.00513 +G1 X128.99 Y105.436 E.01001 +G1 X128.923 Y105.55 E.00448 +G1 X128.868 Y105.803 E.00876 +G1 X128.868 Y106.789 E.03337 +G1 X128.899 Y106.983 E.00665 +G1 X129.028 Y107.202 E.0086 +G1 X129.368 Y107.351 E.01257 +G1 X129.368 Y109.308 E.06624 +M73 Q66 S2 +G1 X129.775 Y109.775 F12000 +M73 P66 R2 +M204 P800 +;TYPE:External perimeter +G1 F931 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.975 E.09478 +G1 X120.521 Y106.975 E.01002 +G1 X120.672 Y106.909 E.00558 +G1 X120.725 Y106.771 E.005 +G1 X120.725 Y103.229 E.11989 +G1 X120.689 Y103.113 E.00411 +G1 X120.521 Y103.025 E.00642 +G1 X120.225 Y103.025 E.01002 +G1 X120.225 Y100.225 E.09478 +G1 X123.339 Y100.225 E.10541 +G1 X123.339 Y100.521 E.01002 +G1 X123.409 Y100.675 E.00573 +G1 X123.543 Y100.725 E.00484 +G1 X124.601 Y100.725 E.03581 +G1 X124.661 Y100.716 E.00205 +;WIDTH:0.476675 +G1 X124.746 Y100.629 E.00439 +;WIDTH:0.50335 +G1 X124.83 Y100.542 E.00463 +G1 X124.83 Y100.251 E.01114 +;WIDTH:0.501597 +G1 X125.289 Y100.251 E.01751 +G1 X125.289 Y100.521 E.0103 +G1 X125.35 Y100.608 E.00405 +;WIDTH:0.475798 +G1 X125.411 Y100.695 E.00382 +;WIDTH:0.449999 +G1 X125.518 Y100.725 E.00376 +G1 X126.519 Y100.725 E.03388 +G1 X126.654 Y100.674 E.00488 +G1 X126.723 Y100.521 E.00568 +G1 X126.723 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10331 +G1 X129.775 Y102.966 E.09278 +G1 X129.479 Y102.966 E.01002 +G1 X129.329 Y103.031 E.00553 +G1 X129.282 Y103.117 E.00332 +G1 X129.275 Y103.481 E.01232 +G1 X129.279 Y104.274 E.02684 +G1 X129.392 Y104.417 E.00617 +G1 X129.775 Y104.436 E.01298 +G1 X129.775 Y105.599 E.03937 +G1 X129.479 Y105.599 E.01002 +G1 X129.316 Y105.681 E.00618 +G1 X129.275 Y105.803 E.00436 +G1 X129.275 Y106.789 E.03337 +G1 X129.328 Y106.927 E.005 +G1 X129.479 Y106.993 E.00558 +G1 X129.775 Y106.993 E.01002 +G1 X129.775 Y109.715 E.09214 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.516 Y106.89 Z6.651 F12000 +G1 Z6.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F931 +G2 X129.002 Y107.572 I.729 J-.005 E.0302 +G1 X129.002 Y108.674 E.0373 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X120.998 Y101.326 E.0111 +G1 X121.326 Y100.998 E.0157 +G1 X122.767 Y100.998 E.04878 +G2 X123.434 Y101.494 I.7 J-.245 E.02987 +G1 X124.892 Y101.455 E.04937 +G3 X125.227 Y101.455 I.167 J.196 E.01234 +G2 X126.875 Y101.434 I.762 J-4.946 E.05604 +G2 X127.297 Y100.998 I-.2 J-.615 E.02138 +G1 X128.674 Y100.998 E.04661 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y100.998 E.0111 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +M73 P67 R2 +M73 Q67 S2 +G1 X121.326 Y109.002 E.0157 +G1 X123.326 Y109.002 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:6.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y109.002 E-.16 +;WIPE_END +G1 X122.556 Y109.002 Z6.6 F12000 +G1 X129.368 Y109.368 Z6.8 +;AFTER_LAYER_CHANGE +;6.8 +G1 X129.368 Y109.368 +G1 Z6.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F934 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.131 E.07572 +G1 X120.984 Y106.97 E.0131 +G1 X121.103 Y106.758 E.00823 +G1 X121.132 Y106.571 E.00641 +G1 X121.132 Y103.429 E.10635 +G1 X121.105 Y103.248 E.00619 +G1 X120.987 Y103.034 E.00827 +G1 X120.632 Y102.868 E.01327 +G1 X120.632 Y100.632 E.07569 +G1 X122.84 Y100.632 E.07474 +G1 X123 Y100.982 E.01303 +G1 X123.225 Y101.107 E.00871 +G1 X123.4 Y101.132 E.00598 +G1 X124.46 Y101.132 E.03588 +G1 X124.539 Y101.127 E.00268 +G1 X124.817 Y101.017 E.01012 +G1 X125.059 Y100.636 E.01528 +G1 X125.313 Y101.017 E.0155 +G1 X125.449 Y101.091 E.00524 +G1 X125.67 Y101.132 E.00761 +G1 X126.668 Y101.132 E.03378 +G1 X126.895 Y101.088 E.00783 +G1 X127.082 Y100.971 E.00747 +G1 X127.23 Y100.632 E.01252 +G1 X129.368 Y100.632 E.07237 +G1 X129.368 Y102.472 E.06228 +G1 X129.04 Y102.611 E.01206 +G1 X128.89 Y102.873 E.01022 +G1 X128.868 Y103.035 E.00553 +G1 X128.868 Y104.102 E.03612 +G1 X128.93 Y104.37 E.00931 +G1 X129.148 Y104.615 E.0111 +G1 X129.368 Y104.68 E.00776 +G1 X129.368 Y105.357 E.02292 +G1 X129.148 Y105.421 E.00776 +G1 X128.93 Y105.666 E.0111 +G1 X128.868 Y105.935 E.00934 +G1 X128.868 Y106.928 E.03361 +G1 X128.901 Y107.126 E.00679 +G1 X129.037 Y107.35 E.00887 +G1 X129.368 Y107.491 E.01218 +G1 X129.368 Y109.308 E.0615 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F934 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.775 E.10155 +G1 X120.521 Y106.775 E.01002 +G1 X120.676 Y106.704 E.00577 +G1 X120.725 Y106.571 E.0048 +G1 X120.725 Y103.429 E.10635 +G1 X120.677 Y103.297 E.00475 +G1 X120.521 Y103.225 E.00582 +G1 X120.225 Y103.225 E.01002 +G1 X120.225 Y100.225 E.10155 +G1 X123.197 Y100.225 E.1006 +M73 P68 R2 +M73 Q68 S2 +G1 X123.197 Y100.521 E.01002 +G1 X123.267 Y100.675 E.00573 +G1 X123.4 Y100.725 E.00481 +G1 X124.46 Y100.725 E.03588 +G1 X124.533 Y100.711 E.00252 +G1 X124.66 Y100.56 E.00668 +G1 X124.663 Y100.225 E.01134 +G1 X125.466 Y100.225 E.02718 +G1 X125.466 Y100.521 E.01002 +G1 X125.551 Y100.687 E.00631 +G1 X125.67 Y100.725 E.00423 +G1 X126.668 Y100.725 E.03378 +G1 X126.806 Y100.671 E.00502 +G1 X126.872 Y100.521 E.00555 +G1 X126.872 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09826 +G1 X129.775 Y102.832 E.08824 +G1 X129.479 Y102.832 E.01002 +G1 X129.332 Y102.894 E.0054 +G1 X129.282 Y102.981 E.0034 +G1 X129.275 Y103.332 E.01188 +G1 X129.277 Y104.129 E.02698 +G1 X129.368 Y104.273 E.00577 +G1 X129.775 Y104.305 E.01382 +G1 X129.775 Y105.731 E.04827 +G1 X129.479 Y105.731 E.01002 +G1 X129.315 Y105.813 E.00621 +G1 X129.275 Y105.935 E.00435 +G1 X129.275 Y106.928 E.03361 +G1 X129.331 Y107.069 E.00514 +G1 X129.479 Y107.131 E.00543 +G1 X129.775 Y107.131 E.01002 +G1 X129.775 Y109.715 E.08747 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z6.901 F12000 +G1 Z6.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F934 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y102.247 E.03117 +G2 X128.502 Y103.024 I.281 J.73 E.03345 +G1 X128.502 Y104.113 E.03686 +G2 X129.002 Y104.945 I.964 J-.013 E.03443 +G3 X128.955 Y105.105 I-.097 J.059 E.00636 +G2 X128.502 Y105.92 I.591 J.862 E.03271 +G2 X128.553 Y107.25 I6.324 J.421 E.04514 +G2 X129.002 Y107.715 I.667 J-.194 E.02276 +G1 X129.002 Y108.674 E.03246 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y102.655 E.04499 +G3 X121.444 Y103.112 I-.182 J.622 E.0226 +M204 P1000 +;LAYER_CHANGE +;Z:7 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G2 X120.998 Y102.655 I-.627 J.166 E-.13874 +G1 X120.998 Y102.553 E-.02126 +;WIPE_END +G1 X120.998 Y102.553 Z6.8 F12000 +G1 X129.368 Y109.368 Z7 +;AFTER_LAYER_CHANGE +;7 +G1 X129.368 Y109.368 +G1 Z7 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F936 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.93 E.08252 +M73 P69 R2 +M73 Q69 S2 +G1 X120.994 Y106.758 E.01357 +G1 X121.105 Y106.552 E.00792 +G1 X121.132 Y106.371 E.00619 +G1 X121.132 Y104.318 E.06949 +G1 X121.127 Y103.551 E.02596 +G1 X120.986 Y103.232 E.01181 +G1 X120.632 Y103.069 E.01319 +G1 X120.632 Y100.632 E.08249 +G1 X122.696 Y100.632 E.06986 +G1 X122.848 Y100.974 E.01267 +G1 X123.107 Y101.113 E.00995 +G1 X123.629 Y101.132 E.01768 +G1 X124.418 Y101.124 E.02671 +G1 X124.684 Y101.011 E.00978 +G1 X124.881 Y100.757 E.01088 +G1 X124.907 Y100.632 E.00432 +G1 X125.233 Y100.632 E.01103 +G1 X125.258 Y100.757 E.00431 +G1 X125.456 Y101.011 E.0109 +G1 X125.574 Y101.08 E.00463 +G1 X125.821 Y101.132 E.00854 +G1 X126.817 Y101.132 E.03371 +G1 X127.049 Y101.086 E.00801 +G1 X127.235 Y100.967 E.00747 +G1 X127.381 Y100.632 E.01237 +G1 X129.368 Y100.632 E.06726 +G1 X129.368 Y102.336 E.05768 +G1 X129.049 Y102.467 E.01167 +G1 X128.891 Y102.734 E.0105 +G1 X128.868 Y102.902 E.00574 +G1 X128.868 Y103.971 E.03618 +G1 X128.911 Y104.197 E.00779 +G1 X129.094 Y104.446 E.01046 +G1 X129.368 Y104.542 E.00983 +G1 X129.368 Y105.495 E.03226 +G1 X129.094 Y105.592 E.00984 +G1 X128.911 Y105.841 E.01046 +G1 X128.868 Y106.066 E.00775 +G1 X128.868 Y107.066 E.03385 +G1 X128.903 Y107.27 E.00701 +G1 X129.047 Y107.498 E.00913 +G1 X129.368 Y107.631 E.01176 +G1 X129.368 Y109.308 E.05676 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F936 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y106.575 E.10832 +G1 X120.521 Y106.575 E.01002 +G1 X120.679 Y106.5 E.00592 +G1 X120.725 Y106.371 E.00464 +G1 X120.725 Y103.629 E.09281 +G1 X120.676 Y103.496 E.0048 +G1 X120.521 Y103.425 E.00577 +G1 X120.225 Y103.425 E.01002 +G1 X120.225 Y100.225 E.10832 +G1 X123.054 Y100.225 E.09576 +G1 X123.054 Y100.521 E.01002 +G1 X123.121 Y100.672 E.00559 +G1 X123.208 Y100.719 E.00335 +G1 X123.629 Y100.725 E.01425 +G1 X124.367 Y100.719 E.02498 +G1 X124.506 Y100.6 E.00619 +G1 X124.522 Y100.225 E.0127 +G1 X125.618 Y100.225 E.0371 +G1 X125.618 Y100.521 E.01002 +G1 X125.7 Y100.685 E.00621 +G1 X125.821 Y100.725 E.00431 +G1 X126.817 Y100.725 E.03371 +G1 X126.957 Y100.67 E.00509 +G1 X127.021 Y100.521 E.00549 +G1 X127.021 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09322 +G1 X129.775 Y102.698 E.08371 +G1 X129.479 Y102.698 E.01002 +G1 X129.335 Y102.757 E.00527 +G1 X129.275 Y102.902 E.00531 +G1 X129.275 Y103.971 E.03618 +G1 X129.304 Y104.076 E.00369 +G1 X129.479 Y104.175 E.00681 +G1 X129.775 Y104.175 E.01002 +G1 X129.775 Y105.863 E.05714 +G1 X129.479 Y105.863 E.01002 +M73 Q70 S2 +G1 X129.351 Y105.908 E.00459 +M73 P70 R2 +G1 X129.275 Y106.066 E.00593 +G1 X129.275 Y107.066 E.03385 +G1 X129.335 Y107.21 E.00528 +G1 X129.479 Y107.27 E.00528 +G1 X129.775 Y107.27 E.01002 +G1 X129.775 Y109.715 E.08276 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z7.101 F12000 +G1 Z7 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F936 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y102.107 E.02644 +G2 X128.502 Y102.89 I.294 J.74 E.03358 +G1 X128.502 Y103.983 E.037 +G2 X129.002 Y104.789 I.878 J.014 E.03392 +G1 X129.002 Y105.248 E.01554 +G2 X128.502 Y106.055 I.378 J.793 E.03395 +G2 X128.556 Y107.397 I6.152 J.423 E.04555 +G2 X129.002 Y107.859 I.678 J-.208 E.02256 +G1 X129.002 Y108.674 E.02759 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y102.855 E.05175 +G3 X121.352 Y103.135 I-.091 J.479 E.01588 +M204 P1000 +;LAYER_CHANGE +;Z:7.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G2 X120.998 Y102.855 I-.445 J.199 E-.09752 +G1 X120.998 Y102.554 E-.06248 +;WIPE_END +G1 X120.998 Y102.554 Z7 F12000 +G1 X129.368 Y109.368 Z7.2 +;AFTER_LAYER_CHANGE +;7.2 +G1 X129.368 Y109.368 +G1 Z7.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F935 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.728 E.08936 +G1 X121.002 Y106.548 E.01393 +G1 X121.106 Y106.347 E.00766 +G1 X121.132 Y106.171 E.00602 +G1 X121.132 Y104.984 E.04018 +G1 X121.114 Y103.683 E.04404 +G1 X120.99 Y103.437 E.00932 +G1 X120.632 Y103.27 E.01337 +G1 X120.632 Y100.632 E.08929 +G1 X122.552 Y100.632 E.06499 +G1 X122.696 Y100.966 E.01231 +G1 X122.846 Y101.07 E.00618 +G1 X123.115 Y101.132 E.00934 +G1 X124.177 Y101.132 E.03595 +G1 X124.443 Y101.071 E.00924 +M73 Q71 S2 +G1 X124.696 Y100.843 E.01153 +G1 X124.756 Y100.632 E.00743 +G1 X125.394 Y100.632 E.0216 +M73 P71 R2 +G1 X125.454 Y100.843 E.00743 +G1 X125.707 Y101.071 E.01153 +G1 X125.973 Y101.132 E.00924 +G1 X126.967 Y101.132 E.03365 +G1 X127.204 Y101.084 E.00819 +G1 X127.385 Y100.967 E.0073 +G1 X127.532 Y100.632 E.01238 +G1 X129.368 Y100.632 E.06215 +G1 X129.368 Y102.201 E.05311 +G1 X129.06 Y102.323 E.01121 +G1 X128.893 Y102.594 E.01077 +G1 X128.868 Y102.768 E.00595 +G1 X128.868 Y103.84 E.03629 +G1 X128.898 Y104.031 E.00654 +G1 X129.054 Y104.279 E.00992 +G1 X129.368 Y104.406 E.01146 +G1 X129.368 Y105.632 E.0415 +G1 X129.054 Y105.759 E.01146 +G1 X128.917 Y105.959 E.00821 +G1 X128.868 Y106.198 E.00826 +G1 X128.868 Y107.205 E.03409 +G1 X128.905 Y107.414 E.00718 +G1 X129.058 Y107.647 E.00944 +G1 X129.368 Y107.771 E.0113 +G1 X129.368 Y109.308 E.05203 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F935 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.375 E.11509 +G1 X120.521 Y106.375 E.01002 +G1 X120.682 Y106.297 E.00606 +G1 X120.725 Y106.171 E.00451 +G1 X120.725 Y104.984 E.04018 +G1 X120.705 Y103.741 E.04208 +G1 X120.521 Y103.625 E.00736 +G1 X120.225 Y103.625 E.01002 +G1 X120.225 Y100.225 E.11509 +G1 X122.912 Y100.225 E.09095 +G1 X122.912 Y100.521 E.01002 +G1 X122.976 Y100.67 E.00549 +G1 X123.115 Y100.725 E.00506 +G1 X124.217 Y100.721 E.0373 +G1 X124.35 Y100.629 E.00547 +G1 X124.38 Y100.225 E.01371 +G1 X125.77 Y100.225 E.04705 +G1 X125.77 Y100.521 E.01002 +G1 X125.852 Y100.685 E.00621 +G1 X125.973 Y100.725 E.00431 +G1 X126.967 Y100.725 E.03365 +G1 X127.106 Y100.67 E.00506 +G1 X127.17 Y100.521 E.00549 +G1 X127.17 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08818 +G1 X129.775 Y102.564 E.07917 +G1 X129.479 Y102.564 E.01002 +G1 X129.339 Y102.62 E.0051 +G1 X129.275 Y102.768 E.00546 +G1 X129.275 Y103.84 E.03629 +G1 X129.304 Y103.945 E.00369 +G1 X129.479 Y104.044 E.00681 +G1 X129.775 Y104.044 E.01002 +G1 X129.775 Y105.995 E.06604 +G1 X129.479 Y105.995 E.01002 +G1 X129.337 Y106.052 E.00518 +G1 X129.275 Y106.198 E.00537 +G1 X129.275 Y107.205 E.03409 +G1 X129.338 Y107.352 E.00541 +G1 X129.479 Y107.408 E.00514 +G1 X129.775 Y107.408 E.01002 +G1 X129.775 Y109.715 E.07809 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X123.326 Y109.002 Z7.301 F12000 +G1 Z7.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F935 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +M73 P72 R2 +M73 Q72 S2 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y101.968 E.02173 +G2 X128.502 Y102.756 I.311 J.75 E.03365 +G1 X128.502 Y103.851 E.03706 +G2 X129.002 Y104.637 I.809 J.037 E.03359 +G1 X129.002 Y105.401 E.02586 +G2 X128.502 Y106.188 I.309 J.749 E.03363 +G2 X128.559 Y107.545 I5.985 J.426 E.04607 +G2 X129.002 Y108.004 I.695 J-.228 E.02234 +G1 X129.002 Y108.674 E.02268 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.058 E.05863 +G1 X121.241 Y103.171 E.00907 +M204 P1000 +;LAYER_CHANGE +;Z:7.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y103.058 E-.05569 +G1 X120.998 Y102.556 E-.10431 +;WIPE_END +G1 X120.998 Y102.556 Z7.2 F12000 +G1 X129.368 Y109.368 Z7.4 +;AFTER_LAYER_CHANGE +;7.4 +G1 X129.368 Y109.368 +G1 Z7.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F919 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.527 E.09616 +G1 X121.01 Y106.337 E.01432 +G1 X121.085 Y106.206 E.00511 +G1 X121.132 Y105.971 E.00811 +G1 X121.132 Y104.035 E.06553 +G1 X121.095 Y103.819 E.00742 +G1 X120.995 Y103.643 E.00685 +G1 X120.632 Y103.47 E.01361 +G1 X120.632 Y100.632 E.09606 +G1 X122.408 Y100.632 E.06012 +G1 X122.544 Y100.956 E.01189 +G1 X122.773 Y101.099 E.00914 +G1 X122.973 Y101.132 E.00686 +G1 X124.029 Y101.132 E.03574 +G1 X124.26 Y101.089 E.00795 +G1 X124.511 Y100.904 E.01055 +G1 X124.607 Y100.632 E.00976 +G1 X125.553 Y100.632 E.03202 +G1 X125.649 Y100.904 E.00976 +G1 X125.9 Y101.089 E.01055 +G1 X126.125 Y101.132 E.00775 +G1 X127.116 Y101.132 E.03354 +G1 X127.359 Y101.081 E.0084 +G1 X127.534 Y100.967 E.00707 +G1 X127.682 Y100.632 E.0124 +G1 X129.368 Y100.632 E.05707 +G1 X129.368 Y102.066 E.04854 +G1 X129.072 Y102.179 E.01072 +G1 X128.895 Y102.454 E.01107 +G1 X128.868 Y102.884 E.01458 +G1 X128.892 Y103.878 E.03366 +G1 X129.023 Y104.116 E.0092 +G1 X129.368 Y104.271 E.0128 +G1 X129.368 Y105.769 E.05071 +G1 X129.023 Y105.923 E.01279 +G1 X128.921 Y106.08 E.00634 +G1 X128.868 Y106.33 E.00865 +G1 X128.868 Y107.343 E.03429 +G1 X128.907 Y107.558 E.0074 +G1 X129.07 Y107.797 E.00979 +G1 X129.368 Y107.911 E.0108 +G1 X129.368 Y109.308 E.04729 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F919 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y106.175 E.12186 +M73 P73 R2 +M73 Q73 S2 +G1 X120.521 Y106.175 E.01002 +G1 X120.684 Y106.093 E.00618 +G1 X120.725 Y105.971 E.00436 +G1 X120.725 Y104.959 E.03425 +G1 X120.7 Y103.93 E.03484 +G1 X120.521 Y103.825 E.00702 +G1 X120.225 Y103.825 E.01002 +G1 X120.225 Y100.225 E.12186 +G1 X122.769 Y100.225 E.08611 +G1 X122.769 Y100.521 E.01002 +G1 X122.83 Y100.666 E.00532 +G1 X122.973 Y100.725 E.00524 +G1 X124.029 Y100.725 E.03574 +G1 X124.14 Y100.696 E.00388 +G1 X124.239 Y100.521 E.00681 +G1 X124.239 Y100.225 E.01002 +G1 X125.921 Y100.225 E.05693 +G1 X125.921 Y100.521 E.01002 +G1 X125.966 Y100.649 E.00459 +G1 X126.125 Y100.725 E.00597 +G1 X127.116 Y100.725 E.03354 +G1 X127.255 Y100.67 E.00506 +G1 X127.319 Y100.521 E.00549 +G1 X127.319 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08313 +G1 X129.775 Y102.431 E.07467 +G1 X129.479 Y102.431 E.01002 +G1 X129.343 Y102.482 E.00492 +G1 X129.275 Y102.634 E.00564 +G1 X129.275 Y103.709 E.03639 +G1 X129.304 Y103.814 E.00369 +G1 X129.479 Y103.913 E.00681 +G1 X129.775 Y103.913 E.01002 +G1 X129.775 Y106.127 E.07494 +G1 X129.479 Y106.127 E.01002 +G1 X129.327 Y106.194 E.00562 +G1 X129.275 Y106.33 E.00493 +G1 X129.275 Y107.343 E.03429 +G1 X129.342 Y107.495 E.00562 +G1 X129.479 Y107.547 E.00496 +G1 X129.775 Y107.547 E.01002 +G1 X129.775 Y109.715 E.07338 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.502 Y106.881 Z7.451 F12000 +G1 Z7.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F919 +G1 X128.502 Y107.358 E.01615 +G2 X129.002 Y108.149 I.856 J.012 E.03351 +G3 X128.674 Y109.002 I-.725 J.211 E.03323 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.261 E.0655 +G1 X121.057 Y103.289 E.00221 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y103.261 E-.01357 +G1 X120.998 Y102.556 E-.14643 +;WIPE_END +G1 X126.884 Y101.498 Z7.504 F12000 +G1 Z7.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F919 +G2 X127.916 Y100.998 I.194 J-.915 E.04178 +G1 X128.674 Y100.998 E.02566 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y100.998 E.0111 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +M73 P74 R2 +M73 Q74 S2 +G1 X121.326 Y109.002 E.0157 +G1 X123.326 Y109.002 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:7.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.556 Y109.002 E-.16 +;WIPE_END +G1 X122.556 Y109.002 Z7.4 F12000 +G1 X129.368 Y109.368 Z7.6 +;AFTER_LAYER_CHANGE +;7.6 +G1 X129.368 Y109.368 +G1 Z7.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F918 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.927 E.02857 +G1 X121.129 Y104.171 E.02559 +G1 X121 Y103.85 E.01171 +G1 X120.632 Y103.671 E.01385 +G1 X120.632 Y100.632 E.10287 +G1 X122.264 Y100.632 E.05524 +G1 X122.391 Y100.945 E.01143 +G1 X122.702 Y101.118 E.01205 +G1 X122.83 Y101.132 E.00436 +G1 X123.894 Y101.132 E.03602 +G1 X124.053 Y101.111 E.00543 +G1 X124.33 Y100.949 E.01086 +G1 X124.459 Y100.632 E.01158 +G1 X125.711 Y100.632 E.04238 +G1 X125.841 Y100.949 E.0116 +G1 X126.036 Y101.083 E.00801 +G1 X126.276 Y101.132 E.00829 +G1 X127.265 Y101.132 E.03348 +G1 X127.515 Y101.078 E.00866 +G1 X127.683 Y100.967 E.00682 +G1 X127.833 Y100.632 E.01242 +G1 X129.368 Y100.632 E.05196 +G1 X129.368 Y101.93 E.04394 +G1 X129.085 Y102.033 E.01019 +G1 X128.897 Y102.314 E.01144 +G1 X128.868 Y102.735 E.01428 +G1 X128.88 Y103.699 E.03263 +G1 X128.999 Y103.957 E.00962 +G1 X129.368 Y104.136 E.01388 +G1 X129.368 Y105.905 E.05988 +G1 X128.999 Y106.084 E.01388 +G1 X128.925 Y106.204 E.00477 +G1 X128.868 Y106.462 E.00894 +G1 X128.868 Y107.482 E.03453 +G1 X128.91 Y107.703 E.00761 +G1 X129.083 Y107.947 E.01012 +G1 X129.368 Y108.052 E.01028 +G1 X129.368 Y109.308 E.04251 +G1 X129.775 Y109.775 F12000 +M73 Q74 S1 +M204 P800 +;TYPE:External perimeter +G1 F918 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +M73 P74 R1 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.927 E.02857 +G1 X120.704 Y104.139 E.02668 +G1 X120.521 Y104.025 E.0073 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.627 Y100.225 E.0813 +G1 X122.627 Y100.521 E.01002 +M73 P75 R1 +M73 Q75 S1 +G1 X122.684 Y100.663 E.00518 +G1 X122.83 Y100.725 E.00537 +G1 X123.894 Y100.725 E.03602 +G1 X123.999 Y100.696 E.00369 +G1 X124.097 Y100.521 E.00679 +G1 X124.097 Y100.225 E.01002 +G1 X126.073 Y100.225 E.06689 +G1 X126.073 Y100.521 E.01002 +G1 X126.131 Y100.664 E.00522 +G1 X126.276 Y100.725 E.00532 +G1 X127.265 Y100.725 E.03348 +G1 X127.404 Y100.67 E.00506 +G1 X127.468 Y100.521 E.00549 +G1 X127.468 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07809 +G1 X129.775 Y102.297 E.07013 +G1 X129.479 Y102.297 E.01002 +G1 X129.347 Y102.345 E.00475 +G1 X129.285 Y102.438 E.00378 +G1 X129.275 Y102.735 E.01006 +G1 X129.294 Y103.665 E.03149 +G1 X129.479 Y103.782 E.00741 +G1 X129.775 Y103.782 E.01002 +G1 X129.775 Y106.258 E.08381 +G1 X129.479 Y106.258 E.01002 +G1 X129.319 Y106.336 E.00603 +G1 X129.275 Y106.462 E.00452 +G1 X129.275 Y107.482 E.03453 +G1 X129.347 Y107.637 E.00578 +G1 X129.479 Y107.685 E.00475 +G1 X129.775 Y107.685 E.01002 +G1 X129.775 Y109.715 E.06871 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.502 Y106.878 Z7.652 F12000 +G1 Z7.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F918 +G1 X128.502 Y107.497 E.02095 +G2 X129.002 Y108.294 I.884 J.001 E.03358 +G3 X128.674 Y109.002 I-.632 J.138 E.02836 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X123.326 Y109.002 Z7.72 F12000 +G1 Z7.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F918 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X129.002 Y101.326 E.0111 +G1 X128.674 Y100.998 E.0157 +G1 X128.072 Y100.998 E.02038 +G3 X127.269 Y101.498 I-.761 J-.327 E.03408 +G1 X126.881 Y101.498 E.01313 +M204 P1000 +;LAYER_CHANGE +;Z:7.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.269 Y101.498 E-.08063 +G2 X127.641 Y101.43 I.042 J-.827 E-.07937 +;WIPE_END +G1 X127.641 Y101.43 Z7.6 F12000 +G1 X129.368 Y109.368 Z7.8 +;AFTER_LAYER_CHANGE +;7.8 +G1 X129.368 Y109.368 +G1 Z7.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F926 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.124 E.10981 +M73 P76 R1 +M73 Q76 S1 +G1 X121.023 Y105.919 E.01494 +G1 X121.09 Y105.794 E.0048 +G1 X121.132 Y105.571 E.00768 +G1 X121.132 Y104.429 E.03866 +G1 X121.039 Y104.105 E.01141 +G1 X120.632 Y103.873 E.01586 +G1 X120.632 Y100.632 E.1097 +G1 X122.12 Y100.632 E.05037 +G1 X122.237 Y100.933 E.01093 +G1 X122.439 Y101.079 E.00844 +G1 X122.688 Y101.132 E.00862 +G1 X123.752 Y101.132 E.03602 +G1 X123.931 Y101.105 E.00613 +G1 X124.153 Y100.982 E.00859 +G1 X124.312 Y100.632 E.01301 +G1 X125.868 Y100.632 E.05267 +G1 X126.028 Y100.982 E.01303 +G1 X126.176 Y101.078 E.00597 +G1 X126.428 Y101.132 E.00872 +G1 X127.414 Y101.132 E.03337 +G1 X127.672 Y101.075 E.00894 +G1 X127.832 Y100.966 E.00655 +G1 X127.984 Y100.632 E.01242 +G1 X129.368 Y100.632 E.04685 +G1 X129.368 Y101.795 E.03937 +G1 X129.1 Y101.887 E.00959 +G1 X129.038 Y101.944 E.00285 +G1 X128.9 Y102.172 E.00902 +G1 X128.868 Y102.586 E.01406 +G1 X128.882 Y103.578 E.03358 +G1 X128.98 Y103.801 E.00825 +G1 X129.368 Y104.001 E.01478 +G1 X129.368 Y106.04 E.06902 +G1 X128.98 Y106.241 E.01479 +G1 X128.948 Y106.291 E.00201 +G1 X128.868 Y106.594 E.01061 +G1 X128.868 Y107.62 E.03473 +G1 X128.912 Y107.849 E.00789 +G1 X129.099 Y108.098 E.01054 +G1 X129.368 Y108.192 E.00965 +G1 X129.368 Y109.308 E.03778 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F926 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +G1 X120.225 Y106.65 E.08293 +G1 X120.225 Y105.775 E.02962 +G1 X120.521 Y105.775 E.01002 +G1 X120.689 Y105.687 E.00642 +G1 X120.725 Y105.571 E.00411 +G1 X120.725 Y104.429 E.03866 +G1 X120.694 Y104.321 E.0038 +G1 X120.521 Y104.225 E.0067 +G1 X120.225 Y104.225 E.01002 +G1 X120.225 Y103.35 E.02962 +G1 X120.225 Y100.9 E.08293 +G1 X120.225 Y100.225 E.02285 +G1 X122.484 Y100.225 E.07646 +G1 X122.484 Y100.521 E.01002 +G1 X122.537 Y100.659 E.005 +G1 X122.688 Y100.725 E.00558 +G1 X123.752 Y100.725 E.03602 +G1 X123.836 Y100.707 E.00291 +G1 X123.956 Y100.521 E.00749 +G1 X123.956 Y100.225 E.01002 +G1 X126.225 Y100.225 E.0768 +G1 X126.225 Y100.521 E.01002 +G1 X126.295 Y100.675 E.00573 +G1 X126.428 Y100.725 E.00481 +G1 X127.414 Y100.725 E.03337 +G1 X127.553 Y100.67 E.00506 +G1 X127.617 Y100.521 E.00549 +G1 X127.617 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07305 +G1 X129.775 Y102.163 E.0656 +G1 X129.479 Y102.163 E.01002 +G1 X129.352 Y102.207 E.00455 +G1 X129.286 Y102.302 E.00392 +G1 X129.275 Y102.586 E.00962 +G1 X129.29 Y103.526 E.03182 +G1 X129.479 Y103.651 E.00767 +G1 X129.775 Y103.651 E.01002 +G1 X129.775 Y106.39 E.09271 +G1 X129.479 Y106.39 E.01002 +G1 X129.312 Y106.476 E.00636 +M73 Q77 S1 +G1 X129.275 Y106.594 E.00419 +G1 X129.275 Y107.62 E.03473 +G1 X129.352 Y107.78 E.00601 +M73 P77 R1 +G1 X129.479 Y107.824 E.00455 +G1 X129.775 Y107.824 E.01002 +G1 X129.775 Y109.715 E.06401 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.502 Y103.125 Z7.916 F12000 +G1 Z7.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F926 +G1 X128.502 Y102.354 E.0261 +G3 X129.002 Y101.546 I.881 J-.013 E.03397 +G2 X128.674 Y100.998 I-.574 J-.029 E.02292 +G1 X129.002 Y100.998 E.0111 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X128.674 Y109.002 E.24872 +G1 X129.002 Y108.674 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z7.8 F12000 +G1 X129.368 Y109.368 Z8 +;AFTER_LAYER_CHANGE +;8 +G1 X129.368 Y109.368 +G1 Z8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y105.923 E.11661 +G1 X121.029 Y105.711 E.01523 +G1 X121.091 Y105.59 E.0046 +G1 X121.132 Y105.371 E.00754 +G1 X121.128 Y104.555 E.02762 +G1 X121.014 Y104.267 E.01048 +G1 X120.632 Y104.074 E.01449 +G1 X120.632 Y100.632 E.11651 +G1 X121.976 Y100.632 E.04549 +G1 X122.082 Y100.919 E.01036 +G1 X122.325 Y101.091 E.01008 +G1 X122.545 Y101.132 E.00757 +G1 X123.611 Y101.132 E.03608 +G1 X123.708 Y101.124 E.00329 +G1 X123.98 Y101.008 E.01001 +G1 X124.167 Y100.632 E.01421 +G1 X126.024 Y100.632 E.06286 +G1 X126.21 Y101.008 E.0142 +M73 P78 R1 +M73 Q78 S1 +G1 X126.319 Y101.073 E.0043 +G1 X126.58 Y101.132 E.00906 +G1 X127.563 Y101.132 E.03327 +G1 X127.829 Y101.071 E.00924 +G1 X128.046 Y100.895 E.00946 +G1 X128.136 Y100.632 E.00941 +G1 X129.368 Y100.632 E.0417 +G1 X129.368 Y101.659 E.03476 +G1 X129.053 Y101.795 E.01161 +G1 X128.903 Y102.03 E.00944 +G1 X128.868 Y102.233 E.00697 +G1 X128.868 Y103.317 E.03669 +G1 X128.905 Y103.528 E.00725 +G1 X129.076 Y103.777 E.01022 +G1 X129.368 Y103.886 E.01055 +G1 X129.368 Y106.156 E.07684 +G1 X129.076 Y106.266 E.01056 +G1 X128.93 Y106.457 E.00814 +G1 X128.868 Y106.725 E.00931 +G1 X128.868 Y107.759 E.035 +G1 X128.916 Y107.995 E.00815 +G1 X129.116 Y108.25 E.01097 +G1 X129.368 Y108.333 E.00898 +G1 X129.368 Y109.308 E.033 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +G1 X120.225 Y106.45 E.0897 +G1 X120.225 Y105.575 E.02962 +G1 X120.521 Y105.575 E.01002 +G1 X120.691 Y105.485 E.00651 +G1 X120.725 Y105.373 E.00396 +G1 X120.725 Y104.629 E.02518 +G1 X120.686 Y104.508 E.0043 +G1 X120.521 Y104.425 E.00625 +G1 X120.225 Y104.425 E.01002 +G1 X120.225 Y103.55 E.02962 +G1 X120.225 Y100.9 E.0897 +G1 X120.225 Y100.225 E.02285 +G1 X122.342 Y100.225 E.07166 +G1 X122.342 Y100.521 E.01002 +G1 X122.391 Y100.654 E.0048 +G1 X122.545 Y100.725 E.00574 +G1 X123.611 Y100.725 E.03608 +G1 X123.686 Y100.711 E.00258 +G1 X123.814 Y100.521 E.00775 +G1 X123.814 Y100.225 E.01002 +G1 X126.376 Y100.225 E.08672 +G1 X126.376 Y100.521 E.01002 +G1 X126.457 Y100.684 E.00616 +G1 X126.58 Y100.725 E.00439 +G1 X127.563 Y100.725 E.03327 +G1 X127.702 Y100.67 E.00506 +G1 X127.766 Y100.521 E.00549 +G1 X127.766 Y100.225 E.01002 +G1 X129.775 Y100.225 E.068 +G1 X129.775 Y102.029 E.06106 +G1 X129.479 Y102.029 E.01002 +G1 X129.337 Y102.087 E.00519 +G1 X129.275 Y102.233 E.00537 +G1 X129.275 Y103.317 E.03669 +G1 X129.307 Y103.427 E.00388 +G1 X129.479 Y103.521 E.00663 +G1 X129.775 Y103.521 E.01002 +G1 X129.775 Y106.522 E.10158 +G1 X129.479 Y106.522 E.01002 +G1 X129.344 Y106.572 E.00487 +G1 X129.275 Y106.725 E.00568 +G1 X129.275 Y107.759 E.035 +G1 X129.358 Y107.923 E.00622 +G1 X129.479 Y107.962 E.0043 +G1 X129.775 Y107.962 E.01002 +G1 X129.775 Y109.715 E.05934 +M204 P1000 +G1 X129.002 Y108.798 F12000 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X129.002 Y108.674 E.0042 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +M73 Q79 S1 +G1 X120.998 Y101.326 E.0157 +M73 P79 R1 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X123.326 Y109.002 Z8.12 F12000 +G1 Z8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y101.202 E.0042 +M204 P1000 +;LAYER_CHANGE +;Z:8.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.002 Y101.326 E-.02577 +G1 X128.674 Y100.998 E-.0964 +G1 X128.856 Y100.998 E-.03783 +;WIPE_END +G1 X128.856 Y100.998 Z8 F12000 +G1 X129.368 Y109.368 Z8.2 +;AFTER_LAYER_CHANGE +;8.2 +G1 X129.368 Y109.368 +G1 Z8.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F911 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y105.722 E.12341 +G1 X121.034 Y105.503 E.0155 +G1 X121.108 Y105.342 E.006 +G1 X121.125 Y104.735 E.02055 +G1 X121.02 Y104.476 E.00946 +G1 X120.632 Y104.275 E.01479 +G1 X120.632 Y100.632 E.12331 +G1 X121.831 Y100.632 E.04058 +G1 X121.927 Y100.904 E.00976 +G1 X122.175 Y101.088 E.01045 +G1 X122.403 Y101.132 E.00786 +G1 X122.753 Y101.132 E.01185 +G1 X123.6 Y101.118 E.02867 +G1 X123.812 Y101.027 E.00781 +G1 X124.021 Y100.632 E.01513 +G1 X126.18 Y100.632 E.07308 +G1 X126.389 Y101.027 E.01513 +G1 X126.464 Y101.07 E.00293 +G1 X126.731 Y101.132 E.00928 +G1 X127.712 Y101.132 E.03321 +G1 X127.988 Y101.066 E.00961 +G1 X128.201 Y100.887 E.00942 +G1 X128.287 Y100.632 E.00911 +G1 X129.368 Y100.632 E.03659 +G1 X129.368 Y101.523 E.03016 +G1 X129.07 Y101.645 E.0109 +G1 X128.906 Y101.887 E.0099 +G1 X128.868 Y102.099 E.00729 +G1 X128.868 Y103.186 E.03679 +G1 X128.899 Y103.378 E.00658 +G1 X129.057 Y103.628 E.01001 +G1 X129.368 Y103.753 E.01135 +G1 X129.368 Y106.291 E.08591 +G1 X129.057 Y106.415 E.01133 +G1 X128.931 Y106.586 E.00719 +G1 X128.868 Y106.857 E.00942 +G1 X128.868 Y107.897 E.0352 +G1 X128.919 Y108.142 E.00847 +G1 X129.136 Y108.403 E.01149 +G1 X129.368 Y108.474 E.00821 +G1 X129.368 Y109.308 E.02823 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F911 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +M73 P80 R1 +M73 Q80 S1 +G1 X120.225 Y106.25 E.09647 +G1 X120.225 Y105.375 E.02962 +G1 X120.521 Y105.375 E.01002 +G1 X120.692 Y105.282 E.00659 +G1 X120.717 Y105.228 E.00201 +G1 X120.711 Y104.755 E.01601 +G1 X120.521 Y104.625 E.00779 +G1 X120.225 Y104.625 E.01002 +G1 X120.225 Y103.75 E.02962 +G1 X120.225 Y100.9 E.09647 +G1 X120.225 Y100.225 E.02285 +G1 X122.199 Y100.225 E.06682 +G1 X122.199 Y100.521 E.01002 +G1 X122.244 Y100.649 E.00459 +G1 X122.327 Y100.71 E.00349 +G1 X122.753 Y100.725 E.01443 +G1 X123.542 Y100.712 E.02671 +G1 X123.673 Y100.521 E.00784 +G1 X123.673 Y100.225 E.01002 +G1 X126.528 Y100.225 E.09664 +G1 X126.528 Y100.521 E.01002 +G1 X126.617 Y100.69 E.00647 +G1 X126.731 Y100.725 E.00404 +G1 X127.712 Y100.725 E.03321 +G1 X127.851 Y100.67 E.00506 +G1 X127.915 Y100.521 E.00549 +G1 X127.915 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06296 +G1 X129.775 Y101.896 E.05656 +G1 X129.479 Y101.896 E.01002 +G1 X129.343 Y101.948 E.00493 +G1 X129.275 Y102.099 E.00561 +G1 X129.275 Y103.186 E.03679 +G1 X129.303 Y103.29 E.00365 +G1 X129.479 Y103.39 E.00685 +G1 X129.775 Y103.39 E.01002 +G1 X129.775 Y106.654 E.11048 +G1 X129.479 Y106.654 E.01002 +G1 X129.338 Y106.71 E.00514 +G1 X129.275 Y106.857 E.00541 +G1 X129.275 Y107.897 E.0352 +G1 X129.364 Y108.066 E.00647 +G1 X129.479 Y108.101 E.00407 +G1 X129.775 Y108.101 E.01002 +G1 X129.775 Y109.715 E.05463 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.436 Y101.171 Z8.351 F12000 +G1 Z8.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F911 +G1 X128.544 Y100.998 E.0069 +G3 X128.952 Y101.276 I-.042 J.501 E.01747 +G1 X129.002 Y101.261 E.00177 +G1 X129.002 Y100.998 E.0089 +G1 X120.998 Y109.002 E.38315 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X128.674 Y109.002 E.24872 +G1 X128.955 Y108.721 E.01345 +G1 X129.002 Y108.735 E.00166 +G1 X129.002 Y109.002 E.00904 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +M73 P81 R1 +M73 Q81 S1 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.4 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z8.2 F12000 +G1 X129.368 Y109.368 Z8.4 +;AFTER_LAYER_CHANGE +;8.4 +G1 X129.368 Y109.368 +G1 Z8.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F900 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F900 +G1 X129.1 Y109.775 E.02285 +G1 X120.225 Y109.775 E.30041 +G1 X120.225 Y100.9 E.30041 +G1 X120.225 Y100.225 E.02285 +G1 X122.424 Y100.225 E.07443 +G1 X122.624 Y100.225 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +G1 X123.248 Y100.225 E.02112 +M204 P800 +;TYPE:External perimeter +G1 X126.953 Y100.225 E.12541 +M204 P1000 +;TYPE:Overhang perimeter +G1 X127.49 Y100.225 E.01818 +M204 P800 +;TYPE:External perimeter +G1 X127.69 Y100.225 E.00677 +G1 X129.775 Y100.225 E.07057 +G1 X129.775 Y102.121 E.06418 +M73 P82 R1 +M73 Q82 S1 +G1 X129.775 Y102.321 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +G1 X129.775 Y102.965 E.0218 +M204 P800 +;TYPE:External perimeter +G1 X129.775 Y103.165 E.00677 +G1 X129.775 Y105 E.06211 +G1 X129.775 Y106.879 E.0636 +G1 X129.775 Y107.079 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +G1 X129.775 Y107.676 E.02021 +M204 P800 +;TYPE:External perimeter +G1 X129.775 Y107.876 E.00677 +G1 X129.775 Y109.715 E.06225 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.1 Y109.775 E-.14027 +G1 X129.065 Y109.775 E-.00726 +;WIPE_END +G1 X123.326 Y109.002 Z8.501 F12000 +G1 Z8.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F900 +G1 X121.326 Y109.002 E.0677 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X129.002 Y100.998 E.38315 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y108.674 E.24872 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X120.998 Y100.998 E.38315 +G1 X121.326 Y100.998 E.0111 +G1 X120.998 Y101.326 E.0157 +G1 X120.998 Y103.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X120.998 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y102.556 Z8.4 F12000 +G1 X129.368 Y109.368 Z8.6 +;AFTER_LAYER_CHANGE +;8.6 +G1 X129.368 Y109.368 +G1 Z8.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1002 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +M73 P83 R1 +M73 Q83 S1 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1002 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X129.002 Y107.577 Z8.638 F12000 +G1 Z8.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1002 +G1 X129.002 Y107.715 E.00467 +G1 X128.594 Y107.308 E.01951 +G1 X128.594 Y108.594 E.04353 +G1 X121.406 Y101.406 E.34409 +G1 X128.594 Y101.406 E.24331 +G1 X120.998 Y109.002 E.36362 +M73 P84 R1 +M73 Q84 S1 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X123.03 Y109.002 E.05768 +G1 X122.821 Y108.793 E.01 +M204 P1000 +G1 X122.821 Y108.793 F12000 +G1 X123.606 Y109.002 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.450028 +G1 F1002 +G1 X128.798 Y109.002 E.17576 +;WIDTH:0.484952 +G1 X128.882 Y108.984 E.00316 +;WIDTH:0.519876 +G2 X128.984 Y108.882 I.017 J-.085 E.00674 +;WIDTH:0.484952 +G1 X129.002 Y108.798 E.00316 +;WIDTH:0.45003 +G1 X129.002 Y108.291 E.01716 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.002 Y108.798 E-.10536 +G1 X128.984 Y108.882 E-.01785 +G3 X128.882 Y108.984 I-.085 J.017 E-.03533 +G1 X128.875 Y108.985 E-.00146 +;WIPE_END +G1 X120.998 Y102.258 Z8.781 F12000 +G1 Z8.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.450032 +G1 F1002 +G1 X120.998 Y101.202 E.03575 +;WIDTH:0.484952 +G1 X121.016 Y101.118 E.00316 +;WIDTH:0.519876 +G3 X121.118 Y101.016 I.085 J-.017 E.00674 +;WIDTH:0.484952 +G1 X121.202 Y100.998 E.00316 +;WIDTH:0.45003 +G1 X128.71 Y100.998 E.25416 +G1 X128.813 Y101.059 E.00405 +;WIDTH:0.416475 +G3 X128.959 Y101.205 I-.071 J.217 E.00666 +;WIDTH:0.45003 +G1 X129.002 Y101.29 E.00322 +G1 X129.002 Y102.571 E.04336 +G1 X128.949 Y102.746 E.00619 +;WIDTH:0.416474 +G2 X128.949 Y103.116 I.371 J.185 E.01191 +;WIDTH:0.450028 +G1 X129.002 Y103.311 E.00684 +G1 X129.002 Y105.17 E.06293 +M204 P1000 +;LAYER_CHANGE +;Z:8.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.002 Y104.4 E-.16 +;WIPE_END +G1 X129.002 Y104.4 Z8.6 F12000 +G1 X129.368 Y109.368 Z8.8 +;AFTER_LAYER_CHANGE +;8.8 +G1 X129.368 Y109.368 +G1 Z8.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1457 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1457 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +M73 P85 R1 +M73 Q85 S1 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X122.821 Y108.793 Z8.91 F12000 +G1 Z8.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1457 +G1 X123.03 Y109.002 E.01 +G1 X121.326 Y109.002 E.05768 +G1 X120.998 Y108.674 E.0157 +G1 X120.998 Y109.002 E.0111 +G1 X122.014 Y107.986 E.04864 +G3 X120.998 Y106.634 I4.977 J-4.798 E.05739 +G1 X120.998 Y106.938 E.01029 +M204 P1000 +G1 X120.998 Y106.938 F12000 +G1 X121.263 Y106.621 +;TYPE:Bridge infill +;WIDTH:0.410715 +;HEIGHT:0.4 +G1 F1500 +G1 X123.622 Y108.98 E.18376 +G1 X124.274 Y108.98 E.03591 +G1 X121.394 Y106.101 E.2243 +G1 X121.556 Y105.967 E.01158 +G1 X122.671 Y107.082 E.08686 +G1 X122.849 Y106.904 E.01387 +G1 X124.925 Y108.98 E.16171 +G1 X125.577 Y108.98 E.03591 +G1 X123.175 Y106.578 E.18711 +G1 X123.5 Y106.252 E.02536 +G1 X126.229 Y108.98 E.21254 +G1 X126.88 Y108.98 E.03586 +G1 X123.826 Y105.926 E.2379 +G1 X124.152 Y105.6 E.02539 +G1 X127.532 Y108.98 E.26329 +G1 X128.183 Y108.98 E.03586 +G1 X121.02 Y101.817 E.55798 +G1 X121.02 Y101.165 E.03591 +G1 X128.835 Y108.98 E.60876 +G1 X128.98 Y108.98 E.00799 +G1 X128.98 Y108.474 E.02787 +G1 X121.526 Y101.02 E.58064 +G1 X122.178 Y101.02 E.03591 +G1 X125.703 Y104.545 E.27459 +G1 X126.028 Y104.219 E.02536 +G1 X122.829 Y101.02 E.24919 +G1 X123.481 Y101.02 E.03591 +M73 P86 R1 +M73 Q86 S1 +G1 X126.354 Y103.893 E.2238 +G1 X126.68 Y103.568 E.02536 +G1 X124.132 Y101.02 E.19848 +G1 X124.784 Y101.02 E.03591 +G1 X127.006 Y103.242 E.17309 +G1 X127.055 Y103.193 E.00382 +G1 X128.98 Y105.118 E.14995 +G1 X128.98 Y104.565 E.03046 +G1 X125.435 Y101.02 E.27614 +G1 X126.087 Y101.02 E.03591 +G1 X128.98 Y103.913 E.22536 +G1 X128.98 Y103.317 E.03283 +G1 X128.893 Y103.174 E.00922 +G1 X126.739 Y101.02 E.16779 +G1 X127.39 Y101.02 E.03586 +G1 X128.959 Y102.589 E.12222 +G1 X128.98 Y102.563 E.00184 +G1 X128.98 Y101.958 E.03332 +G1 X128.042 Y101.02 E.07307 +G1 X128.693 Y101.02 E.03586 +G1 X129.183 Y101.509 E.03813 +;LAYER_CHANGE +;Z:9 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X128.693 Y101.02 E-.14386 +G1 X128.615 Y101.02 E-.01614 +;WIPE_END +G1 X128.615 Y101.02 Z8.8 F12000 +G1 X129.368 Y109.368 Z9 +;AFTER_LAYER_CHANGE +;9 +G1 X129.368 Y109.368 +G1 Z9 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1722 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1722 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X129.002 Y106.674 Z9.054 F12000 +G1 Z9 F720 +M73 P87 R1 +M73 Q87 S1 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1722 +G1 X129.002 Y108.674 E.0677 +G1 X128.674 Y109.002 E.0157 +G1 X129.002 Y109.002 E.0111 +G1 X128.516 Y108.516 E.02326 +G2 X128.35 Y106.875 I-2.579 J-.567 E.05678 +G1 X126.021 Y103.979 E.12579 +G1 X126.403 Y103.597 E.01829 +G1 X127.708 Y103.593 E.04417 +G2 X128.653 Y102.559 I-.142 J-1.078 E.05151 +G2 X128.595 Y101.405 I-3.784 J-.389 E.03926 +G1 X129.002 Y100.998 E.01948 +G1 X128.674 Y100.998 E.0111 +G1 X129.002 Y101.326 E.0157 +G1 X129.002 Y103.326 E.0677 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.002 Y102.556 E-.16 +;WIPE_END +G1 X120.998 Y103.326 Z9.14 F12000 +G1 Z9 F720 +G1 E.8 F1500 +M204 P2000 +G1 F1722 +G1 X120.998 Y101.326 E.0677 +G1 X121.326 Y100.998 E.0157 +G1 X120.998 Y100.998 E.0111 +G1 X121.405 Y101.405 E.01948 +G2 X121.378 Y102.792 I3.917 J.771 E.0472 +G1 X121.577 Y103.216 E.01585 +G1 X123.891 Y106.109 E.1254 +G1 X123.601 Y106.399 E.01388 +G2 X122.381 Y106.486 I-.393 J3.104 E.04167 +G2 X121.723 Y107.459 I.497 J1.044 E.0417 +G1 X121.734 Y108.266 E.02732 +G1 X120.998 Y109.002 E.03523 +G1 X120.998 Y108.674 E.0111 +G1 X121.326 Y109.002 E.0157 +G1 X122.026 Y109.002 E.02369 +G1 X121.748 Y108.468 E.02038 +M204 P1000 +G1 X121.748 Y108.468 F12000 +G1 X122.131 Y107.507 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F1722 +G1 X122.276 Y107.081 E.01523 +G1 X122.606 Y106.839 E.01385 +G1 X122.808 Y106.806 E.00693 +G1 X124.298 Y106.806 E.05043 +G1 X124.492 Y106.669 E.00804 +G1 X126.825 Y109.002 E.11168 +G1 X122.558 Y109.002 E.14443 +M73 Q87 S0 +G2 X122.331 Y108.721 I-.461 J.14 E.01253 +M73 P87 R0 +G1 X122.142 Y108.346 E.01421 +G1 X122.134 Y107.71 E.02153 +G1 X122.54 Y107.645 E.01392 +G1 X122.62 Y107.333 E.0109 +G1 X122.841 Y107.213 E.00851 +G1 X124.298 Y107.213 E.04932 +G1 X124.398 Y107.15 E.004 +G1 X125.842 Y108.594 E.06912 +G1 X122.796 Y108.594 E.1031 +G3 X122.548 Y108.247 I.601 J-.691 E.01457 +G1 X122.543 Y107.849 E.01347 +G1 X122.995 Y107.864 E.01531 +;WIDTH:0.530031 +G1 X123.007 Y108.147 E.01147 +G1 X124.763 Y108.147 E.07112 +G1 X124.275 Y107.66 E.02792 +G1 X122.987 Y107.66 E.05217 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.757 Y107.66 E-.16 +;WIPE_END +G1 X122.204 Y103.589 Z9.076 F12000 +G1 Z9 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.40698 +G1 F1722 +G1 X123.771 Y105.342 E.07118 +;WIDTH:0.449999 +G1 X124.028 Y105.63 E.01307 +G1 X127.4 Y109.002 E.16142 +G1 X127.864 Y109.094 E.01601 +;WIDTH:0.38292 +G3 X127.804 Y109.154 I-.04 J.02 E.00315 +G1 X127.823 Y109.083 E.00208 +;WIDTH:0.41646 +G1 X127.822 Y108.964 E.0037 +;WIDTH:0.449999 +G1 X127.82 Y108.846 E.00399 +G1 X121.638 Y102.668 E.29583 +;WIDTH:0.38292 +G1 X121.609 Y102.339 E.00934 +;WIDTH:0.41646 +G1 X121.685 Y102.274 E.00311 +;WIDTH:0.449999 +G1 X121.76 Y102.21 E.00334 +G1 X128.072 Y108.522 E.30215 +G1 X128.133 Y108.007 E.01755 +G1 X121.773 Y101.647 E.30445 +G1 X122.015 Y101.313 E.01396 +G1 X128.105 Y107.403 E.29152 +G1 X128.071 Y106.965 E.01487 +;WIDTH:0.409322 +G1 X126.783 Y105.535 E.05863 +;WIDTH:0.449999 +G1 X126.524 Y105.247 E.01311 +G1 X122.319 Y101.042 E.20129 +G1 X122.851 Y100.998 E.01807 +M73 P88 R0 +M73 Q88 S0 +G1 X125.293 Y103.44 E.1169 +G1 X125.362 Y103.583 E.00537 +;WIDTH:0.41646 +G1 X125.431 Y103.727 E.00496 +;WIDTH:0.38292 +G1 X126.033 Y104.397 E.02546 +M204 P1000 +G1 X126.033 Y104.397 F12000 +G1 X125.621 Y103.193 +M204 P1500 +;WIDTH:0.449999 +G1 F1722 +G1 X123.427 Y100.998 E.10505 +G1 X127.709 Y100.998 E.14494 +G1 X127.797 Y101.22 E.00808 +G1 X128.172 Y101.492 E.01568 +G1 X128.244 Y101.742 E.00881 +G1 X128.246 Y102.534 E.02681 +G1 X128.128 Y102.897 E.01292 +G3 X127.659 Y103.186 I-.634 J-.502 E.01903 +G1 X126.055 Y103.191 E.05429 +G1 X126.053 Y103.049 E.00481 +G1 X124.147 Y101.142 E.09126 +M204 P1000 +G1 X124.147 Y101.142 F12000 +G1 X124.986 Y101.406 +M204 P1500 +G1 F1722 +G1 X127.473 Y101.406 E.08418 +G2 X127.784 Y101.714 I.594 J-.288 E.0151 +G1 X127.837 Y101.819 E.00398 +G1 X127.824 Y102.571 E.02546 +G1 X127.682 Y102.752 E.00779 +G3 X126.341 Y102.761 I-.742 J-10.557 E.04542 +G1 X125.13 Y101.549 E.05799 +M204 P1000 +G1 X125.13 Y101.549 F12000 +G1 X126.062 Y101.852 +M204 P1500 +;WIDTH:0.527828 +G1 F1722 +G1 X127.255 Y101.851 E.0481 +;WIDTH:0.527089 +G1 X127.392 Y101.98 E.00758 +G1 X127.393 Y102.335 E.01429 +G1 X127.137 Y102.335 E.01031 +;WIDTH:0.527828 +G1 X126.548 Y102.337 E.02375 +G1 X126.206 Y101.995 E.0195 +M204 P1000 +;LAYER_CHANGE +;Z:9.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.2 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.548 Y102.337 E-.10051 +G1 X126.834 Y102.336 E-.05949 +;WIPE_END +G1 X126.834 Y102.336 Z9 F12000 +G1 X129.368 Y109.368 Z9.2 +;AFTER_LAYER_CHANGE +;9.2 +G1 X129.368 Y109.368 +G1 Z9.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1949 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1949 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X121.109 Y101.031 Z9.406 F12000 +G1 Z9.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F1949 +G2 X121.093 Y101.093 I-.058 J.018 E.01318 +M204 P1000 +G1 X121.093 Y101.093 F12000 +G1 X120.866 Y101.888 +M204 P1500 +;WIDTH:0.38292 +G1 F1949 +G1 X120.795 Y101.869 E.00208 +G2 X120.855 Y101.929 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X120.927 Y101.836 E.00365 +;WIDTH:0.449999 +G1 X120.998 Y101.743 E.00396 +G1 X121.743 Y100.998 E.03566 +G1 X121.836 Y100.927 E.00396 +;WIDTH:0.41646 +G1 X121.929 Y100.855 E.00365 +;WIDTH:0.38292 +G3 X121.869 Y100.795 I-.02 J-.04 E.00315 +G1 X121.94 Y100.814 E.00208 +;WIDTH:0.449999 +G1 X122.318 Y100.998 E.01423 +G1 X120.998 Y102.318 E.06319 +G1 X120.998 Y102.894 E.0195 +G1 X122.894 Y100.998 E.09076 +G1 X123.47 Y100.998 E.0195 +M73 P89 R0 +M73 Q89 S0 +G1 X120.998 Y103.47 E.11833 +G1 X120.998 Y104.046 E.0195 +G1 X124.046 Y100.998 E.14591 +G1 X124.621 Y100.998 E.01946 +G1 X120.998 Y104.621 E.17343 +G1 X120.998 Y105.197 E.0195 +G1 X125.197 Y100.998 E.201 +G1 X125.773 Y100.998 E.0195 +G1 X120.998 Y105.773 E.22858 +G1 X120.998 Y106.348 E.01946 +G1 X126.348 Y100.998 E.2561 +G1 X126.924 Y100.998 E.0195 +G1 X120.998 Y106.924 E.28367 +G1 X120.998 Y107.5 E.0195 +G1 X127.5 Y100.998 E.31125 +G1 X128.075 Y100.998 E.01946 +G1 X120.998 Y108.075 E.33877 +G1 X120.998 Y108.651 E.0195 +G1 X128.651 Y100.998 E.36634 +G1 X129.132 Y100.898 E.01663 +;WIDTH:0.38292 +G1 X129.061 Y100.879 E.00208 +G2 X129.121 Y100.939 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X129.061 Y101.081 E.00479 +;WIDTH:0.449999 +G1 X129.002 Y101.224 E.00524 +G1 X121.224 Y109.002 E.37233 +G1 X121.081 Y109.061 E.00524 +;WIDTH:0.41646 +G1 X120.939 Y109.121 E.00479 +;WIDTH:0.38292 +G3 X120.879 Y109.061 I-.02 J-.04 E.00315 +G1 X120.95 Y109.08 E.00208 +M204 P1000 +G1 X120.95 Y109.08 F12000 +G1 X121.799 Y109.002 +M204 P1500 +;WIDTH:0.449999 +G1 F1949 +G1 X129.002 Y101.799 E.3448 +G1 X129.002 Y102.375 E.0195 +G1 X122.375 Y109.002 E.31723 +G1 X122.951 Y109.002 E.0195 +G1 X129.002 Y102.951 E.28966 +G1 X129.002 Y103.527 E.0195 +G1 X123.527 Y109.002 E.26208 +G1 X124.102 Y109.002 E.01946 +G1 X129.002 Y104.102 E.23456 +G1 X129.002 Y104.678 E.0195 +G1 X124.678 Y109.002 E.20699 +G1 X125.254 Y109.002 E.0195 +M73 P90 R0 +M73 Q90 S0 +G1 X129.002 Y105.254 E.17941 +G1 X129.002 Y105.829 E.01946 +G1 X125.829 Y109.002 E.15189 +G1 X126.405 Y109.002 E.0195 +G1 X129.002 Y106.405 E.12432 +G1 X129.216 Y106.813 E.01559 +;WIDTH:0.38292 +G1 X129.145 Y106.794 E.00208 +G2 X129.205 Y106.854 I.04 J.02 E.00315 +;WIDTH:0.41646 +G1 X129.103 Y106.918 E.00374 +;WIDTH:0.449999 +G1 X129.002 Y106.981 E.00403 +G1 X126.981 Y109.002 E.09674 +G1 X126.918 Y109.103 E.00403 +;WIDTH:0.41646 +G1 X126.854 Y109.205 E.00374 +;WIDTH:0.38292 +G3 X126.794 Y109.145 I-.02 J-.04 E.00315 +G1 X126.865 Y109.164 E.00208 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X126.794 Y109.145 E-.01527 +G2 X126.854 Y109.205 I.04 J.02 E-.02315 +G1 X126.918 Y109.103 E-.02502 +G1 X126.981 Y109.002 E-.02474 +G1 X127.225 Y108.758 E-.07182 +;WIPE_END +G1 X128.858 Y107.7 Z9.234 F12000 +G1 Z9.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1949 +G1 X127.556 Y109.002 E.06233 +G1 X129.002 Y109.002 E.04895 +G1 X129.002 Y107.556 E.04895 +M204 P1000 +G1 X129.002 Y107.556 F12000 +G1 X128.631 Y108.564 +M204 P1500 +;WIDTH:0.48239 +G1 F1949 +G2 X128.617 Y108.617 I-.05 J.015 E.00999 +M204 P1000 +;LAYER_CHANGE +;Z:9.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.4 + + +G1 X128.617 Y108.617 Z9.2 F12000 +G1 X129.368 Y109.368 Z9.4 F4313.115 +;AFTER_LAYER_CHANGE +;9.4 +G1 X129.368 Y109.368 F12000 +G1 Z9.4 F720 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1915 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1915 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X121.031 Y108.891 Z9.541 F12000 +G1 Z9.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F1915 +G2 X121.093 Y108.907 I.018 J.058 E.01318 +M204 P1000 +G1 X121.093 Y108.907 F12000 +G1 X121.888 Y109.134 +M204 P1500 +;WIDTH:0.38292 +G1 F1915 +G1 X121.869 Y109.205 E.00208 +G2 X121.929 Y109.145 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X121.836 Y109.073 E.00365 +;WIDTH:0.449999 +G1 X121.743 Y109.002 E.00396 +G1 X120.998 Y108.257 E.03566 +G1 X120.927 Y108.164 E.00396 +;WIDTH:0.41646 +G1 X120.855 Y108.071 E.00365 +;WIDTH:0.38292 +G3 X120.795 Y108.131 I-.04 J.02 E.00315 +G1 X120.814 Y108.06 E.00208 +;WIDTH:0.449999 +G1 X120.998 Y107.682 E.01423 +G1 X122.318 Y109.002 E.06319 +G1 X122.894 Y109.002 E.0195 +G1 X120.998 Y107.106 E.09076 +G1 X120.998 Y106.53 E.0195 +G1 X123.47 Y109.002 E.11833 +G1 X124.046 Y109.002 E.0195 +G1 X120.998 Y105.954 E.14591 +G1 X120.998 Y105.379 E.01946 +G1 X124.621 Y109.002 E.17343 +G1 X125.197 Y109.002 E.0195 +M73 P91 R0 +M73 Q91 S0 +G1 X120.998 Y104.803 E.201 +G1 X120.998 Y104.227 E.0195 +G1 X125.773 Y109.002 E.22858 +G1 X126.348 Y109.002 E.01946 +G1 X120.998 Y103.652 E.2561 +G1 X120.998 Y103.076 E.0195 +G1 X126.924 Y109.002 E.28367 +G1 X127.5 Y109.002 E.0195 +G1 X120.998 Y102.5 E.31125 +G1 X120.998 Y101.925 E.01946 +G1 X128.075 Y109.002 E.33877 +G1 X128.651 Y109.002 E.0195 +G1 X120.998 Y101.349 E.36634 +G1 X120.898 Y100.868 E.01663 +;WIDTH:0.38292 +G1 X120.879 Y100.939 E.00208 +G2 X120.939 Y100.879 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X121.081 Y100.939 E.00479 +;WIDTH:0.449999 +G1 X121.224 Y100.998 E.00524 +G1 X129.002 Y108.776 E.37233 +G1 X129.061 Y108.919 E.00524 +;WIDTH:0.41646 +G1 X129.121 Y109.061 E.00479 +;WIDTH:0.38292 +G3 X129.061 Y109.121 I-.04 J.02 E.00315 +G1 X129.08 Y109.05 E.00208 +M204 P1000 +G1 X129.002 Y108.201 F12000 +M204 P1500 +;WIDTH:0.449999 +G1 F1915 +G1 X121.799 Y100.998 E.3448 +G1 X122.375 Y100.998 E.0195 +G1 X129.002 Y107.625 E.31723 +G1 X129.002 Y107.049 E.0195 +G1 X122.951 Y100.998 E.28966 +G1 X123.527 Y100.998 E.0195 +G1 X129.002 Y106.473 E.26208 +G1 X129.002 Y105.898 E.01946 +G1 X124.102 Y100.998 E.23456 +G1 X124.678 Y100.998 E.0195 +G1 X129.002 Y105.322 E.20699 +G1 X129.002 Y104.746 E.0195 +G1 X125.254 Y100.998 E.17941 +G1 X125.829 Y100.998 E.01946 +G1 X129.002 Y104.171 E.15189 +G1 X129.002 Y103.595 E.0195 +M73 P92 R0 +M73 Q92 S0 +G1 X126.405 Y100.998 E.12432 +G1 X126.813 Y100.784 E.01559 +;WIDTH:0.38292 +G1 X126.794 Y100.855 E.00208 +G2 X126.854 Y100.795 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X126.918 Y100.897 E.00374 +;WIDTH:0.449999 +G1 X126.981 Y100.998 E.00403 +G1 X129.002 Y103.019 E.09674 +G1 X129.083 Y103.077 E.00337 +;WIDTH:0.41646 +G1 X129.164 Y103.135 E.00309 +;WIDTH:0.38292 +G1 X129.145 Y103.206 E.00208 +G2 X129.205 Y103.146 I.02 J-.04 E.00315 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X129.145 Y103.206 I-.04 J.02 E-.02315 +G1 X129.164 Y103.135 E-.01527 +G1 X129.083 Y103.077 E-.0207 +G1 X129.002 Y103.019 E-.0207 +G1 X128.729 Y102.746 E-.08018 +;WIPE_END +G1 X127.7 Y101.142 Z9.433 F12000 +G1 Z9.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1915 +G1 X129.002 Y102.444 E.06233 +G1 X129.002 Y100.998 E.04895 +G1 X127.556 Y100.998 E.04895 +M204 P1000 +G1 X127.556 Y100.998 F12000 +G1 X128.564 Y101.369 +M204 P1500 +;WIDTH:0.48239 +G1 F1915 +G2 X128.617 Y101.383 I.015 J.05 E.00999 +M204 P1000 +;LAYER_CHANGE +;Z:9.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.6 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X128.564 Y101.369 I-.038 J.036 E-.05679 +;WIPE_END +G1 E-.10321 F2100 +G1 X128.564 Y101.369 Z9.4 F12000 +G1 X129.368 Y109.368 Z9.6 +;AFTER_LAYER_CHANGE +;9.6 +G1 X129.368 Y109.368 +G1 Z9.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2100 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2099.999 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X129.02 Y102.548 Z9.726 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.42503 +G1 F2189 +G1 X127.6 Y103.968 E.06381 +M204 P1000 +G1 X127.6 Y103.968 F12000 +G1 X127.06 Y103.968 +M204 P800 +G1 F2189 +G1 X129.02 Y102.007 E.08809 +M204 P1000 +G1 X129.02 Y102.007 F12000 +G1 X128.99 Y101.497 +M204 P800 +G1 F2189 +G1 X126.519 Y103.968 E.11103 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X127.063 Y103.424 E-.16 +;WIPE_END +G1 X128.914 Y108.058 Z9.687 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P800 +G1 F2189 +G1 X127.785 Y109.186 E.05071 +M204 P1000 +G1 X127.785 Y109.186 F12000 +G1 X127.245 Y109.186 +M204 P800 +G1 F2189 +G1 X128.914 Y107.518 E.07497 +M204 P1000 +G1 X128.914 Y107.518 F12000 +G1 X128.846 Y107.045 +M204 P800 +G1 F2189 +G1 X126.705 Y109.186 E.0962 +M204 P1000 +G1 X126.705 Y109.186 F12000 +G1 X126.164 Y109.186 +M204 P800 +G1 F2189 +G1 X128.67 Y106.681 E.11258 +M204 P1000 +G1 X128.67 Y106.681 F12000 +G1 X128.434 Y106.376 +M204 P800 +G1 F2189 +M73 Q93 S0 +G1 X125.624 Y109.186 E.12626 +M204 P1000 +G1 X125.624 Y109.186 F12000 +M73 P93 R0 +G1 X125.084 Y109.186 +M204 P800 +G1 F2189 +G1 X128.192 Y106.078 E.13965 +M204 P1000 +G1 X128.192 Y106.078 F12000 +G1 X127.95 Y105.779 +M204 P800 +G1 F2189 +G1 X124.543 Y109.186 E.15309 +M204 P1000 +G1 X124.543 Y109.186 F12000 +G1 X124.003 Y109.186 +M204 P800 +G1 F2189 +G1 X127.709 Y105.48 E.16652 +M204 P1000 +G1 X127.709 Y105.48 F12000 +G1 X127.467 Y105.182 +M204 P800 +G1 F2189 +G1 X123.462 Y109.186 E.17994 +M204 P1000 +G1 X123.462 Y109.186 F12000 +G1 X122.922 Y109.186 +M204 P800 +G1 F2189 +G1 X127.225 Y104.883 E.19335 +M204 P1000 +G1 X127.225 Y104.883 F12000 +G1 X126.984 Y104.584 +M204 P800 +G1 F2189 +G1 X122.382 Y109.186 E.20678 +M204 P1000 +G1 X122.382 Y109.186 F12000 +G1 X121.841 Y109.186 +M204 P800 +G1 F2189 +G1 X126.742 Y104.286 E.2202 +M204 P1000 +G1 X126.742 Y104.286 F12000 +G1 X126.5 Y103.987 +M204 P800 +G1 F2189 +G1 X121.545 Y108.942 E.22264 +M204 P1000 +G1 X121.545 Y108.942 F12000 +G1 X121.396 Y108.55 +M204 P800 +G1 F2189 +G1 X128.85 Y101.097 E.33491 +M204 P1000 +G1 X128.85 Y101.097 F12000 +G1 X128.593 Y100.814 +M204 P800 +G1 F2189 +G1 X121.357 Y108.05 E.32514 +M204 P1000 +G1 X121.357 Y108.05 F12000 +G1 X121.357 Y107.509 +M204 P800 +G1 F2189 +G1 X122.834 Y106.032 E.06637 +M204 P1000 +G1 X122.834 Y106.032 F12000 +G1 X123.126 Y105.74 +M204 P800 +G1 F2189 +G1 X128.052 Y100.814 E.22134 +M204 P1000 +G1 X128.052 Y100.814 F12000 +G1 X127.512 Y100.814 +M204 P800 +G1 F2189 +G1 X122.886 Y105.44 E.20786 +M204 P1000 +G1 X122.886 Y105.44 F12000 +G1 X122.646 Y105.14 +M204 P800 +G1 F2189 +G1 X126.971 Y100.814 E.19436 +M204 P1000 +G1 X126.971 Y100.814 F12000 +G1 X126.431 Y100.814 +M204 P800 +G1 F2189 +G1 X122.405 Y104.839 E.18088 +M204 P1000 +G1 X122.405 Y104.839 F12000 +G1 X122.165 Y104.539 +M204 P800 +G1 F2189 +G1 X125.891 Y100.814 E.1674 +M204 P1000 +G1 X125.891 Y100.814 F12000 +G1 X125.35 Y100.814 +M204 P800 +G1 F2189 +G1 X121.925 Y104.239 E.1539 +M204 P1000 +G1 X121.925 Y104.239 F12000 +G1 X121.685 Y103.939 +M204 P800 +G1 F2189 +G1 X124.81 Y100.814 E.14042 +M204 P1000 +G1 X124.81 Y100.814 F12000 +G1 X124.27 Y100.814 +M204 P800 +G1 F2189 +G1 X121.445 Y103.638 E.12691 +M204 P1000 +G1 X121.445 Y103.638 F12000 +G1 X121.212 Y103.331 +M204 P800 +G1 F2189 +G1 X123.729 Y100.814 E.1131 +M204 P1000 +G1 X123.729 Y100.814 F12000 +G1 X123.189 Y100.814 +M204 P800 +G1 F2189 +G1 X121.041 Y102.962 E.09652 +M204 P1000 +G1 X121.041 Y102.962 F12000 +G1 X120.98 Y102.482 +M204 P800 +G1 F2189 +G1 X122.648 Y100.814 E.07495 +M204 P1000 +G1 X122.648 Y100.814 F12000 +G1 X122.108 Y100.814 +M204 P800 +G1 F2189 +G1 X120.98 Y101.942 E.05068 +M204 P1000 +G1 X120.98 Y101.942 F12000 +G1 X120.981 Y100.981 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.415596 +G1 F2189 +G1 X120.887 Y101.549 E.01784 +;WIDTH:0.38292 +G2 X120.922 Y103.064 I10.493 J.519 E.04288 +;WIDTH:0.449999 +G2 X121.119 Y103.586 I1.24 J-.169 E.01905 +M73 Q94 S0 +G1 X122.898 Y105.812 E.09645 +G2 X121.808 Y106.15 I.041 J2.059 E.03914 +M73 P94 R0 +G1 X121.466 Y106.496 E.01647 +G1 X121.238 Y106.949 E.01717 +;WIDTH:0.496959 +G1 X121.181 Y107.103 E.0062 +;WIDTH:0.543919 +G1 X121.125 Y107.257 E.00683 +;WIDTH:0.590878 +G1 X121.069 Y107.41 E.00742 +G1 X121.045 Y107.267 E.00661 +;WIDTH:0.543919 +G1 X121.022 Y107.124 E.00603 +;WIDTH:0.496959 +G1 X120.998 Y106.981 E.00547 +;WIDTH:0.449999 +G1 X120.998 Y103.579 E.11515 +M204 P1000 +G1 X120.998 Y103.579 F12000 +G1 X121.449 Y104.72 +M204 P1500 +;WIDTH:0.536586 +G1 F2189 +G1 X122.061 Y105.486 E.04025 +G1 X122.054 Y105.518 E.00134 +G1 X121.449 Y105.862 E.02857 +G1 X121.449 Y104.924 E.0385 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.449 Y105.694 E-.16 +;WIPE_END +G1 X121.069 Y107.41 Z9.631 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.590878 +G1 F2189 +G1 X121.068 Y108.327 E.04178 +;WIDTH:0.592776 +G1 X121.048 Y108.479 E.00701 +;WIDTH:0.546909 +G1 X121.027 Y108.585 E.00453 +;WIDTH:0.501041 +G1 X121.005 Y108.679 E.00368 +;WIDTH:0.460312 +G1 X120.984 Y108.774 E.00338 +;WIDTH:0.419583 +G1 X120.963 Y108.869 E.00305 +;WIDTH:0.378854 +G1 X120.963 Y109.037 E.00469 +G1 X121.406 Y109.037 E.01237 +;WIDTH:0.420343 +G1 X121.339 Y108.907 E.00459 +;WIDTH:0.461832 +G1 X121.272 Y108.777 E.00509 +;WIDTH:0.503321 +G1 X121.205 Y108.646 E.00563 +;WIDTH:0.54481 +G1 X121.138 Y108.516 E.0061 +;WIDTH:0.586298 +G1 X121.071 Y108.386 E.00661 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.138 Y108.516 E-.03039 +G1 X121.205 Y108.646 E-.03039 +G1 X121.272 Y108.777 E-.03058 +G1 X121.339 Y108.907 E-.03039 +G1 X121.406 Y109.037 E-.03039 +G1 X121.368 Y109.037 E-.00786 +;WIPE_END +G1 X129.012 Y100.988 Z9.794 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.428874 +G1 F2189 +G1 X129.12 Y101.655 E.02168 +;WIDTH:0.38292 +G1 X129.121 Y102.554 E.02542 +;WIDTH:0.449999 +G1 X129.002 Y103.324 E.02637 +G1 X129.014 Y106.694 E.11407 +;WIDTH:0.424738 +G1 X129.027 Y106.97 E.00877 +;WIDTH:0.407166 +G1 X129.011 Y106.913 E.00179 +;WIDTH:0.449999 +G1 X128.773 Y106.442 E.01786 +G1 X126.95 Y104.189 E.0981 +G1 X127.646 Y104.187 E.02356 +G2 X128.569 Y103.85 I-.339 J-2.357 E.0335 +G1 X128.874 Y103.555 E.01436 +G1 X128.903 Y103.502 E.00204 +M204 P1000 +G1 X128.903 Y103.502 F12000 +G1 X128.551 Y104.365 +M204 P1500 +;WIDTH:0.536172 +G1 F2189 +G1 X128.551 Y105.453 E.04462 +G1 X127.865 Y104.605 E.04474 +G2 X128.366 Y104.448 I-.215 J-1.569 E.02163 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X127.865 Y104.605 I-.717 J-1.411 E-.10961 +G1 X128.018 Y104.793 E-.05039 +;WIPE_END +G1 X129.027 Y106.97 Z9.642 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.399476 +G1 F2189 +G1 X129.069 Y107.474 E.01499 +;WIDTH:0.38292 +G1 X129.062 Y108.425 E.02689 +;WIDTH:0.420595 +G1 X129.034 Y108.609 E.00584 +;WIDTH:0.45827 +G1 X129.006 Y108.792 E.00639 +;WIDTH:0.495944 +G1 X128.979 Y108.975 E.00697 +;WIDTH:0.4968 +G1 X128.978 Y108.978 E.00012 +M204 P1000 +;LAYER_CHANGE +;Z:9.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.8 + + +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X128.979 Y108.975 E-.00066 +G1 X129.006 Y108.792 E-.03844 +G1 X129.034 Y108.609 E-.03847 +G1 X129.062 Y108.425 E-.03868 +G1 X129.064 Y108.215 E-.04375 +;WIPE_END +G1 X129.064 Y108.215 Z9.6 F12000 +G1 X122.353 Y108.692 Z9.8 +;AFTER_LAYER_CHANGE +;9.8 +G1 X122.353 Y108.692 +G1 Z9.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1754 +G1 X122.185 Y108.377 E.01208 +G1 X122.175 Y107.461 E.03101 +G1 X122.312 Y107.076 E.01383 +G1 X122.585 Y106.885 E.01128 +G1 X122.786 Y106.851 E.0069 +G1 X125.063 Y106.851 E.07707 +G1 X121.932 Y102.937 E.16966 +G1 X121.808 Y102.661 E.01024 +G1 X121.799 Y101.729 E.03155 +G1 X121.844 Y101.498 E.00797 +;WIDTH:0.495848 +G1 X122.3 Y101.105 E.02267 +G1 X122.409 Y101.098 E.00411 +;WIDTH:0.489381 +G1 X127.591 Y101.098 E.19237 +G1 X128.156 Y101.498 E.0257 +;WIDTH:0.449999 +G1 X128.201 Y101.729 E.00797 +G1 X128.201 Y102.539 E.02742 +G1 X128.106 Y102.866 E.01153 +G1 X127.893 Y103.069 E.00996 +G1 X127.591 Y103.149 E.01057 +G1 X124.769 Y103.149 E.09552 +G1 X127.959 Y107.091 E.17165 +G1 X128.095 Y107.475 E.01379 +G1 X128.095 Y108.271 E.02694 +G1 X128.061 Y108.473 E.00693 +G1 X127.869 Y108.746 E.0113 +;WIDTH:0.495848 +G1 X127.594 Y108.895 E.01178 +G1 X127.484 Y108.902 E.00415 +;WIDTH:0.489381 +G1 X122.786 Y108.902 E.17441 +G1 X122.397 Y108.728 E.01582 +G1 X122.397 Y108.728 F12000 +G1 X122.641 Y108.408 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F1754 +G1 X122.582 Y108.271 E.00505 +G1 X122.582 Y107.461 E.02742 +G1 X122.628 Y107.333 E.0046 +G1 X122.786 Y107.258 E.00592 +G1 X125.939 Y107.258 E.10673 +G1 X125.591 Y106.859 E.01792 +G1 X125.585 Y106.852 E.00031 +G1 X122.25 Y102.683 E.18071 +G1 X122.206 Y102.556 E.00455 +G1 X122.206 Y101.729 E.02799 +G1 X122.221 Y101.652 E.00266 +G1 X122.409 Y101.525 E.00768 +G1 X127.591 Y101.525 E.1754 +G1 X127.779 Y101.652 E.00768 +G1 X127.794 Y102.539 E.03003 +G1 X127.763 Y102.648 E.00384 +G1 X127.619 Y102.74 E.00578 +G1 X127.591 Y102.742 E.00095 +G1 X123.911 Y102.742 E.12456 +G1 X123.989 Y102.832 E.00403 +G1 X127.643 Y107.347 E.19661 +G1 X127.688 Y107.475 E.00459 +G1 X127.688 Y108.271 E.02694 +G1 X127.613 Y108.43 E.00595 +G1 X127.484 Y108.475 E.00462 +G1 X122.786 Y108.475 E.15902 +G1 X122.691 Y108.437 E.00346 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.641 Y108.408 E-.01201 +G1 X122.582 Y108.271 E-.031 +G1 X122.582 Y107.708 E-.11699 +;WIPE_END +G1 X129.368 Y109.368 Z9.922 F12000 +G1 Z9.8 F720 +M73 P95 R0 +M73 Q95 S0 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F1754 +G1 X128.09 Y109.368 E.04326 +;WIDTH:0.489381 +G1 X127.484 Y109.348 E.02251 +G1 X122.786 Y109.348 E.17441 +G1 X122.103 Y109.368 E.02537 +;WIDTH:0.449999 +G1 X120.632 Y109.368 E.04979 +G1 X120.632 Y100.632 E.2957 +G1 X121.261 Y100.632 E.02129 +;WIDTH:0.489381 +G1 X122.409 Y100.652 E.04262 +G1 X127.591 Y100.652 E.19237 +G1 X128.739 Y100.632 E.04262 +;WIDTH:0.449999 +G1 X129.368 Y100.632 E.02129 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1754 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X129.775 Y109.775 E-.01247 +G1 X129.065 Y109.775 E-.14753 +;WIPE_END +G1 X128.226 Y108.95 Z9.821 F12000 +G1 Z9.8 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.552538 +G1 F1754 +G1 X128.462 Y108.587 E.01835 +G1 X128.504 Y108.151 E.01856 +G1 X128.821 Y108.469 E.01903 +G1 X128.95 Y108.485 E.00551 +G1 X128.95 Y108.95 E.0197 +G1 X128.43 Y108.95 E.02203 +M204 P1000 +G1 X128.43 Y108.95 F12000 +G1 X129.002 Y108.001 +M204 P1500 +;WIDTH:0.449999 +G1 F1754 +G1 X128.462 Y107.461 E.02585 +G1 X128.372 Y107.317 E.00575 +;WIDTH:0.41646 +G1 X128.283 Y107.173 E.00526 +;WIDTH:0.38292 +G2 X127.914 Y106.696 I-1.96 J1.135 E.0171 +M204 P1000 +G1 X127.914 Y106.696 F12000 +G1 X127.423 Y105.846 +M204 P1500 +;WIDTH:0.449999 +G1 F1754 +G1 X129.002 Y107.425 E.07559 +G1 X129.002 Y106.849 E.0195 +G1 X125.666 Y103.514 E.15967 +G1 X127.696 Y103.508 E.06871 +G1 X128.038 Y103.403 E.01211 +G1 X128.384 Y103.099 E.01559 +;WIDTH:0.469127 +G1 X128.566 Y102.673 E.01642 +G1 X128.574 Y101.735 E.03324 +;WIDTH:0.495672 +G1 X128.53 Y101.402 E.01265 +;WIDTH:0.508327 +G1 X128.445 Y101.257 E.0065 +G1 X128.163 Y101.053 E.01347 +G1 X128.972 Y101.032 E.03132 +G1 X128.995 Y101.738 E.02734 +;WIDTH:0.463332 +G1 X129.002 Y103.131 E.0487 +;WIDTH:0.449999 +G1 X129.002 Y106.273 E.10635 +G1 X126.384 Y103.656 E.1253 +M204 P1000 +G1 X126.384 Y103.656 F12000 +G1 X127.22 Y103.916 +M204 P1500 +G1 F1754 +G2 X128.023 Y103.838 I.192 J-2.184 E.02746 +G2 X128.594 Y103.461 I-.49 J-1.364 E.02338 +G1 X128.594 Y105.291 E.06194 +G1 X127.364 Y104.06 E.0589 +G1 X128.147 Y104.192 E.02688 +;WIDTH:0.500374 +G2 X128.203 Y104.207 I.016 J.052 E.01078 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G3 X128.147 Y104.192 I-.04 J.037 E-.05889 +G1 X127.667 Y104.111 E-.10111 +;WIPE_END +G1 X120.998 Y104.431 Z9.917 F12000 +G1 Z9.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1754 +G1 X120.998 Y109.002 E.15472 +G1 X122.146 Y108.999 E.03886 +M73 P96 R0 +M73 Q96 S0 +G1 X121.845 Y108.498 E.01978 +G1 X121.809 Y107.471 E.03478 +G1 X121.996 Y106.906 E.02014 +G3 X122.499 Y106.531 I1.455 J1.426 E.02132 +G3 X123.256 Y106.485 I.562 J2.967 E.02574 +G1 X120.998 Y104.227 E.10809 +G1 X120.998 Y103.652 E.01946 +G1 X123.831 Y106.485 E.13561 +G1 X124.315 Y106.492 E.01638 +;WIDTH:0.435616 +G1 X124.242 Y106.362 E.00487 +;WIDTH:0.438543 +G1 X123.944 Y106.029 E.0147 +;WIDTH:0.48801 +G1 X123.646 Y105.696 E.01654 +;WIDTH:0.537477 +G1 X123.348 Y105.363 E.01838 +;WIDTH:0.586944 +G1 X123.05 Y105.031 E.02018 +;WIDTH:0.63641 +G1 X122.752 Y104.698 E.02205 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X123.05 Y105.031 E-.09287 +G1 X123.266 Y105.272 E-.06713 +;WIPE_END +G1 X121.017 Y102.896 Z9.857 F12000 +G1 Z9.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.512018 +G1 F1754 +G1 X121.029 Y103.18 E.01109 +G1 X121.131 Y103.17 E.004 +G1 X121.242 Y103.303 E.00676 +;WIDTH:0.478015 +G1 X121.353 Y103.435 E.00624 +;WIDTH:0.444011 +G1 X121.863 Y103.974 E.02475 +;WIDTH:0.402911 +G1 X122.372 Y104.512 E.02217 +;WIDTH:0.407578 +G1 X122.436 Y104.543 E.00216 +;WIDTH:0.453344 +G1 X122.499 Y104.574 E.0024 +;WIDTH:0.499111 +G1 X122.562 Y104.605 E.00266 +;WIDTH:0.544877 +G1 X122.625 Y104.636 E.00293 +;WIDTH:0.590644 +G1 X122.689 Y104.667 E.00324 +;WIDTH:0.63641 +G1 X122.752 Y104.698 E.00346 +G1 X122.728 Y104.631 E.00351 +;WIDTH:0.590644 +G1 X122.705 Y104.565 E.00318 +;WIDTH:0.544877 +G1 X122.681 Y104.499 E.00293 +;WIDTH:0.499111 +G1 X122.657 Y104.433 E.00266 +;WIDTH:0.453344 +G1 X122.633 Y104.366 E.00243 +;WIDTH:0.407578 +G1 X122.61 Y104.3 E.00212 +;WIDTH:0.402911 +G1 X122.131 Y103.734 E.0222 +;WIDTH:0.444011 +G1 X121.652 Y103.168 E.02473 +;WIDTH:0.489151 +G1 X121.444 Y102.779 E.01637 +G1 X121.426 Y101.735 E.03874 +;WIDTH:0.507544 +G1 X121.476 Y101.358 E.01469 +G1 X121.775 Y101.057 E.01639 +G1 X121.027 Y101.034 E.02891 +G1 X121.007 Y101.651 E.02385 +;WIDTH:0.4694 +G1 X121.008 Y102.693 E.03695 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.007 Y101.923 E-.16 +;WIPE_END +G1 X121.406 Y106.734 Z9.884 F12000 +G1 Z9.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F1754 +G1 X121.406 Y105.21 E.05159 +G1 X122.356 Y106.16 E.04548 +G2 X121.613 Y106.77 I.503 J1.372 E.03316 +;WIDTH:0.482646 +G1 X121.422 Y107.258 E.01916 +G1 X121.412 Y106.937 E.01174 +G3 X121.404 Y107.476 I-2.941 J.226 E.01974 +;WIDTH:0.48372 +G1 X121.422 Y108.578 E.0404 +M204 P1000 +;LAYER_CHANGE +;Z:10 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10 + + +G1 X121.422 Y108.578 Z9.8 F12000 +G1 X122.353 Y108.692 Z10 F3893.314 +G1 X122.353 Y108.692 Z10 F12000 +;AFTER_LAYER_CHANGE +;10 +G1 Z10 F720 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1754 +G1 X122.185 Y108.377 E.01208 +G1 X122.175 Y107.461 E.03101 +G1 X122.312 Y107.076 E.01383 +G1 X122.585 Y106.885 E.01128 +G1 X122.786 Y106.851 E.0069 +G1 X125.063 Y106.851 E.07707 +G1 X121.932 Y102.937 E.16966 +G1 X121.808 Y102.661 E.01024 +G1 X121.799 Y101.729 E.03155 +G1 X121.844 Y101.498 E.00797 +;WIDTH:0.495848 +G1 X122.3 Y101.105 E.02267 +G1 X122.409 Y101.098 E.00411 +;WIDTH:0.489381 +G1 X127.591 Y101.098 E.19237 +G1 X128.156 Y101.498 E.0257 +;WIDTH:0.449999 +G1 X128.201 Y101.729 E.00797 +G1 X128.201 Y102.539 E.02742 +G1 X128.106 Y102.866 E.01153 +G1 X127.893 Y103.069 E.00996 +G1 X127.591 Y103.149 E.01057 +G1 X124.769 Y103.149 E.09552 +G1 X127.959 Y107.091 E.17165 +G1 X128.095 Y107.475 E.01379 +G1 X128.095 Y108.271 E.02694 +G1 X128.061 Y108.473 E.00693 +G1 X127.869 Y108.746 E.0113 +;WIDTH:0.495848 +G1 X127.594 Y108.895 E.01178 +G1 X127.484 Y108.902 E.00415 +;WIDTH:0.489381 +G1 X122.786 Y108.902 E.17441 +G1 X122.397 Y108.728 E.01582 +G1 X122.397 Y108.728 F12000 +G1 X122.646 Y108.404 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F1754 +G3 X122.582 Y108.271 I.172 J-.164 E.00508 +G1 X122.582 Y107.461 E.02742 +G3 X122.786 Y107.258 I.238 J.034 E.01045 +G1 X125.939 Y107.258 E.10673 +G1 X125.591 Y106.859 E.01792 +G1 X122.25 Y102.683 E.18102 +G1 X122.206 Y102.556 E.00455 +G1 X122.206 Y101.729 E.02799 +G1 X122.221 Y101.652 E.00266 +G1 X122.409 Y101.525 E.00768 +G1 X127.591 Y101.525 E.1754 +G1 X127.779 Y101.652 E.00768 +G1 X127.794 Y102.539 E.03003 +G1 X127.763 Y102.648 E.00384 +G1 X127.619 Y102.74 E.00578 +G1 X127.591 Y102.742 E.00095 +G1 X123.911 Y102.742 E.12456 +G1 X127.643 Y107.347 E.20063 +G1 X127.688 Y107.475 E.00459 +G1 X127.688 Y108.271 E.02694 +G3 X127.484 Y108.475 I-.237 J-.033 E.01048 +G1 X122.786 Y108.475 E.15902 +G3 X122.692 Y108.441 I.032 J-.235 E.00341 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X122.646 Y108.404 E-.01227 +G3 X122.582 Y108.271 I.171 J-.164 E-.03119 +G1 X122.582 Y107.711 E-.11654 +;WIPE_END +G1 X129.368 Y109.368 Z10.122 F12000 +G1 Z10 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F1754 +G1 X128.09 Y109.368 E.04326 +;WIDTH:0.489381 +G1 X127.484 Y109.348 E.02251 +M73 P97 R0 +M73 Q97 S0 +G1 X122.786 Y109.348 E.17441 +G1 X122.103 Y109.368 E.02537 +;WIDTH:0.449999 +G1 X120.632 Y109.368 E.04979 +G1 X120.632 Y100.632 E.2957 +G1 X121.261 Y100.632 E.02129 +;WIDTH:0.489381 +G1 X122.409 Y100.652 E.04262 +G1 X127.591 Y100.652 E.19237 +G1 X128.739 Y100.632 E.04262 +;WIDTH:0.449999 +G1 X129.368 Y100.632 E.02129 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F12000 +M204 P800 +;TYPE:External perimeter +G1 F1754 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M204 P1000 +G1 X129.186 Y108.392 F12000 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.435268 +G1 F1754 +G1 X128.392 Y109.186 E.03663 +M204 P1000 +G1 X128.392 Y109.186 F12000 +G1 X127.891 Y109.132 +M204 P800 +G1 F1754 +G1 X129.186 Y107.837 E.05975 +M204 P1000 +G1 X129.186 Y107.837 F12000 +G1 X129.186 Y107.282 +M204 P800 +G1 F1754 +G1 X128.267 Y108.201 E.0424 +M204 P1000 +G1 X128.267 Y108.201 F12000 +G1 X128.275 Y107.639 +M204 P800 +G1 F1754 +G1 X129.186 Y106.727 E.04205 +M204 P1000 +G1 X129.186 Y106.727 F12000 +G1 X129.186 Y106.172 +M204 P800 +G1 F1754 +G1 X128.173 Y107.186 E.04676 +M204 P1000 +G1 X128.173 Y107.186 F12000 +G1 X127.978 Y106.826 +M204 P800 +G1 F1754 +G1 X129.186 Y105.617 E.05576 +M204 P1000 +G1 X129.186 Y105.617 F12000 +G1 X129.186 Y105.062 +M204 P800 +G1 F1754 +G1 X127.73 Y106.519 E.0672 +M204 P1000 +G1 X127.73 Y106.519 F12000 +G1 X127.482 Y106.212 +M204 P800 +G1 F1754 +G1 X129.186 Y104.508 E.07862 +M204 P1000 +G1 X129.186 Y104.508 F12000 +G1 X129.186 Y103.953 +M204 P800 +G1 F1754 +G1 X127.233 Y105.906 E.09011 +M204 P1000 +G1 X127.233 Y105.906 F12000 +G1 X126.985 Y105.599 +M204 P800 +G1 F1754 +G1 X129.186 Y103.398 E.10155 +M204 P1000 +G1 X129.186 Y103.398 F12000 +G1 X129.186 Y102.843 +M204 P800 +G1 F1754 +G1 X126.737 Y105.292 E.11299 +M204 P1000 +G1 X126.737 Y105.292 F12000 +G1 X126.489 Y104.986 +M204 P800 +G1 F1754 +G1 X129.186 Y102.288 E.12445 +M204 P1000 +G1 X129.186 Y102.288 F12000 +G1 X129.186 Y101.733 +M204 P800 +G1 F1754 +G1 X128.383 Y102.536 E.03705 +M204 P1000 +G1 X128.383 Y102.536 F12000 +G1 X128.383 Y101.981 +M204 P800 +G1 F1754 +G1 X129.186 Y101.178 E.03705 +M204 P1000 +G1 X129.186 Y101.178 F12000 +G1 X128.992 Y100.818 +M204 P800 +G1 F1754 +G1 X128.333 Y101.476 E.03038 +M204 P1000 +G1 X128.333 Y101.476 F12000 +G1 X128.033 Y101.222 +M204 P800 +G1 F1754 +G1 X128.423 Y100.832 E.01799 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X128.033 Y101.222 E-.11462 +;WIPE_END +G1 E-.04538 F2100 +G1 X127.596 Y103.324 Z10.037 F12000 +G1 Z10 F720 +G1 E.8 F1500 +M204 P800 +G1 F1754 +G1 X126.241 Y104.679 E.06252 +M204 P1000 +G1 X126.241 Y104.679 F12000 +G1 X125.992 Y104.372 +M204 P800 +G1 F1754 +G1 X127.039 Y103.325 E.04831 +M204 P1000 +G1 X127.039 Y103.325 F12000 +G1 X126.483 Y103.327 +M204 P800 +G1 F1754 +M73 P98 R0 +M73 Q98 S0 +G1 X125.744 Y104.065 E.03407 +M204 P1000 +G1 X125.744 Y104.065 F12000 +G1 X125.496 Y103.759 +M204 P800 +G1 F1754 +G1 X125.926 Y103.329 E.01984 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X125.496 Y103.759 E-.12637 +;WIPE_END +G1 E-.03363 F2100 +G1 X121.598 Y109.186 Z10.117 F12000 +G1 Z10 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.436959 +G1 F1754 +G1 X122.136 Y108.649 E.02491 +M204 P1000 +G1 X122.136 Y108.649 F12000 +G1 X122.004 Y108.224 +M204 P800 +G1 F1754 +G1 X121.041 Y109.186 E.0446 +M204 P1000 +G1 X121.041 Y109.186 F12000 +G1 X120.814 Y108.856 +M204 P800 +G1 F1754 +G1 X121.996 Y107.674 E.05477 +M204 P1000 +G1 E-.64 F2100 +;WIPE_START +G1 F9600 +G1 X121.451 Y108.219 E-.16 +;WIPE_END +G1 X124.115 Y106.669 Z10.054 F12000 +G1 Z10 F720 +G1 E.8 F1500 +M204 P800 +G1 F1754 +G1 X124.432 Y106.353 E.01467 +M204 P1000 +G1 X124.432 Y106.353 F12000 +G1 X124.184 Y106.043 +M204 P800 +G1 F1754 +G1 X123.558 Y106.669 E.02901 +M204 P1000 +G1 X123.558 Y106.669 F12000 +G1 X123.001 Y106.669 +M204 P800 +G1 F1754 +G1 X123.936 Y105.734 E.04332 +M204 P1000 +G1 X123.936 Y105.734 F12000 +G1 X123.689 Y105.424 +M204 P800 +G1 F1754 +G1 X120.814 Y108.299 E.13322 +M204 P1000 +G1 X120.814 Y108.299 F12000 +G1 X120.814 Y107.742 +M204 P800 +G1 F1754 +G1 X123.441 Y105.114 E.12175 +M204 P1000 +G1 X123.441 Y105.114 F12000 +G1 X123.193 Y104.805 +M204 P800 +G1 F1754 +G1 X120.814 Y107.184 E.11023 +M204 P1000 +G1 X120.814 Y107.184 F12000 +G1 X120.814 Y106.627 +M204 P800 +G1 F1754 +G1 X122.946 Y104.495 E.09879 +M204 P1000 +G1 X122.946 Y104.495 F12000 +G1 X122.698 Y104.186 +M204 P800 +G1 F1754 +G1 X120.814 Y106.07 E.0873 +M204 P1000 +G1 X120.814 Y106.07 F12000 +G1 X120.814 Y105.513 +M204 P800 +G1 F1754 +G1 X122.451 Y103.876 E.07585 +M204 P1000 +G1 X122.451 Y103.876 F12000 +G1 X122.203 Y103.566 +M204 P800 +G1 F1754 +G1 X120.814 Y104.955 E.06436 +M204 P1000 +G1 X120.814 Y104.955 F12000 +G1 X120.814 Y104.398 +M204 P800 +G1 F1754 +G1 X121.955 Y103.257 E.05287 +M204 P1000 +G1 X121.955 Y103.257 F12000 +G1 X121.733 Y102.922 +M204 P800 +G1 F1754 +G1 X120.814 Y103.841 E.04258 +M204 P1000 +G1 X120.814 Y103.841 F12000 +G1 X120.814 Y103.284 +M204 P800 +G1 F1754 +G1 X121.626 Y102.471 E.03765 +M204 P1000 +G1 X121.626 Y102.471 F12000 +G1 X121.619 Y101.921 +M204 P800 +G1 F1754 +G1 X120.814 Y102.726 E.0373 +M204 P1000 +G1 X120.814 Y102.726 F12000 +G1 X120.814 Y102.169 +M204 P800 +G1 F1754 +G1 X122.129 Y100.854 E.06093 +M204 P1000 +G1 X122.129 Y100.854 F12000 +G1 X121.597 Y100.829 +M204 P800 +G1 F1754 +G1 X120.814 Y101.612 E.03628 +M204 P1000 +M486 S-1 +G1 E-.64 F2100 +;WIPE_START +G1 F9600;_WIPE +G1 X121.358 Y101.068 E-.16 +;WIPE_END +M107 +;TYPE:Custom +; Filament-specific end gcode +G1 Z11 F720 ; Move print head up +M104 S0 ; turn off temperature +M140 S0 ; turn off heatbed +M107 ; turn off fan +G1 X241 Y170 F3600 ; park +G1 Z33 F300 ; Move print head up +M73 P99 R0 +M73 Q99 S0 +G4 ; wait +M900 K0 ; reset LA +M142 S36 ; reset heatbreak target temp +M84 X Y E ; disable motors +; max_layer_z = 10 +M73 P100 R0 +M73 Q100 S0 +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl","polygon":[[130.000,110.000],[120.000,110.000],[120.000,100.000],[130.000,100.000]]}]} +; filament used [mm] = 279.86 +; filament used [cm3] = 0.67 +; filament used [g] = 0.83 +; filament cost = 0.02 +; total filament used [g] = 0.83 +; total filament cost = 0.02 +; total filament used for wipe tower [g] = 0.00 +; estimated printing time (normal mode) = 7m 52s +; estimated printing time (silent mode) = 7m 52s +; estimated first layer printing time (normal mode) = 50s +; estimated first layer printing time (silent mode) = 50s + +; prusaslicer_config = begin +; arc_fitting = emit_center +; autoemit_temperature_commands = 1 +; avoid_crossing_curled_overhangs = 0 +; avoid_crossing_perimeters = 0 +; avoid_crossing_perimeters_max_detour = 0 +; bed_custom_model = +; bed_custom_texture = +; bed_shape = 0x0,250x0,250x210,0x210 +; bed_temperature = 60 +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n +; between_objects_gcode = +; binary_gcode = 0 +; bottom_fill_pattern = monotonic +; bottom_solid_layers = 4 +; bottom_solid_min_thickness = 0.5 +; bridge_acceleration = 1000 +; bridge_angle = 0 +; bridge_fan_speed = 100 +; bridge_flow_ratio = 1 +; bridge_speed = 25 +; brim_separation = 0.1 +; brim_type = outer_only +; brim_width = 5 +; chamber_minimal_temperature = 0 +; chamber_temperature = 0 +; color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change +; colorprint_heights = +; compatible_printers_condition_cummulative = "printer_model==\"MK4\" and nozzle_diameter[0]==0.4";"printer_model==\"MK4\" and printer_notes!~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6" +; complete_objects = 0 +; cooling = 1 +; cooling_tube_length = 5 +; cooling_tube_retraction = 91.5 +; default_acceleration = 1000 +; default_filament_profile = "Prusament PLA @PG" +; default_print_profile = 0.20mm QUALITY @MK4 0.4 +; deretract_speed = 25 +; disable_fan_first_layers = 1 +; dont_support_bridges = 0 +; draft_shield = disabled +; duplicate_distance = 6 +; elefant_foot_compensation = 0.2 +; enable_dynamic_fan_speeds = 0 +; enable_dynamic_overhang_speeds = 1 +; end_filament_gcode = "; Filament-specific end gcode" +; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM900 K0 ; reset LA\nM142 S36 ; reset heatbreak target temp\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +; external_perimeter_acceleration = 800 +; external_perimeter_extrusion_width = 0.45 +; external_perimeter_speed = 40 +; external_perimeters_first = 0 +; extra_loading_move = -2 +; extra_perimeters = 0 +; extra_perimeters_on_overhangs = 0 +; extruder_clearance_height = 13 +; extruder_clearance_radius = 45 +; extruder_colour = "" +; extruder_offset = 0x0 +; extrusion_axis = E +; extrusion_multiplier = 1 +; extrusion_width = 0.45 +; fan_always_on = 1 +; fan_below_layer_time = 100 +; filament_abrasive = 0 +; filament_colour = #FF8000 +; filament_cooling_final_speed = 3.5 +; filament_cooling_initial_speed = 10 +; filament_cooling_moves = 2 +; filament_cost = 25.4 +; filament_density = 1.24 +; filament_deretract_speed = nil +; filament_diameter = 1.75 +; filament_infill_max_crossing_speed = 0 +; filament_infill_max_speed = 0 +; filament_load_time = 10.5 +; filament_loading_speed = 10 +; filament_loading_speed_start = 50 +; filament_max_volumetric_speed = 15 +; filament_minimal_purge_on_wipe_tower = 15 +; filament_multitool_ramming = 0 +; filament_multitool_ramming_flow = 10 +; filament_multitool_ramming_volume = 10 +; filament_notes = "" +; filament_purge_multiplier = 81.25% +; filament_ramming_parameters = "250 100 40.1613 40.3548 40.4516 40.3548 40.2581| 0.05 40.1483 0.45 40.3419 0.95 40.3419 1.45 40.3419 1.95 40.3419 2.45 40.3419 2.95 40.3419 3.45 40.3419 3.95 40.3419 4.45 40.3419 4.95 40.3419" +; filament_retract_before_travel = nil +; filament_retract_before_wipe = nil +; filament_retract_layer_change = nil +; filament_retract_length = nil +; filament_retract_length_toolchange = nil +; filament_retract_lift = nil +; filament_retract_lift_above = nil +; filament_retract_lift_below = nil +; filament_retract_restart_extra = nil +; filament_retract_restart_extra_toolchange = nil +; filament_retract_speed = nil +; filament_settings_id = "Generic PLA @PG" +; filament_shrinkage_compensation_xy = 0% +; filament_shrinkage_compensation_z = 0% +; filament_soluble = 0 +; filament_spool_weight = 0 +; filament_stamping_distance = 45 +; filament_stamping_loading_speed = 29 +; filament_toolchange_delay = 0 +; filament_travel_lift_before_obstacle = nil +; filament_travel_max_lift = 0.6 +; filament_travel_ramping_lift = 1 +; filament_travel_slope = 1 +; filament_type = PLA +; filament_unload_time = 8.5 +; filament_unloading_speed = 100 +; filament_unloading_speed_start = 100 +; filament_vendor = Generic +; filament_wipe = nil +; fill_angle = 45 +; fill_density = 15% +; fill_pattern = grid +; first_layer_acceleration = 600 +; first_layer_acceleration_over_raft = 0 +; first_layer_bed_temperature = 60 +; first_layer_extrusion_width = 0.5 +; first_layer_height = 0.2 +; first_layer_speed = 20 +; first_layer_speed_over_raft = 30 +; first_layer_temperature = 215 +; full_fan_speed_layer = 3 +; fuzzy_skin = none +; fuzzy_skin_point_dist = 0.8 +; fuzzy_skin_thickness = 0.3 +; gap_fill_enabled = 1 +; gap_fill_speed = 45 +; gcode_comments = 0 +; gcode_flavor = marlin2 +; gcode_label_objects = firmware +; gcode_resolution = 0.0125 +; gcode_substitutions = +; high_current_on_filament_swap = 0 +; host_type = prusalink +; idle_temperature = 70 +; infill_acceleration = 2000 +; infill_anchor = 2 +; infill_anchor_max = 12 +; infill_every_layers = 1 +; infill_extruder = 1 +; infill_extrusion_width = 0.45 +; infill_first = 0 +; infill_overlap = 10% +; infill_speed = 200 +; inherits_cummulative = ;;"Original Prusa MK4 0.4 nozzle" +; interface_shells = 0 +; ironing = 0 +; ironing_flowrate = 15% +; ironing_spacing = 0.1 +; ironing_speed = 15 +; ironing_type = top +; layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +; layer_height = 0.2 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 2500,2500 +; machine_max_acceleration_extruding = 2000,2000 +; machine_max_acceleration_retracting = 1200,1200 +; machine_max_acceleration_travel = 2000,2000 +; machine_max_acceleration_x = 2500,2500 +; machine_max_acceleration_y = 2500,2500 +; machine_max_acceleration_z = 200,200 +; machine_max_feedrate_e = 100,100 +; machine_max_feedrate_x = 200,160 +; machine_max_feedrate_y = 200,160 +; machine_max_feedrate_z = 40,40 +; machine_max_jerk_e = 10,10 +; machine_max_jerk_x = 8,8 +; machine_max_jerk_y = 8,8 +; machine_max_jerk_z = 2,2 +; machine_min_extruding_rate = 0 +; machine_min_travel_rate = 0 +; max_fan_speed = 100 +; max_layer_height = 0.3 +; max_print_height = 220 +; max_print_speed = 200 +; max_volumetric_extrusion_rate_slope_negative = 0 +; max_volumetric_extrusion_rate_slope_positive = 0 +; max_volumetric_speed = 0 +; min_bead_width = 85% +; min_fan_speed = 100 +; min_feature_size = 25% +; min_layer_height = 0.07 +; min_print_speed = 15 +; min_skirt_length = 4 +; mmu_segmented_region_interlocking_depth = 0 +; mmu_segmented_region_max_width = 0 +; multimaterial_purging = 140 +; notes = +; nozzle_diameter = 0.4 +; nozzle_high_flow = 0 +; only_one_perimeter_first_layer = 0 +; only_retract_when_crossing_perimeters = 0 +; ooze_prevention = 0 +; output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +; overhang_fan_speed_0 = 0 +; overhang_fan_speed_1 = 0 +; overhang_fan_speed_2 = 0 +; overhang_fan_speed_3 = 0 +; overhang_speed_0 = 15 +; overhang_speed_1 = 15 +; overhang_speed_2 = 20 +; overhang_speed_3 = 80% +; overhangs = 1 +; parking_pos_retraction = 92 +; pause_print_gcode = M601 +; perimeter_acceleration = 1000 +; perimeter_extruder = 1 +; perimeter_extrusion_width = 0.45 +; perimeter_generator = arachne +; perimeter_speed = 70 +; perimeters = 2 +; physical_printer_settings_id = +; post_process = +; prefer_clockwise_movements = 0 +; print_settings_id = 0.20mm SPEED @MK4 0.4 +; printer_model = MK4 +; printer_notes = Do not remove the keywords below.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK4\nPG +; printer_settings_id = School Prusa MK4 0.4 nozzle +; printer_technology = FFF +; printer_variant = 0.4 +; printer_vendor = +; raft_contact_distance = 0.2 +; raft_expansion = 1.5 +; raft_first_layer_density = 80% +; raft_first_layer_expansion = 3 +; raft_layers = 0 +; remaining_times = 1 +; resolution = 0 +; retract_before_travel = 1.5 +; retract_before_wipe = 80% +; retract_layer_change = 1 +; retract_length = 0.8 +; retract_length_toolchange = 0 +; retract_lift = 0.2 +; retract_lift_above = 0 +; retract_lift_below = 219 +; retract_restart_extra = 0 +; retract_restart_extra_toolchange = 0 +; retract_speed = 35 +; seam_position = aligned +; silent_mode = 1 +; single_extruder_multi_material = 0 +; single_extruder_multi_material_priming = 0 +; skirt_distance = 2 +; skirt_height = 3 +; skirts = 0 +; slice_closing_radius = 0.049 +; slicing_mode = regular +; slowdown_below_layer_time = 8 +; small_perimeter_speed = 35 +; solid_infill_acceleration = 1500 +; solid_infill_below_area = 0 +; solid_infill_every_layers = 0 +; solid_infill_extruder = 1 +; solid_infill_extrusion_width = 0.45 +; solid_infill_speed = 200 +; spiral_vase = 0 +; staggered_inner_seams = 0 +; standby_temperature_delta = -5 +; start_filament_gcode = "M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" +; start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] A{(filament_abrasive[0] ? 1 : 0)} F{(nozzle_high_flow[0] ? 1 : 0)} ; nozzle check\nM115 U6.1.2+7894\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; set extruder temp for bed leveling\nM109 T0 R{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; wait for temp\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; support_material = 0 +; support_material_angle = 0 +; support_material_auto = 1 +; support_material_bottom_contact_distance = 0 +; support_material_bottom_interface_layers = 0 +; support_material_buildplate_only = 0 +; support_material_closing_radius = 2 +; support_material_contact_distance = 0.2 +; support_material_enforce_layers = 0 +; support_material_extruder = 0 +; support_material_extrusion_width = 0.37 +; support_material_interface_contact_loops = 0 +; support_material_interface_extruder = 0 +; support_material_interface_layers = 5 +; support_material_interface_pattern = rectilinear +; support_material_interface_spacing = 0.2 +; support_material_interface_speed = 70% +; support_material_pattern = rectilinear +; support_material_spacing = 2 +; support_material_speed = 50 +; support_material_style = snug +; support_material_synchronize_layers = 0 +; support_material_threshold = 45 +; support_material_with_sheath = 0 +; support_material_xy_spacing = 80% +; support_tree_angle = 40 +; support_tree_angle_slow = 30 +; support_tree_branch_diameter = 2 +; support_tree_branch_diameter_angle = 3 +; support_tree_branch_diameter_double_wall = 3 +; support_tree_branch_distance = 1 +; support_tree_tip_diameter = 0.6 +; support_tree_top_rate = 30% +; temperature = 210 +; template_custom_gcode = +; thick_bridges = 0 +; thin_walls = 0 +; thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 480x240/QOI, 640x480/PNG +; thumbnails_format = PNG +; toolchange_gcode = +; top_fill_pattern = monotoniclines +; top_infill_extrusion_width = 0.42 +; top_one_perimeter_type = none +; top_solid_infill_acceleration = 800 +; top_solid_infill_speed = 40 +; top_solid_layers = 5 +; top_solid_min_thickness = 0.7 +; travel_acceleration = 0 +; travel_lift_before_obstacle = 0 +; travel_max_lift = 1.5 +; travel_ramping_lift = 1 +; travel_slope = 1 +; travel_speed = 200 +; travel_speed_z = 12 +; use_firmware_retraction = 0 +; use_relative_e_distances = 1 +; use_volumetric_e = 0 +; variable_layer_height = 1 +; wall_distribution_count = 1 +; wall_transition_angle = 10 +; wall_transition_filter_deviation = 25% +; wall_transition_length = 100% +; wipe = 1 +; wipe_into_infill = 0 +; wipe_into_objects = 0 +; wipe_tower = 1 +; wipe_tower_acceleration = 0 +; wipe_tower_bridging = 10 +; wipe_tower_brim_width = 2 +; wipe_tower_cone_angle = 0 +; wipe_tower_extra_flow = 100% +; wipe_tower_extra_spacing = 100% +; wipe_tower_extruder = 0 +; wipe_tower_no_sparse_layers = 0 +; wipe_tower_rotation_angle = 0 +; wipe_tower_width = 60 +; wipe_tower_x = 170 +; wipe_tower_y = 140 +; wiping_volumes_matrix = 0 +; wiping_volumes_use_custom_matrix = 0 +; xy_size_compensation = 0 +; z_offset = 0 +; prusaslicer_config = end diff --git a/server/xyz-cali-cube-mini_MK4S.gcode b/server/xyz-cali-cube-mini_MK4S.gcode new file mode 100644 index 00000000..fbb469d6 --- /dev/null +++ b/server/xyz-cali-cube-mini_MK4S.gcode @@ -0,0 +1,3855 @@ +; generated by PrusaSlicer 2.8.1+win64 on 2024-10-28 at 18:14:42 UTC + + +; +; thumbnail_QOI begin 16x16 608 +; cW9pZgAAABAAAAAQBAD+XV1dxf6QYzW/iP6BenQoyaHh/rRmF/7RaQDA/sByI/6WiXz+ZWVlKMb+iG +; E6Nf6/YACcTDXAoC/+sYNV/nd3dyjE/qVkIzX+uV0AEjXD/sR2J/6LhX7+YGBgKMD+a15R/sJnDDX+ +; v2AANcT+wGEANaEv/qOJb/5tbW0o/m9SNP6nVAD+zGcANcKbTaKW/rNaABw1/ppNAP5sUDT+xcXFKA +; j+h0QAp/H+w2IAp/HB/sBgABA1Ev6CQQD+cDgA/q6Sdv65ubn+XV1dCB7Aorb+sVkAHDXBDh3+j0gA +; Hf5zVzv+YGBgKAj+h0QApcM3Hv6ZTQD+yGQAFP6ORwAdo6X+ikUAHf5mSi4owAgeN6OlHsA3/nY7AB +; 3ALB3AGyjACB7AnFwrEh4dwKK2o5UdPhsowP5lWEz+f0kRHlYewR3A/phMAB0C/mw/Ef5hVkwowZ7U +; /nVPKf6VSwDAHsAdOwId/mhIKSjEtYj+w7Kh/o5TGB7AHR7+bzoG/mRPOijE/paWlv7i4uL+mpqa/m +; RgW/56TB0eHf5sPxH+XltXKMOsiP7AwMCxiC6HiCjACJs9KMUAAAAAAAAAAQ== +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 313x173 13832 +; cW9pZgAAATkAAACtBAD+XFxc8H/96aSI/p+fn/7z8/OsiMCSiP60tLT+b29vKP392BnuKP3sf7qI/t +; ra2ibAnIj+09PT/oiIiP5fX18o/f3WGewo/e3+c19M/q1lHf6BYUD+ZmZm/rCwsP75+fkmwIqI/qen +; p/5nZ2co/f3VGeoo/e7+l2Mu/tFpAMCfL/6QYjT+ZmBZ/oWFhf7n5+cmwJmI/sbGxv59fX0o/f3UGe +; go/e7+a15R/rtnETXD/rRmF/5sX1L+bW1t/sDAwCbBgoj+mZmZ/mRkZCj9/dIZ5yj97v6IYTr+0WkA +; xgU7/mBgYP6Xl5f+8PDwJsAy/rq6uv5ycnIo/f3PEf6xsbEZ5Sj97qHh/qVkIzXJMP5lX1i1L/7S0t +; ImwJ2I/tfX1/6NjY0VKP39zLqI/t7e3iYZ4yj97/5zX0z+wmcMNcufLx3+Y2Nj/qqqqv739/cmwI6I +; /q2trf5qamoo/f3K/qKiov719fUmwBnhKP3w/p5kKTXPIv5mX1n+gICA/uLi4ibADP7Ly8v+goKCKP +; 39x7CI/szMzCbCGeAo/e89IP7RaQDRIP5zYEwr/ri4uP79/f0mwIWI/p+fn/5lZWUo/f3E/o+Pj/7u +; 7u4mwxneKP3wOzXUBTv+Z2Ba/pCQkCcmwJeI/sDAwP53d3co/f3Bqoj+ubm5/v7+/ibEGdwo/fCh4f +; 6tZR3+0WkA1zD+bF9S/nFxcf7KysomwAj+29vb/pGRkf5iYmIo/fz+fX19ESbGGdso/fD+emBGBTXZ +; Bf6IYTr+YWFh/qKiov709PQmwJGI/rKysv5ubm4o/fkV/qqqqv74+PgmxxnZKP3xIjXdDTG7Hv7d3d +; 0mwJuI/tHR0f6Ghob+Xl5eVf32s4j+1NTUJskZ1yj98f5zX0z+wmcMNd/+u2cR/oJhQD7+s7OzGybA +; iIj+paWl/mdnZyj99P6Xl5f+8fHxJsoZ1ij98Ts14p8vO/5nYFr+iIiIHCbAmIj+xMTE/nx8fCj98a +; yI/sHBwSbMGdQo/fEYMDXlMP5sX1In/sPDwybAF4GIDv5jY2Mo/e7+g4OD/unp6SbNGdMo/fEINegF +; OxX+m5ubFCbAk4j+uLi4/nFxcSj96wL+r6+vGybMnYj+vLy8GdEo/fINNesw/mVfWLYv/tXV1SbAKv +; 7W1tb+i4uLFSj96BD+3NzcJs2PiP6VlZX+ZGRkmIjQKP3x/nNfTP7CZww17QUdEf6tra3++Pj4JsCM +; iP6rq6v+aWlpKP3m/p+fnwEmzSn+d3d3iIgoGc4o/fL+l2MuNfH+nmQp/mZgWRP+5OTkJsA9/snJyf +; 6AgIAo/eOviDwmzQH+pqam/mZmZijBGc0o/fH+c19M/rtnEf7RaQDzIP5zYEz+a2tr/ru7uxcmwISI +; /p2dnf5lZWWYiP3gGv7t7e0mzf7d3d3+gYGBFZ2IwhnMKP3xOzX2BTsV/pOTk/7v7+8mwJaI/r29vf +; 51dXUo/d0g/rW1tf79/f0mzJqI/re3twk3KMMZyij98aHh/q1lHTXV/q1XAKKmNeD+tGYX/mxfUv5y +; cnL+zs7OJsCdiP7a2toWJCj92r2I/uLi4ibNioj+jo6O/mJiYijFGcko/fH+emBG/spoBjXV/pRKAP +; 5wOAD+h0QA/plNAP7MZwA13wX+gWFAJP6lpaX+9fX1JsA2/rCwsAko/dcG/qenp/739/cmzf7FxcX+ +; dHR0BijGGcco/fIiNdX+v2AA/nw+AJouwB7Aorb+ul0ANeAi/mZfWb0e/t/f3ybAm4j+z8/P/oWFhS +; j91bKI/tHR0SbNk4j+n5+f/mVlZSjIGcYo/fE9/rtnETXV/qBQAB3CNx7B/qdUABw13yD+c2BM/mho +; aP61tbX++/v7JsCHiP6jo6P+ZmZmKP3S/pOTk/7w8PAmzT/+fX19FSjJGcUo/fH+kGI0NdX+v2AA/o +; JBAB3BABKipv6VSwAewf6xWQA14J8vO/5nYFoa/uvr6ybALv7CwsL+enp6g4j9zw3+vb29Js0u/rGx +; sf5paWkoyxnDKP3xGDA11Q4dwv6aTQA1wZ5aDjceDjXiMP5sX1I2/sbGxibAF4CI/pWVlf5jY2Mo/c +; z+gICA/ufn5ybNhoj+iIiIMyjMGcIo/fH+iGE6/tFpANWdW/6ORwAdwaO1/rldADXE/r5gAP6QSQCk +; 1DXjBQgk/p6env7z8/MmwJKI/ra2tjYo/ckz/q2trQwmzED+vr6+/nFxcY2IKM0ZwSj98f6lZCM11T +; E+HcH+lEoANccc/qNSADXlDf5mX1m4Hv7Y2NgmwCoh/omJif5fX18o/ca2iP7Z2dkmzZCI/piYmP5k +; ZGQozxnAKP3wPf7CZww11QT+cDgAwv6tVwA1yf6xWQD+yGQANeX+u2cR/oFhQP5lZWX+r6+vDCbAi4 +; j+qKio/mhoaCj9xP6cnJz+8/PzJs0W/nl5eQYo/f3F/pdjLjXV/r9gAP58PgCaLsH+gkEA/sVjADXK +; /rpdAKXDNeafLyv+ZmBZ/oSEhP7m5uYmwJmI/sfHx/5+fn7+XV1d/cGuiP7FxcUmzZaI/qqqqv5nZ2 +; co/f3F/nNfTP67ZxE11Q4dwiU1zJkfNDXo/rRmF/5zYEwJ/r6+vibBgoj+mpqaEZmI/C3+7OzsJs2B +; iP6CgoL+YWFhKP39xf6QYjQ11Z1b/ohEAB3B/oJBABI1zg416QU7/mBgYP6Wlpb+8PDwJsCViP67u7 +; v+dHR0KPkR/rOzs/78/PwmzJyI/rm5uf5tbW03KP39xKHh/qVkIzXV/q1XAB3CBDXQ/qxWADXrMP5s +; X1IB/tHR0SbAOf7Y2Nj+jY2NFSj2u4j+39/fJs2MiP6QkJD+Y2NjKP39xf5zX0z+wmcMNdUj/nA4AM +; GjtS810Sc17J8v/oFhQP5iYmL+qamp/vb29ibAjoj+rq6u/mtrayjzN/6kpKQQJs3+yMjI/nZ2domI +; KP39xSI11f6/YAD+fD4Ami7BAP7FYwA10g417iL+Zl9Z/n9/f/7h4eG+iMCaiCn+g4ODKPGwiP7Ozs +; 4mzZOI/qKiov5mZmYo/f3FPSA11f6gUAAdwg411P6xWQD+yGQANe4g/nNgTP5paWn+t7e3OSbAhYj+ +; oKCgLyju/pCQkP7v7+8mzSz+fn5+gogo/f3FOzXVEgAdwQASNdUPnXs1758vO/5nYFr+jo6O/u3t7S +; bAl4j+wcHB/nh4eCjrqoj+urq6Js2ZiP60tLT+ampqNyj9/cSh4f6tZR011Q4dwv6aTQA115kfmR81 +; 8TD+bF9S/nBwcP7JyckmwAj+3Nzc/pKSkv5iYmKbiOgX/uXl5SbNiIgaMyj9/cX+emBG/spoBjXUFP +; 6USgAdwaO1/rNaADXZ/qxWADXyBf6IYTr+YWFh/qGhof709PQmwJGIMv5ubm4o5RX+q6urPSbMVf7B +; wcH+cnJyNyj9/cX+nmQpNdX+v2AA/nw+AB3BIRQ12g419A0xuh7+29vbJsCbiP7S0tL+iIiINyjitI +; j+1tbWJs0F/pubm/5lZWUo/f3F/nNfTP7CZww11f6aTQAdwg413P6sVgA19f67ZxH+gmFAL/6ysrIb +; JsCJiP6mpqb+Z2dnKOD+mJiY/vLy8ibNA/57e3sGKP39xTs11f6/YAD+gkEA/nA4AMEA/sVjADXd/r +; FZADX2ny87/mdgWv6Ghob+6enpJsCYiP7FxcX+fX19KN0r/sLCwibNl4j+rKysPij9/cUYMDXVDh3C +; BDXf/rVbAP7IZAA19zD+bF9S/m5ubhMmwBeCiP6YmJj+Y2NjKNr+hYWF/urq6ibNg4gA/mFhYSj9/c +; X+iGE6NdaaLv52OwCdW8A+/rldADXg/r5gADQ1+AU7/mBgYP6ZmZn+8fHxJsCUiP65ubn+cXFxKNcC +; /rCwsP77+/smzCr+u7u7/m9vbzco/f3F/qVkIzXYnVv+jkcAo7U14gEbNfow/mVfWLYv/tTU1CbAnI +; g//oyMjBUo1LmI/t3d3SbNjoj+lJSU/mNjY5qI/f3FPf7CZww1/cP+sVkANfufL/6BYUD+ZGRkCS4m +; wI2I/qysrP5qamoo0v6goKD+9PT0Js3+y8vL/nd3dwYo/f3F/pdjLjX9xf6sVgA1/SL+ZmBZ/oGBgf +; 7j4+MmwJqICwQoz7CICybNAf6lpaX+ZmZmKP39xT3+u2cRNf3GDjX9wCD+c2BM/mtra/66urr+/f39 +; JsCEiP6enp7+ZWVlKMw4/u7u7ibN/tvb2/6AgICAiCj9/cX+bl1M/sxnAKK2/cf+sVkA/shkADXbFP +; 6WSwAFHDXdny/+blpGFf6SkpInJsCWiP6+vr4fKMkv/re3twgmzAz+tra2/mxsbJKIKP39xv5tVDr+ +; lUsA/sNiADX9xv61WwABNdr+v2AA/nw+AJtN/odEAP6ZTQD+yGQANdsS/oJBAP5lTTT+XV1dN7SI/s +; zMzCbACP7a2tol/mFhYSjGvogCJs2KiDj+YmJiKP39yDYeorb+sVkANf3FmR80Ndn+mk0A/nA4AMAr +; HsA3GzXZDh3AEf5dXV3BJP6kpKQQJsCQiP6xsbEYKMMG/qmpqf739/cmzf7ExMT+dHR0Bij9/cn+bV +; Q6HsH+mU0A/shkADX9w55aDjXXEgAdwf6EQgA3HsH+o1IANdadW/6ORwAdwf5lTTQowga9iP7e3t4m +; wJuI/tDQ0P6Ghob+Xl5eKMCyiP7T09MmzZKI/p6eniAo/f3L/m1UOh7Corb+tVsANf3D/qxWADXWDh +; 3CBDUB/p5QAB7AGzXV/rldAD4dwhEoxKqI/rS0tP77+/smwIiI/qSkpC/+lZWV/vDw8CbN/tXV1TkV +; KP39zDYexDgcNf3BJzXUnVsCHcGjtTE1wRwb/shkADXVBB3E/mVNNCjFFf6Kior+6+vrJsAujYgmzS +; 7+sLCw/mlpaSj9/c42HsWn8f6+YAA1/cAnNdMxPh3B/pRKADXb/r9gAP58PgAdxf5lTTQoxn+xiP7F +; xcUmz4WILSQo/f3P/m1UOh7Gorb+sVkA/sxnADX8BQE10QQdwv6tVwA12w4dx/5lTTQoxzP+sLCw/v +; z8/CbMOf69vb3+cHBwjogo/f3QNh7I/plNAP7IZAA1+w/ANc8SHx3B/oJBAP7FYwA12hT+iEQAHcgR +; KMa3iDsmzZCI/peXl/5kZGQo/f3S/m1UOh7Jorb+sVkANfqZHxs1zv6nVAAdwv6gUAA12y8dyv5lTT +; T+XV1dxf6dnZ0yJs3+zs7O/nl5eQZA/f3TNh7L/plNABw1+Q41zBQhHcEAEjXbIx3LESjDroj+x8fH +; Js2ViP6oqKj+Z2dnKP391Tb+h0QAzDc0NfgnNcsvHcIENdsSHx3MESjCC/7t7e0mzSr+sbGx/mhoaC +; j9/dY2Hs4OHDX2JzXKIx3Bo7X+s1oANdslHc4RKMAg/rS0tDkmz5WI/ry8vP50dHQo/f3VNh7PK/7D +; YgA19Sc1yBIfHcEA/stmADXaEgAdzxH+XV1dDP7g4OAmzYyI/p6env7Q0NAmwJ2I/tnZ2f6Ojo4kKP +; 390zYe0KK2BTX0Bf7IZAA1xv6gUAAdwg412w4d0f5nTjb+paWl/vb29ibN/sfHx/51dXUGo4j+qKio +; HybAj4j+r6+vCSj9/dL+bVQ6/odEANL+mU0A/shkADXyDzQ1xBIAHcEAEjXaFCMd0v6zm4ImzZOI/q +; Ghof5mZmYowQa/iBUmwJqI/s7Ozv6EhIQo/f3RNh7TNzQ18QEbNcMOHcIENdsS/nw+AB3T/sCoj/7/ +; ///M/tfX1xeCiCjErIj+tra2OSbAhogkLyj9/c82HtX+p1QA/sxnADXw/rBZADXBFP6ORwAdwT7+s1 +; oANdsEHdX+wKiP/v///8qZiP6ysrL+ampqlIgoxhX+jY2NGCbALv7BwcH+eXl5KP39zjYe1qfx/sNi +; ADXv/rBZADXA/rldAD4dwf6USgA12xIAHdb+wKiP/v///8mHiAszm4jJN7KI/sjIyCbAQICI/pOTkz +; ObiP39zDYe16K2/rFZABw17f6wWAA1/ppNAB3C/q1XADXbDh3Y/sCoj/7////HCP7AwMD+cnJyjIgo +; zKSI/qCgoDImwJKI/rS0tP5vb28o/f3L/m1UOh7Z/plNAP7IZAClw+wQ/nw+AB3BAP7FYwA12p1b/o +; 5HAB3Z/sCoj/7////GkYj+mpqa/mVlZSjPN7qI/tra2ibAnIj+0tLS/oiIiAYo/f3JNh7aorYbNev+ +; rFYAHcElNdsQPh3a/sCoj/7////F/tHR0f56enoGKNKpiP6xsbH++fn5JsCKiP6np6f+Z2dnKP39yD +; Ye3P6jUgD+zGcANen+sVkAHQD+v2AANdv+lEoAHdz+wKiP/v///8OWiP6srKw+KNUG/oWFhf7o6Ogm +; wJmI/sbGxv59fX0o/f3HNh7dK/66XQA16BIENdsSHx3No6Udzf7AqI/+////woKI/oSEhP5hYWEo13 +; +viP7AwMAmwYGI/pmZmf5kZGQo/f3FNh7eorYFHKK25p1bNdslHc7+hUIA/p9QAB3N/sCoj/7////A +; Kv67u7v+bm5ukIgo2qOI/piYmP7x8fEmwDL+ubm5/nJycij9/cQ2HuD+mU0A/shkADX9wzP+gkEA/n +; A4AM4i/sNiAP56PQAdzf7AqI/+////jYj+kpKS/mNjY5qI3Te2iP7S0tImwDn+19fX/o2NjRUo/f3C +; Nh7horYFNf3B/q1XAB3O/oBAAP6zWgD+w2IA/qRSAB3O/sCoj/7Kysr+d3d3BkDgAv6rq6v+9/f3qI +; jACf6tra3+ampqKP39wTYe4/6ZTQAcorb7nVv+lEoAHc4j/sNiAMH+ej0AHc7+gGdP/mZmZpeI4wb+ +; gICA/uPj4ybAmoga/oKCgij9/cA2HuQ3NP7RaQD5Eh8dzjv+w2IAwf6pVQAdz/5mTTX+XV1d5iv+ub +; m5/v39/SbAhYj+n5+fICj9/DYe5v6nVAAcNfYEHc+jpf7DYgDCFB3P/mVNNP5dXV3nFf6QkJD+7u7u +; JsCXiP6/v7/+d3d3KP37Nh7np/H+w2IANfMSAB3Q/qRSACjBGR3QEf5dXV3of7OIGv7////ACAr+kZ +; GR/mJiYij9+TYe6KK2BRw18A4d0aOl/sNiAML+gEAAHdAR/l1dXeok/qOjo/709PQmwJGI/rKysv5t +; bW0o/fg2Hur+mU0A/shkAKXD7Z1b/o5HAB3S/qRSAP7DYgDBEB3REf5dXV3rf72I/t3d3SbAm4j+0N +; DQ/oaGhjdV/fY2Huuitv61WwA16/65XQD+djsAHdKjpf7DYgDC/oBAAB3REf5dXV3tqoj+s7Oz/vr6 +; +ibAiIj+paWl/mdnZyj99TYe7f6jUgAcorboBB3U/qRSAP7DYgDBEB3SEf5dXV3uFS3+6urqJsCYiP +; 7ExMQqKP30Nh7up/H+ul0ANeUS/nw+AB3Uo6X+w2IAwv6KRQAd0hH+XV1d73+wiP7Dw8MmwFWBiP6X +; l5f+Y2NjKP3yNh7vorb+sVkAHDXi/qdUAB3W/p9QAP7DYgDBEB3TEf5dXV3xFf6bm5v+8vLyJsAj/r +; i4uP5wcHAo/fE2HvH+mU0A/shkADXfFP6IRAAd1jv+w2IAwiwd0xH+XV1d8n+3iD8mwJyIMP6Li4v+ +; X19fKP3vNh7yorb+sVkANd3+rVcAHdgi/sNiAMGbPR3UEf5dXV30p4j+rq6u/vj4+CbAjIj+qqqq/m +; lpaSj97jYe9P6ZTQD+zGcANdr+lEoAHdg7/sNiAML+j0gAHdT+ZU00/l1dXfUGE/7l5eUmwD08/oCA +; gCj97TYe9Tc0/tFpANcSHx3ZIv7DYgDBMR3VEf5dXV32f62I/ry8vBcmwIOI/pycnP5lZWUo/es2Ht +; Gitg7+vmAA/qxWADce3w4cNdT+oFAAHdo7/sNiAMIKHdUR/l1dXfgV/pOTk/7v7+8mwJaI/r29vf51 +; dXUo/er+bVQ6Hs/+nlAA/sNiADXCD/6VSwAe3ysoNdH+v2AAAB3bASjBnWsd1hH+XV1d+X+1iAcmwJ +; 2I/tnZ2f6Pj48kKP3oNh7PorYFNcMcDh7fNwU1zw4d3Dv+w2IAwv6PSAAd1hH+XV1d+yT+p6en/vX1 +; 9SbAj4j+sLCw/mxsbCj95zYe0QH+yGQANcMP/pBJAB7f/plNAP7IZAA1yxT+jkcAHd3+mU0A/sNiAM +; Gdax3XEf5dXV38Br6I/uDg4CbAGwf+hYWFKP3mNh7SNzQ1wxwfHt83NDXJMf52OwAd3Tv+w2IAwv6P +; SAAd1xH+XV1d/cCriP61tbUqJsCHiP6ioqL+ZmZmKP3kNh7UDhw1w/6xWQA3Ht8OHDXGBB3fAf7DYg +; DBnWsd2BH+XV1d/cGjiBr+7OzsJsAu/sLCwv55eXko/eM2HtUr/sNiADXDKCse3ysoNcP+v2AA/oJB +; AB3fOzEowf6PSAAd2BH+XV1d/cJ/soj+xsbGJsAX/t3d3f6UlJT+YmJim4j94TYe1qK2/rFZABw1wh +; wOHt83BRw1wA4d4QH+w2IAwZ1rHdkR/l1dXf3EJP6fn5/+8/PzJsCSiBD+b29vKP3g/m1UOh7YAf7I +; ZAA1wTH+ej0AHuH+mU0A/sJhAP6IRAAd2UYdxTsxpdPB/o9IAB3ZEf5dXV39xX+5iCwmwCr+09PT/o +; mJif5fX18o/d42Htmitv61WwA1BB2iph7imi4d251LHcQK/sNiAMGdax3aEf5dXV39x6iI/rCwsP75 +; +fkmwIqI/qioqP5oaGgo/d02HtubPR3AMx7imi4d3DlCHcIxpdPBIx3aEf5dXV39yAb+hISE/ufn5y +; bAPf7Hx8f+fn5+KP3cNv6HRADb/nc8AB3AMx7iHB3dnlqcbKXDHf6PSAD+w2IAwZ1rHdsR/l1dXf3J +; f66I/r+/v/7////Bgoj+mpqa/mRkZCj92jb+h0QA2wYdwDMe4hwd3hKbTRL+s1oA/sNiAMEBHdv+ZU +; 00/l1dXf3LFf6Wlpb+8PDwJsCViP66urr+c3NzKP3ZNv6HRADbBh3AMx7iHB3hIgqjpQod3BH+XV1d +; /cw3toj+0dHRJsCdiP7Y2Nj+jY2NFSj91zYe2wacXMAzHuIcHeL+hUIA/rldAP6ZTQAd3BEo/c6liP +; 6qqqr+9vb2JsCOiP6tra06KP3WNh7bBh3AoqYex6fx/rpdAP6MRgAe1hwd46OlwB3cESj9z6KI/n9/ +; f/7i4uK9iMAM/szMzCj91jYe2/53PAAdwKKmHsb+sVkANcCeWv6eUAAe1ZouHf3FESj90a2I/re3tz +; kmwCj91jYe2wYdwDMexDf+vmAANcMFNx7THB39xREo/dIVFv7u7u4mKP3WNh7bBh3AMx7Fp/H+w2IA +; NcP+rFYAHtMcHf3FEf5dXV3903+ziAso/dY2HtsGHcAzHsaitgX+zGcANcEc/pBJAB7Smi4d/cURKP +; 3VpIgo/dY2HtsGHcAzHsgB/shkADXBNB7SHB3dNx3jESj9/e42HtsGHcAzHsk3/rVbADXB/plNAB7R +; HB3cp/H+u14A/qhUAP51OgAd4f5lTTQo/f3u/m1UOh7bBh3AMx7L/qNSAP7MZwA1Dzce0JouHdz+pF +; IA/rxeADyeWv6DQQAd4BEo/f3uNh7bBh3AMx7MKzQ1/qdUAB7QHB3bKv69XwA/wDz+eT0AHeD+ZU00 +; KP397jYe2wYdwDMezTcF/shkADcezxwd2/6mUwAHwD8lHc+jtcAdzhEo/f3uNh7b/nc8AB3AMx7P/p +; lNAA4ezxwd2qXT/r9gAArAByodzv6USgA1Mf58PgAdzREo/f3uNh7bBh3AMx7iHB3aOP7BYQBWCpkP +; /nA4AM7+rVcANcGdW/6aTQAdzBEo/f3uNh7bBh3AMx7iHB3ZpdP+wWEAwVoC/nA4AMwA/sVjADXDEj +; 4dyxEo/f3uNh7bBh3AoqYe4hwd2Tj+wWEAwT/+cDgAzA41xAQdzREo/f3u/mdXRv6ERgYe2gYdwDMe +; 4hwd2KOl/sFhAMIC/nA4AMoAEjXDEgAdzv5iVEYo/f3v/mVYTP56TB0e2QYdwDMe4hwd2Dj+wWEAwT +; /+cDgAyx8QNcIOHc7+bD8R/l9YUSj9/fGe1P5yUC4JHtcGnFzAMx7iHB3XO/7BYQDCAv5wOADMPiX+ +; y2YAwAIdzv5mSi4o/f31B/5/SREe1gYdwDMe4hwd1zj+wWEAwT/+cDgAzyE+Hc0r/mFWTCj9/fc9/n +; VPKR7VBh3AMx7iHB3WO/7BYQDC/pNKAP5wOADf/mhIKSj9/fsH/oJHDB7TBh3AMx7iHB3WOP7BYQDB +; P/5wOADeolz+ZE86KP39/T0qHtIGHcAzNx7hHB3VO/7BYQDC/phMAP5wOADdKwEo/f39wv5vUjQJHt +; AGHSM1D/6QSQAe4Bwd1f6eTwD+wWEAwT/+ej0Amz3cESj9/f3FMBYezwYvNcGeWv6nVAAe35ouHdQ7 +; /sFhAMI4/nA4ANsr/mFWTCj9/f3HPT4JHs0g/sxnADXDDzce3ZouHdT+nk8A/sFhAMIUmz3aNCj9/f +; 3LBzIezTf+ul0ANcOeWv6eUAAe3JouHdM7/sFhAMI4/nA4ANmiXP5jUUAo/eS6iIeIKP3jPf53TSMe +; zv6nVAD+zGcANcMFHtuaLh3TOf7BYQDCFJs92P5rQRf+XltXKP3l/vLy8v6enp4kKP3kNgkezafx/s +; NiADXCKB7bmi4d0jv+t1wApdPBOP5wOADYEf5dXV395/7////A/sbGxv5vb2+PiCj94zAqHs2itgU1 +; wZkfHtscHdI5/sFhAMIUmz3W/mw/EQGlH/3o/vf39ybAjYj+i4uL/mBgYCj94z0+/oRGBh7N/plNAP +; 7IZAA1mR8e2xwd0Tv+t1wApdPB/qhUAP5wOADW/mhIKf5dXV396v6ioqL+5ubmuYjAnIj+tbW1DSj9 +; 5AcyHs03/rpdAKXDHtscHdH+jkcA/sFhAML+f0AA/nA4ANSiXP5hVkz+XV1d/ez+hISE/s7Ozv76+v +; omwIGI/n19fYKIKP3jPT4ezv6ZTQAe2xwd0f63XACl08H+slkA/nA4ANT+aUYjKP3vr4j+sLCw/u7u +; 7rGIwJaI/qampiQo/eT+Z1dG/oRGBh7qHB3QAv7BYQDC/olFAP5wOADT/mRPOij98ST+j4+P/tra2v +; 78/PwmwP7Ozs7+cnJyjIgo/eM9/npMHR7pHB3Q/rdcAKXTwSb+cDgA0iv+X1hRKP30uIj+vb29/vX1 +; 9aqIwJCI/pOTk/5gYGAo/eT+b1I0CR7nHB3P/o5HAP7BYQDCAv5wOADR/mZKLij996iIGf7i4uImwF +; X+u7u7/mtrazco/eMw/n9JER7mHB3P/sJhAKGXn2nAmz3+cDgA0P5sPxEaKP36/oCAgP7Jycn++Pj4 +; p4jAhoj+goKCBij9457UPh7lHB3O/pRKAP7RaQBWnEydSwL+cDgAz/5oSCko/f2siP6rq6v+6+vrJs +; CZiBj+ZGRkKP3jrYj+uqmY/pZbIP6HRADjmi4dzv7FYwA1wf67XgAdzg7+Y1FAKP39wQb+i4uL/tbW +; 1v77+/smwD/+dXV1Nyj94P6GhoY6JpTE/rSKYB7iHB3N/pRKADXBJR3O/mtBF/5eW1co/f3EtIj+uL +; i4/vLy8ibAkoj+m5ubFSj93RH+sbGxKibC/tK5n/6OUBAe4JouHc3+xWMANf6/YAAAHc7+ZU00KP39 +; x6aI/peXl/7f398XJsD+w8PD/m5ubpCIKP3auoj+3t7eJsX+6NzP/qVzQB7fHB3M/o5HADX+p1QAHc +; 7+bD8R/l9YUSj9/cq+iP7ExMT+9/f3JsCLiP6IiIgVKP3Y/qGhof719fUmx5TE/ruWcB8e3RwdzP65 +; XQD+lEoAHc40KP39zaqI/qWlpf7n5+cmwJuI/rOzs/5nZ2co/dWwiP7Ly8smy/7h0L/+llsg/odEAN +; wcHcum4sAdzaJc/mJURij9/c9//oeHh/7Q0NAbJsD+3d3dKjdV/dL+jo6O/u7u7ibN/tTQzP6AWjT+ +; h0QA2xwd2wMo/f3TsYj+srKy/vDw8CbAlYj+o6Oj/mFhYSj9z6mIPRcmzAz+tra2/mtrazf+Z1dGCR +; 7ZHB3a/mRPOij9/dWliP6RkZH+29vb/v39/SbAGv5xcXE3KP3Mv4j+4+PjJs2JiP6MjIwzKMH+YlpR +; /npMHR7YHB3Y/mw/Ef5fWFEo/f3Yuoj+v7+//vb29qmIwI+I/pCQkP5gYGAo/ckV/qmpqf739/cmzB +; f+w8PD/nR0dIuIKMOe1P5vUjQJHtaaLh3X/mZKLij9/duoiP6fn5/+5OTkJsBA/ri4uP5qamoo/cey +; iP7T09MmzZKI/p2dnSCYiMcwFh7VHB3V/mw/Ef5hVkwo/f3eE/7Ly8sMJsAC/oCAgP5fX18o/cT+lp +; aWFCbN/tTU1DkVKMme1P51Tyke1Bwd1P5oSCko/f3hOv6tra3+7e3tJsAu/qqqqv5jY2Mo/cGsiP7A +; wMAmzR/+r6+vHCjNB/6CRwwe0pouHdIO/mRPOij9/eMV/o2Njf7X19f+/Pz8JsD+0tLSATco/BP+6e +; npJs2EiP6Hh4ckKM89Kv6HRADRmi4d0f5sPxH+X1hRKP395rWI/rq6uv7z8/MmwAX+mJiYFSj5M/6u +; rq7++vr6Jsw5/r29vf5wcHA3KNL+b1I0CR7PHJo+0P5lTTQo/f3pp4j+mZmZ/uHh4SbBNf5tbW03KP +; a3iAomzZCIPxEo1TAWHs4cHc4r/mFWTCj9/ez+fX19/sbGxv74+PgmwA3+hYWFBij0/p6enjImzf7N +; zc3+eHh4BijXntQ+CR7MHB3NNCj9/e+qiP6np6f+6enpJsAM/rGxsS8o8a+I/sjIyCbNlYg+/mdnZy +; jbB/6CRwwey5ouHcuiXP5jUUAo/f3xBv6IiIgD/vv7+ybAO/54eHiGiCju/ouLi/7t7e0mzf7e3t7+ +; gYGBgIgo3Z7U/ndNIx7KHB3K/mpEHf5eW1co/f30soj+tLS0/vHx8SbAlIj+oKCgJCjrIAH+/f39Js +; wb/ri4uP5tbW2RiCjg/m1UOv6ERgYeyBwdyf5lTTQo/f33pYj+lJSU/t3d3QgmwP7IyMgFN1XovYj+ +; 4eHhJs2LiP6Pj4/+Y2NjKOMw/npMHR7HHB3H/mw/Ef5fWFEo/f36vIj+wcHBLibAjoj+jY2N/mBgYC +; jlN/6mpqYfJs0P/nV1dQYo5Z7U/nVPKQkexRwdxv5mSi4o/f39qYj+oaGh/uXl5SbAOf62trb+aWlp +; KOOxiP7Q0NAmzZOI/qCgoP5lZWUo6Qf+gkcMHsSaLh3EpR/+YVZMKP39/cL+hISE/s7Ozv75+fkmwI +; GI/n5+fgYo4P6SkpL+8PDwJs3+19fX/n19fYOIKOs9Ph7DHB3D/mlGIyj9/f3Fr4j+r6+v/u7u7ibA +; loj+p6enMyjdq4j+vb29Js2ZiP6ysrL+ampqKO/+Z1dG/oRGBh7BHB3C/mRPOij9/f3HpIj+jo6O/t +; nZ2TkmwP7Pz8/+c3NzNyja/n9/f/7m5ua5iM2GiP6JiYkzKPGe1CoewBwdwP5sPxEBKP39/cq3iP68 +; vLz+9PT0JsCQiCEVKNck/qysrAwmzED+v7+//nFxcTco9P5vUjT+hEYGHB0RKP39/c2niP6bm5v+4u +; LivYjAVf69vb3+bGxsNyjUtYj+2NjYJs0F/pmZmf5lZWUo9zD+c0gdGij9/f3Q/n9/fy3++Pj4p4jA +; L/6Dg4MGKNIK/vLy8ibNJf56enoGKP39/f3QrIj+qqqq/urq6ibAPf6urq4gKM+tiP7ExMQmzRD+q6 +; ur/mdnZyj9/f3eAAAAAAAAAAE= +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 440x240 20680 +; cW9pZgAAAbgAAADwBAD+XFxc/cl//f3TpIj+h4eH/tzc3P7////CjYj+vr6+/oSEhCQo/f3911X9xy +; j9/daiiLCI/ru7u/74+PgmwZqI/tTU1P6enp7+aWlpKP39/dYZ/cUo/f3ZofKlHv6VlZX+6enpJsFV +; iIj+tbW1/nx8fIKIKP39/dQZ/cMo/f3ZoeH+rWUdoeH+a15R/mBgYLeI/srKyv78/PwmwZaI/svLy/ +; 6Tk5P+ZGRkKP39/dMZ/cEo/f3a/oFhQP7KaAah4cAF/ohhOjeqiP6jo6P+8vLyJsE5g4j+rKys/nR0 +; dCj9/f3RoogZ/Sj9/dv+pWQjNcQNGCT+goKC/tfX1ybCkIj+wsLC/omJif5iYmIo/f39zq2I/r29vR +; n7KP392/5zX0z+wmcMNcb+u2cRHQauiP60tLT+9vb2JsGbiA7+oaGh/mxsbCj9/f3M/oWFhf7f398m +; Gfko/f3c/pdjLjXJBf6QYjQiM/6Pj4/+5OTkJsIc/rm5uf5/f3+AiCj9/f3IqIj+qqqqEKqIwBn4KP +; 392wP+u2cRNcwwAxWziP7ExMT++/v7JsEf/s7Ozv6YmJj+ZmZmKP39/cYu/tHR0SbCGfYo/f3c/ohh +; OjXPBTs3L/6dnZ3+7u7uJsFAhoj+sLCwLoaI/f39w6SI/pmZmTYmwxn0KP393KHhDTXSMBgVvIg0Js +; Ij/sbGxv6NjY3+Y2NjKP39/cCwiP7Dw8MqJsQZ8ij9/d09EjXUny/+gWFAN62I/qysrP709PQmwSqA +; iP6mpqb+b29vKP39/P6Li4v+5eXlJsYZ8Sj9/d3+nmQpNdgi/mVeWCT+ioqK/t/f3ybCjIj+vb29/o +; KCghUo/f34qoj+srKy/vf39ybHGe8o/f3dPf67ZxE12iA9/l9fX7GI/r6+vv75+fkmwZmI/tLS0v6c +; nJz+aWlpKP399v59fX0OJsn+XFxc7Sj9/d47Nd2fLzsiAv6Xl5f+6urqJsFAiIj+tLS0/nt7ezco/f +; 3yAv6fn5/+8vLyJsoZ7Cj9/d2h4f6tZR014DD+a15RFbmI/szMzAgmwZWI/snJyf6SkpL+ZGRkKP39 +; 8LWIPAgmyxnqKP393v56YEYFNeIF/ohhOjcc/qWlpf7z8/MmwZyIg4j+qqqq/nNzcyj9/e1//pGRkf +; 7p6ekmzRnoKP393/6eZCk15g3+ZV5YJP6EhIT+2dnZJsIn/sHBwR4kKP396qyI/rq6uv75+fkmzhnn +; KP393v5zX0wSNej+u2cRHf5fX1+viP63t7f+9/f3JsEM/tbW1v6goKD+a2trKP396P6Dg4P+3d3dJt +; AZ5X/9/d87NesFO/5lXlgzA/7m5uYmwVWKiP64uLj+fn5+Bij9/eSoiP6np6cBJtEZ4yj9/d8YMDXu +; MP5rXlH+YGBgtYgPKibBH/7Nzc3+lpaW/mZmZij9/eIQBybTGeIo/f3fCDXxBTs3qYj+n5+f/vDw8C +; bBnYiFiP6urq7+dnZ2KP393xUO/u3t7SbTARngKP394P6lZCM19DD+ZF5Xoy8XISbCBf7ExMQaMyj9 +; /dyviAT++vr6JtOAiP6QkJAZ3yj9/d/+c19MEjX2ny8d/l9fXwn+r6+v/vX19SbBGyz+pKSk/m5ubi +; j9/dr+iYmJ/uPj4ybTmYj+wMDA/nl5eTMZ3Sj9/eD+l2Mu/tFpAPr+nmQp/mVeWDP+jIyM/uHh4SbC +; i4j+u7u7/oGBgRUo/f3WLzb+9vb2JtMr/p2dnf5qamoGQBncKP393/5zX0z+u2cRNfwgPQayiP7Bwc +; EbJsEu/tHR0f6ampr+aGhoKP391L6IISbTQDj+gYGBg4gowRnaKP394P6QYjQ1/cEFOzeniP6ampr+ +; 7OzsJsEIh4j+srKyDCj9/dEz/p2dnf7x8fEm05OI/qqqqv5wcHAVnYjCGdko/f3fGAA1/cQw/mxfUh +; W6iAf+/v7+JsGUiP7IyMgl/mRkZCj9/c4FDzkm0/7a2toaLzcowxnXKP394P56YEb+ymgGNf3GBR03 +; rIj+qKioMibBnIiCiA3+cXFxjIj9/cwW/ujo6CbTLv65ubn+dnZ2i4goxRnWKP394P6eZCk1/coi/m +; VeWCQeCibCjoj+v7+//oWFhSQo/f3Iq4j+tra2/vj4+KeI04eI/piYmBw3KMYZ1Sj9/d/+c19M/rtn +; ETX9zCA9BrCI/rq6uv74+PgmwZqIIf6enp4rKP39xv6AgIA7JtMq/sjIyP59fX2GiCjIGdMo/f3g/p +; BiNP7RaQD9zwU7IjP+lJSU/ujo6CbBFy8f/nx8fIKIKP39whH+pKSkMibTkIj+paWl/m1tbQYoyRnS +; KP3936HhMDXemi7+plMA/sxnADXtMAMVtoj+ycnJ/vz8/CbBEP7Ly8sh/mVlZSj9/cC3iP7MzMwXJt +; P+1dXV/oaGhoCIKMsZ0Cj9/eD+iGE6Nd/+rVcA/nA4AKfx/pVLAP66XQA17QUIN6qI/qKiov7x8fEm +; wTmEiP6srKz+dXV1KP37oogh/uzs7CbTloj+s7OzASQozBnPKP394P6lZCM13/6USgAdwCqlw6K2/r +; FZABw17f6tZR0YJAT+1tbWJsI2/sPDw/6JiYn+YmJiKP34rYj+vr6+/vr6+ibTgoj+k5OT/mhoaJaI +; KM0Zzij9/d/+c19M/sJnDDXe/r9gAP58PgAdwSoewf6ZTQD+yGQANe3+u2cR/oFhQAauiP6ysrL+9v +; b2JsEb/tjY2P6ioqIYKP32/oeHhxUm0wwi/np6egIozxnMKP394P6XYy413/6aTQD+cDgAwyqlw8Ki +; tgU17Z8vK/5lXlikHv6Ojo7+4+PjJsKKiP66urr+gICABij98qmI/qysrBAm042I/p+fnzqUiCjQGc +; so/f3fPSD+0WkA3hL+gkEAHcMA/rhdAP6QSQAew/6eUAA17v60Zhc9/mBgYLKI/sPDw/76+vomwZiI +; /s/Pz/6YmJg+KP3wu4j+0tLSJtNA/tDQ0BODiCjSGcoo/f3fOzXf/qdUAP5wOADE/qBQADXAnlr+o1 +; IAHsM17wU7/l5eXiD+nJyc/u3t7SbBCAL+sbGxPSj97aSI/pqamjYm05WI/qysrP5xcXEVKNNVySj9 +; /d6h4f6lZCM13p1b/o5HAB3DAP6/YAA1wzT+jEYAHsH+yGQANfAw/mxfUhW8iP7R0dEmwpOI/sfHxw +; f+Y2NjKP3qJzH+/Pz8JtP+3Nzc/o2NjT6XiCjUVcco/f3f/nNfTP7CZww13v65XQD+djsAHcMENcYB +; H/6HRADA/rpdADXxBf6BYUA3Ov6rq6v+9PT0JsGciAr+pqam/nBwcCj96P6MjIz+5ubmJtOZiP68vL +; z+d3d3MyjWGcYo/f3f/p5kKTXfBP5wOADDPv6zWgA1yBz+rFYA/oxGAP61WwA18yL+ZV5YJDz+3t7e +; JsKNiP69vb3+g4ODFSj95KuIMv739/cm04mIO/5paWmViCjXGcUo/f3e/nNfTP67ZxE13v6/YAD+fD +; 4AHcP+iEQAFDXLNA419CA9BgUIDCbBmYj+09PT/p2dnRyUiP3i/n5+fv7Y2Ngm0yoaF4aIKNkZwyj9 +; /d/+kGI0Nd8O/nA4AMQONc7+mU0ANfWfLzv+ZV5YAv6Wlpb+6urqJsFVL/60tLT+e3t7Nyj93gL+oa +; GhIybTkoj+pqam/m9vbwYo2hnCKP393hj+rWUdNd6dWyEdw/6CQQD+v2AANc/+nlAANfcw/mteURW4 +; iBr+/f39JsGViP7Kysr+kpKSESj93LWICwgm0/7X19f+iIiI/mZmZjco2xnBKP393v56YEYFNd7+rV +; cAHcQENdEfNfgF/ohhOjeqiP6kpKT+8vLyJsGdiIOIOv5zc3Mo/dk3Aysm05eI/rW1tf51dXWMiCjd +; GcAo/f3e/p5kKTXf/pRKAB3DPjE10v6nVAD+yGQANfn+pWQj/mVeWCT+g4OD/tjY2CbCj4j+wcHB/o +; iIiCQo/datiP67u7sMJtOEiP6VlZX+aGhoNyjeGSj9/d3+c19M/sJnDDXe/r9gAP58PgCaLsMjNdT+ +; sVkANDX6IP6BYUD+X19fr4j+tbW1LibBDP7W1tb+oaGh/mtrayj91P6EhIT+3d3dJtObiP7FxcX+e3 +; t7iIiaiP39/cE7Nd8l/nA4AMQvNdX+vmAA/qxWADX7ny87/mVeWDP+kZGR/ubm5ibBF4qI/ri4uP5/ +; f38GKP3QqIj+qamp/vT09KuI046IJP5sbGwGKP39/cCh4f67ZxE13hL+gkEAHcMA/sVjADXWm03+o1 +; IANf3+tGYX/mteURW0iP7Gxsb++/v7JsGXiP7Ozs7+l5eX/mZmZij9zrmI/tDQ0CbTF/7S0tIxgYgo +; /f39wQg13/6nVAAdxP6gUAA12Tg1/cAFOzcv/p6env7v7+8mwTmGiP6vr68fKP3LFf6YmJj+7u7uJt +; OViDb+cnJyFSj9/f3B/qVkIzXeFP6ORwD+cDgAwwASNdr+mU0ANf3CMBijL7yI/tPT0ybCkoj+xcXF +; Kf5jY2Mo/ciwiAQqJtP+3t7e/pCQkP5nZ2eXiCj9/f3A/nNfTP7CZww13v65XQD+djsAHcP+mk0ANd +; z+nlAA/shkADX9wgX+gWFABgn+rq6u/vX19SbBG4CI/qWlpf5vb28o/cb+ioqK/uTk5CbTmYj+v7+/ +; /nh4eIqIKP39/cH+l2MuNd8E/nA4AMM+/q1XADXd/rFZAKfhNf3E/p5kKf5lXlgz/ouLi/7g4OC/iM +; KMiP68vLwT/mBgYCj9wqqI/rCwsP729vYm04uI/pycnP5qamoGKP39/cD+c19M/rtnETXe/r9gAP6C +; QQAdwwD+xWMANd7+tVsAorY1/cUgPQayiP7AwMAMJsGZiP7R0dH+m5ub/mhoaCj9wL+IPybTnYj+zc +; 3N/oCAgISIKP39/cH+kGI0/tFpAN8OHcQONeCZHw41/cafLzs3Ef6ZmZk6JsFAiIj+s7Oz/np6eoOI +; +6WI/p6env7x8fEm05OIHP5wcHAVKP39/cCh4f6tZR013p1bIR3D/oJBABI14Z5a/qNSADX9yDD+bF +; 9SFRv+zc3NFybBlIj+yMjI/pGRkREo+AX+x8fHCCbT/tnZ2Qv+ZmZmmIgo/f39wP56YEb+ymgGNd7+ +; rVcAHcQENeT+nlAANf3JBf6IYTo3q4j+p6enMibBKoKI/qmpqf5ycnKLiPYl/ujo6CbTl4j+uLi4/n +; Z2diQo/f39wf6eZCk13/6USgAdw6O1/rNaADXl/qNSADX9y/6lZCP+ZV5YJP6Ghob+29vbJsKOiP7A +; wMAPJCjyrIg9/vj4+CbTIP6Xl5ccNyj9/f3A/nNfTP7CZwz+0WkA3v6/YAD+fD4AHcMhFDXmOAE1/c +; v+u2cRLQaviP65ubn++Pj4JsGaiP7V1dX+n5+fKyjwEwom0yr+yMjI/nx8fAIo/f39wTs13/6gUAAd +; xP6nVAA16P6xWQCk1DX9zJ8vOyIz/pOTk/7o6OgmwReJiP63t7f+fX19NyjsEf6lpaUyJtOPiP6kpK +; T+bW1tkogo/f39wKHh/rRmFzXe/r9gAAAdwwD+xWMANek0nlo1/c4w/mteURW2iC3+/Pz8JsGWiP7M +; zMz+lZWV/mVlZSjqt4j+zc3NJtT+1NTU/oWFhYGIKP39/cH+iGE6Nd8OHcQENeuZHw41/c8FOzepiP +; 6hoaH+8fHxJsE5hYj+ra2t/nV1dYiI5wb+lpaW/uzs7CbTloj+sbGx/nNzcxUo/f39wf6lZCM13p1b +; Ix3Do7UxNe3+nlAANf3R/rRmF/5kXlejL7+I/tXV1SbCkYj+w8PDC/5iYmIo5K6I/r+/v/76+vqliN +; OBiP6SkpL+aGhoNyj9/f3A/nNfTP7CZwz+0WkA3/6tVwA+HcIjNe8fNf3Sny/+gWFABq2I/rGxsf72 +; 9vYmwRv+2NjY/qOjo/5tbW0o4v6Hh4f+4eHhJtMM/sLCwv56enqJiCj9/f3B/pdjLjXi/r9gAP6CQQ +; D+cDgAwC818P6eUAA1/dMF/p5kKf5lXlgz/o6Ojv7i4uImwouI/rq6uv6AgICAiCjeqYj+ra2tECbT +; jIj+np6e/mtrawYo/f39wD3+u2cR/tFpAOSdWyX+xWMANfH+o1IA/shkADX91CA9FbKIE/76+vomwZ +; iI/tDQ0P6ZmZn+Z2dnKNy8iP7T09Mm00AW/oKCgoOIKP39/cE7Nf3e/rFZADQ1/dWfLzv+Xl5eIP6b +; m5v+7e3tJsEIhoj+sbGxDCjZpIgZ/vDw8CbTlIj+q6ur/nFxcRUo/f39wKHh/qVkIzX93/6+YACZLz +; X91/60Zhf+bF9SFbuIJSbCk4j+x8fH/o+Pj/5jY2Mo1jb+xcXFOSbT/tvb2/6MjIw+Nyj9/f3APf7C +; Zww1/eABDjX92J8v/oFhQDesiP6qqqr+9PT0JsGciIGI/qenp/5wcHAo1Af+5+fnJtMu/ru7u/53d3 +; eLiCj9/f3B/p5kKTX94x816p5aNesi/mVeWCT+iIiI/t3d3SbCjYj+vr6+/oSEhCSciNCriP61tbX+ +; 9/f3JtM+LP5paWk3KP39/cH+rWARNf3k/plNADXonVv+iEQA/phMAP7IZAA16/6pXhEoBjb+vLy8/v +; j4+CbBmoj+1NTU/p2dnRyUiM7+f39//tnZ2f7////TnIj+ysrK/n19fYeIKP39/cP+iUgG/rFZAP7M +; ZwA1/eIfATXm/q1XAP5wOADA/oZDAKO1/rVbADXpDh0owH8C/pWVlf7p6ekmwVWIiBD+fHx8NyjKAv +; 6ioqIyJtORiP6mpqY2Bij9/f3E/oRGBp7U/plNAP7IZAA1/eH+sVkAATXl/pRKAB3BFh7AH/7MZwA1 +; 5RT+iEQAHcAowhW3iAv+/f39JsGViAv+k5OTESjItoj+y8vLFybT/tbW1v6Hh4f+ZmZmNyj9/f3FCZ +; 7UwKK2BTX94Bs0NeP+v2AA/nw+AB3CFh7BpcM0NeP+rVcAHcIow38N/qOjo/7y8vImwZ2Ig4g6/nR0 +; dCjFooj+k5OT/uvr6ybTl4j+tLS0/nR0dCQo/f39xwkewv6ZTQAcNf3eDw414v6gUAAdxP6KRgAewq +; K2/qxWABw14P6USgAdwyjFJP6CgoL+19fXJsKQiP7CwsIt/mJiYijCrYj+vb29DCbTg4j+lJSUDZaI +; KP39/cgJHsOlwzQ1/d2bTTg14P6/YAD+gkEAHcMA/sVjAEb+nlAAHsKn8Rw13hL+fD4AHcT+XV1dxg +; auiP60tLT+9vb2JsGbiA7+oaGh/mxsbCjA/oWFhf7f398m0xv+xcXF/np6eomIKP39/cr+hEYGHsSi +; tv6nVAAcNf3c/p5QADXfDh3EJTXBHA43Hg413yUdxijHf6SI/pCQkP7l5eUmwoqI/rm5uf6FhYX+q6 +; ur/vT09CbTjoj+oKCg/mxsbJOIKP39/cv+hEYGHsb+mU0A/sNiADX92x813RQjHcOjtRI1xA/ANd4S +; /oJBAB3H/l1dXcmjiDL+xcXFKibBl4g2JtMX/tHR0f6Dg4OCiCj9/f3NCR7HorYFNf3aOP7IZAA12x +; L+fD4AHcMjNecOHckoyn+oiP6dnZ3+7u7uJtaViP6urq7+cnJyFZ2I/f39zgkeyf6ZTQD+yGQANf3Y +; BaTUNdoEHcT+rVcANeYU/pRKAB3KKMykiCn+8vLyJtT+3d3dB/5nZ2c3Vf39/c8JHsqitjQ1/df+tV +; sAwDXYEv6CQQAdwwD+xWMANeYSHx3LKMz+i4uL/uXl5SbTPf6+vr7+eHh4iogo/f390QkezA7+zGcA +; Nf3VmR8ONdcOHcT+oFAANecEHc3+XV1dyj7+srKy/vb29ibTioj+m5ub/mpqagYo/f390gkezafx/s +; NiADX91J5a/p5QADXVFP6ORwAdwwASNeYSAB3O/l1dXcn+fX19/tfX1ybTOf7MzMz+f39/ESj9/f3U +; CR7ONwUcNf3THzXU/rldAP52OwAdwwQ15/6nVAAd0CjHpoj+n5+f/vLy8q2I05KI/qioqP5vb28VKP +; 39/dUJHtD+mU0A/shkADX90h810wQdwz7+s1oANeadW/6ORwAd0SjGtIj+ycnJ/v39/SbT/tjY2P6J +; iYn+ZmZmmIgo/f391gke0aK2GzX90TgBNdAS/nw+AJouw/6IRAD+y2YANeYxPh3SKMR//pGRkf7p6e +; km0wj+19fX/oeHhyQo/f392Ame1NM4/sxnADX9zwWk1DXPJR3E/qdUADXnBB3UKMOsiP66urr++Pj4 +; JtWPiP7AwMD+h4eHJCj9/f3XCZ7U1Cs0/tFpAP3OD5tNNc0UAB3DAP7FYwA15hIfHdUowiIZJtMIOS +; bBmog//qCgoP5ra2so/f391gke1aK2Bf7MZwA1/cyZHw41zP6tVwAdxAQ15yUd1/5dXV3AEf6np6f+ +; 9PT0JtMn/qamppaI/ufn5ybBVYqI/re3t/5+fn6BiCj9/f3UCR7X/plNAP7IZAA1/cz+mU0ANcv+lE +; oAHcOjtTE15jMAHdgouIj+zs7OJtNV/tPT0/6FhYWBiJqIEP7Hx8c5JsGWiP7Nzc3+lpaW/mVlZSj9 +; /f3TCZ7U2DcFNf3LATXJ/r9gAB8dwyM15/6tVwAd2v6Xl5cYJtMQ/rCwsDIVKMB/qYj+oKCg/vDw8C +; bBOYWI/q6urv52dnYo/f390gke2gEcNf3J/p5QABw1xwQdxC815yMd2/76+vom04CI/pGRkf5oaGg3 +; KMOkiL6I/tTU1P7////CBf7ExMQa/mJiYij9/f3QCR7borb+ul0ANf3I/qdUAP7IZAA1xRL+gkEAHc +; MA/sVjADXmEv58PgAd3CbSDP7BwcH+eXl5iogoxqKIrYj+sLCwECbBGyz+pKSk/m5ubij9/f3P/oRG +; Bh7dDhw1/cb+sVkANDXEDh3EJTXn/ppNAB3eJtGMiP6enp7+ampqBijJpYj+jIyM/uHh4SbCi4j+u7 +; u7/oGBgf5gYGAo/f39zQke3qfx/sNiADX9xQ+bTTXCFP6ORwAdwwASNeYSAB3fJs8IBwQgmIjMFbGI +; /sHBwf76+vomwZiI/tHR0f6ampoNKP39/cwJHt+itgUcNf3DAf6sVgA1wf65XQD+djsAHcP+mk0ANe +; cOHeEmzpSI/qqqqhQVKM5/IDv+7OzsJsEIh4j+srKyDCj9/f3L/oRGBh7h/plNAP7IZAA1/cP+sFgA +; NcAEHcM+LzXmnVsCHeImzf7a2top/mZmZjco0RW6iP7Pz88XJsEy/sjIyP6Pj4/+Y2NjKP39/ckJHu +; Kitv61WwA1/cIOEh8dwwD+xWMANeYxPh3jJssu/rq6uv53d3eLiCjUf6yI/qmpqf709PQmwZyIgYj+ +; qKio/nFxcSj9/f3ICR7k/qNSAP7MZwA1/cD+pFIAHcQONecEHeUmyoeI/pmZmf5paWk3KNekiP6Hh4 +; cZJsKNiP6/v7/+hYWFJCj9/f3G/oRGBp7U5afx/rpdADX9/p1OAB3C/oJBAP6/YAA15hIfHeb+//// +; yCr+ycnJ/n19fYeIKNoGNhv++Pj4JsGaiCH+np6e/mpqaij9/f3FCR7morYF/sxnADX7/qtWAB3BBD +; XnDh3oJseQiP6lpaX+bm5uBijcfwL+lJSU/unp6SbBFy/+tra2/nx8fDco/f39wwke6P6ZTQD+yGQA +; Nfr+r1gAHT7+s1oANeadW/6IRAAd6SbG/tXV1f6Hh4f+ZmZmNyjfFS48/vz8/CbBloj+y8vL/pSUlP +; 5lZWUo/f39wgme1OmitgU1+TH+jkcAFDXm/q1XAB3VpdMd0ybEEP6zs7P+dHR0jYgo4n+qiP6ioqL+ +; 8vLyJsE5hIj+rKysASj9/f3B/oRGBh7r/plNAP7MZwA1951bNef+lEoAHdU7/oBAAB3TJsODiP6Tk5 +; MNNyjlJP6BgYH+1tbWJsI2/sPDw/6JiYn+YmJiKP39/Qke7KK2NP7RaQD94P6/YAD+fD4AHdT+gEAA +; MZo+HdQmwQwi/np6eomIKOgGroj+s7Oz/vb29ibBm4j+2NjY/qKiohgo/f38CR7uDhyitv3dJf5wOA +; DVAf7DYgDA/oBAAB3UJsCNiP6goKD+bGxsBkDqf6SIFv7k5OQmwoqI/rq6uv5/f38GKP39+v6ERgYe +; 76fx/sNiAKfx/doSAB3U/oBAAP6zWgAowBAd1f79/f3+0NDQ/oODgyCYiO6jiLOI/sTExCqkiMEf/s +; /Pz/6YmJj+Z2dnKP39+Qke8KK2Bf7RaQD92A7+cDgA1SP+w2IAwv6KRQAd1f6srKz+cXFxFZ2I8H+o +; iP6dnZ3+7u7uJsEIhoj+sLCw/nh4eIWI/f34/oRGBh7yAf7IZAA1/dSdW/6ORwAd1KOl/qRSAP7DYg +; DCEB3WPjdV8xW8iP7R0dEmwpOI/sbGxv6Ojo7+Y2NjKP399gke86K2/rpdADX90hL+djsAHdU7/sNi +; AMMsHdb+XV1d9gasiP6srKz+9PT0JsEqgIj+pqam/nBwcCj9/fX+hEYGHvUO/sxnADX9z/6aTQAd1q +; XT/sNiAMObPR3X/l1dXfgzPP7e3t4mwo2I/r29vSIVKP398/6ERgYe9iv+w2IANf3MEgAd1zsowywd +; 1/5dXV35BgUIDCbBmYj+09PT/pycnP5paWko/f3yCR73orb+sVkA/sxnADX9yQ4d2KOl/sNiAMMxHd +; j+XV1d+n8C/peXl/7q6uomwUCJiP60tLT+e3t7Nyj9/fAJHvn+mU0A/shkADX9xp1b/o5HAB3Z/qRS +; AP7DYgDD/o9IAB3Y/l1dXfwVuIj+zMzMCCbBlYgLA/5kZGQo/f3vCR76orb+tVsANf3E/q1XAB3ao6 +; X+w2IAwzEd2f5dXV39fw3+pKSk/vPz8ybBnIiDiP6qqqr+c3NzKP397gke/DgcNf3B/pRKAB3b/qRS +; AP7DYgDDCh3Z/l1dXf3BJP6EhIT+2dnZJsIn/sHBwf6Hh4ckKP397Ame1P2n8TQ1/P6/YAD+fD4AHd +; ujpf7DYgDDnWsd2v5dXV39wgaviP62trb+9/f3JsEMP/6goKA6KP396wke/cCitgUcNfn+oFAAHd3+ +; pFIA/sNiAMP+j0gAHdr+XV1d/cN/M/6RkZH+5ubmJsEXioj+uLi4/n5+fgYo/f3pCR79wv6ZTQD+w2 +; IANfYSAB3do6Uow51rHdv+XV1d/cWjiLSIDyomwZeI/s3Nzf6Wlpb+ZmZmKP396Ake/cOitgU19P6n +; VAAd3/6kUgD+w2IAw/6PSAAd2/5dXV39xn8v/p+fnzYmwZ2Ihoj+r6+v/nZ2dij9/ecJHv3F/plNAB +; w18BQjHd+jpf7DYgDDnWsd3P5dXV39yCQX/tPT0ybCkoj+xcXF/oyMjP5iYmIo/f3lCR79xqK2/rpd +; ADXu/r9gAP58PgAd4P6kUgD+w2IAw/6PSAAd3P5dXV39yaKIrYj+rq6u/vX19SbBm4iAiBH+bm5uKP +; 395P6ERgYe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd3+XV1d/csz/ouLi/7h4eEmwouI/ry8vP6CgoIV +; KP394gke/cmn8f7DYgA16BL+gkEAHeL+pFIAKMMjHd3+XV1d/cwGsoj+wMDAGybBLv7R0dH+mpqa/m +; hoaCj9/eEJHv3KNwUcorblDh3jo6X+w2IAwwod3v5dXV39zX+miP6ZmZn+7OzsJsEIh4j+srKy/nl5 +; eSj9/eD+hEYGHv3MAf7IZAA14p1bAh3k/p9QAP7DYgDD/pRKAB3e/l1dXf3PFbqI/s7Ozv7+/v4mwZ +; SILf6QkJD+ZGRkKP393gke3KK2Huw3/rVbADXg/rldAD4d5Dv+w2IAwwod3/5dXV390H+siP6np6cy +; JsEqgoj+qamp/nJycouI/f3dCR7YorYO/rpdADWZH/6eUAAe7f6jUgD+zGcANd0EHeYiKMP+mU0AHd +; /+XV1d/dKkiP6Ghob+29vbJsKOiP6/v7/+hYWFJCj9/dsJHtWn8f6sVgD+w2IANcMcDjce7Cs0NdoS +; /nw+AB3mOyjDnWv+ej0AHd/+XV1d/dMGsIj+ubm5/vj4+KeIwZqIMP6fn5/+ampqKP392gke1Tc0Nc +; b+vmAA/pBJAB7sNwUcNdcOHegi/sNiAMMBHeD+XV1d/dR/MyH+6OjoJsEXiYj+t7e3/n19fTco/f3Y +; CR7XDhw1xRwOHu0B/shkADXUnVv+iEQAHeg7/sNiAMMK/no9AB3g/l1dXf3WFbaILf78/PwmwRD+zM +; zM/pWVlSAo/f3XCR7Yp/H+w2IANcYP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3913+p +; iP6hoaH+8fHxJsE5hIj+ra2t/nV1dSj9/dYJHtmitgUcNcUc/p5QAB7tARw1z/6USgAd6qOl/sNiAM +; MK/no9AB3h/l1dXf3ZpIi/iP7V1dUmwpCI/sPDw/6KioozKP391Ake2wH+yGQApcPGBTce7Dc0Ncz+ +; v2AA/nw+AB3r/plNAP7DYgDD/qRSAB3i/l1dXf3aooiuiP6xsbH+9vb2JsGbiP7Y2Nj+o6OjGCj9/d +; MJHtw3/rVbADXGmR8rHu0OHDXJ/qBQAP5wOADso6UoxP56PQAd4v5dXV393DP+jo6O/uPj4ybCioj+ +; urq6/oCAgAYo/f3RCR7eOP7MZwCitsUcDh7tK/7DYgA1xhL+gkEAHe0BKMP+pFIAHeP+XV1d/d2jiL +; KIIv76+vomwZiI/tDQ0Cw+KP390Ake3ys0NcYP/pBJAB7sNwU1xA4d7qOlMaXTwxQd4/5dXV393n8g +; /pubm/7t7e0mwUAC/rGxsf54eHgo/f3PCR7gorYFHDXFNP6KRgAe7QH+yGQANcCdW/6ORwAd7/6ZTQ +; D+w2IAw/6kUgAd5P5dXV394BW7iCUmwpOI/sfHxwf+Y2NjKP39zQme1OIB/shkADXDDh3+hEIAHu43 +; NFr+djsAHe+jpTGl08P+ej0AHeT+XV1d/eF/rIj+qqqq/vT09CbBnIiBiP6np6f+cHBwKP39zAke46 +; K2/rFZADXAnVshHcALHvAd6EYdxv6PSAD+w2IAw/6kUgAd5f5dXV394yT+iIiI/t3d3SbCjYj+vr6+ +; /oSEhBWdiP39yv6ERgYe5f6ZTQD+rlcAHcILHvAd6J1LEh3F/rldAKXTw/56PQAd5f5dXV395AaxiP +; 68vLz++Pj4JsE9/tPT0/6dnZ3+aWlplIj9/ckJHub+djsAHcILHvAd6Zkfoacdwwr+w2IAw/6pVQAd +; 5v5dXV395X8CP/7p6ekmwVWIiP61tbX+fHx8Nyj9/ccJntTmPh3CCx7wHeqbTZ1Lp/EdwTGl08MUHe +; b+XV1d/ecVuIj+ysrKCCbBlYgL/pOTkxEo/f3GCR7mPh3C/oRCAB7wHetGmi6lwx3+gEAA/sNiAMMZ +; Hef+XV1d/eg3qoj+o6OjIybBnYiDiP6rq6v+c3NzKP39xQke5j4dwgse8B3tBBI7/sNiAMM1Hef+XV +; 1d/eok/oODg/7Y2Ngmwjb+wsLCLTMo/f3DCR7mPp1bwgse8B3vo6UZ/sNiAMEZHej+XV1d/euiiK6I +; /rW1tS4mwRv+19fX/qGhof5ra2so/f3CCR7mPh3CCx7wHfH+lEoA/r5fAKOlNR3o/l1dXf3sNzP+kJ +; CQ/uXl5SbCiYj+ubm5/n9/fwYo/f3ACR7mPh3CCx7LpcMe4h3yNf6kUgAd6Sj97hW0iP7FxcUqpIjB +; H/7Ozs7+l5eX/mZmZij9/Qke5j4dwgseyif+0WkAD/6MRgAe4B394Sj9738v/p6enjYmwTmGiP6vr6 +; /+d3d3KP38CR7mPh3CCx7IEv7DYgA1wZ5a/p5QAB7fHf3h/l1dXf3xFb2I/tLS0ibCkogA/oyMjCj9 +; +wke5j4dwgsexzg1xf6xWQD+jEYAHt0d/eEo/fIGOv6tra3+9fX1JsEbgIgo/fsJHuY+HcILHscFNc +; aZHzce3B394f5dXV399KWIGv7f398mwij9+wke5j4dwgseyP6ZTQAcNcUFHtwd/eEo/fWiiLKI/r+/ +; v/75+fmmiMAo/fsJHuY+HcILHsk3NDXEHP6VSwAe2x394Sj99n8R/piYmP7r6+smKP37CR7mPp1bwg +; sey/6nVAAcNcM0Htsd6aK2HfMo/fgVuYj+zc3NKP37CR7mPh3CCx7MK/7DYgA1wx8e2h3pp/H+qFQA +; /nk9AB3x/l1dXf35N6uIKP37CR7mPh3CCx7NorYFNcKbTTce2R3p/p9QAP67XgCeWh4d8Cj9/f36CR +; 7mPh3CCx7P/plNAP7IZAA1wSce2R3oEf68XgA8wf6aTQA2He4o/f39+gke5j4dwgse0Dc0NcCeWhIe +; 2B3o/qBQAD/BPMA3He4o/f39+gke5j4dwgse0g4cNTQe2B3nNv69XwDAP8GeWh3vKP39/foJHuY+Hc +; ILHtMr/sNiADX+mU0AHtcd5/6hUAAHwT/A/pFJAB3v/l1dXf39/foJHuY+HcILHtQ3BTQ3HtYd5jYK +; wQfAnWs2HdX+lEoA/stmACMd1Sj9/f36CR7mPh3CCx7WAR8e1h3m/qNSAP7AYQBSCsAHJR3V/q1XAD +; XBMT4d0yj9/f36CR7mPh3CCx7wHeWjpf7BYQDBRgrA/no9AJs90/6CQQD+xWMANcOdW/6ORwAd0ij9 +; /f36CR7mPh3CCx7wHeU4/sFhAMJW/qFRAP5wOADTJTXGFAAd0Sj9/f36CR7mPh3CCx7wHeQ7/sFhAM +; T+f0AA/nA4ANEAEjXGMT4d0ij9/f36CR7mPh3CCx7wHeT+nk8A/sFhAMMv/nA4ANEENccEHdQo/f39 +; +v56TB0e5j4dwgse8B3jO/7BYQDEMv5wOADPPi81xhL+fD4AHdT+bD8RKP39/fqe1P51TykJHuT+dj +; sAHcILHvAd4/6YTAD+wWEAw/6yWQD+cDgA0AIUNcUOHdX+aEgpKP39/f3+Z1dG/oJHDB7jPh3CCx7w +; HeI7/sFhAMT+iUUA/nA4ANEAMTXCFCEd1KJc/mJURij9/f39wT3+dU8pHuL+djsAHcILHvAd4jn+wW +; EAwyb+cDgA0z7+p1QAFDUvHdX+aUYjKP39/f3FBwke4D4dwgse8B3hOyal08MC/nA4ANUhIx3V/mRP +; Oij9/f39xz0qHt8+HcILHvAd4f6ORwD+wWEAwz/+cDgA7Sv+X1hRKP39/f3K/m9SNAke3T4dwgse8B +; 3gOyal08MC/nA4AOz+ZkouKP39/f3N/mVYTP5/SREe3D4dwgse8B3gAv7BYQDDP/5wOADrK/5hVkwo +; /f39/c89/nVPKR7b/nY7AB3CCx7wHeAmpdPDAv5wOADqNCj9/f390wcyHtk+HcEjDjce7x3fAv7BYQ +; DDP/5wOADpolz+Y1FAKP39/f3VPSoe2D4dwC81wA/+kEkAHu4d3yal08P+k0oA/nA4AOj+a0EX/l5b +; Vyj9/f392AgJHtY+AP7FYwA1wp5a/qdUAB7tHd4C/sFhAMM//nA4AOj+ZU00KP39/f3bMBYe1f6dTw +; A1xg/+jEYAHusd3ial08M5/nA4AOYrGij9/c2uiAYo/f3MPf51TykJHtM3BRw1xRz+nlAAHuod3QL+ +; wWEAwz/+ej0Amz3lNCj9/c/+0tLS/n19fYOIKP39zQcyHtT+mU0A/shkADXGBTce6B3dJqXTwzj+cD +; gA5KJcKSj9/dD+////kIj+np6e/mZmZpiIKP39zD3+d00jHtSitv61WwA1xpkf/pVLAB7nHdz+iUUA +; /sFhAMQUmz3j/mlGI/5dXV39/dImwJyI/sXFxf50dHQVKP39zf5tVDoJHtQfHDXFmR8e5x3c/rdcAK +; XTwzj+cDgA4/5kTzr+XV1d/f3T/v///8KGiP6QkJD+YmJinIgo/f3MMP56TB0e1BL+ul0ANcSZHx7n +; Hdsp/sFhAMQUmz3h/mw/Ef5fWFGlH/391P7X19f++vr6JsGYiP60tLT+bW1tBij9/cw9Pgke06K2Jx +; w1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXf391v6IiIgT/u/v7ybC/tjY2P6Dg4P+YWFhKP39 +; zQcWHtT+mU0A/sNiADXBKB7n/nA4ANoL/sFhAMQUmz3fKxr+XV1d/f3Ytoj+q6ur/t/f372IJsGTiP +; 6jo6P+aGhologo/f3MPT4e1KK2BTXAmR8e5x3aCKjww/6oVAD+cDgA3/5oSCn+XV1d/f3bp4j+k5OT +; /srKyv709PQmwUAL/nd3dxUo/f3NB/6CRwwe1P6ZTQD+yGQAnloe5x3ZqPD+wWEAxDL+cDgA3aJc/m +; RPOv5dXV39/d1/voj+tbW1/ubm5riIJsGKiP6Wlpb+Y2NjNyj9/cw9Kh7UorYnHucd2f6oVAD+wWEA +; w/6yWQD+cDgA3Sv+X1hRKP394KyI/p2dnf7T09P++Pj4JsE9/ry8vP5wcHCPiED9/c3+b1I0CR79Hd +; gU/sFhAMQp/nA4ANz+ZU00KP394xX+hISE/r6+vv7s7Owmwv7d3d0tJJyI/f3NMBYe/B3YOP7BYQDD +; mz3+cDgA2ysaKP395gX+p6en/tzc3L+IpIjBlYj+qqqq/mpqapSIKP39zJ7U/nVPKf6ERgYe+h3XFP +; 7BYQDE/o5HAP5wOADaNCj9/emmiP6Ojo7+x8fHIybC/tDQ0P57e3sVKP39zf5nV0b+gkcM/odEAPkd +; 1zj+wWEAw51r/nA4ANkO/mNRQCj9/ey7iP6xsbH+4+PjuogmwY6I/pubm/5lZWU3KP39zJ7U/ndNIx +; 74Hdam0v7CYQBaw/6ORwD+cDgA2P5rQRf+XltXKP397qqILCX+9/f3JsGbiP7Dw8P+cnJyFSj9/c3+ +; bVQ6CR72Hdb+rVcA/s1nAJs9RsE//nA4ANgR/l1dXf398Qb+gICA/rq6uv7p6ekmwoSI/o6Ojv5iYm +; Io/f3NMP56TB0e9R3Vo7X+0WkAwFacTJ1LwP6ORwD+cDgA1v5sPxH+X1hRKP399LCI/qOjo/7Y2Nj+ +; +vr6JsGXiBQYBij9/cw9/npVL/6LTAwe8/5wOADVLzXCnmr+vV8AHdY0KP399zP+ioqKIv7w8PCviM +; L+1dXV/oCAgCQo/f3L/oGBgf7b29v+4dC//pZbIP6HRADyHdQ+/tFpAMOdW/6CQQAd1A7+YlRGKP39 +; +riI/q2trf7g4OC8iCbBkoj+oaGh/mdnZ5eIKP39x6eI/qSkpP7z8/MmwJTE/ruWcB7xHdQvNcIvHd +; UDKP39/aiI/pWVlf7MzMz+9fX1JsE5/sjIyP52dnaKiCj9/cW3iP7Nzc3+/v7+JsMh/o5QEB7vHdOj +; tTXC/pRKAB3V/mRPOij9/f3BN7+I/re3t/7n5+cXJsGJiP6UlJQzNyj9/cEGMP7s7OwmxiL+rH5QHu +; 4d0y81wP6/YAD+fD4AHdQr/l9YUSj9/f3ErYj+n5+f/tXV1f75+fkmwZmI/rm5uf5vb2+QiCj9/Sv+ +; vr6+GybJ/sqtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o/f39x6SI/oWFhf6/v7/+7e3tsojCCv +; 6GhoYkKP37/oeHh/7g4OAmzP7o3M/+nWcw/odEAOsd0v6nVAAS/oJBAB3U/mw/ERoo/f39yrWI/qmp +; qf7d3d2+iCbBMv6np6f+aWlpN1X996mI/qysrBAmzpTE/ruWcB7qHdGjtQ4d1TQo/f39zaeI/pCQkC +; 0yJsFV/s7Ozv56enqGiCj99byI/tLS0ibS/uHQv/6WWyD+h0QA6B3RPh3Uolz+Y1FAKP39/dAM/rKy +; sv7k5OS5iCbBjYgs/mRkZDco/fGkiP6bm5sFJtMy/qmlof5+VCoe5x3n/mtBF/5eW1co/f390quI/p +; qamv7R0dH+9/f3JsGbiP7AwMD+cXFxBij97zb+xMTE/vz8/CbTGf6NjY3+Z2dnl4j+bVQ6/oRGBh7l +; Heb+ZU00KP39/dUV/oKCgv68vLz+6urqJsKCiP6Li4v+YmJiKP3tOP7m5uYm0y45/nh4eDMowf5lWE +; z+fEoXHuQd5P5sPxEBKP39/dixiP6kpKT+2traGybBloj+rq6u/mxsbAYo/ekN/rS0tC4m04mI/pqa +; mv5qamoGKMOe1P51Tyn+hEYGHuId4/5oSCko/f3926aI/oyMjP7FxcX+8fHxJsL+09PT/n5+foOIKP +; 3nF/7Y2Ngm0yr+y8vLF4aIKMf+Z1dG/oJHDB7h/nA4AOEO/mJURij9/f3euYj+r6+v/uLi4rqIJsGQ +; iP6fn5/+ZmZmNyj94wL+oaGh/vLy8ibTBf6np6f+b29vFSjJPf51Tyke4B3g/mlGIyj9/f3hL/6Wlp +; b+zc3N/vb29ibBKv7Gxsb+dHR0FSj94bWI/srKyv7+/v4m0/7X19f+iIiILzcozAcJHt4d3/5kTzoo +; /f3946KIv4j+uLi4DbaIJsGHiP6RkZH+YmJiNyj93Tf+k5OT/urq6ibTH/61tbX+dXV1jIgoz/5iWl +; H+ekwdHt0d3f5sPxH+X1hRKP39/eauiP6goKD+1tbW/vn5+SbBLv62trb+bm5uBij9262I/ru7uwwm +; 04SI/pWVlf5paWk3KNGe1P5vUjQJHtsd3P5mSi4o/f396ST+h4eH/sHBwf7u7u4mwv7Z2dn+hISEJC +; j92TH+3t7eJtObiAD+e3t7iIgo1f5lWEwW/odEANod2v5sPxH+YVZMKP39/ey2iP6qqqo3vYgmwZSI +; /qSkpP5oaGiWiCj91SD+qamp/vT09CbTGP6ioqL+bGxsBijXPT4e2R3Z/mhIKSj9/f3vp4j+kpKSCw +; EmwUD+zMzM/nh4eIiIKP3TuYj+0NDQJtMX/tLS0jEgKNsH/oJHDB7XHdeiXP5kTzoo/f398Te9iP60 +; tLQvCCbBi4j+l5eX/mNjYzco/c8V/piYmCcm05WI/q+vr/5zc3MVKN2e1P56TB0e1v5wOADW/mw/Ef +; 5fWFEo/f399KyI/pycnP7T09P++Pj4JsEM/r29vf5wcHAGKP3NsIj+wsLC/vv7+ybT/t7e3v6QkJD+ +; Z2dnl4go4P5vUjT+hEYGHtQd1f5lTTQo/f399xX+g4OD/r29vf7s7Owmwv7e3t7+iYmJ/mJiYij9y/ +; 6Kior+5OTkJtM9/r+/v/54eHgCKOMwFh7THdMrGij9/f36Bf6mpqb+29vbKqSIwZWI/qysrDoGKP3H +; Pv6xsbH+9vb2JtOLiBn+ampqBijlntT+dU8p/oRGBh7RHdI0KP39/f0C/o6Ojg/+8vLyJsL+0dHR/n +; x8fBUo/cU5PybTnYg4/oCAgISIKOn+Z1dG/oJHDB7QHdCiXP5jUUAo/f39/cK7iP6wsLD+4+Pjuogm +; wScZ/mZmZpiIKP3BpYj+np6e/vHx8SbTI/6pqan+cHBwFSjrntT+d00jHs8dz/5qRB3+XltXKP39/f +; 3Eqoj+mJiY/s/Pzx8mwSr+xMTE/nNzcxUo/QX+x8fHCCbTLAsvmIgo7v5tVDoJntTN/nA4AM7+ZU00 +; KP39/f3HBv5/f3/+urq6/unp6baIwoWI/o+PjzM3KPk3Jf7o6Ogm0x/+uLi4/nZ2djMo8TD+ekwdHs +; wdzP5sPxH+X1hRKP39/f3KGP6ioqL+2NjY/vr6+ibBl4j+s7OzGAYo96yIPf74+Pgm04aI/peXlxw3 +; KPOe1P51TykJHsr+cDgAy/5mSi4o/f39/c2liDz+w8PD/u/v7ybCP/6BgYEkKPX+goKC/tzc3CbTnI +; j+yMjI/nx8fIeIKPcH/oJHDB7JHcmlHxoo/f39/dC3iP6srKz+4ODgvIgmwRT+oqKi/mhoaDco8aeI +; /qWlpf7z8/Mm0yf+pKSk/m5ubgYo+T0+HsgdyP5pRiMo/f39/dOoiP6UlJT+y8vL/vX19SbBOf7Jyc +; n+dnZ2iogo77iIOCbU/tTU1P6FhYUvKP0H/oRGBh7GHcf+ZE86KP39/f3VN76I/ra2tv7m5uYXJsGK +; iP6UlJT+Y2NjNyjrBv6Wlpb+7OzsJtOWiP6xsbH+c3NzJCj9wT3+ekwdHsUdxSv+X1hRKP39/f3YrY +; j+np6e/tTU1AwmwZmI/rq6uv5vb28GKOk6/r+/v/76+vqliNOBiP6SkpINlogo/cT+b1I0/oRGBh7D +; HcT+ZU00KP39/f3bJAD+v7+//uzs7LOIwhn+h4eHJCjnHv7h4eEm0wz+wcHB/np6egIo/ccw/n9JEf +; 6HRADCHcL+bD8R/mFWTCj9/f393rSI/qioqBm/iCbBlYj+qamp/mpqajco46mI/q6urhAm04yI/p6e +; nv5ra2sGKP3JntQ+/oRGBh7AHcH+aEgpKP39/f3hAv6Pj48t/vPz8ybBF/7Pz88bhogo4Rv+09PTJt +; NAFhMgKP3NB/6CRwweHaJc/mNRQCj9/f395LyI/rKysv7k5OQIJsGNiDsgmYgo3aSI/pycnP7w8PAm +; 05SI/qurqxQVKP3PPf53TSOUiP5eW1co/f39/eariDv+0dHRLibBm4gEFBUo2zb+xcXF/vz8/CbTCv +; 6MjIz+Z2dnl4go/f39/f39Ff6BgYH+u7u7/urq6ibCgogp/mJiYijZ/o6Ojv7n5+cm0y4q/nd3dzMo +; /f39/f39w7GI/qSkpCwbJsGWiP6wsLD+bGxsBijVDf61tbX+9/f3JtM+/pmZmf5paWk3KP39/f33AA +; AAAAAAAAE= +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 480x240 22316 +; cW9pZgAAAeAAAADwBAD+XFxc/d1//f3TpIj+h4eH/tzc3P7////CjYj+vr6+/oSEhCQo/f393KuI/r +; W1tf739/cmyv5cXFz92yj9/daiiLCI/ru7u/74+PgmwZqI/tTU1P6enp7+aWlpKP39/dr+gICA/tnZ +; 2SbMGf3ZKP392aHypR7+lZWV/unp6SbBVYiIEP58fHyCiCj9/f3WAv6jo6P+8/PzJs0Z/dco/f3Zoe +; H+rWUdoeH+a15R/mBgYLeI/srKyv78/PwmwZaI/svLy/6Tk5P+ZGRkKP39/dS2iBoXJs4Z/dUo/f3a +; /oFhQP7KaAah4cAF/ohhOjcNAv7y8vImwTmDiP6srKz+dHR0KP39/dGiiBL+6+vrJtAZ/dMo/f3b/q +; VkIzXEDRgk/oKCgv7X19cmwpCI/sLCwv6JiYn+YmJiKP39/c6tiP69vb0MJtEZ/dEo/f3b/nNfTP7C +; Zww1xv67ZxEdBq6I/rS0tP729vYmwZuIDv6hoaH+bGxsKP39/cz+hYWF/t/f3ybTGf3PKP393P6XYy +; 41yQX+kGI0IjP+j4+P/uTk5CbCHP65ubn+f39/gIgo/f39yKiI/qqqqhCqiNONiBn9zij9/dsD/rtn +; ETXMMAMVMv7ExMT++/v7JsEf/s7Ozv6YmJj+ZmZmKP39/cYu/tHR0SbTF/7Q0ND+goKCGf3MKP393P +; 6IYTo1zwU7Ny/+nZ2d/u7u7ibBQIaI/rCwsC6GiP39/cOkiP6ZmZk2JtOViP6tra3+cnJyFRn9yij9 +; /dyh4Q010jAYFbyINCbCk4j+xsbG/o2Njf5jY2Mo/f39wLCI/sPDwyom0/7d3d3+jo6O/mdnZzdVGf +; 3IKP393T0SNdSfL/6BYUA3rYj+rKysASbBKoCI/qampv5vb28o/f38/ouLi/7l5eUm05mI/r29vf54 +; eHgzKMEZ/cco/f3d/p5kKTXYIv5lXlgk/oqKiv7f398mwoyICBMVKP39+D7+srKy/vf39ybTHP6bm5 +; v+aWlpNyjCGf3FKP393f5zX0z+u2cRNdogPf5fX1+xiP6+vr7++fn5JsGZiP7S0tL+nJycHCj9/fb+ +; fX19DibTnYj+zMzM/n9/f4WIKMRV/cMo/f3eOzXdny87IgL+l5eX/urq6rWIwUCIiP60tLT+e3t7Ny +; j9/fIC/p+fn/7y8vIm05KI/qenpzaQiCjFGf3CKP393aHh/q1lHTXgMP5rXlEVuYgpCCbBlYj+ycnJ +; /pKSkhEo/f3wtYg8CCbT/tjY2P6JiYn+ZmZmNyjGGf3AKP393v56YEYFNeIF/ohhOjcc/qWlpf7z8/ +; MmwZyIg4j+qqqq/nNzcyj9/e1//pGRkf7p6ekm0x/+tra2/nV1dSQoyBn8KP393/6eZCk15g3+ZV5Y +; JP6EhIT+2dnZJsIn/sHBwR4kKP396qyI/rq6uv75+fkm04WI/pWVlf5oaGg3KMkZ+yj9/d7+c19MEj +; Xo/rtnEf6BYUAGr4j+t7e3/vf39ybBDP7W1tb+oKCg/mtrayj9/ej+g4OD/t3d3SbTm4j+x8fH/nt7 +; ewKaiMsZ+Sj9/d87NesFO/5lXlgzA/7m5uYmwVWKiP64uLj+fn5+Bij9/eSoiD4BJtOPiP6ioqL+bW +; 1tBijMGfco/f3foeH+tGYXNe4w/mteUf5gYGAQD/77+/smwZeI/s3Nzf6Wlpb+ZmZmKP394hD+z8/P +; JtNV/tLS0jEgKM4Z9ij9/d8INfEFOzepiP6fn5/+8PDwJsE5hYj+rq6u/nZ2dij9/d8VDv7t7e0m0w +; H+sLCwMhUozxn0KP394P6lZCM19DD+ZF5Xoy+9iCEmwpGI/sTExBr+YmJiKP393K+IBBsm04CI/pCQ +; kD43KNAZ8yj9/d/+c19MEjX2ny8d/l9fXwn+r6+v/vX19SbBGyz+pKSk/m5ubij9/do8/uPj4ybTmY +; j+wMDA/nl5eTMo0hnxKP394P6XYy7+0WkA+v6eZCn+ZV5YM/6MjIz+4eHhJsKLiP67u7v+gYGBFSj9 +; /dY+Nv729vYm0yv+nZ2d/mpqagZA0xnwKP393/5zX0z+u2cRNfwgPQayiP7BwcEbJsEu/tHR0f6amp +; r+aGhoKP391L6IISbTQDj+gYGBg4go1RnuKP394P6QYjQ1/cEFOzeniP6ampr+7OzsJsEIh4j+srKy +; DCj9/dEz/p2dnf7x8fEm05OI/qqqqv5wcHAVnYjWGe0o/f3fGAA1/cQw/mxfUhW6iP7Ozs7+/v7+Js +; GUiP7IyMgl/mNjYyj9/c4FDzkm0/7a2toaLzco1xnrKP394P56YEb+ymgGNf3GBR03rIj+qKioMibB +; nIiCiA3+cXFxjIj9/cz+j4+P/ujo6CbTLv65ubn+dnZ2i4go2RnqKP394P6eZCk1/coi/mVeWCT+h4 +; eH/tvb2ybCjoj+v7+//oWFhSQo/f3Iq4j+t7e3/vj4+KeI04eIDhw3KNoZ6Sj9/d/+c19M/rtnETX9 +; zCA9BrCI/rq6uv74+PgmwZqIIf6enp4rKP39xgQ7JtMq/sjIyP59fX0CKNwZ5yj9/eD+kGI0Nf3PBT +; siM/6UlJT+6OjoJsEXL/62trb+fHx8gogo/f3Cp4j+pKSkMibTkIgR/m1tbQYo3RnmKP3936HhMDXe +; mi7+plMA/sxnADXtMP5rXlEVtoj+ycnJ/vz8/CbBEP7Ly8sh/mVlZSj9/cC3iP7MzMwXJtP+1dXV/o +; aGhoCIKN8Z5Cj9/eD+iGE6Nd/+rVcA/nA4AKfx/pVLAP66XQA17QUIN6qI/qKiov7x8fEmwTmEiP6s +; rKz+dXV1KP37oogh/uzs7CbTloj+s7OzASQo4BnjKP394P6lZCM13/6USgAdwCqlw6K2/rFZABw17f +; 6tZR0YJAT+1tbWJsI2/sPDw/6JiYn+YmJiKP34rYj+vr6+/vr6+ibTgoj+kpKS/mhoaJaIKOEZ4ij9 +; /d/+c19M/sJnDDXe/r9gAP58PgAdwSoewf6ZTQD+yGQANe3+u2cR/oFhQAauiP6ysrL+9vb2JsEb/t +; jY2P6ioqIYKP32/oeHhxUm0wwi/np6egIo4xngKP394P6XYy413/6aTQD+cDgAwyqlw8KitgU17Z8v +; K/5lXlikHv6Ojo7+4+PjJsKKiP66urr+gICABij98qmI/qysrBAm042I/p+fnzqUiCjkGd8o/f3fPS +; D+0WkA3hL+gkEAHcMA/rhdAP6QSQAew/6eUAA17v60Zhc9/mBgYLKI/sPDw/76+vomwZiI/s/Pz/6Y +; mJg+KP3wu4j+0tLSJtNA/tDQ0BODiCjmGd4o/f3fOzXf/qdUAP5wOADE/qBQADXAnlr+o1IAHsM17w +; U7/l5eXiD+nJyc/u3t7SbBCAL+sbGxPSj97aSI/pqamjYm05SI/qysrP5xcXEVKOdV3Sj9/d6h4f6l +; ZCM13p1b/o5HAB3DAP6/YAA1wzT+jEYAHsEBNfAw/mxfUhW8iP7R0dEmwpOI/sfHxwf+Y2NjKP3qJz +; H+/Pz8JtP+3Nzc/o2NjT6XiCjoVdso/f3f/nNfTP7CZww13v65XQD+djsAHcMENcYBH/6HRADA/rpd +; ADXxBf6BYUA3Ov6rq6v+9PT0JsGciAr+pqam/nBwcCj96P6MjIz+5ubmJtOZiP68vLz+d3d3MyjqGd +; oo/f3f/p5kKTXfBP5wOADDPv6zWgA1yBz+rFYA/oxGAP61WwA18yL+ZV5YJDz+3t7eJsKNiP69vb3+ +; g4ODFSj95KuI/rOzs/739/cm04mIO/5paWmViCjrGdko/f3e/nNfTP67ZxE13v6/YAD+fD4AHcP+iE +; QAFDXLNA419CA9BgUIDCbBmYj+09PT/p2dnRyUiP3i/n5+fv7Y2Ngm0yoaF4aIKO0Z1yj9/d/+kGI0 +; Nd8O/nA4AMQONc7+mU0ANfWfLzv+ZV5YAv6Wlpb+6urqJsFVL/60tLT+e3t7Nyj93gL+oaGhIybTko +; j+pqam/m9vbwYo7hnWKP393hj+rWUdNd6dWyEdw/6CQQD+v2AANc/+nlAANfcw/mteURW4iBr+/f39 +; JsGViP7Kysr+kpKSESj93LWICwgm0/7X19f+iIiI/mZmZjco7xnVKP393v56YEYFNd7+rVcAHcQENd +; EfNfgF/ohhOjeqiP6kpKT+8vLyJsGciISIOv5zc3Mo/dk3Aysm05eI/rW1tf51dXWMiCjxGdQo/f3e +; /p5kKTXf/pRKAB3DPjE10v6nVAD+yGQANfn+pWQj/mVeWCT+g4OD/tjY2CbCj4j+wcHB/oiIiCQo/d +; atiP67u7sMJtOEiP6VlZX+aGhoNyjyGdMo/f3d/nNfTP7CZww13v6/YAD+fD4Ami7DIzXU/rFZADQ1 +; +iD+gWFA/l9fX6+I/rW1tS4mwQz+1tbW/qGhof5ra2so/dT+hISE/t3d3SbTm4j+xcXF/nt7e4iImo +; j0GdEo/f3eOzXfJf5wOADELzXV/r5gAP6sVgA1+58vO/5lXlgz/pGRkf7m5uYmwReKiP64uLj+f39/ +; Bij90KiI/qmpqf709PSriNOOiCT+bGxsBij1GdAo/f3doeH+u2cRNd4S/oJBAB3DAP7FYwA11ptN/q +; NSADX9/rRmF/5rXlEVtIj+xsbG/vv7+ybBl4j+zs7O/peXl/5mZmYo/c65iP7Q0NAm0xf+0tLSMYGI +; KPcZzyj9/d0INd/+p1QAHcT+oFAANdk4Nf3ABTs3L/6enp7+7+/vJsGdiIaI/q+vrx8o/csV/piYmP +; 7u7u4m05WINv5ycnIVKPgZzij9/d3+pWQjNd4U/o5HAP5wOADDABI12v6ZTQA1/cIwGKMvvIj+09PT +; JsKSiP7FxcUp/mNjYyj9yLCIBCom0/7e3t7+kJCQ/mdnZ5eIKPkZzSj9/dz+c19M/sJnDDXe/rldAP +; 52OwAdw/6aTQA13P6eUAD+yGQANf3CBf6BYUAGCf6urq7+9fX1JsEbgIj+paWl/m9vbyj9xv6Kior+ +; 5OTkJtOZiP6/v7/+eHh4iogo+xnMKP393P6XYy413wT+cDgAwz7+rVcANd3+sVkAp+E1/cT+nmQp/m +; VeWDP+i4uL/uDg4L+IwoyI/ry8vBP+YGBgKP3Cqoj+sLCw/vb29ibTi4j+nJyc/mpqagYo/FXLKP39 +; 2/5zX0z+u2cRNd7+v2AA/oJBAB3DAP7FYwA13v61WwCitjX9xSA9BrKI/sDAwAwmwZmI/tHR0f6bm5 +; v+aGhoKP3Av4g/JtOdiP7Nzc3+gICAhIgo/cAZySj9/dz+kGI0/tFpAN8OHcQONeCZHw41/cafLzs3 +; Ef6ZmZk6JsFAiIj+s7Oz/np6eoOI+6WI/p6env7x8fEm05OIHP5wcHAVKP3BGcgo/f3boeH+rWUdNd +; 6dWyEdw/6CQQASNeGeWv6jUgA1/cgw/mxfUhUb/s3NzRcmwZSI/sjIyP6RkZERKPgF/sfHxwgm0/7Z +; 2dkL/mZmZpiIKP3CGcco/f3b/npgRv7KaAY13v6tVwAdxAQ15P6eUAA1/ckF/ohhOjeriP6np6cyJs +; Eqgoj+qamp/nJycouI9iX+6OjoJtOXiP64uLj+dnZ2JCj9xBnGKP392/6eZCk13/6USgAdw6O1/rNa +; ADXl/qNSADX9y/6lZCP+ZV5YJP6Ghob+29vbJsKOiP7AwMAPJCjyrIg9/vj4+CbTIP6Xl5ccNyj9xR +; nFKP392v5zX0z+wmcM/tFpAN7+v2AA/nw+AB3DIRQ15jgBNf3L/rtnES0Gr4j+ubm5/vj4+CbBmoj+ +; 1dXV/p+fnyso8BMKJtMq/sjIyP58fHwCKP3HGcQo/f3aOzXf/qBQAB3E/qdUADXo/rFZAKTUNf3Mny +; 87IjP+k5OT/ujo6CbBF4mI/re3t/59fX03KOwR/qWlpTIm04+I/qSkpP5tbW2SiCj9yBnDKP392aHh +; /rRmFzXe/r9gAAAdwwD+xWMANek0nlo1/c4w/mteURW2iC3+/Pz8JsGWiP7MzMz+lZWV/mVlZSjqt4 +; j+zc3NJtT+1NTU/oWFhYGIKP3KGcIo/f3Z/ohhOjXfDh3EBDXrmR8ONf3PBTs3qYj+oaGh/vHx8SbB +; OYWI/q2trf51dXWIiOcG/paWlv7s7Owm05aI/rGxsf5zc3MVKP3LGcEo/f3Z/qVkIzXenVsjHcOjtT +; E17f6eUAA1/dH+tGYX/mReV6Mvv4j+1dXVJsKRiP7Dw8ML/mJiYijkroj+v7+//vr6+qWI04GI/pKS +; kv5oaGg3KP3MGcAo/f3Y/nNfTP7CZwz+0WkA3/6tVwA+HcIjNe8fNf3Sny/+gWFABq2I/rGxsf729v +; YmwRv+2NjY/qOjo/5tbW0o4v6Hh4f+4eHhJtMM/sLCwv56enqJiCj9zhko/f3Y/pdjLjXi/r9gAP6C +; QQD+cDgAwC818P6eUAA1/dMF/p5kKf5lXlgz/o6Ojv7i4uImwouI/rq6uv6AgICAiCjeqYj+ra2tEC +; bTjIj+np6e/mtrawYo/f396D3+u2cR/tFpAOSdWyX+xWMANfH+o1IA/shkADX91CA9FbKIE/76+vom +; wZiI/tDQ0P6ZmZn+Z2dnKNy8iP7T09Mm00AW/oKCgoOIKP39/ek7Nf3e/rFZADQ1/dWfLzv+Xl5eIP +; 6bm5v+7e3tJsEIhoj+sbGxDCjZpIj+nJyc/vDw8CbTlIj+q6ur/nFxcRUo/f396KHh/qVkIzX93/6+ +; YACZLzX91/60Zhf+bF9SFbuIJSbCk4j+x8fH/o+Pj/5jY2Mo1jb+xcXFOSbT/tvb2/6MjIw+Nyj9/f +; 3oPf7CZww1/eABDjX92J8v/oFhQDesiP6qqqr+9PT0JsGciIGI/qenp/5wcHAo1Af+5+fnJtMu/ru7 +; u/53d3eLiCj9/f3p/p5kKTX94x816p5aNesi/mVeWCT+iIiI/t3d3SbCjYj+vr6+/oSEhCSciNCriP +; 61tbX+9/f3JtM+LP5paWk3KP39/en+rWARNf3k/plNADXonVv+iEQA/phMAP7IZAA16/6pXhEoBjb+ +; vLy8/vj4+CbBmoj+1NTU/p2dnRyUiM7+f39//tnZ2f7////TnIj+ysrK/n19fYeIKP39/ev+iUgG/r +; FZAP7MZwA1/eIfATXm/q1XAP5wOADA/oZDAKO1/rVbADXpDh0owH8C/pWVlf7p6ekmwVWIiBD+fHx8 +; NyjKAv6ioqIyJtORiP6mpqY2Bij9/f3s/oRGBp7U/plNAP7IZAA1/eH+sVkAATXl/pRKAB3BFh7AH/ +; 7MZwA15RT+iEQAHcAowhW3iAv+/f39JsGViAv+k5OTESjItoj+y8vLFybT/tbW1v6Hh4f+ZmZmNyj9 +; /f3tCZ7UwKK2BTX94Bs0NeP+v2AA/nw+AB3CFh7BpcM0NeP+rVcAHcIow38N/qOjo/7y8vImwZ2Ig4 +; g6/nR0dCjFooj+k5OT/uvr6ybTl4j+tLS0/nR0dCQo/f397wkewv6ZTQAcNf3eDw414v6gUAAdxP6K +; RgAewqK2/qxWABw14P6USgAdwyjFJP6CgoL+19fXJsKQiP7CwsIt/mJiYijCrYj+vb29DCbTg4j+lJ +; SUDZaIKP39/fAJHsOlwzQ1/d2bTTg14P6/YAD+gkEAHcMA/sVjAEb+nlAAHsKn8Rw13hL+fD4AHcT+ +; XV1dxgauiP60tLT+9vb2JsGbiA7+oaGh/mxsbCjA/oWFhf7f398m0xv+xcXF/np6eomIKP39/fL+hE +; YGHsSitv6nVAAcNf3c/p5QADXfDh3EJTXBHA43Hg413yUdxijHf6SI/pCQkP7l5eUmwoqI/rm5uf6F +; hYX+q6ur/vT09CbTjoj+oKCg/mxsbJOIKP39/fP+hEYGHsb+mU0A/sNiADX92x813RQjHcOjtRI1xA +; /ANd4S/oJBAB3H/l1dXcmjiDL+xcXFKibBl4g2JtMX/tHR0f6Dg4OCiCj9/f31CR7HorYFNf3aOP7I +; ZAA12xL+fD4AHcMjNecOHckoyn+oiP6dnZ3+7u7uJtaViP6urq7+cnJyFZ2I/f399gkeyf6ZTQD+yG +; QANf3YBaTUNdoEHcT+rVcANeYU/pRKAB3KKMykiCn+8vLyJtT+3d3dB/5nZ2c3Vf39/fcJHsqitjQ1 +; /df+tVsAwDXYEv6CQQAdwwD+xWMANeYSHx3LKMz+i4uL/uXl5SbTPf6+vr7+eHh4iogo/f39+QkezA +; 7+zGcANf3VmR8ONdcOHcT+oFAANecEHc3+XV1dyj7+srKy/vb29ibTioj+m5ub/mpqagYo/f39+gke +; zafx/sNiADX91J5a/p5QADXVFP6ORwAdwwASNeYSAB3O/l1dXcn+fX19/tfX1ybTOf7MzMz+f39/ES +; j9/f38CR7ONwUcNf3THzXU/rldAP52OwAdwwQ15/6nVAAd0CjHpoj+n5+f/vLy8q2I05KI/qioqP5v +; b28VKP39/f0JHtD+mU0A/shkADX90h810wQdwz7+s1oANeadW/6ORwAd0SjGtIj+ycnJ/v39/SbT/t +; jY2P6JiYn+ZmZmmIgo/f39/cAJHtGiths1/dE4ATXQEv58PgCaLsP+iEQA/stmADXmMT4d0ijEf/6R +; kZH+6enpJtMI/tfX1/6Hh4ckKP39/f3CCZ7U0zj+zGcANf3PBaTUNc8lHcT+p1QANecEHdQow6yI/r +; q6uv74+Pgm1Y+I/sDAwP6Hh4ckKP39/f3BCZ7U1Cs0/tFpAP3OD5tNNc0UAB3DAP7FYwA15hIfHdUo +; wiIZJtMIOSbBmog//qCgoP5ra2so/f39/cAJHtWitgX+zGcANf3MmR8ONcz+rVcAHcQENeclHdf+XV +; 1dwBH+p6en/vT09CbTJ/6mpqaWiP7n5+cmwVWKiP63t7f+fn5+gYgo/f39/Ake1/6ZTQD+yGQANf3M +; /plNADXL/pRKAB3Do7UxNeYzAB3YKLiI/s7OzibTVf7T09P+hYWFgYiaiBD+x8fHOSbBloj+zc3N/p +; aWlv5lZWUo/f39+wme1Ng3BTX9ywE1yf6/YAAfHcMjNef+rVcAHdr+l5eXGCbTEP6wsLAyFSjAf6mI +; /qCgoP7w8PAmwTmFiP6urq7+dnZ2KP39/foJHtoBHDX9yf6eUAAcNccEHcQvNecjHdv++vr6JtOAiP +; 6RkZH+aGhoNyjDpIi+iP7U1NT+////wgX+xMTEGv5iYmIo/f39+Ake26K2/rpdADX9yP6nVAD+yGQA +; NcUS/oJBAB3DAP7FYwA15hL+fD4AHdwm0gz+wcHB/nl5eYqIKMaiiK2I/rCwsBAmwRss/qSkpP5ubm +; 4o/f399/6ERgYe3Q4cNf3G/rFZADQ1xA4dxCU15/6aTQAd3ibRjIj+np6e/mpqagYoyaWI/oyMjP7h +; 4eEmwouI/ru7u/6BgYH+YGBgKP39/fUJHt6n8f7DYgA1/cUPm001whT+jkcAHcMAEjXmEgAd3ybPCA +; cEIJiIzBWxiP7BwcH++vr6JsGYiP7R0dH+mpqaDSj9/f30CR7forYFHDX9wwH+rFYANcH+uV0A/nY7 +; AB3D/ppNADXnDh3hJs6UiP6qqqoUFSjOfyA7/uzs7CbBCIeI/rKysgwo/f398/6ERgYe4f6ZTQD+yG +; QANf3D/rBYADXABB3DPi815p1bAh3iJs3+2traKf5mZmY3KNEVuoj+z8/PFybBMv7IyMj+j4+P/mNj +; Yyj9/f3xCR7iorb+tVsANf3CDhIfHcMA/sVjADXmMT4d4ybLLv66urr+d3d3i4go1H+siP6pqan+9P +; T0JsGciIGI/qioqP5xcXEo/f398Ake5P6jUgD+zGcANf3A/qRSAB3EDjXnBB3lJsqHiP6ZmZn+aWlp +; NyjXpIj+h4eHGSbCjYj+v7+//oWFhSQo/f397v6ERgae1OWn8f66XQA1/f6dTgAdwv6CQQD+v2AANe +; YSHx3m/v///8gq/snJyf59fX2HiCjaBjYb/vj4+CbBmogh/p6env5qamoo/f397Qke5qK2Bf7MZwA1 +; +/6rVgAdwQQ15w4d6CbHkIj+paWl/m5ubgYo3H8C/pSUlP7p6ekmwRcv/ra2tv58fHw3KP39/esJHu +; j+mU0A/shkADX6/q9YAB0+/rNaADXmnVv+iEQAHekmxv7V1dX+h4eH/mZmZjco3xUuPP78/PwmwZaI +; /svLy/6UlJT+ZWVlKP39/eoJntTporYFNfkx/o5HABQ15v6tVwAd1aXTHdMmxBD+s7Oz/nR0dI2IKO +; J/qoj+oqKi/vLy8ibBOYSI/qysrAEo/f396f6ERgYe6/6ZTQD+zGcANfedWzXn/pRKAB3VO/6AQAAd +; 0ybDgoj+k5OTDTco5aSI/oGBgf7W1tYmwjb+w8PD/omJif5iYmIo/f395wke7KK2NP7RaQD94P6/YA +; D+fD4AHdT+gEAAMZo+HdQmwQwi/np6eomIKOgGroj+s7Oz/vb29ibBm4j+2NjY/qKiohgo/f395gke +; 7g4corb93SX+cDgA1QH+w2IAwP6AQAAd1CbAjYj+oKCg/mxsbAZA6n+kiBb+5OTkJsKKiP66urr+f3 +; 9/Bij9/f3k/oRGBh7vp/H+w2IAp/H92hIAHdT+gEAA/rNaACjAEB3V/v39/f7Q0ND+g4ODIJiI7qOI +; s4j+xMTEKqSIwR/+z8/P/piYmP5nZ2co/f394wke8KK2Bf7RaQD92A7+cDgA1SP+w2IAwv6KRQAd1f +; 6srKz+cXFxFZ2I8H+oiP6dnZ3+7u7uJsEIhoj+sLCw/nh4eIWI/f394v6ERgYe8gH+yGQANf3UnVv+ +; jkcAHdSjpf6kUgD+w2IAwhAd1j43VfMVvIj+0dHRJsKTiP7Gxsb+jo6O/mNjYyj9/f3gCR7zorb+ul +; 0ANf3SEv52OwAd1Tv+w2IAwywd1v5dXV32BqyI/qysrP709PQmwSqAiP6mpqb+cHBwKP39/d/+hEYG +; HvUO/sxnADX9z/6aTQAd1qXT/sNiAMObPR3X/l1dXfgzPP7e3t4mwo2I/r29vSIVKP39/d3+hEYGHv +; Yr/sNiADX9zBIAHdc7KMMsHdf+XV1d+QYFCAwmwZmI/tPT0/6cnJz+aWlpKP39/dwJHveitv6xWQD+ +; zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV36fwL+l5eX/urq6ibBQImI/rS0tP57e3s3KP39/doJHvn+mU +; 0A/shkADX9xp1b/o5HAB3Z/qRSAP7DYgDD/o9IAB3Y/l1dXfwVuIj+zMzMCCbBlYgLA/5kZGQo/f39 +; 2Qke+qK2/rVbADX9xP6tVwAd2qOl/sNiAMMxHdn+XV1d/X8N/qSkpP7z8/MmwZyIg4j+qqqq/nNzcy +; j9/f3YCR78OBw1/cH+lEoAHdv+pFIA/sNiAMMKHdn+XV1d/cEk/oSEhP7Z2dkmwif+wcHB/oeHhyQo +; /f391gme1P2n8TQ1/P6/YAD+fD4AHdujpf7DYgDDnWsd2v5dXV39wgaviP62trb+9/f3JsEMP/6goK +; A6KP39/dUJHv3AorYFHDX5/qBQAB3d/qRSAP7DYgDD/o9IAB3a/l1dXf3DfzP+kZGR/ubm5ibBF4qI +; /ri4uP5+fn4GKP39/dMJHv3C/plNAP7DYgA19hIAHd2jpSjDnWsd2/5dXV39xaOItIgPKibBl4j+zc +; 3N/paWlv5mZmYo/f390gke/cOitgU19P6nVAAd3/6kUgD+w2IAw/6PSAAd2/5dXV39xn8v/p+fnzYm +; wZ2Ihoj+r6+v/nZ2dij9/f3RCR79xf6ZTQAcNfAUIx3fo6X+w2IAw51rHdz+XV1d/cgkF/7T09Mmwp +; KI/sXFxf6MjIz+YmJiKP39/c8JHv3Gorb+ul0ANe7+v2AA/nw+AB3g/qRSAP7DYgDD/o9IAB3c/l1d +; Xf3JooitiP6urq7+9fX1JsGbiICIEf5ubm4o/f39zv6ERgYe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd +; 3+XV1d/csz/ouLi/7h4eEmwouI/ry8vP6CgoIVKP39/cwJHv3Jp/H+w2IANegS/oJBAB3i/qRSACjD +; Ix3d/l1dXf3MBrKI/sDAwBsmwS7+0dHR/pqamv5oaGgo/f39ywke/co3BRyituUOHeOjpf7DYgDDCh +; 3e/l1dXf3Nf6aI/pmZmf7s7OwmwQiHiP6ysrL+eXl5KP39/cr+hEYGHv3MAf7IZAA14p1bAh3k/p9Q +; AP7DYgDD/pRKAB3e/l1dXf3PFbqI/s7Ozv7+/v4mwZSILf6QkJD+ZGRkKP39/cgJHtyith7sN/61Ww +; A14P65XQA+HeQ7/sNiAMMKHd/+XV1d/dB/rIj+p6enMibBKoKI/qmpqf5ycnKLiP39/ccJHtiitg7+ +; ul0ANZkf/p5QAB7t/qNSAP7MZwA13QQd5iIow/6ZTQAd3/5dXV390qSI/oaGhv7b29smwo6I/r+/v/ +; 6FhYUkKP39/cUJHtWn8f6sVgD+w2IANcMcDjce7Cs0NdoS/nw+AB3mOyjDnWv+ej0AHd/+XV1d/dMG +; sIj+ubm5/vj4+KeIwZqIMP6fn5/+ampqKP39/cQJHtU3NDXG/r5gAP6QSQAe7DcFHDXXDh3oIv7DYg +; DDAR3g/l1dXf3UfzMh/ujo6CbBF4mI/re3t/59fX03KP39/cIJHtcOHDXFHA4e7QH+yGQANdSdW/6I +; RAAd6Dv+w2IAwwr+ej0AHeD+XV1d/dYVtogt/vz8/CbBEP7MzMz+lZWVICj9/f3BCR7Yp/H+w2IANc +; YP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3913+piP6hoaH+8fHxJsE5hIj+ra2t/nV1 +; dSj9/f3ACR7ZorYFHDXFHP6eUAAe7QEcNc/+lEoAHeqjpf7DYgDDCv56PQAd4f5dXV392aSIv4j+1d +; XVJsKQiP7Dw8P+ioqKMyj9/fwJHtsB/shkAKXDxgU3Huw3NDXM/r9gAP58PgAd6/6ZTQD+w2IAw/6k +; UgAd4v5dXV392qKIroj+sbGx/vb29ibBm4j+2NjY/qOjoxgo/f37CR7cN/61WwA1xpkfKx7tDhw1yf +; 6gUAD+cDgA7KOlKMT+ej0AHeL+XV1d/dwz/o6Ojv7j4+MmwoqI/rq6uv6AgIAGKP39+Qke3jj+zGcA +; orbFHA4e7Sv+w2IANcYS/oJBAB3tASjD/qRSAB3j/l1dXf3do4iyiCL++vr6JsGYiP7Q0NAsPij9/f +; gJHt8rNDXGD/6QSQAe7DcFNcQOHe6jpTGl08MUHeP+XV1d/d5/IP6bm5v+7e3tJsFAAv6xsbH+eHh4 +; KP399wke4KK2BRw1xTT+ikYAHu0B/shkADXAnVv+jkcAHe/+mU0A/sNiAMP+pFIAHeT+XV1d/eAVu4 +; glJsKTiP7Hx8cH/mNjYyj9/fUJntTiAf7IZAA1ww4d/oRCAB7uNzRa/nY7AB3vo6UxpdPD/no9AB3k +; /l1dXf3hf6yI/qqqqv709PQmwZyIgYj+p6en/nBwcCj9/fQJHuOitv6xWQA1wJ1bIR3ACx7wHehGHc +; b+j0gA/sNiAMP+pFIAHeX+XV1d/eMk/oiIiP7d3d0mwo2I/r6+vv6EhIQVnYj9/fL+hEYGHuX+mU0A +; /q5XAB3CCx7wHeidSxIdxf65XQCl08P+ej0AHeX+XV1d/eQGsYj+vLy8/vj4+CbBPf7T09P+nZ2d/m +; lpaZSI/f3xCR7m/nY7AB3CCx7wHemZH6GnHcMK/sNiAMP+qVUAHeb+XV1d/eV/Aj/+6enpJsFViIj+ +; tbW1/nx8fDco/f3vCZ7U5j4dwgse8B3qm02dS6fxHcExpdPDFB3m/l1dXf3nFbiI/srKyggmwZWIC/ +; 6Tk5MRKP397gke5j4dwv6EQgAe8B3rRpoupcMd/oBAAP7DYgDDGR3n/l1dXf3oN6qI/qOjoyMmwZ2I +; g4j+q6ur/nNzcyj9/e0JHuY+HcILHvAd7QQSO/7DYgDDNR3n/l1dXf3qJP6Dg4P+2NjYJsI2/sLCwi +; 0zKP396wke5j6dW8ILHvAd76OlGf7DYgDBGR3o/l1dXf3rooiuiP61tbUuJsEb/tfX1/6hoaH+a2tr +; KP396gke5j4dwgse8B3x/pRKAP6+XwCjpTUd6P5dXV397Dcz/pCQkP7l5eUmwomI/rm5uf5/f38GKP +; 396Ake5j4dwgsey6XDHuId8jX+pFIAHeko/e4VtIj+xcXFKqSIwR/+zs7O/peXl/5mZmYo/f3nCR7m +; Ph3CCx7KJ/7RaQAP/oxGAB7gHf3hKP3vfy/+np6eNibBOYaI/q+vr/53d3co/f3mCR7mPh3CCx7IEv +; 7DYgA1wZ5a/p5QAB7fHf3h/l1dXf3xFb2I/tLS0ibCkogA/oyMjP5jY2Mo/f3kCR7mPh3CCx7HODXF +; /rFZAP6MRgAe3R394Sj98gY6/q2trf719fUmwRuAiP6lpaX+b29vKP394wke5j4dwgsexwU1xpkfNx +; 7cHf3h/l1dXf30pYga/t/f3ybCjIj+vLy8/oKCghUo/f3hCR7mPh3CCx7I/plNABw1xQUe3B394Sj9 +; 9aKIsoj+v7+//vn5+aaIwZmIA/6bm5v+aGhoKP394Ake5j4dwgseyTc0NcQc/pVLAB7bHf3hKP32fx +; H+mJiYOibBQIiI/rOzs/56enoo/f3fCR7mPp1bwgsey/6nVAAcNcM0Htsd6aK2HfMo/fgVuYj+zc3N +; FybBlIj+ycnJ/pGRkREo/f3dCR7mPh3CCx7MK/7DYgA1wx8e2h3pp/H+qFQA/nk9AB3x/l1dXf35N6 +; uI/qampjImwZyIg4j+qamp/nJycij9/dwJHuY+HcILHs2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3w +; KP37pIj+hYWFOybCjoj+wMDA/oaGhiQo/f3aCR7mPh3CCx7P/plNAP7IZAClw8EnHtkd6BH+vF4APM +; H+mk0ANh3uKP38Bq+I/ri4uP739/cmwZqIMP6fn5/+a2trKP392Qke5j4dwgse0Df+ul0ANcCeWhIe +; 2B3o/qBQAD/BPMA3He4o/f1/M/6SkpL+5+fnJsEXPv63t7f+fX19Nyj9/dcJHub+djsAHcILHtIOHD +; U0Htgd5zb+vV8AwD/Bnlod7yj9/cEVtYj+x8fH/vz8/KOIwZaI/szMzP6VlZX+ZWVlKP391gme1OY+ +; HcILHtMr/sNiADX+mU0AHtcd5/6hUAAHwT/A/pFJAB3v/l1dXf39wjepiP6goKD+8PDwJsE5hYj+ra +; 2t/nZ2dij9/dUJHub+djsAHcILHtSitv6xWQA0Nx7WHeY2/r5fAMEHwJ1rNh3V/pRKAP7LZgAjHdUo +; /f3EpIi+iP7U1NT+////wpGI/sTExBozKP390wke5j4dwgse1gGjpR7WHeb+o1IA/sBhAFIKwAclHd +; X+rVcANcH+uV0APh3TKP39xaKIrYj+sLCwECbBm4j+2dnZ/qOjo/5tbW0o/f3S/oRGBh7mPh3CCx7w +; HeWjpf7BYQDBRgrA/no9AJs90/6CQQD+xWMANcOdW/6ORwAd0ij9/celiP6NjY3+4uLiJsKLiP67u7 +; v+gYGBKP390Qke5j4dwgse8B3l/qNSAP7BYQDCVv6hUQD+cDgA0yU1xhQAHdEo/f3IBrOI/sLCwhsm +; wZiI/tDQ0P5eXl4o/f3QCR7mPh3CCx7wHeQ7/sFhAMT+f0AA/nA4ANEAEjXGMT4d0ij9/ck3IP6amp +; r+7OzsJsFA/nFxcQYo/f3P/oRGBh7mPh3CCx7wHeT+nk8A/sFhAMMv/nA4ANH+mk0ANccEHdQo/f3L +; o4i7iBYXJsAz/o2Njf5iYmIo/f3O/npMHR7mPh3CCx7wHeOjpf7BYQDEMv5wOADPPi81xhL+fD4AHd +; T+bD8RKP39zDesiP6pqan+9PT0JsAQBf5sbGwGKP39zJ7U/nVPKf6ERgYe5P52OwAdwgse8B3j/phM +; AP7BYQDD/rJZAP5wOADQAv7LZgA1xQ4d1f5oSCko/f3PJP6Hh4f+3NzcJsEh/n9/fyQo/f3N/mdXRv +; 6CRwz+h0QA4z4dwgse8B3iO/7BYQDE/olFAP5wOADRADE1whT+iEQAHdSiXP5iVEYo/f3RBrCI/vz8 +; /KOIwZGI/qCgoP5nZ2c3KP39zD3+dU8pHuL+djsAHcILHvAd4v6YTAD+wWEAw5s9/nA4ANM+/qdUAB +; Q1Lx3V/mlGIyj9/dQ3/szMzBCqiMGdiP7Hx8f+dXV1i4go/f3NBwme1OA+HcILHvAd4Tv+t1wApdPD +; Av5wOADVIabiHdX+ZE86KP391v59fX3+t7e3/ufn5xd/wT7+kpKSMzco/f3MPSoe3/52OwAdwgse8B +; 3h/o5HAP7BYQDDP/5wOADt/mw/Ef5fWFEo/f3YOv6fn5/+1dXVDCbBmIj+uLi4J5GIKP39zf5vUjQJ +; Ht0+HcILHvAd4Dv+t1wApdPDAv5wOADs/mZKLij9/dskD/7AwMD+7e3tsojC/tra2v6FhYUkKP39zf +; 5lWEz+f0kRHtw+HcILHvAd4AL+wWEAwz/+cDgA6yv+YVZMKP393rWIHP7e3t69iCbBlIj+pqam/mlp +; aZWIKP39zJ7U/nVPKR7b/nY7AB3CCx7wHeD+t1wApdPDAv5wOADqNCj9/eGniP6RkZH+ycnJ/vT09K +; uIwRf+zc3N/nl5eRUo/f3NB/6CRwwe2T4dwf6USgAO/oxGAB7vHd8C/sFhAMM//nA4AOmiXP5jUUAo +; /f3kvYj+s7Oz/uXl5biIJsGMiP6YmJgRmogo/f3MPf56TB0e2D6dW8D+rVcA/tFpAMD+vmAA/pBJAB +; 7uHd/+t1wApdPD/pNKAP5wOADo/mtBF/5eW1co/f3mq4j+m5ub/tLS0v74+PiniMGaiP6/v7/+cHBw +; Bij9/c3+b1I0CR7WPqbi/sVjADXCnlr+p1QAHu0d3gL+wWEAwz/+cDgA6P5lTTQo/f3pFf6CgoL+vL +; y8OrSIwoCI/ouLiyQo/f3NMBYe1f6dTwA1xg/+jEYAHusd3v63XACl08P+mEwA/nA4AOYr/mFWTCj9 +; /ew2/qWlpTv++vr6pYjBloj+ra2t/mtra5SIKP39zJ7U/nVPKQke0zf+sVkAHDXFHP6eUAAe6h3dAv +; 7BYQDDP/56PQCbPeX+aEgpKP3976aI/oyMjP7FxcX+8fHxJsID/n19fRUo/f3NB/6CRwwe1P6ZTQD+ +; yGQANcYFNx7oHd3+t1wApdPD/qNSAP5wOADkolz+YlRGKP398rqI/q+vr/7i4uK6iKOIwZCI/p6env +; 5mZmaYiCj9/cw9/ndNIx7Uorb+tVsANcaZH/6VSwAe5x3c/olFAP7BYQDE/no9AJs94/5pRiP+XV1d +; /f31L/6Xl5f+zs7O/vb29ibBnIgA/nR0dBUo/f3N/m1UOgke1P6eUAAcNcWZHx7nHdz+t1wApdPDOP +; 5wOADj/mRPOv5dXV39/fcG/n9/f/65ubn+6Ojot4jChoj+kJCQ/mJiYpyIKP39zDD+ekwdHtQS/rpd +; ADXEmR8e5x3bKf7BYQDEFJs94f5sPxH+X1hRpR/9/fo6/qGhof7X19f++vr6JsGYiP60tLT+bW1tBi +; j9/cw9Pgke06K2/qxWABw1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXf39/TP+iIiI/sLCwv7v +; 7+8mwv7Y2Nj+g4OD/mFhYSj9/c3+Z1dGFh7U/plNAP7DYgA1wSge5/5wOADaC/7BYQDEFJs93ysa/l +; 1dXf39/cK2iP6rq6v+39/fOSbBk4j+o6Oj/mhoaJaIKP39zD0+HtSitgU1wJkfHucd2gio8MP+qFQA +; /nA4AN/+aEgp/l1dXf39/cWniP6Tk5P+ysrK/vT09CbBQAv+d3d3FSj9/c0H/oJHDB7U/plNAP7IZA +; CeWh7nHdmo8P7BYQDEMv5wOADdolz+ZE86/l1dXf39/cd/voj+tbW1/ubm5riIJsGKiP6Wlpb+Y2Nj +; Nyj9/cw9Kh7UorYnHucd2f6oVAD+wWEAw/6yWQD+cDgA3Sv+X1hRKP39/cqsiP6dnZ3+09PT/vj4+C +; bBPf68vLz+cHBwj4hA/f3N/m9SNAke/R3YFP7BYQDEKf5wOADc/mVNNCj9/f3NFf6EhIT+vr6+/uzs +; 7CbC/t3d3S0knIj9/c0wFh78Hdg4/sFhAMObPf5wOADbKxoo/f390AX+p6enGb+IpIjBlYj+qqqq/m +; pqapSIKP39zJ7U/nVPKf6ERgYe+h3XFP7BYQDE/o5HAP5wOADaNCj9/f3Tpoj+jo6O/sfHxyMmwv7Q +; 0ND+e3t7FSj9/c3+Z1dG/oJHDP6HRAD5Hdc4/sFhAMOda/5wOADZDv5jUUAo/f391ruI/rGxsf7j4+ +; O6iCbBjogK/mVlZTco/f3MntT+d00jHvgd1qbS/sJhAFrD/o5HAP5wOADY/mtBF/5eW1co/f392KqI +; /pmZmSX+9/f3JsGbiP7Dw8P+cnJyFSj9/c3+bVQ6CR72Hdb+rVcA/s1nAJs9RsE//nA4ANgR/l1dXf +; 39/dsG/oCAgP66urr+6enpJsKEiP6Ojo7+YmJiKP39zTD+ekwdHvUd1aO1/tFpAMBWnEydS8D+jkcA +; /nA4ANb+bD8R/l9YUSj9/f3esIj+o6Oj/tjY2P76+vomwZeIFBgGKP39zD3+elUv/otMDB7z/nA4AN +; UvNcKeav69XwAd1jQo/f394TP+ioqKIv7w8PCviML+1dXV/oCAgCQo/f3L/oGBgf7b29v+4dC//pZb +; IP6HRADyHdQ+/tFpAMOdW/6CQQAd1A7+YlRGKP39/eS4iP6tra3+4ODgvIgmwZKI/qGhof5nZ2eXiC +; j9/ceniP6kpKT+8/PzJsCUxP67lnAe8R3ULzXCLx3VAyj9/f3nqIj+lZWV/szMzP719fUmwTn+yMjI +; /nZ2doqIKP39xbeI/s3Nzf7+/v4mwyH+jlAQHu8d06O1NcL+lEoAHdX+ZE86KP39/ek3v4j+t7e3/u +; fn5xcmwYmI/pOTkzM3KP39wQYw/uzs7CbGIv6sflAe7h3TLzXA/r9gAP58PgAd1Cv+X1hRKP39/eyt +; iP6fn5/+1dXV/vn5+SbBmYj+ubm5/m9vb5CIKP39K/6+vr4bJsn+yq2P/o5QEB7s/nA4ANKjtTXA/q +; BQAB3V/mZKLij9/f3vpIj+hYWF/r+/v/7t7e2yiMIK/oaGhiQo/fv+h4eH/uDg4CbM/ujcz/6dZzD+ +; h0QA6x3S/qdUABL+gkEAHdT+bD8RGij9/f3ytYj+qamp/t3d3b6IJsEy/qenp/5paWk3Vf33qYj+rK +; ysECbOlMT+u5ZwHuod0aO1Dh3VNCj9/f31p4j+kJCQLTImwVX+zs7O/np6eoaIKP31vIj+0tLSJtIh +; /pZbIP6HRADoHdE+HdSiXP5jUUAo/f39+Az+srKy/uTk5LmIJsGNiCz+ZGRkNyj98aSI/pubmwUm0z +; L+qaWh/n5UKh7nHef+a0EX/l5bVyj9/f36q4j+mpqa/tHR0f739/cmwZuI/sDAwP5xcXEGKP3vNv7E +; xMT+/Pz8JtMZ/o2Njf5nZ2eXiP5tVDr+hEYGHuUd5v5lTTQo/f39/RX+goKC/ry8vP7q6uomwoKI/o +; uLi/5iYmIo/e04/ubm5ibTLjn+eHh4MyjB/mVYTP58Shce5B3k/mw/EQEo/f39/cKxiP6kpKT+2tra +; GybBloj+rq6u/mxsbAYo/ekN/rS0tC4m04mI/pqamv5qamoGKMOe1P51Tyn+hEYGHuId4/5oSCko/f +; 39/cWmiP6MjIz+xcXF/vHx8SbC/tPT0/5+fn6DiCj95xf+2NjYJtMq/svLyxeGiCjH/mdXRv6CRwwe +; 4f5wOADhDv5iVEYo/f39/ci5iP6vr6/+4uLiuogmwZCI/p+fn/5mZmY3KP3jAv6hoaH+8vLyJtMF/q +; enp/5vb28VKMk9/nVPKR7gHeD+aUYjKP39/f3LL/6Wlpb+zc3N/vb29ibBKv7Gxsb+dHR0FSj94bWI +; /srKyv7+/v4m0/7X19f+iIiILzcozAcJHt4d3/5kTzoo/f39/c2iiL+I/ri4uA22iCbBh4j+kZGR/m +; JiYjco/d03/pOTk/7q6uom0x/+tbW1/nV1dYyIKM/+YlpR/npMHR7dHd3+bD8R/l9YUSj9/f390K6I +; /qCgoP7W1tb++fn5JsEu/ra2tv5ubm4GKP3brYj+vLy8DCbThIj+lZWV/mlpaTco0Z7U/m9SNAke2x +; 3c/mZKLij9/f390yT+h4eH/sHBwf7u7u4mwv7Z2dn+hISEJCj92TH+3t7eJtObiAD+e3t7iIgo1f5l +; WEwW/odEANod2v5sPxH+YVZMKP39/f3Wtoj+qqqqN72IJsGUiP6kpKT+aGhologo/dUg/qmpqf709P +; Qm0xj+oaGh/mxsbAYo1z0+Htkd2f5oSCko/f39/dmniP6SkpILASbBQP7MzMz+eHh4iIgo/dO5iP7Q +; 0NAm0xf+0tLSMSAo2wf+gkcMHtcd16Jc/mRPOij9/f392ze9iP60tLQvCCbBi4j+l5eX/mNjYzco/c +; 8V/piYmCcm05WI/q+vr/5zc3MVKN2e1P56TB0e1v5wOADW/mw/Ef5fWFEo/f39/d6siP6cnJz+09PT +; /vj4+CbBDP69vb3+cHBwBij9zbCI/sLCwv77+/sm0/7e3t7+j4+P/mdnZ5eIKOD+b1I0/oRGBh7UHd +; X+ZU00KP39/f3hFf6Dg4P+vb29/uzs7CbC/t7e3v6JiYkzKP3L/oqKiv7k5OQm0z3+v7+//nh4eAIo +; 4zD+f0kRHtMd0ysaKP39/f3kBf6mpqb+29vbKqSIwZWI/qysrDoGKP3HPv6xsbH+9vb2JtOLiBn+am +; pqBijlntT+dU8p/oRGBh7RHdI0KP39/f3nAv6Ojo4P/vLy8ibC/tHR0f58fHwVKP3FOT8m052IKf6A +; gICEiCjp/mdXRv6CRwwe0B3Qolz+Y1FAKP39/f3qu4j+sLCw/uPj47qIJsEnGf5mZmaYiCj9waWI/p +; 6env7x8fEm0yP+qamp/nBwcBUo657U/ndNIx7PHc/+akQd/l5bVyj9/f397KqI/piYmP7Pz88fJsEq +; /sTExP5zc3MVKP0F/sfHxwgm0ywLL5iIKO7+bVQ6CZ7Uzf5wOADO/mVNNCj9/f397wb+f39//rq6uv +; 7p6em2iMKFiP6Pj48zNyj5N/6QkJD+6OjoJtMf/ri4uP52dnYzKPEw/npMHR7MHcz+bD8R/l9YUSj9 +; /f398hj+oqKi/tjY2P76+vomwZeI/rOzsxgGKPesiD3++Pj4JtOGiP6Xl5ccNyjzntT+dU8pCR7K/n +; A4AMv+ZkouKP39/f31pYg8/sPDw/7v7+8mwj/+gYGBgIgo9f6CgoL+3NzcJtOciP7IyMj+fHx8h4go +; 9wf+gkcMHskdyaUfGij9/f39+LeI/qysrP7g4OC8iCbBFP6ioqL+aGhoNyjxp4j+paWl/vPz8ybTJ/ +; 6kpKT+bm5uBij5PT4eyB3I/mlGIyj9/f39+6iI/pSUlP7Ly8v+9fX1JsE5/snJyf52dnaKiCjvuIg4 +; JtMX/tTU1P6FhYUvKP0H/oRGBh7GHcf+ZE86KP39/f39N76I/ra2tv7m5uYXJsGKiP6UlJT+Y2NjNy +; jrBv6Wlpb+7OzsJtOWiP6xsbH+c3NzJCj9wT3+ekwdHsUdxSv+X1hRKP39/f39wq2I/p6env7U1NQM +; JsGZiP66urr+b29vBijpOv6/v7/++vr6pYjTgYj+kpKSDZaIKP3E/m9SNP6ERgYewx3E/mVNNCj9/f +; 39/cUkAP6/v7/+7Ozss4jCGf6Hh4ckKOce/uHh4SbTDP7BwcH+enp6Aij9xzD+f0kR/odEAMIdwv5s +; PxH+YVZMKP39/f39yLSI/qioqBm/iCbBlYj+qamp/mpqajco46mI/q6urhAm04yI/p6env5ra2sGKP +; 3JntQ+/oRGBh7AHcH+aEgpKP39/f39ywL+j4+PLf7z8/MmwRf+z8/PG4aIKOEb/tPT0ybTQBYTICj9 +; zQf+gkcMHh2iXP5jUUAo/f39/f3OvIj+srKy/uTk5AgmwY2IOyCZiCjdpIj+nJyc/vDw8CbTlIj+q6 +; urFBUo/c89/ndNI5SI/l5bVyj9/f39/dCriDv+0dHRLibBm4gEFBUo2zb+xcXF/vz8/CbTCv6MjIz+ +; Z2dnl4go/f39/f395xX+gYGB/ru7u/7q6uomwoKIKf5iYmIo2f6Ojo7+5+fnJtMuKv53d3czKP39/f +; 39/euxiP6kpKQsGybBloj+sLCw/mxsbAYo1Q3+tbW1/vf39ybTPv6ZmZn+aWlpNyj9/f39/c0AAAAA +; AAAAAQ== +; thumbnail_QOI end +; + +; +; thumbnail begin 640x480 35568 +; iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAABn+UlEQVR4Ae29V5Bc15mtmU8d89AP00 +; 96UsxE6GV6YoJzZ6Zn5oYU6kt1q0GBElsULyW2KJEEAYIkCILwrlDwQMEXCt57bwreew8QAOEJ7x1B +; eE9IovRfrl19gKxiZp6Tp3Zm7v/stSO+q75SoZAnq1B71b/3Wiv1xz/+UYrBO++8QwghJeO9996Txo +; 0bS7NmzWTAgAGyceNGuX37toStb7/9Vm7evClHjx6VdevWyaRJk6Rbt27SpEkTVc9PCCHppCj8CCG+ +; 8O6778r7778vH3zwgXTq1Enmzp0rZ86ckb/85S+hQvDRo0dy9uxZ2blzp8ybN08GDRokLVq0MJ9T03 +; tACCGgYAJQ05tACPGLYBrYvHlzGT58uOzatcsIvLD15z//Wa5fvy4HDhyQlStXytixY42QhKjU9PyE +; EGJdAGp6eEKIvwTTwKZNm0r37t1lyZIlcuXKFfnrX/+aUwT+7W9/k/v378vJkydly5YtMnPmTOnbt6 +; 85Wtb0/IQQv7EmADU9NCGEBDRq1Mjc52vZsqVMmDBBDh48KM+ePQudBuJjLl++LHv37jXiEZPEtm3b +; mumipucnhPhJvQWgpoclhJB0Pnj3LfOfmAbiSPjjjz+WiooKWbt2rTF+hC1MC2EkOXbsmGzYsEGmTJ +; kiPXr0MFNFTe8DIcQ/YgtATQ9JCCHptH3vV7L+o38w4P/GfwcRiGkgDCLt27c3R7s45sW9v7D15MkT +; OX/+vLlLuGDBAhkyZIh89tlnNIgQQpwlbwGo6eEIIaQuVY3/SQ43Sz1n78d/Z/674H8PDCKffPKJDB +; 06VLZt22bu/IUtOIlv3Lghhw4dktWrV8v48eOlS5cu5nNpen8IIX6QovgjhPgAjntnffCjWuIvnRUf +; /kCav/sb87HpcTHl5eVSXV0tFy9eNJmAuRYMIg8fPpTTp08b4Thnzhzp16+fcRtreq8IIcknReFHCE +; k6OObFpC+b+EufBvZ8/+Xnfy4wiCDvb8yYMbJv3z55+vRp6DTwT3/6k1y9elX2798vy5cvl5EjR0qH +; Dh3M59P0vhFCkkuKwo8QkmQmf/CPocKvLvgz6QYRTAM//PBD6d27t6xatUq++uqrUBEIg8jdu3fl+P +; HjsmnTJpk2bZr06tXLfB5N7x8hJJmkKPwIIUkkMHrkK/4C0g0iIJgGtmnTRqZOnWqcv5j0hS1MDHF8 +; vGfPHlm0aJG5V9iqVSvGxRBCSkqKwo8QkjTqGj3iEhhEgmlgep8wquAw2cOUL2wFfcJHjhwxETMTJ0 +; 6Url27sk+YEFIyUhR+hJCkAKFW3fSHVsRfFINI586dTS/wuXPn8uoT3rFjh/lzAwcOlE8//VTVe0wI +; SQYpTS+WEEKyAfNGFKNHfaaB/d//8fO/D9NATPAg4GDy2L17tzx+/DhUBNbtEx49erTpE6ZBhBBSTC +; gACSHqyRXvYhv8XXUNImj+QAPIsmXLjPsXcTC5Fgwi9+7de94nPGPGDNMnjCYSTe87IUQvFICEELXA +; pLHto78vmvhLN4hkiouBuWPSpEkmDDpKn/A333xTq0942LBhxmRCgwghpNBQABJCVDKmyUtFF35RDC +; KY4iH8ef369XLr1q1QEQiDCD4OrmL8mcmTJ0v37t3NHUNNXw9CiC4oAAkhqqhvvIttYBDJ1CfcsWNH +; mTVrlpw6dSqSQQT3B2Em2blzp+kTHjx4MPuECSEFgwKQEKIGW/EuhZoGBq8zmAaiAq6qqsq4fh88eB +; AqAiEUETJ98OBB0yc8btw4KSsrM/cMNX2dCCHuQwFICHGesB5fl6aBdQ0imAYi8w8h0JcuXTIGkFwL +; BhKIRUwOt27dKrNnzzZHyp988omqrxkhxG0oAAkhTlMqo0d9poGZDCI4zsVE74svvojcJ3zlyhXTPw +; x38YgRI6Rdu3aMiyGEWIECkBDiLHF6fF2hbp8wjoQ/+ugj6dOnjznevXHjRqgIxLTwzp078uWXX8rG +; jRtNBV3Pnj3ZJ0wIqTcUgIQQ53DN6BGXbH3CmORNnz7dCDsEQ4ctTAwvXLhgwqarq6ulsrJSWrZsSY +; MIISQ2FICEEKdw1egRl1x9wnD6IggaodBhC3ExX3/9tRw+fNj0CU+YMEHKy8vN59L09SWEuAEFICHE +; CQrV4+sK2fqE4fJF7Mv58+eNyMu1YBBBn/CZM2dk+/btMnfuXBkwYIBxG2v6WhNCSg8FICGk5JQ1al +; DQHl+XpoHZ+oRHjRoln3/+uTx58iR0Gohj42vXrhlDyYoVK0yfMHIHaRAhhESFApAQUlI0Gz3ikqlP +; GMYOGDyWL19uxF3UPuETJ07I5s2bzZ1CGExgNNH09SeElAYKQEJISdAW72KbbH3CrVu3NnVwR44cMV +; EwYQt9wsgXxPRw8eLFJngan4N9woSQXFAAEkKKTtKMHnHJZRDB3b4NGzbI7du3Q0Vg0Cd89OhR0yc8 +; adIk6datG/uECSFZoQAkhBQNCB2YITSJtGKQzSCCe31z5swxpo98+4Tnz58vgwYNkhYtWjAuhhDyPS +; gACSFFAeYHTaKs2OTqEx4+fLgRdXAAh630PuFVq1bJ2LFjpXPnzuwTJoTUggKQEFJwNPT4ukKmPuGm +; TZuaI90lS5aYerh8+4RnzZolFRUV5mhZ0/cNIaRwUAASQgqG70aPuGTrE0b7BwKgDxw4YMwfYevZs2 +; dy+fJl0ye8dOnS533CNIgQQigACSEFYUyTl1SJLhfJ1ifct29f0waCZpCwhWkhjCSonYOpZMqUKdKj +; Rw8zVdT0/UQIsQsFICHEKknp8XWF9D5hiEBMA2EQad++vcyYMcPkAEbpE0bAdNAnvHDhQhkyZAj7hA +; nxGApAQog1GO9SGLIZRD755BOprKyUbdu2yf3790NFIAwiN27cMH3Ca9askfHjx0uXLl3YJ0yIh1AA +; EkLqTdJ7fF0hW1xMeXm5mepdvHgxUp/ww4cP5fTp00Y4Imamf//+7BMmxDMoAAkh9cKXHl9XyGYQQd +; 7fmDFjjOEjSp8wWkauXr0q+/fvN/Vz6CLu0KED+4QJ8QQKQEJIbHzs8XWFTAYR9An36tVLVq5cabIA +; o/QJ3717V44fPy6bNm0yfcK9e/dmnzAhHkABSAjJGxo93CDdIAKCaWCbNm1k6tSpphouSp/w06dPzf +; Hxnj17ZNGiRTJ06FD2CROScCgACSF5QaOHW+TqEx44cKCZ7N25cydUBOLu4M2bN+XIkSOybt06mThx +; onTt2tUISk3fn4SQaFAAEkIiwR5ft8lmEEEN3Lx58+Ts2bOR+oRRN4eP3bFjh/lzEJGffvqpqu9VQk +; g4FICEkFDY46sDTAPxtQq+bpgGYoIHAYcWEGQARukTRq7g9evXTeMI7hPCXNKpUyf2CROSICgACSE5 +; YY+vPhDJk6lPGA0gqISD+zfMIIL/HdmCJ0+elC1btsjMmTNNA8nHH3+s6vuXEJIZCkBCSEbY46sbfO +; 0yxcW0atXK3O87dOiQ6QoOW0Gf8N69e2XJkiUyfPhwadu2LQ0ihCiHApAQ8j3Y45sMcCSMr2Vdgwim +; eP369TNmDxg/wlbQJ3zs2DHTJzx58mQzTcQdQ03f14SQF1AAEkKew3iXZAKDSHqfcGAQQfDzrFmzzD +; Fv1D7h8+fPy65du2TBggWmT/izzz5jnzAhCqEAJIQYGO+SbHL1CSP3b/v27fLgwYNQERj0CeMIefXq +; 1TJu3DgpKytjnzAhyqAAJMRzcDxIo4c/ZIuLQeYfQqAvXbpkjnxzLRhEIBaDPuHZs2ebI2WISU3f+4 +; T4DAUgIR5Do4efZOsTxnHu2LFjTT8w2kHCVnqf8LJly2TkyJHSvn179gkTogAKQEI8hT2+JFOfMHqA +; 0QeM4130CYetun3C06ZNM33E6CXW9O+BEN+gACTEM2j0IOlk6hPGkTCiXiDmvvzyy0gGEUwML1y4YP +; qEq6urzb1CRM4wLoYQN6EAJMQjaPQgmcjVJzx48GDZvHmzmfKFLfQJf/3116ZPeO3atSZvsLy8nH3C +; hDgIBSAhHoCNHe0QmkQJKT7ZDCJw+c6fP99EwEDk5VowiKBu7syZM8ZZPHfuXBkwYAD7hAlxDApAQh +; JOWaMGZsKjSYiQ0lG3TzgwiEDAjRo1yhzxPn78OHQaiGPja9euyRdffCErVqyQ0aNHS8eOHWkQIcQR +; KAAJSTA0epC4IBqobp8wjB09e/aU5cuXG3EX1icMg8i9e/fkxIkT5hh5xowZ0qdPH/YJE+IAFICEJB +; DGuxAbwCCSKS6mdevWpg7u8OHDJgombH3zzTcmX/Dzzz+XxYsXy7Bhw6RNmzY0iBBSQigACUkY7PEl +; NslmEMEUr3///qYb+NatW6EiEHcH8XFHjx6V9evXGwHZvXt39gkTUiIoAAlJCNigGe9CCkU2gwju9a +; EJBK0gqIkLW7g/eO7cOdm5c6cxlgwaNEhatGjBPmFCigwFICEJAJf2NYkJopNMfcI4Em7evLk51oWo +; e/jwYagIhFBEyPTBgwdl1apVpn2kc+fORlRq+ndHiGYoAAlRDnt8SbHBNLCuQaRp06bSrVs3c8fv8u +; XLkfuET506JVu3bpVZs2ZJRUWFyR7U9O+PEK1QABKiFBo9SCnJ1ifcsmVLGT9+vBw4cMCYP8IWTCRX +; rlyRffv2ydKlS2XEiBHSrl07xsUQUmAoAAlRCI0exBXS+4QDgwj6hPv27Str1qwxzSBhC9PCO3fumN +; q5jRs3ytSpU03cDKaKmv5dEqIJCkBCFMEe39rsbJKS/R/pes1JJL1PGEfCQZ8wJnnI/jt+/HikPuEn +; T56YPuHdu3fLwoULpbKy0kwUaRAhxD4UgIQogT2+tZn6eko6/CQlo36Zku2Ndb32JJLJIIJp4CeffC +; JDhgwx9/wQCh22gj5hZAxigojjZPQJ43Np+vdKiOtQABLiOOzxrQ2mfr1+lpJW/7VGAPb7eUoGN0jJ +; 0t/reo6kki0upkuXLmaqhwlflD5huImDPuE5c+aYzEG4jTX92yXEZSgACXEY9vjWpvqtGuEXEAjAgH +; GvpWTPB7qeKYlk6xNG3h86gffu3WuOe8MWDCJBnzDq59BF3KFDBxpECLEABSAhjsIe39qMfLW2+Msk +; AINp4Mo/6Hq2pFK3TxjHuOgT7tWrl6xYsUKuX78eqU/47t27pk9406ZNMn36dOndu7cxmmj690yIa1 +; AAEuIYjHepzeZGKSn/5++Lv2wCMGDGGzSIuAAMIphkB9/fwTQQXcBTpkwx1XBR+oSfPn1aq0+4qqrK +; dBKzT5iQeFAAEuIQNHrUZvxrmYVfFAEIYBDZ1EjXMyeRbH3CCH0eOHCgiX5BDEzYwt3BmzdvGtG4bt +; 06mTRpkgmfhqDU9O+cEBegACTEAbAx4vK8pk29kGDqFxg96iMA0w0inAaWnmwGkU6dOsncuXON6SNK +; n/CjR4/k7NmzsmPHDpk3bx77hAmJAQUgISWGPb61mfNmuPDLRwDSIOIWufqEhw8fLrt27TICL2whVx +; B3CNE4snLlShkzZowRkuwTJiQaFICElBD2+L5g34cpqXwluvjLVwDSIOIW2fqEu3fvbirhUA8XpU/4 +; /v37cvLkSdmyZYvMnDnTNJCwT5iQcCgACSkBNHrUZu07NWIuH/EHJv265p5fPiIQTH6dR8IukK1PuF +; WrVjJhwgQ5ePCgPHv2LHQaiI+5fPmyiZdZsmSJmSS2bduWBhFCckABSEiRYY9vbcKMHpnA/cDN/2nu +; gJDDsTGme/mKQE3vU5LJ1Cf88ccfS0VFhTF7wPgRtjAtvH37thw7dkw2bNhgHMY9evRgnzAhWaAAJK +; RIsMe3NrniXXIBwZjp88HtW9UwugDE5FDLe+UDdfuEA4MIgp9xtItj3qh9wufPnzd3CRcsWGBq6D77 +; 7DMaRAipAwUgIUWA8S61ycfokX7fD00guT4vpoHI/4sqAjW8Vz6Rq0946NChphYOd/7CFpzEN27ckE +; OHDsnq1atNnzCq6NgnTMgLKAAJKSA41qLR4wUwevT/ebwjX3QAR/17YPSIciRMV7CbZIuLKS8vl+rq +; arl48WLkPuHTp0/Ltm3bZPbs2aZPGGJS088QQgoFBSAhBYJGj9rU7fGNCqaFcf6+7Y1r7vnlEoDr3n +; H7PfOZbAYRHOeOHTtW9u3bZ9pBwhZaRq5evSr79+83fcIjR46U9u3bs0+YeA8FICEFgD2+tcnU45uP +; 0SMuYQYRRsK4T7pBJOgTRg8w+oBXrVolX331VagIDPqEjx8/bvqEp02bZvqI0Uus6ecKITahACTEIj +; R61Ma20SMu2QwicaeLpLikG0QApnc4EkbUC8QcnL9R+4RxfLxnzx5zlIx7hYicYVwM8REKQEIsQaNH +; baa+Hs/osfztwryeYBpYtx2k1O8TiUauPmFUwW3evNlM+cJW0Cd85MgRWbt2rUycOFG6du3KPmHiHR +; SAhNQTbEjVTX+oajMtJFF7fOsCcwhMIoV+fZgGBkfCVQ1L9z6ReGQziHTu3Fnmz58v586di2QQSe8T +; Rg/xwIED5dNPP1X1s4eQ+kABSEg9wCV1TCY0baCFJE68S32MHnHBNDAwiNAJrA/8m0OHdvDvMDCIQM +; DB5IEj3sePH4dOAzP1CXfs2JF9wsQLKAAJiQnjXV4Qp8c3TryLTTAJpADUDf4N1u0ThrEDDSDLli0z +; 7l9M+3ItGETu3bv3vE94xowZ0qdPH9NEounnESH5QgFISJ4w3qU2OPKN0+M7vsT37yD8GAWjHxhEsv +; UJT5o0yYRBR+kT/uabb+TSpUvP+4SHDRsmbdq0oUGEJBYKQELygD2+tYnT4wuxuNYR0VXVkE7gJJDN +; IIIpHsKf169fL7du3QoVgbg7iI87evSo+TOTJ0+W7t27mzuGmn5OERIFCkBCIoBL54x3eUFcowfyAF +; 16jqqGNdVxLr0mEp9sBhHc60MTyKlTp0xNXNjC/UGYSXbu3Gn6hAcPHsw+YZI4KAAJCQGXzTVtgoUm +; rtEjrMe3FMAIMuqX7r0uEp9MfcI4Em7evLk51oXrFxVxYQtCESHTBw8eNIHT48aNk7KyMhpESGKgAC +; QkBzR6vECj0SOMpb9nFExSwTSwrkEE08Bu3brJokWL5PLly8YAkmvBQPLgwQMzOdy6dauZIlZUVLBP +; mCQCCkBCMkCjR23i9vgiDNrl50IVHJ3AySVXnzAmel988UXkPuErV66Y/mG4i0eMGME+YaIeCkBC6s +; Ae39qUqse3GGxvTCewD6T3CQcGEfQJI+5l9erVcuPGjVARiGnhnTt35Msvv5SNGzfK1KlTpWfPnuwT +; JmqhACTkP2GPb21c6fEtJEEUDCaBWl4ziUd6nzCOhIM+4Xbt2sn06dPl+PHjJhg6bGFieOHCBdm9e7 +; fpE66srJSWLVvSIELUQQFIyDvs8a2Laz2+haSqIaNgfCFbXAzu9A0ZMsQEQSMUOmwhLubrr7+Ww4cP +; y5o1a2TChAlSXl5uPpemn3vEX9q2bUsBSPyGPb61cb3HtxDACTzZ8buKxC7Z4mK6dOliYl8w4YvaJ3 +; zmzBnZvn276RMeMGCAcRtr+hlI/AK/pPTr189cY6AAJN5S1qgBe3zT0NLjaxvkADIKxj+y9Qm3aNFC +; Ro8eLZ9//rk8efIkdBqIY+Nr164ZQ8mKFStk1KhRJneQBhHiGujKRjvOsWPHjLGJApB4CY0eL8DkLq +; 7Rw9V4l3xAFAzuAWp6zcQedfuEMSGBsaNXr15G0EHcRe0TPnHihGzevNncKYTBBEYTTT8XSTLBVYeu +; Xbua72dkWwaLApB4BeNdauOD0SMMOIAZBeM32fqEW7dubergjhw5YiYmYSvoE8b0cPHixVJVVWU+B/ +; uESanALzMjR440Hdd1I48oAIk30OhRG+09vrZgFAwB2QwizZo1M3f7cGfq9u3boSIwvU943bp15sgN +; 4dPsEybFpkOHDjJ//ny5ePFixtBzCkCSePADHZe+NW1GhSQpPb42YRQMCchmEOnUqZPMmTPHmD6i9A +; nDIBL0CWMTHjRokLlfyLgYUmgwvR44cKBpr0GTTbZFAUgSDXt8a5OkHl+bVDVkFAx5Qa4+4eHDh8uu +; XbuMwAtbMIik9wmPHTtWOnfuzD5hUjCQSTlt2jQ5efJkaK4lBSBJLOzxfUESe3xtghiYcQm610jskK +; lPuGnTptK9e3dZsmSJqYeL0id8//79533CM2fONH3COFrW9POUuA3uraKZBs02N2/elCiLApAkDho9 +; aoM7e7i7l6/4c73H1yaMgiHZyNYnjEkLAqAPHDhgzB9h69mzZ8/7hJcuXWr6hBHGy7gYUl/wywSmy/ +; hexPdZ1EUBSBLFmCYvqdpcCk0co4eWHl+bMAqGhJGtT7hv376ydu1a0wwStjAthJEEOWwbNmyQKVOm +; SI8ePcxUUdPPWeIGmErjSsGiRYvMLxdhcUUUgCSRsMe3Nox3yY9NjRgFQ8Kp2yccGETat29vjnaRAx +; ilTxgB0+fPnzd9wgsXLjQ1dOwTJvmA7zv0UMNk9PjxY4mzKACJehjvUps4Rg8cESfd6JELRsGQqGQy +; iAR9wtiQt23bZu78hS04iW/cuCGHDh0yfcLjx483VXTsEyZhtGnTRmbNmiVnz54NrSykACSJhD2+tY +; HRA528NHrEAwJw4e90vWZSOrLFxZSXl0t1dbXJXovSJ/zw4UM5ffq0EY6Imenfvz/7hElG8D2GKwe4 +; PnDnzh2p76IAJCphj29tML2LE+/C6JMXVDWsMYNoes2ktGQziCDvb8yYMcbwEaVPGC0jV69elf3798 +; vy5ctNnzBCfGkQIQH4pQCmo6itNBSAJJGwx7c2cXt8fTN6hMEoGBKXdINI0CcMgwj6hFeuXGmyAKP0 +; Cd+9e1eOHz8umzZtMlluvXv3Zp+w5+CKAabKy5Ytk+vXr4vNRQFI1ECjR21o9LALjn+rGup6zcQd0g +; 0iANM7HAnjvtbUqVNNNVyUyQ36WnF8vGfPHuPuHDp0qLRq1Yp9wh6CHl8Ej6Nbum6PLwUg8QYaPWqD +; jL44Ro/lb+t6zmKCKjg6gUl9yNUnjCo4TPai3N3C3UGE+eK4D33CEydOlK5du5rjZU0/t0l84CyfN2 +; +eXLhwoV5GDwpAohb2+NYmbo8vzCEwiWh61mITRMFs4tE4qSfZDCLIbMOmDvdm1D5hfOyOHTvMn0O/ +; 66effqrqZzjJD/zCMGDAANmyZUskNzkFIEkk7PGtTdweXxo9ooHJH6NgiC0wDcTPsODnWWAQgYAbOX +; KkyQCMkt+GXEHc/ULLA+4TwlzSqVMn9gknkM8++8yEg+MeaJQ8SQpAkkjY4/sC9vgWj6qGFMzELvhZ +; lqlPGA0guNgP928UgwimQSdPnjSTIYROIw7k448/VvVznWQGvxygX3rVqlWRGmVsLQpA4hTs8a0Ne3 +; yLC1zAjIIhtoFBJFNcDMwduN+HMOgoHa74mMuXL8vevXtlyZIlxiCAPmEaRPQCET969Gj54osvInVK +; 21wUgMQZ2ONbmzg9vnAFM94lPoiCGfVLXa+Z6CCbQQQCoF+/fsbscevWrdBNu26f8OTJk830CHcMNf +; 289x1Mg3GUjypAiHp8XYu9KABJyWG8S23ixrsgD1DTc7rI0t+nZHADXa+Z6AIGkUx9wgh+Rr3XqVOn +; It3/wv1B9Anv2rVLFixYIIMHDzZ3yNgn7D6Y/uLrtX37dmP0KdWiACQlhfEutYlr9PC5x9cmjIIhxS +; BXn3BVVZURBg8ePAjdwOEkRsg0jpBXr14t48aNk7KyMvYJO0zr1q1lxowZpv4vihO8kIsCkJQEHIPQ +; 6PEC9vi6wfbGdAKT4oFpYF2DCKaByPxDCPSlS5dCjwZhIIFYDPqEZ8+ebY6UISY17QlJB19bNLvgqB +; 9H+C4sCkBSdGj0qA17fN1h/0c1AhCTQE2vm+glW58wjnMx0UM/cJQWCLSMXLlyxXw83MWImkGYMPuE +; Sw/E+Pjx4+Xw4cPWenxtLApAUlTY41sb9vi6R1VDimtSfLL1Cffp08cc7964cSN0Q8e0EE0jyJHbuH +; Gj6RNGHzEqxTTtE0kBR/tdunQxju1r166Fxv0Ue1EAkqJAo0dt2OPrLnACT2aMDikB6X3CEIFBn3C7 +; du2MmPvyyy8jGUQwMUSFGPqEq6urpbKy0kTO0CBSPJD1iPucCPx+8uSJuLgoAEnBodGjNuzxdRvkAD +; IKhpSKbHExOEaEcxRB0Hfv3g3d3NEfi1BhHDuuXbtWJkyYIOXl5ewTLgIQ7HPmzDEu7UL1+NpYFICk +; YOAHWHXTH6r64VtI2OOrA0TB4B6gptdMkke2PmG4fOfPnx9JXODIETEjZ86cMc7iuXPnmp5Z9gkXBg +; h1GHA2bdok9+7dE9cXBSApCGWNGpjfZDX9wC0k7PHVAxzAjIIhLpCrT3jUqFHy+eefR+4Txh00tE2s +; WLHCNE907NiRBhGLtGjRwoRyRz2md2FRABLr0OjxAkzu4ho9GO9SGhgFQ1wjU58wjB09e/aU5cuXRz +; IYwCCCqdSJEydk8+bNJosOBhP2CdcPiOhu3boZYR3FqOPSogAk1mC8S21o9NALBCCOgjW9ZpJssvUJ +; I1gYk6cjR45EihhB3yzyBTE9XLx4sQwbNkzatGnDPuEYwKWNuJ19+/YVvce3vgsh1BSAxAo0etRm7T +; s1xo18jR5rOXVygqqGPH4n7pGrT7h///6mGzhKnzDuDuLjjh49KuvXr5dJkyaxTzhPcISOCr4oYd2u +; LVwbQF4kBSCpF/hBhMvKmn6IFguIuagTQPb4ugViYMZxEkscJZtBBKIE7tOoNWMQAufOnZOdO3caYw +; lcxrjLxriY7GDqOmjQINm6das8fPhQNC1cE7h+/bqsWbPGPAMFIIkNLidr+qFZCqLcAWSPr3tg+lfV +; UNdrJn6RqU8Y4qR58+bmWBeiLopACfqEDx48KKtWrZKxY8dK586djajUtB8Vg5YtW8r06dPl5MmTJe +; /xzXc9e/ZMjh07JrNmzTLh1DASUQCSWLDHNz+Q4Vf3SJhGD3cJomDoBCauk6lPGCHEMCaggeLy5cuR +; +4RPnTplJlsQCRUVFdKsWTNV+1KhwHsKww0mZ1GO2F1b6B5GfiR+MWjbtq35uuKZKABJXtDoER+Ivc +; pXasTfVDZNOM2mRhSARA/Z+oQxsUIH7YEDByKZFDAlQp8wTA1Lly6VESNGmFBjn+NiIJYwFcWEFO+P +; poUpJTIg0QYDAYvjfbjHA8MPBSCJzJgmL6n6oegiOBJmqLP7QPgxCoZoI71PODCIwKnat29f0waCZp +; CwFfQJI88OfcJTp0414gFTRU37VX3BNBVH4XBKX7161bke37CF439UAY4bN046dOhgmmTw/ZB+v5MC +; kITCHl/iI1UN6QQm+sjWJ9y+fXuT/Xf8+PFIQcXor0WfMLpsFy5caPqEMVH0wSCC92vo0KHmHmWUoG +; 2XFgQ8prgrV640znB8zfBLQKYpLgUgyQnjXYivVDWs6QXW9JoJAZkMIkGfMIQc7vlFqSrDEWLQJ4z7 +; b+gThoEAn0vTPpYPyEScPXu2nD171uke30wLx/yHDh2SadOmmcpAGD0gZrOJdgpAkhH2+BLfQRTMqF +; /qes2EpJMtLgYiDlM9TPii9AnjODHoE0bMDCZLcBtr2tPCgKjFUTmyFO/evSva1s2bN82RPQQ+RCyy +; IcOc3BSA5Huwx5eQGifw4Aa6XjMhdcnWJwxDwJgxY2Tv3r3muDdsoWUEd+HQJ4z6OXQR425ZEgwimJ +; RNnDjRBGNHaVNxaeE4H7E08+bNM2He+LrivmaUo3oKQFIL9vgSUsPKP9AJTJJD3T5hTLzgCO3Vq5e5 +; L4aA4Ch9wpiOoU9406ZNJhOvd+/e5o6Zpn0uAEfjXbt2NYIWWYja1v379809xdGjR5s7noHRI+rzUw +; ASA+NdCKnN9sZ0ApNkAYMITniCn/vBNBBHhnD7Rp2APX36VC5evGj6hBctWiRVVVWmk1hTnzDEL2Ju +; MAHF82haOLbH+4+oHhxbf/bZZ+Z58p3GUgASGj0IycD+j2oEICaBml43IbnI1ieMvLuBAweae2SIgY +; kiQnDv7MiRI7Ju3TrTJ4zwaQhK1/c8TMtwZAoRpa3HF8f1OIafPHmydOrUydzFxHsex51NAegx7PEl +; JDdVDRkFUwwgtjW93iSQzSACUTF37lzjgo1Sd/bo0SPzsTt27DCiCiIS99Bc3PMglAYMGGBaMdB8om +; nheB7H1MhzRGdzq1atjNGjPncwKQA9hT2+hIQDJ/BktrYUjM2NaioRR7xac+Su6bUngWx9wjBF4Hh0 +; 165dRuCFLRgRcIcQjSO4TwhzCYSkS33COCbFMTfuL0bJQXRp4VgewdyIpykvLzdfn6hGj1xQAHrKmF +; //HX/rJiQE5AAyCqYwYLIa9GJX/GuN4xrOa/5cKh5oJRr5nfhO3xvS+4ThKsU9MwQLR+kThikBjlRM +; 2GbOnGnup5W6TxgTsh49esiqVasiNaG4tnAcj9zG4cOH1+rxtfHeUAB6Sp8Gf2d+4PKCOyHZgSDBPU +; BNr9l1IDqCTux0AYj3GYx7jc7rYoDpa/k/17z/mfaIwCCCo0ZEpETtwsXHXL582ZgrlixZ8ly4lMIg +; giNSOGQxmdTY43vu3DljsoFTOzB62HwfKQA9BQIw+IGL38T5Wzch3we/IDEKxh5rv3s/O/yktvirKw +; ABfjml+aZwjH+t9vufbZ8IDCIQUv369TNmDxg/whamhbdv35Zjx46ZYOUpU6aYKVyx+oQxxcQRdHV1 +; tZleauvxxbE7BDSaVzp27GjiXeIaPXJBAegp6QIQ4JiLd3AIqQ2jYOxRV3TkEoABOILnL6f2CO5c1n +; 3/c+0V6QYRBD/PmjXLHPNG7RM+f/68uUu4YMECGTJkiJlkFbJPGEIJfw9MKVHuL7q0IJwRto3japhV +; cvX42oAC0FPqCsDgt27ewSGkNoyCqR/pR435CsDgl1MK8PqTfucyHwEYkN4nPHToUFMLF8VJi6PMGz +; dumI7a1atXy7hx4wrWJ4wsQtw9RG1dFAezSws9vojUQbg23p+wHl8bUAB6SiYByDs4hHyfqoaMgolL +; LtERVQDyl9P6kenOZRwBCNKngWjQwBHrpUuXIvcJnz59WrZt22bcrDhShpi0sZ/hNfXp00fWr18fKc +; PQtXXr1i3TrIJAbYRy2zR65IIC0FNyCUDewSHkBYiBwS9Fml5zqYHo6P/zaOIvigDkL6fxyHbnMq4A +; DAgMIjjOHTt2rOzfvz9Sm0bQJ4yPX7ZsmYwcOdKEMtfniBMicvz48XL48GGVPb4QxTgexx3JoMe3WI +; YZCkBPCROAvINDSA34N1DVUNdrLiXVb0UXfvkKwPRpoKb3pBTkunNZXwEIgj5h3FFDHzDurUXp0w36 +; hI8fP26mXtOmTTMuVzhc8/n7IZJwVIqYmmvXrom2heNz3I1EZmKcHl8bUAB6SlQByDs4xHeCKBj+Ih +; QOMuXyFX9BEHTUn0fp00B+Tb5PlDuXNgQggAjE9A5Hwoh6gZhDYHE+fcJ79uwxR8m4V4jImSjTL0zJ +; hg0bZv4sjCaaFo7LEZOzfPlyqaioMFPUQho9ckEB6Cn5CEDewSE+s6kRo2DCiCM6wPj/PFrHzxXcF8 +; TPmXx+Li38na73qdBEvXNpSwAGpPcJo6Zs8+bNZsoXRQwFfcKoOEPeIO4W5uoTbteunamqg7s47O6h +; awuiF5mEiMXp3Lmz6fEttNEjFxSAnpKvAOQdHOIr+H5nFEx2pr6ev+DAvbTlb3//c0FsVzWM9gspvx +; 4vyPfOpW0BCNINImVlZTJ//nwTZBzFIJLeJwxxhz5huGDTPz8EZv/+/c2x8b1790TTwjPCCQ2TCiJq +; 4FZGtmKpq/IoAD0lrgDkHRziI/iepxO4Ntky5cKAUIFgyfZ5MQ3EvUse/UYjzp3LQgjAgMAgAgEHkw +; eOaR8/fhwqkur2CaPBAyHI+HwwR0yePNkcL2vs8cV9xzlz5pjpZmD0KNXULx0KQE+pjwDkD2LiG1UN +; a0SJptdcSOIeNeYjopFCUPdImL941ibOnctCC0AQTANh7IC7Fffd4P4Na+SAQQTTPQRN4xgZodOTJk +; 0yDSSYoGlbOAZHXiKEMI6ucURebKNHLigAPcWGAORRDPEFRsHUECVTLhOYFO5skv/fhyYWvPdsKqpN +; 3DuXxRKAAcE0EEeeEHKIaonSyYtQZFS44VgYDl9t8S4IocYdxcWLF5t8wkL0+NqAAtBTbAnAAPYJky +; SDyVNVQ12v2TYQHVEy5eoyvp7CmT9XahPnzmWpBCBI7xPGHT50AyP4OGxhGgghpa3HF8fd+/btM4YW +; HGHD6FGIHl8bUAB6im0BCPhbOkkqOI7E97ivBqh8MuUCIBbX8nTAGnHvXJZaAIJ0gwhEEZpAEICsra +; 4t14JQxR3GNWvWGBMLIm1KFe8SFQpATymEAKRBhCSVIAoG/6npddeXuKIDd9M0PafrxL1z6YoADMA0 +; ENMwTMWQ4wfXLyritC8cax89etT0EBerx9cGFICeUigBmG4QYVwMSQpBFIxP9YhxRQdcqZqe02Xi3r +; l0VQCCYBoIJ2y3bt1Mk8fXX3+t7qg3WLdv35YtW7aYHl+EYRerx9cGFICeUmgBSIMISRpVDf2Igomb +; KRfX6EEyE7XHV5sADMDRaMuWLY3TF80YuPOnaeH4+syZM7Jw4ULp2bOniXdx0eiRCwpATymGAAxgnz +; BJAphqJz0KJm6mHIwJmp7TdeLcudQkADEFRFh00OOrbfqHY+vdu3fLuHHjpEOHDs97fF0/8q0LBaCn +; FFMA0iBCkgDEH76PNb3mfIjb47vZs3uRhaSQRg9XBCCOftH7CwEVJSDapYUpJeJpVqxYIf369TMTTN +; eNHrmgAPSUYgvA4EiYfcJEK/jexfewptcchfr2+BI7FNro4YIAxB05OICjVMS5tpBNeOjQIZk2bZrp +; 8dVi9MgFBaCnlEIABtAgQjSSxCgYmz2+JB717fHVIABxPFpRUSEbN2407RjaFkwqyC+srKyUNm3aON +; HjawMKQE8ppQAMpoE+OSqJfnCFAd+7STA2FarHl+SHjR5f1wUgJmVoATl27Ji6Rg/0DqOWbt68ecax +; 7FKPrw0oAD2l1AIwADVPPBImGsD3Kb5ntf/iUoweXxKOrR5fVwUg3LBdu3Y1PcBfffWVaFvoJEZO4a +; hRo0yPb2D0cG0vrw8UgJ7iigAEVQ0ZF0N0UNVQrxDC5C6u0YPxLvaw3eProgCEMWLEiBGyd+9eefr0 +; qWhauJt48eJF41Du27fv8x5frUaPXFAAeopLAjA4El74O04DidtgYj1OofmBRg83KESPr2sCELEo8+ +; fPNyJKW7bfkydPZP/+/ebIulOnTk73+NqAAtBTXBOAAdhcGRdDXEVjFAx7fEtPqeJdiikAIZTQgbt1 +; 61Z58OCBaFrIIcQx9dq1a2XQoEGmxzcpRo9cUAB6iqsCMJgGsk+YuAi+L/E9quG1ssfXDUoZ71IsAY +; g8PMSjnDhxwhgnNC0YU2BQQSNJeXm5Ma0kyeiRCwpAT3FZAKZPAxkXQ1xiU6Oa703Xvy/Z41t64t65 +; 1CQAcS8ONWirV6+WmzdviraFIOqDBw/K+PHj1fX42oAC0FM0CMBgGkiDCHEF16NgIDoqX8lfANDoYR +; cXjR62BSCOSMeMGSMHDhyQZ8+eibaF13zp0iWT7wcBiGq6pLl8w6AA9BQtAjAAEw0aRIgL4PvRxSsK +; uLOHu3v5bv7s8bVLsXt8iy0AcTSKJoxFixaZWjRtPb7BgkHlzp075vgXInDq1KnSo0cPc/zr+v5tCw +; pAT9EmAAH7hIkLVDV0Lwomjuhgj69dXDd62BCAqD5DG8bOnTvV9fhmW3D+nj9/Xnbt2iULFiyQIUOG +; mDuNvANIEotGAQjYJ0xKjUtRMIx3cQMNRo/6CkBUoMEocebMGfnLX/4iSVp4nhs3bpiuX9xnxJFwly +; 5dEn8kTAHoKVoFYAANIqRUYLOvaujG68h3o8cRMY0e9oh751KTAIQpAoHI69evN0em2hYmlQ8fPgzN +; JMRRNj7u9OnTsm3bNpkzZ47079/fZAFq2NPjQAHoKdoFYDANZJ8wKTb4nsP3X6l+AYHoQCdvvps8jR +; 52iXvnUpMAhPiZMGGCHDlyRGWPL6aVy5Ytk+rqajPdi9JKgue8evWqCYRGjd3IkSNNuDWbQEhiSIIA +; DEA4L4+ESbEIomBKcR8V07s4Gzx7fO2i0eiRjwBEjy8y8SCerl+/LtrW/fv3zZ2+sWPHGsMKIl769e +; snq1atitRLjGnh3bt35fjx47Jp0yaTcdi7d29TCadlj48CBaCnJEkAAhhEGBdDigEmf/ieK/b3W9we +; Xxo97KEt3iWOAITIGT58uOzZs8cYJDQt9Pgi2gXCtaKiwvT4opcYLSUwsEAIwu0L52+UiSYmhqi0w3 +; sB1/PQoUNNSwgEspa9PhcUgJ6SNAEIaBAhxaKqYfGmajR6uIGrPb42BWD79u1l3rx5cuHCBSOmNC2I +; VWQSTpkyJWOPL0QbTB0Ie0bd2+bNmyPdacT7gJBrHIOjKm7ixInStWtX87k17fmZoAD0lCQKwAAaRE +; ihwfcYrh4U+u+JIzpwL23524V/bb6AO5da412iAmEEwwNEEY5PNS2YN+DgXbdunQwePFhat26dtccX +; YhD/PaaBOBqG2D137lwkV/OjR4/k7NmzsmPHDvPn0HuM2jgte34mKAA9JckCMH0aqGmjIXpAFAyuHR +; Tq88fNlIM5BIKlkM/uE3HvXGoDUzPcd9PY44vXDccupnItWrSI1OMLQwcmeBBwMHngiDdKriHeH9yJ +; xKRx5cqVpgkF00at9XEUgJ6SdAGYPg3kkTCxDX65wC8ZhfjccTPlaPSwi5YeXxt8/fXXom3BpIG4lh +; EjRki7du3M0W4+uX3BNBCCEQ0guDcI929YswkMIpiSnjx5UrZs2SIzZswwMTmYOrq+79eFAtBTfBGA +; wTSQBhFik0JEwbDH1w2SavTIhaaF41o0dyxevNg4c2H0gHElrjEjmAbC3DFp0iQTFxOl2/ibb76Ry5 +; cvy969e2XJkiXGOAOTiSaDCAWgp/gkAAPYJ0xsgQgYfE/Z+sWCPb5ukLR4l6QJQBzTQnAhm7Bjx47f +; M3rEJTCIYIqHuBiEXt+6dSv09cAgcvv27ed9wpMnT5bu3bubO4au7v3pUAB6io8CELBPmNgAv0jg+8 +; lGEHkc0YEJFeNd7KG5x9cHAYhj12vXrpmatgEDBphpHeJdbIYzpxtEEPw8e/ZsOXXqVCSDCIRpep8w +; zCiYTLreJ0wB6Cm+CkBAgwixQVXDlCz8Xfw/H/eoEXfTSvXMSUR7j2/SBSCOY48ePSozZ840/bwwbk +; CkFUpcYRqIqSKmi1VVVcb1++DBg9DXCaGIkOmDBw8aoTpu3DgpKytz2iBCAegpPgvAAMbFkPoAJ3Bc +; 40Vc0cEeX3skpcc3yQIQx7CIpoEQw/06GD2KIajSp4FwF+O+IQKmo/QJQyxicgiDCqaIOFL+5JNPnN +; r/AygAPYUC8MU0kH3CJA7IAZyeZxYge3zdIEk9vkkUgJimnT59WhYuXCg9e/Y08S71MXrEJTCI4DgX +; E70vvvgicp/wlStXTJ8w3MWImkHItmt9whSAnkIBWBv2CZN8wTWCfI5j2ePrBr4aPbQIQEzQdu/ebX +; p8IZowPYNBo1T36QKDCO4c9unTxxzvIng6bGFaiKaRL7/8UjZu3Ggq6Hr16uVUnzAFoKdQAH4f9gmT +; fMD3StUvon0se3xLj4/xLpoEIBy1mJotX77cHJu2bNnSutEjLhCfeB04Ekbm4PTp042wixKcjYkhqv +; UQNl1dXS2VlZXm2VwwiFAAegoFYGbYJ0yiAjd5xb/m/hj2+LoBjR5uC0CIJJgnMCVDRRsMGIU0esQl +; mAZiKjlkyBATBH3v3r3Q54O4Rdj24cOHTZ8wYmzKy8tL3idMAegpFIC5oUGERAECMNv3CXt8S0/cO5 +; e+UaoF0wSEETL0IKhy9fi6QrpBBC5fxL4gAgYiL+xZ0Sd85swZ2b59u8ydO9dE2kDslupZKAA9hQIw +; HMbFkDBwBFw3V5I9vm7gS4+vVgGI49MTJ04YIdStW7fIPb6ukN4nPGrUKPn888/lyZMnkZ4bmYYwlK +; xYsUJGjx5tQq1LcdRNAegpFIDRYZ8wycakX9e+N8oeXzfwqcdXowDEsSny9SCccKcuMHpo2kMBxCpe +; N4wdcCtD0EHcRekTxnsAAYyYG/QJw2BS7D5hCkBPoQDMD/YJk0xM/01NjBAmd3GNHox3sQeNHm4LQB +; yTwhCB7lwInvr2+LpCMA3EETbq4I4cOWKiYMIW+oSRL4jpIbIGhw0bJm3atCna+0EB6CkUgPFgnzBJ +; Z8l/pGTCazR6uECcO5ekeAIQdWnIxZs4caJ06tTJWo+vKwQGEYRV424f7jWiJzhsQRQj8BptJ+ggnj +; RpkjkSL0afMAWgp1AAxod9wiQA0798xR+MHms5TbaG7z2+rgtAHIdev35d1qxZI4MGDTI9vjjqdC0U +; 2QbpBhHc65szZ44xfUTtEz537pzs3LlT5s+fb94r3IsspECmAPQUCsD6QYMIAZvey08AssfXLox3cV +; sA4hj02LFjMmvWLBN7AsOEJqNHXNL7hIcPH25E3cOHD0Pfr/Q+4VWrVpkwbMTiFMoVTQHoKRSAdmBc +; jN9gEhxVALLH1x5x71yS4glAHH8iJw/32orZ4+sKwTQQghdHurj3iKDrfPqEt27dasRzRUWFef9sv0 +; YKQE+hALQHDSL+AvEfJgBp9LALjR72sbkwxTp79qxpvUD1WVKMHnEJDCJo/xg/frwcOHDAmD/C1rNn +; z4xg3LdvnyxdulRGjBhhHNM2j84pAD2FAtA+NIj4SS4xAmOCpmdxHfb4ui0AccwJRyuETocOHUy8S5 +; KMHnFJ7xPu27evaQNBAHbYwrQQk9T0PmHEzWCqaON1UQB6CgVgYaBBxD/QBlJ3Q2WPr11o9HBbAEKo +; XL16VVauXCn9+/d3qsfXFdL7hNu3b2+y/5ADGKVPGAHTiM/ZvXu3LFy40LSm2OgTpgD0FArAwsE+Yb +; 8Y+ovamynjXexCo4fbAhDHmei4nT59uqlGg9HDxR5fV0jvE66srJRt27bJ/fv3Q99nHK3fuHHDvNdw +; VGPK2qVLl3oFaFMAegoFYOGhQcQPAjMC4l1o9LAHjB6Vr+gSUr4JwJs3b5qjSQgZBBi73uPrCulxMX +; BHY6p38eLFSH3COGZHtAyEI2JmMHGN2ydMAegpFIDFAdNAZMVp2nhJfoz9Vc3xJHt87bH87RpBrUlE +; pdP3X1LSXtHrz3fh2PLkyZMmr6579+7Pe3x9NXrEJTCI4P0bM2aMMXxE6RNGvA6O3NEnvHz5clOphz +; uX+R65UwB6CgVgcZnxBo+EkwodvnbRHu8y5fWUzHxD12vOZ+G4Erl2o0ePNnfZtPb4ukJ6nzBc07hH +; ieDsKH3Cd+/elePHj8umTZvMEXzv3r3N3cuofzcFoKdQABYfGEQYF0NIZrTHu/R4uabhBSawsp/qeu +; 1RFo4n0Vu7bNkyk0uHeBcaPewRTANxlA63L6rhovQJP3361Bwf79mzRxYtWiRVVVWmkzjKNJYC0FMo +; AEsDDSKEfB/tPb647wvhB+b9VtdrjyIAcSyJ48bJkycnssfXFdL7hAcOHGgme3fu3An9+kCc4z7mkS +; NHZN26daZvGeHT+Brl+vsoAD2FArC00CBCSM3xueZ4F0z6cF8xEH8Ak0BNz5BLAOIYEtVkEBWDBw82 +; kyUaPQpLukEENXDz5s0zwdpR+oQfPXpkPnbHjh3mz0FEwpWd7e+iAPQUCsDSwz5h4jNwTGsSSXVB/E +; +68NM6/csmAHH8iADi2bNnS9euXb3p8XWFoE8Y7ztaQJABCIEXtmDQwR1CNI7gPiHMJZjaZhLtFICe +; QgHoDpgG8kiY+EISenxh8qgr/rRO/zIJQBw7ooc2qB/zrcfXFdL7hOG2RiUc6uHCDCL432HWgVMbfc +; wzZ840DSSY3qZ/fgpAT6EAdAv2CRMfSJLRoy44Ctb0LJkEII4Zz507Z8wEcJT63uPrCoFBpFWrVuZ+ +; 36FDh0xXcNjCx1y+fFn27t0rS5YskeHDh0vbtm2ffz0pAD2FAtBN2CdMkor2Hl/Eu2QSftqnf4EAfP +; z4sREKEyZMkI4dO9Lo4RiBQQRTvH79+pl7mTB+hK2gT/jYsWOyYcMGmTJlivTo0cNMFSkAPYUC0F3Y +; J0yShPYeXwi7ukaPJE3/wLVr12TVqlUyYMAAM2VivIubpBtEEPw8a9Ysc8wbtU/4/PnzsmvXLlmwYI +; HpE6YA9BQKQLdhXAxJAtp7fHFXMZfwCxj4b7qeqy4zZswwvbLs8dVBep8wcv+2b98uDx48CBWBQZ8w +; jpBXr15NAegrFIA6YFwM0Yj2Hl/UuMHRG0X8aZ/+AYQP0+ihi/RpIFzauLeJoG4c+eZaQZ/w6dOnKQ +; B9hQJQD+wTJpqASUJ7j++m96KJvyRM/wCNHnoJDCIw7IwdO1b2799v2kHCFmJ+KAA9hQJQH+wTJq6j +; 3eiByV9U4QcgdjU9XzY07V3k+wR9wri7Cfc27nMiwDtsUQB6CgWgTtgnTFxEe7xLQLZ8v2wgDFrT81 +; EAJheIQEwDcSSMqJdp06aZIO9cBhEKQE+hANQLDSLEJbQbPQJ8nf5RACaLdIMI6vs2b94sd+/epQAk +; L6AA1A8NIqSUwOjR/+e6hE4uwnL+kjr9owBMHukGkbKyMpk/f76JgPn2228pAAkFYFJgnzApBdp7fO +; uS7/QPJhFNz0cB6CeBQQTxPqNGjZI9e/aYwG8KQM+hAEwW7BMmxUJ7j28mfJ7+UQAmm2AaCKd3z549 +; Zfny5Sb4G3EwFICeQgGYPNgnTApJUowedfF9+kcB6AfBNLB169YyadIkOXz4MAWgr1AAJhf2CRPbTH +; 1dl6DJB9+nfxSA/pDeJ9y/f38KQF+hAEw27BMmNtDe4xsGp38UgL6RbhChAPQUCsDkQ4MIqQ9JiXfJ +; Be7O+j79owD0E0wDKQA9hQLQHxgXQ/JBe49vVDj9owD0HQpAT6EA9AsaREgUtPf45kO+078kup8pAP +; 2GAtBTKAD9hAYRkg3tPb75wOkfBSChAPQWCkB/oUGEpJN0o0cmOP2jACQUgN5CAeg37BMmwAejR10w +; /cNRN6d/FIC+QwHoKRSABNAg4idJ6/HNh3ynf+M8OBrXtHcRe1AAegoFIAnANHDlH3QJGBKfpPX45g +; OnfxSA5AUUgJ5CAUjqMuMNHgknnaTfZQuD078UBSB5DgWgp1AAkkzAIMK4mOSR1B7ffIgz/Sv7qa5n +; jIumvYvYgwLQUygASTZwJLzwd5wGJoUk9/jmA1o88pn+TfHofdO0dxF7UAB6CgUgCSM4LtMkdsgLfI +; x3yQanf7nRtHcRe1AAegoFIIkC+4R14mO8Sy7ynf7NfEPX89UXTXsXsQcFoKdQAJJ8YFyMDhDv4rvR +; oy75Tv+AT9M/CkB/oQD0FApAki/sE3YbGj0yw+lfOJr2LmIPCkBPoQAkcWGfsHv41OObL5z+haNp7y +; L2oAD0FApAUh/YJ+wGNHrkhtO/aGjau4g9KAA9hQKQ1Bf2CZcWGj3C4fQvGpr2LmIPCkBPoQAktqBB +; pLjA6FH5ii6BUQo4/YuOpr2L2IMC0FMoAIlN2CdcHDDR6vATXeKiVHD6Fx1NexexBwWgp1AAkkLAPu +; HCQaNHdDj9yw9NexexBwWgp1AAkkLBPmG7MN4lfzj9yw9NexexBwWgp1AAkkJCg4gdaPTIn3ynf/N+ +; q+v5CoGmvYvYgwLQUygASTGgQSQeMHr0/7kuEeEKnP7lj6a9i9iDAtBTKABJsWCfcH5Uv6VLPLgEp3 +; /x0LR3EXtQAHoKBSApNpgG8kg4N+zxrR/5Tv96vKzr+QqFpr2L2IMC0FN8FYAwKGh6vUmDfcKZodGj +; /uQ7/VvESetzNO1dxB4UgJ7iiwDE1Gnh71KyqVHNRosJFC7Wa3qGJMI+4RdMfV2XWHAVTv/io2nvIv +; agAPSUJAvAya+HO1AhCKsa6nqupOF7nzB7fO2R7/Rv+du6nq/QaNq7iD0oAD0lSQIQQgITpWDKFxUI +; RIhFTc+aNHw1iDDexS75Tv8G/puu5ys0mvYuYg8KQE/RLgCrGtYIBxsRI/g8ECKanj9p+BIXwx5f+0 +; DMcfpXPzTtXcQeFICekoQJoM3aMWwMNIiUlqT3CbPHtzBw+ld/NO1dxB4UgJ6SlCNgiLZ8j36zERhE +; OA0sLUnsE2aPb2HId/oHsajp+YqFpr2L2IMC0FOSdAfQdu0YDSKlx6awLyWMdyksnP7ZQdPeRexBAe +; gpSXQB27xHBjGJSZSm508a2vuEafQoLJz+2UPT3kXsQQHoKUmNgbHtKsWdNB4JlxZtBhH2+BYHmDny +; EYCIitH0fMVE095F7EEB6ClJD4K2WTuGzYNxMaVFS1wMe3yLA6d/dtG0dxF7UAB6ig9NIFUN7dWO0S +; DiBi73CbPHt3hw+mcXTXsXsQcFoKf4UgUHwWazdgzGBMbFlBabwt4GNHoUl3ynf5ve0/V8pUDT3kXs +; QQHoKb4IwACbtWPsEy49toV9XNjjW3w4/bOPpr2L2IMC0FN8E4CBaLB5jwzTQB4JlxYcCdsS9vlO/d +; jjW3x6vMzpXyHQtHcRe1AAeoqPAjBdNNiMi6FBpLQU2yDCeJfSwelfYdC0dxF7UAB6is8CMBANNu+R +; sU+49BQ6LgbxLjR6lA5O/wqHpr2L2IMC0FN8F4ABNu+RYdOhQaS02Bb26Ue+NHqUlkVvcfpXKDTtXc +; QeFICeQgH4gkIYRDgNLC02hT17fEsPp3+FRdPeRexBAegpFIC1YZ9w8qivsKfRwx3m/TY/Acij+vzQ +; tHcRe1AAegoFYGbYJ5ws4gp7Gj3coeynnP4VGk17F7EHBaCnUADmFg3oALYhAgE+V8W/6noPkkZUYQ +; +jR+UrujbvpMPpX+HRtHcRe1AAegoFYDiY3tk4EsaG1PtfdD17EgkT9uiL7fATXRt30uH0rzho2ruI +; PSgAPYUCMBq4RxbXVZruHKUAdIdMwp5GDzeZ+UZ+AnAcv46x0LR3EXtQAHoKBWB04twjqysoKADdIh +; D2jHdxl/Y/yX/6h4mhpmd0BU17F7EHBaCnUADmT5R7ZNmcoxSA7gFhr2WDdpnm/18Ntj8vp3/FQ9Pe +; RexBAegpFIDxRUO22rFczlEKQDfRsDlr4LP/PyXN/t+a/7Tx+Tj9Ky6a9i5iDwpAT6EArB+YNgRHwl +; GcoxSAbqJhc9bER/9PjRCs7+eZ8np+AhDTwlI8b1LQtHcRe1AAegoFYP3BNHD2f4/mHKUAdBMNm7M2 +; Gv9fNcSdBnL6V3w07V3EHhSAnkIBaIcuEQ0EFIBu4vrGrJWm/3dK3vo/4t0NxHSd07/iomnvIvagAP +; QUCkA7UADqxvWNWTM4Dn79f6uZBubz55DHmI8A5PSv/mjau4g9KAA9hQLQDhSAunF9Y9YO7gP+9n9P +; yR9finYkPPQXnP6VAk17F7EHBaCnUADagQJQN65vzEkAwg8CEEIw7EiY07/SoGnvIvagAPQUCkA7UA +; DqxvWNOUngSPi//2PN/cBM/3vff+H0r1Ro2ruIPSgAPYUC0A4UgLpxfWNOGpgGvvt/pqTRf/n+/zbv +; t5z+lQpNexexBwWgp1AA2oECUDeub8xJBdNACMHg/59v9Aunf3bRtHcRe1AAegoFoB0oAHXj+sacZD +; ANhBDE/51v9Aunf3bRtHcRe1AAegoFoB0oAHXj+sbsC5gAQgQi1JnTv+Kjae8i9qAA9BQKQDtQAOrG +; 9Y3ZN2AEWf42p3/FRtPeRexBAegpFIB2oADUjesbs49gGpitCxhGEU3PogVNexexBwWgp1AA2oECUD +; eub8w+g2lg3VzAHi/regYtaNq7iD0oAD2FAtAOFIC6cX1j9h1MA4N4GE7/CoemvYvYgwLQUygA7eCq +; ABzcICUz3iju36kR1zdmUsPIVzn9KySa9i5iDwpAT6EAtIOLAhBuyv0fpeRws5RsapSSqobF+7u14f +; rGTEgx0LR3EXtQAHoKBaAdXBOAS39fI/zSgRic/Hrx3hNNuL4xE1IMNO1dxB4UgJ5CAWgHVwTgqF+m +; ZM8H3xd/6UAc4mi42O+Ry7i+MRNSDDTtXcQeFICeQgFoBxcE4MLf5RZ+6eAyPcRiKd4rF3F9YyakGG +; jau4g9KAA9hQLQDqUUgJjmQdBFFX/pR8Jz3uQ0ELi+MRNSDDTtXcQeFICeQgFoh1IJQDh88xV+daFB +; xP2NmZBioGnvIvagAPQUCkA7lEIAYnIXdt8vn2mgz3Exrm/MhBQDTXsXsQcFoKdQANqhVBNAiMCVf7 +; AjAgGiY0r9XpYC1zdmQoqBpr2L2IMC0FMoAO1QahMIpndB5l99j4NL9R6WEtc3ZkKKgaa9i9iDAtBT +; KADt4IILGK7ede/UXwT6eBTs+sZMSDHQtHcRe1AAegoFoB1cyQHEkTBy/uozDcS9Qt+cwa5vzIQUA0 +; 17F7EHBaCnUADawbUmENzlixMNE4BMwWK9dy7g+sZMSDHQtHcRe1AAegoFoB1c7AIOpoFxXcE+BUW7 +; vjETUgw07V3EHhSAnkIBaAcXBWAApoFxjoRxn7DYr7VUuL4xa6b3z3S9Xp/RtHcRe1AAegoFoB1cFo +; AA08A4BhFfYmFc35g10/mnKZn7Zs1/anrdPqJp7yL2oAD0FApAO7guAANQ/ZbPNNAXQ4jrG7Nmprye +; kvXvpmTVH/leu46mvYvYgwLQUygA7aBFAALc7cvHIOKDIcT1jVkr7X9cI/7SGf9azX+v6Tl8QdPeRe +; xBAegpFIB20CQAQT5xMfiYpE8BXd+YtRJM/+pS/VZKuv03Xc/iA5r2LmIPCkBPoQC0gzYBGIA7flH6 +; hCEWXXrdtnF9Y9YIpnwb3q0hkwjEkfCwhpwGuoSmvYvYgwLQUygA7aBVAIKofcJJNoS4vjFrBNO/QA +; DmEoI0iLiDpr2L2IMC0FMoAO2gWQAGhPUJJ7kn2PWNWRvp078oIhBHwpqeL6lo2ruIPSgAPYUC0A5J +; EIAgrE8YLmKXX39cXN+YtTHh31Oy8b3oIhBTaE3Pl1Q07V3EHhSAnkIBaIekCECQyyCSVEOI6xuzJj +; D9g/gLyCYCN6TdBdT0fElG095F7EEB6CkUgHZIkgAMyGYQSaIhxPWNWROjf1lbAIYJQcTCaHq+JKNp +; 7yL2oAD0FApAOyRRAIJMfcJJ7Al2fWPWAqZ/m97LLACziUAaQNxB095F7EEB6CkUgHZIqgAMqNsnnL +; SeYNc3Zi2M+WWNAMwlAtOFIJzCmp4v6Wjau4g9KAA9hQLQDkkXgKBun/Dk13W9/ly4vjFrANO/FW+/ +; EIBhQhACMAlh0B1+kpLl3z332u/+bZT/c/zP4wKa9i5iDwpAT6EAtIMPAjAg6BNOUk+w6xuzBka8+n +; 3xl0sEQjRper5M9P/ue2ffhy9+KcL/XfmKrmdIR9PeRexBAegpFIB28EkAgqBPOCk9wa5vzK7T/rsp +; 2Oo/pmTr+ynZ3CiaEByiWCgB/CKULS4JuYaYDGp6HqBp7yL2oAD0FApAO/gmAAGmfxCAVQ11ve5MuL +; 4xuw6mXhB/AVtCROCad3Q9Xzq9fpaSnU3Cm3MghLVNAzXtXcQeFICeQgFoBx8FYEASjoFd35hdJ5j+ +; 1RKBmAZmEYHDG+p6vgBE1oQJv0zh6VqmgZr2LmIPCkBPoQC0g88CMAm4vjG7TN3pX13qHgnDLAHDiK +; ZnhIBb+07+4i99GojJoevPqWnvIvagAPQUCkA7UADqxvWN2WUyTf++Nw1ME4HTlEW/jHw1vvBLBwYR +; 10OvNe1dxB4UgJ5CAWgHCkDduLwpu0zY9C/TNLBMUfAzzBw2xF9gDHH9eTXtXcQeFICeQgFoBwpA3b +; i+MbtKlOlfOjPe0PFcUY0eUcEUUcNza9q7iD0oAD2FAtAOFIC6cX1jdpF8p39Aw/Rv6uv2hB8mnprC +; oTXtXcQeFICeQgFoBwpA3bi+MbtIvtO/uW+6/TyY+kGw2RJ/UxXW3Gnau4g9KAA9hQLQDhSAunF9Y3 +; aNAf+W//Sv+8vuPk+ceBftjt9MaNq7iD0oAD2FAtAOFIC6cX1jdo18p3/Lfu/mcyDexabRY47jU84w +; NO1dxB4UgJ7y0VsNpNcrf69qs3YRCkDduL4xu0Sc6V+ff3HvOTClS+/xrW/Ei+YO4JY//jtp8puXVe +; 1dxB4UgB7T6A9vSdmvfqhqw3YNCkDduL5BuwSmedqnf/1/bk/8ISBaY+9vQPOf/UDe/W4P0LRnEbtQ +; ABJp+cY/8Ug4JhSAunF9k3YFTPLynf5hYujis9gwfbge7BxG09f+SdUeRQoDBSAxNP2PX0n3hv+gav +; N2AQpA3bi+UbtCvtM/3BV0+XkwucO9vThGD03xLnX59L/9g7z/u1+p2ptI4aAAJM/BkXD7X/8jp4F5 +; QAGoG9c3bBeAizff6Z+We3H5HAlrN3p8/It/VLUfkcJDAUi+Bw0i0aEA1I3rm7YLIMcvH/G34V1dz4 +; dp4PK3cxs9+iv+XoHRo/GbDVTtQaQ4UACSjGAa2PG1H6nazEsBBaBuXN+8S02Sp391wb2+utNADT2+ +; uWjW4Eeq9h1SXCgASU4++e3LPBLOAQWgblzfwEtN0qd/dUk3iGjp8c3GB7/+saq9hhQfCkASSuO3f8 +; O4mCxQAOrG9U28lKC/N9/p3wjlogngSFi70eO9//iNqj2GlAYKQBIJHAm3ef0lTgPrQAGoG9c381Iy +; 4w2/pn9J4MNXX1K1r5DSQgFI8gIGEcbFvIACUDeub+ilIs70b+yvdD1jkmC8C4kDBSDJmyAuRtNGXy +; goAHXj+sZeKuJM/9orbsXQDONdSFwoAElsGBdDAagd1zf3UgAhx+mf+yDehUYPUh8oAEm98L1PmAJQ +; N65v8qUgzvQPR8aanlE7NHoQG1AAEiv42idMAagb1zf6YhNn+gfBqOkZtcMeX2ILCkBiDR/7hCkAde +; P6Zl9s8p3+AU7/igONHsQ2FIDEKr71CVMA6sb1Tb+YcPrnLjR6kEJAAUgKgi8GkSgCEMGyE/5d13P5 +; gobNv1hw+uceMHo0+c3Lqn72Ez1QAJKC4UOfcJgARJ1U0Cu68g8pGdxA1/MlHQ0ioBhw+ucezX/2A3 +; n3u5+hmn7mE11QAJKC0+LNHyf2SDiXAESRfHqxPNjeOCWTX9f1jElGgxAoBpN/zemfS9DoQYoBBSAp +; CjCIJDEuJpMARKH8zibfF38B+z9KyZw3OQ10AS2CoJDEmf7NfVPXM2qB8S6kmFAAkqKRRINIXQEIYZ +; dN+NVlU6OUVDXU9bxJQ4swKCRxpn/dX9b1jBqg0YMUGwpAUnSSZBAJBCCmfpsbRRd/daeBmp45SWgS +; CIUgzvRv2e91PaPrwOjBeBdSCigASUlISp8wBOD41/IXfpmmgTwSLj6ahEIhQIVbvgJwwL/pekaXad +; bgR6p+bpNkQQFISgqmgZqPhIf+omaKV18BGEwDaRApLprEgm0w/Vv9x/zEHz5e0zO6DHt8SamhACQl +; R3ufMCZ3696xIwLB0t9zGlgsNAkG28SZ/lW+ousZXYRGD+IKFIDEGbT3CeMun61pIOJiRv1S1/NrRJ +; NwsEmc6d+Gd3U9o4t8+OpLqn4mk2RDAUicQnufMEQbxJutI2EaRAqLJvFgE0zyOP0rHuzxJS5CAUic +; Q7tBBMe3OMa1NQ1kXEzh0CQibMLpX/FgvAtxFQpA4iza42LGvZaSPR/QIOIymoSELTj9Kw7s8SWuQw +; FInEZ7nzCmgegApkHETTQJCltw+ld42ONLNEABSFSgvU94xhv2joQhAjU9u8toEhU2iDP9G/Gqrmcs +; NezxJVqgACRqgEGk/NUfqBIY6cAgYiMuhgLQHpqEhQ04/SscjHch2qAAJKrQ3idswyBCAWgPTQKjvs +; SZ/iErUNMzlgoaPYhGKACJSnw2iFAA2kOTyKgvcaZ/ZT/V9YzFBkaPxm82UPWzk5AACkCilqTExVAA +; lg5NYqM+xJn+4d6qpmcsNuzxJdqhACTq0d4njGlgPkfCFID20CQ46kO+0z/A6V922ONLkgAFIEkEPv +; UJUwDaQ5PoiMuAf+P0zxY0epAkQQFIEgNEoA99whSA9tAkPuKy7Pec/tmAPb4kaVAAksSR9D5hCkB7 +; aBIgceD0r/6wx5ckFQpAkkiSbBChALSHJiESB07/6gfjXUiSoQAkiSaJcTEUgPbQJEbypfvL+Yu/uW +; /qesZCgXgXGj1I0qEAJIknaQYRCkB7aBIl+RJn+gfRqOkZCwGNHsQXKACJNyTFIEIBaA9NwiQf4kz/ +; IBg1PWMhYI8v8QkKQOIVSTCI4JK+ptfsMprEST7gKDdfAQjDiKZntAmNHsRHKACJd2jvEyb20CRSog +; ITB6d/0aHRg/gKBSDxFu0GEVJ/NAmVqGBCnK8ARFWcpme0AYweTX7zsqqfWYTYhAKQeA2mgR1f+5Eq +; 0ULsoUmwRKH9T/IXf6iJ0/SMNvjk5z+Ud7/7t6/pZxUhtqEAJOQ7Wrz5Yx4Je4gm0RIFTv/CodGDkB +; ooAAn5T2AQ0RwXQ/JHk3AJI870b8O7up6xPjDehZDaUAASkgYNIn6hScCEwelfdtjjS8j3oQAkJAM0 +; iPiBJhGTC07/MvPZT/+e8S6EZIECkJAsaO8TJuFoEjO5mPzr/AXgiFd1PWO+NGvwI1U/bwgpNhSAhI +; SAaSCPhJOJJkGTDU7/vg97fAkJhwKQkAho7xMmyRWAuPu3LU8BOPZXup4xKjR6EBIdCkBC8kB7nzBJ +; lgDs8N3073CzlBz4KCU7m0Sf/qEtRNNzRoHxLoTkBwUgIXmivU+YJEcAznmzRgCCQx+nZM8H4dNA3B +; fU9IxhsMeXkHhQABISA8bFJANNQqcuwfSvLl98Nw3c3tiP6R97fAmJDwUgIfWAcTG60SR26pI+/asL +; poG7P/i+AMR9QU3PmA32+BJSfygACakn7BPWiybRk0626V9d9n9Y+0g4CdO/5j/7AXt8CbEABSAhFm +; jSpIkMavN7GfQrTgM1oUn4pDP19WgCEBz8bhq4q0kypn80ehBiDwpAQupJq1atZPr06XLq1Cm5enKf +; LOz2uioR5DOaxE9A1OlfOpveq/lzmp4zHca7EGIfCkBCYvL+++9Lr169ZO3atXLr1i0J1jeP7sn60a +; 2k8vX/WZUY8hFNIiggn+kfwF1BTc9XFxo9CCkMFICExKBZs2Yybtw4OXjwoPzpT3+STOvSoc0y+g// +; qypB5BuahBDAFA95f1GE374PU9Jf4TMGwOjR+M0Gqn4uEKIJCkBC8uDdd9+VsrIyWbJkiVy9elX+9r +; e/Sa4VTAM1iSKf0CSIwMhXo4m/6rd0PVdd2ONLSOGhACQkIh988IEMHTpUdu3aJY8fP5Z8FqaBPBJ2 +; D02iKOr0DyJR03PVhT2+hBQHCkBCItC2bVuZPXu2nDt3Tr799luJszANpEHELTQJo7Dp3+ZGKSn/Z1 +; 3PlA6NHoQUFwpAQnLQuHFjqaiokI0bN8rdu3fFxtpbPZTTQEfQJJByTf9gDNH0LHX58NWXVP1cICQJ +; UAASkoVPP/1UJk2aJEePHs1q9Ii7bpw9KJM++i+qxFIS0SKQsk3/MPXr9TM9z1EX9vgSUjooAAmpw3 +; vvvSddu3aV5cuXy1dffSWFWjSIlB4tQinT9I/xLoSQ+kABSEgaH330kYwYMUL27t0rT58+lWIsxsWU +; Dg1Cqe70D/Eula/oeO2ZYI8vIW5AAUjIf9KhQweZP3++XLx4Uf76179KMRcNIqVBg2BKn/6tfUd3ow +; d7fAlxBwpA4j3o8R04cKBs3bpVHjx4IKVcNIgUF9cFU/r0b/xr7r/eXLDHlxC3oAAkXtOyZUuZNm2a +; nDhxQv785z+LC4sGkeLhumjC9I9GD0JIIaAAJF7SqFEj6dmzp6xevVpu3rwpri32CRcHl4UT7vnR6E +; EIKRQUgMQ7Pv74YxkzZowcOHBAnj17Ji4vGkQKi8viSXOoM3t8CXEfCkDiDejx7dy5syxatEiuXLkS +; 2uPrysI0cMXARqqElRY0iSotsMeXEB1QABIvQI9vZWWl7NixQx49eiSaFhzJV69elRkDW0m/X/xPqg +; SW62gSVhpgjy8heqAAJImnTZs2MnPmTDlz5oz85S9/EU3rm2++kcOHD8v06dOlrKxMWn/wH1L+7/+L +; KpHlMprElcuwx5cQfVAAksTy/vvvS9++fWX9+vVy584d0bZgTtm0aZMMHTrUiNhmzZqZZ2r0x7ek/a +; //Ufo0+DtVYstFNIksV2GPLyE6oQAkiaR58+YyYcIEOXLkiPUe30IvxNGcOnXKhFL36NFDWrRoIU2b +; NjUVdenP+NFbDaR7w39QJbhcQ5PQcg3GuxCiGwpAkiggksrLy2XZsmVy/fp10bbu378vu3btMi7l9u +; 3byyeffCKNGzfO+ryN/lAzDdQkulxCk+ByCca7EKIfCkCSGD788EMZPny47NmzR548eSKa1rfffiuX +; Ll0ywrWiosIEVKOXGHmFUZ4d00AeCVMAFhrEu9DoQUgyoAAkiQDTsrlz58qFCxeMmNK0nj59ajIJp0 +; yZIp06dTLH13AtI7Ymn/cA08CyX/1QlQArNZrEV6mh0YOQZEEBSFSD49H+/fvL5s2bzfGppoUcwhs3 +; bsi6detk8ODB0rp1axNSDaNHfd6Tlm/8E6eBFIBWYY8vIcmDApCo5bPPPjNTs+PHjzvT4xt1wZiC1z +; 1nzhzp2rXrc6NHvlO/bDT9j1/RIEIBWG9o9CAkuVAAEnXgXlz37t1l1apV8vXXX4u2dffuXdm+fbuM +; HDlS2rVrZ+Jdchk9Yr9Pf2BcDAVgfGj0ICTZUAASVeCIdPTo0bJ//34TkqxpIYT6/PnzsnjxYundu7 +; eZYMK4UjfexTYwiPR65e9VCTMKwNIBo0eT37zs3L99QohdKACJCnA0CoPEwoUL5fLly6YeTdN6/Pix +; 7Nu3z2QTduzY0Rg9mjRpYu3INwxMAzu+9iNV4owCsPg0/9kP5N3vvldc+rdPCCkMFIDEeSCUYJLAse +; nDhw9F04JQvXbtmqxevVoGDhworVq1MlPMqPEutmnx5o95JEwBmBEaPQjxCwpA4jRwxs6YMUNOnz6t +; rsf32bNncvToUdND3KVLF/n0009jxbvYBgYRxsVQAAYw3oUQP6EAJE6CKBTck1u7dq3cvn1btK1bt2 +; 6ZaJqqqipp27bt8x5fV95fGkQoAAGNHoT4CwUgcQ6IpXHjxsmhQ4fU9fhiSnnmzBlzV7Fnz54m3qUY +; Ro+4+G4Q0STWbAKjR+M3Gzj5PUkIKQ4UgMQZcDRaVlYmS5YsMffmEJSsaT148EB2794tY8eOlQ4dOj +; zv8S31kW8YPvcJaxJttmjW4EdOfz8SQooDBSBxAoQg47gUAkpbjy+MHleuXJEVK1ZIv3798u7xdQUf +; +4Q1CTcbsMeXEBJAAUhKDu7IoREDGXkae3wPHjwoU6dOlc6dOztj9IiLb33CmsRbfaDRgxBSFwpAUj +; JwPIqJ2caNG+XevXuiaeF4Gi0kGzZskMrKSmnTpo2VHl9X8KVPWJOIi8uHr76k6nuPEFIcKABJScCk +; bNKkSXLs2DF1Pb54vSdOnJC5c+dKt27drPf4uoIPfcKahFycqR97fAkh2aAAJEUF9+IgmnBf7saNG6 +; JtYVK5Y8cOGTVqlOnxDYwemr4GeX29Em4Q0STo8oHxLoSQMCgASdGAMWLkyJGmEk1bjy/uJl64cEGW +; Ll0qffr0ed7jq83oEftrl9C4GE2iLgrs8SWERIUCkBQFxKIsWLBALl26pK7HF67k/fv3myNr9BEXu8 +; fXFZLYJ6xJ3IXBHl9CSD5QAJKCAqE0aNAg2bp1q7oeXxg9rl+/btpI8AxBj29SjB5xSVKfsCaBlwv2 +; +BJC8oUCkBQM5OFNmzZNTp48qa7HFw0kMKjMmjVLysvLjWkliUaPuMAgUv7qD1SJvSQKQMa7EELiQg +; FIrIMJGWrQ1qxZIzdv3hRtC93DmFgOHz7cyR5fV0hCn7AmsVcXGj0IIfWBApBYBWIJVWgIR3727Jlo +; WphSnj17Vqqrq6VXr17PjR6u9vi6gmaDiCbBF8AeX0KIDSgAiRVwNIomjEWLFplaNG09vrif+Pnnn8 +; v48eOf9/j6aPSIi1aDiCbhB9jjSwixBQUgqTeoPkMbxs6dO+Xx48eiacGRfPXqVVm5cqX0799fbY+v +; K3zy25dVHQlrEn/s8SWE2IQCkNQLVKDBKIGjU209vsgiPHz4sEyfPl3KysrU9/i6QuO3f6OmT1iD8K +; PRgxBSCCgASSxgiujbt6/pwr1z545oWzCnoIN46NChRsTS6GEXHAm3ef0l56eBros/9vgSQgpFikdd +; JF8QhDxx4kQ5evSoiUvRtNDje+rUKZk/f7507979eY8vjR6FAQYRl/uEXRV+7PElhBSaFI68MPngsR +; cJAyIJmXjLli0zAcna1v379809xTFjxkj79u0T3+PrCi73Cbso/hjvQggpBikcgQUbIacgJBuIQ0Eu +; HpyyT58+FU0LdxNRQQfhWlFRYeJdaPQoPi7Gxbgk/BDvQqMHIaRYpNDSMHPmTBN9wWkgyQSmZfPmzZ +; MLFy6o7PH94osvZPLkyV73+LoCpoEuGURcEX80ehBCik0KmyQuxK9bt85MR9B1ymkgARBKAwYMkC1b +; tpjjU00LOYRfffWV+b4ePHiwtG7dmj2+DtHyjX9ywiDigvhjjy8hpBSkgg0TrQ1ob5gwYYIpvcfmzy +; Myf8Ex6dSpU+X48ePGOKFpwZiC1z179mzp2rXrc6MHp35ugT7hUhtESj31o9GDEFIqUukbJ4730OKw +; dOlS45DEpskjYb+A6O/Ro4esWrVKvv76a9G2EEmzbds2GTFihLRr187Eu9Do4fD3W4n7hEsl/mj0II +; SUmlSmTfTRo0eya9cus4kGd6Z4JJx8cEQ6evRoc2cOIcmaFnp8z58/b6roevfuzR5fZZTKIFJs4Qej +; R5PfvKzqa0MISSapXBsq2h3mzp1rLs/TIJJc8DXF17i6ulouX76szuiB+rm9e/ea6wsdO3ak0UMppe +; gTLqb4a/6zH8i73z2jpq8JISS5pMI2VxypoTFh4MCBz4/TOFVJDhD2Q4YMkR07dpjJr6YFoXrt2jVZ +; vXq1Mavg7iqmmLy7qpsWb/64aEfCxRJ/NHoQQlwjFSXTDZfq0fowZcoUU5tFg0gygDMWEUCnT582E1 +; 9NC6alI0eOyIwZM6RLly7s8U0YMIgUIy6m0MKP8S6EEFdJnThxQu7evRt67IdYDbQ/rFixQnr16mXu +; V2EayA1XHzjKxz05RKTcvn1btK1bt27J5s2bpaqqij2+CaYYBpFCij/2+BJCXCa1fPlyc+kfR2lRel +; 0RrIv7VjALIF6D00BdoPVl/PjxcvjwYZU9vphWLliwwDiV8f1Ho0fyKaRBpBDC77Of/j3jXQghzpPq +; 37+/zJkzR7Zv3y5nzpyRhw8fmmlfroVqLbRCLFy40By/0SDiPhBJ+Foh4gdiP+xr7Np68OCB7N69W8 +; aOHVurx5ffc35QqD5h2+KvWYMfqXpfCSH+ksImWl5ebhyUa9asMZMh5L9B5IWte/fuydatW6WyspJ9 +; wg6DPMdhw4bJnj17zARX08L3IZzJmFT369dPWrZsyR5fj8E00OaRsE3xxx5fQogmUvh/MEXBxgohh6 +; keJi2Y8EURCziWQ+sCLuMjeBfTQGzOnMy4Ab4miPJBRl4UUe/SgkEJ7TRoJOncubOJd6HRg9jsE7Yh +; /Gj0IIRoJJX+/8GkqGfPnmbDRfTLl19+aWJgouTCYWqICWLfvn3NhIbTwNKC9x8Ts02bNplJraaF4+ +; kbN27I+vXrTUQNe3xJJmz0CddX/DHehRCilVTd/wLTO0yN0AKC+2L79u0z9XBRDANojzhw4IAxGWCi +; SINIaYA5YvLkyUbAa+zxhTMdU8tu3bqxx5fkpL59wvWZ+tHoQQjRTCrb/4BojYqKCpk1a5a553fq1C +; lzET/MPIBpIe5sLV682Gzg7BMuHhDbeM9XrlxpJmjaFuKIYEYaOXKk+SUkuFeq6WtAik994mLiiD/2 +; +BJCkkAq1/8I4Ya7V3Berlq1ytzH+uqrryKFBsNNvHPnTmM+YJ9w4cGx+6hRo8zEVmOPL+6cLlmyRP +; r06cMeXxKLOHEx+Qg/9vgSQpJEKuwDMLnDMdygQYNk/vz5RtSdO3fO9K9G2diR24aYGXS0Mi6mMOC9 +; RTbepUuXVPb4QrROnDiRPb6k3uTbJxxV/LHHlxCSNFJRPxDirXv37uZuGS7noxoOjQxRnKX4uA0bNg +; gyB3GZnwYRO0AoQZhv27bNTFw1raBZBsYh9Eyzx5fYJGqfMI0ehBBfSeXzwRBtqN7CsS7u+H3++edm +; 6hTlyBGX+5ExCAEJVycNIvUDgmn69Oly8uRJlT2++AUC90vZ40sKRZQ+Yca7EEJ8JRXnD2FSg7tayP +; 5DJytcm4gaidInjBYKhPoibgb3vHgknB94v9DFvHbtWjNZ1bbQPbxlyxbzS0Tbtm3Z40sKSphBhEYP +; QoivpOL+QUzvcGcLncArVqx43iccJXYE977QSgHTAqY/nAZGA2Jp3LhxxoyDKZqmhSnl2bNnTdA4xD +; +NHqSYZDOIZDJ6NH6zgapnI4SQOKTq+wkg4AYMGGBy24I+4UePHkXqE0Y7BYwlZWVlNIjkAO8J3iMc +; u1+9elVdjy/uJ0LwQ7x26NDBxLvQ6EGKTaY+4Vbs8SWEeErKxifBZh70CeNoMp8+YWS/4Uhw8ODB7B +; POAITx0KFDZdeuXZGc1y4tXAlAiDgmxDAAsceXuEB6nzB7fAkhvpKy9Ykg2mBMgFiprq42Ex9ku6HP +; NWzh2BitFdOmTWOfcBq4Izd79mwTu6OtxxfGoEOHDpmvKaaXNHoQlwj6hGn0IIT4Ssr2J8S9LpgUsP +; Gjh/b48eOR+4QRMr169WpjMAn6hH0UDHhutLAgOgcTUm0L0190SVdWVhrXOHt8CSGEELdIFeKTYnrX +; vn17U+m1bNky2b9/v7m7FqVPGBNDfDzaR2AU8M0ggkkZQpERkxLl/XJpYZKLWJp58+aZzMigx5dH+o +; QQQohbpAr5yXGnr1+/fuYYE2HFaAWJ2ieMfMFFixZJ165dvTCIQCThWRGRg0motnX//n3TEgNXOMQ/ +; e3wJIYQQd0kV+i+ACMAdMDhAcbyLe2E3btyIFF4MsQhncVVV1XPnaBKnSTg2HzFihOzduzfSnUmXFu +; 4mXrx4UZYuXSp9+/Y1U1saPQghhBC3SRXjL8HkDsIATl901sLRigiYKK5WHCueOnXKtEYgQiRp00A8 +; E6JwIKK09fg+efLE5D+i3aVTp07s8SWEEEKUkCrmXwbx1qNHDyMYYHA4duyYaYaIInxu3rwp69atM0 +; fKSegThlBCBy4icDDp1LRwhI9jakT+QNSj2o9GD0IIIUQP/wMgz7W7tKzNVgAAAABJRU5ErkJggg== +; thumbnail end +; +; + +; external perimeters extrusion width = 0.45mm +; perimeters extrusion width = 0.45mm +; infill extrusion width = 0.45mm +; solid infill extrusion width = 0.45mm +; top infill extrusion width = 0.42mm +; first layer extrusion width = 0.50mm + +M73 P0 R1 +M73 Q0 S1 +M201 X4000 Y4000 Z200 E2500 ; sets maximum accelerations, mm/sec^2 +M203 X300 Y300 Z40 E100 ; sets maximum feedrates, mm / sec +M204 P4000 R1200 T4000 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2 +M205 X8.00 Y8.00 Z2.00 E10.00 ; sets the jerk limits, mm/sec +M205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec + +M486 S0 +M486 Axyz-cali-cube-by-r3d_v1.stl +M486 S-1 + +;TYPE:Custom +M17 ; enable steppers +M862.1 P0.4 ; nozzle diameter check +M862.3 P "MK4S" ; printer model check +M862.5 P2 ; g-code level check +M862.6 P"Input shaper" ; FW feature check +M115 U6.0.4+14924 + +M555 X118.1 Y94.1 W32 H17.8 + +G90 ; use absolute coordinates +M83 ; extruder relative mode + +M140 S60 ; set bed temp +M104 T0 S170 ; set extruder temp for bed leveling +M109 T0 R170 ; wait for temp + +M84 E ; turn off E motor + +G28 ; home all without mesh bed level + +G1 X42 Y-4 Z5 F4800 + +M302 S160 ; lower cold extrusion limit to 160C + + +G1 E-2 F2400 ; retraction + + +M84 E ; turn off E motor + +G29 P9 X10 Y-4 W32 H4 + +M106 S100 + +G0 Z40 F10000 + +M190 S60 ; wait for bed temp + +M107 + +; +; MBL +; +M84 E ; turn off E motor +G29 P1 ; invalidate mbl & probe print area +G29 P1 X0 Y0 W50 H20 C ; probe near purge place +G29 P3.2 ; interpolate mbl probes +G29 P3.13 ; extrapolate mbl outside probe area +G29 A ; activate mbl + +; prepare for purge +M104 S230 +G0 X0 Y-4 Z15 F4800 ; move away and ready for the purge +M73 P2 R1 +M73 Q2 S1 +M109 S230 + +G92 E0 +M569 S0 E ; set spreadcycle mode for extruder + +; +; Extrude purge line +; +G92 E0 ; reset extruder position +G1 E2 F2400 ; deretraction after the initial one before nozzle cleaning +M73 P3 R1 +M73 Q3 S1 +G0 E7 X15 Z0.2 F500 ; purge +G0 X25 E4 F500 ; purge +M73 P7 R1 +M73 Q7 S1 +G0 X35 E4 F650 ; purge +M73 P9 R1 +M73 Q9 S1 +G0 X45 E4 F800 ; purge +M73 P10 R1 +M73 Q10 S1 +G0 X48 Z0.05 F8000 ; wipe, move close to the bed +M73 P11 R0 +M73 Q11 S1 +G0 X51 Z0.2 F8000 ; wipe, move quickly away from the bed + +G92 E0 +M221 S100 ; set flow to 100% +G21 ; set units to millimeters +G90 ; use absolute coordinates +M83 ; use relative distances for extrusion +M572 S0.036 ; Filament gcode + +M142 S36 ; set heatbreak target temp +M107 +;LAYER_CHANGE +;Z:0.2 +;HEIGHT:0.2 +G1 E-.7 F2100 +M73 P12 R0 +G1 Z.8 F720 +G1 X119.793 Y100.346 F18000 +G1 Z.2 F720 +M73 Q13 S1 +G1 E.7 F1500 +M73 P13 R0 +M73 Q13 S0 +M204 P500 +;TYPE:Skirt/Brim +;WIDTH:0.5 +G1 F2400 +G3 X122.509 Y98.796 I3.45 J2.89 E.12138 +G1 X127.541 Y98.802 E.19125 +G3 X131.211 Y102.562 I-.798 J4.45 E.21308 +G1 X131.243 Y107.123 E.17335 +G3 X127.321 Y111.223 I-4.475 J-.354 E.23343 +M73 P14 R0 +M73 Q14 S0 +G1 X122.82 Y111.238 E.17107 +G3 X118.777 Y107.321 I.41 J-4.469 E.23126 +G1 X118.762 Y102.82 E.17107 +G3 X119.755 Y100.392 I4.481 J.416 E.10117 +M204 P4000 +M204 T4000 +G1 X119.755 Y100.392 F18000 +G1 X120.108 Y100.68 +M204 P500 +G1 F2400 +G3 X122.539 Y99.253 I3.133 J2.553 E.10944 +G1 X127.511 Y99.259 E.18897 +M73 Q15 S0 +G3 X130.754 Y102.593 I-.772 J3.995 E.18817 +M73 P15 R0 +G1 X130.786 Y107.104 E.17145 +G3 X127.248 Y110.771 I-4.013 J-.33 E.20969 +G1 X122.842 Y110.781 E.16746 +G3 X119.229 Y107.248 I.384 J-4.007 E.20763 +M73 Q16 S0 +G1 X119.219 Y102.842 E.16746 +M73 P16 R0 +G3 X120.071 Y100.727 I4.022 J.391 E.08785 +M204 P4000 +G1 X120.071 Y100.727 F18000 +G1 X120.429 Y101.01 +M204 P500 +G1 F2400 +G3 X122.571 Y99.71 I2.811 J2.22 E.09729 +G1 X127.424 Y99.709 E.18444 +G1 X127.911 Y99.835 E.01912 +G3 X130.297 Y102.625 I-1.178 J3.423 E.14633 +G1 X130.328 Y107.084 E.16947 +G3 X127.178 Y110.319 I-3.55 J-.306 E.18585 +M73 P17 R0 +M73 Q17 S0 +G1 X122.865 Y110.324 E.16392 +G3 X119.681 Y107.178 I.356 J-3.546 E.18394 +G1 X119.676 Y102.865 E.16392 +G3 X120.392 Y101.057 I3.564 J.365 E.07485 +M204 P4000 +G1 X120.392 Y101.057 F18000 +G1 X120.754 Y101.333 +M204 P500 +G1 F2400 +G3 X122.659 Y100.16 I2.479 J1.891 E.08697 +G1 X127.338 Y100.159 E.17783 +M73 Q18 S0 +G3 X129.84 Y102.658 I-.588 J3.091 E.14272 +M73 P18 R0 +G1 X129.871 Y107.064 E.16746 +G3 X127.11 Y109.867 I-3.088 J-.281 E.16199 +G1 X122.889 Y109.867 E.16042 +G3 X120.133 Y107.111 I.328 J-3.084 E.16018 +G1 X120.133 Y102.889 E.16046 +G3 X120.718 Y101.381 I3.1 J.335 E.06219 +M73 P19 R0 +M73 Q19 S0 +M204 P4000 +G1 X120.718 Y101.381 F18000 +G1 X121.086 Y101.651 +M204 P500 +G1 F2400 +G3 X122.745 Y100.609 I2.141 J1.568 E.07626 +G1 X127.254 Y100.609 E.17137 +G1 X127.622 Y100.702 E.01443 +G3 X129.391 Y102.745 I-.878 J2.547 E.10759 +G1 X129.414 Y107.043 E.16335 +G3 X127.046 Y109.414 I-2.627 J-.256 E.13798 +G1 X122.955 Y109.414 E.15548 +G3 X120.586 Y107.046 I.258 J-2.627 E.13791 +M73 P20 R0 +M73 Q20 S0 +G1 X120.586 Y102.955 E.15548 +G3 X121.051 Y101.7 I2.641 J.264 E.05142 +M204 P4000 +G1 X121.051 Y101.7 F18000 +G1 X121.425 Y101.961 +M204 P500 +G1 F2400 +G3 X122.828 Y101.059 I1.796 J1.254 E.06503 +G1 X127.172 Y101.059 E.1651 +G3 X128.941 Y102.828 I-.405 J2.174 E.10106 +G1 X128.957 Y107.021 E.15936 +G3 X127.022 Y108.957 I-2.165 J-.229 E.1125 +M73 P21 R0 +M73 Q21 S0 +G1 X122.978 Y108.957 E.1537 +G3 X121.043 Y107.022 I.231 J-2.165 E.11246 +G1 X121.043 Y102.978 E.1537 +G3 X121.391 Y102.01 I2.178 J.237 E.03946 +M204 P4000 +G1 X121.391 Y102.01 F18000 +G1 X121.772 Y102.261 +M204 P500 +G1 F2400 +G3 X122.907 Y101.51 I1.444 J.95 E.05316 +G1 X127.093 Y101.51 E.15909 +G1 X127.306 Y101.561 E.00832 +G3 X128.49 Y102.907 I-.543 J1.671 E.0715 +G1 X128.5 Y107 E.15556 +M73 P22 R0 +M73 Q22 S0 +G3 X127 Y108.5 I-1.706 J-.206 E.0869 +G1 X123 Y108.5 E.15203 +G3 X121.5 Y107 I.206 J-1.706 E.0869 +G1 X121.5 Y103 E.15203 +G3 X121.74 Y102.311 I1.716 J.211 E.02794 +M204 P4000 +G1 X121.74 Y102.311 F18000 +G1 X122.129 Y102.549 +M204 P500 +G1 F2400 +G3 X122.981 Y101.962 I1.083 J.66 E.0405 +G1 X127.019 Y101.962 E.15347 +G3 X128.038 Y102.981 I-.238 J1.257 E.05818 +M73 P23 R0 +M73 Q23 S0 +G1 X128.043 Y106.977 E.15187 +G3 X126.977 Y108.043 I-1.248 J-.182 E.06141 +G1 X123.023 Y108.043 E.15028 +G3 X121.957 Y106.977 I.182 J-1.248 E.06141 +G1 X121.957 Y103.023 E.15028 +G3 X122.099 Y102.601 I1.255 J.186 E.01701 +M204 P4000 +G1 X122.099 Y102.601 F18000 +G1 X122.498 Y102.82 +M204 P500 +G1 F2400 +G3 X123.045 Y102.414 I.716 J.394 E.02671 +G1 X126.999 Y102.423 E.15028 +G3 X127.586 Y103.045 I-.22 J.795 E.03417 +M73 P24 R0 +M73 Q24 S0 +G1 X127.586 Y106.955 E.1486 +G3 X126.955 Y107.586 I-.8 J-.169 E.03588 +G1 X123.045 Y107.586 E.1486 +G3 X122.414 Y106.955 I.169 J-.8 E.03588 +G1 X122.414 Y103.045 E.1486 +G3 X122.471 Y102.874 I.8 J.169 E.00686 +M204 P4000 +G1 X122.471 Y102.874 F18000 +G1 X122.889 Y103.07 +M204 P500 +G1 F2400 +G3 X123.113 Y102.871 I.343 J.162 E.01171 +G1 X126.887 Y102.871 E.14344 +G3 X127.129 Y103.113 I-.119 J.361 E.01349 +G1 X127.129 Y106.887 E.14344 +M73 P25 R0 +G3 X126.887 Y107.129 I-.361 J-.119 E.01349 +M73 Q25 S0 +G1 X123.113 Y107.129 E.14344 +G3 X122.871 Y106.887 I.119 J-.361 E.01349 +G1 X122.871 Y103.127 E.1429 +M204 P4000 +G1 E-.7 F2100 +G1 X122.871 Y103.127 F18000 +M486 S0 +G1 X126.093 Y103.907 Z.258 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;TYPE:Perimeter +;WIDTH:0.499999 +G1 F2400 +G1 X126.093 Y105 E.04154 +G1 X126.093 Y106.093 E.04154 +G1 X123.907 Y106.093 E.08308 +M73 P26 R0 +G1 X123.907 Y103.907 E.08308 +M73 Q26 S0 +G1 X126.033 Y103.907 E.0808 +M204 P4000 +G1 X126.55 Y103.45 F18000 +M204 P500 +;TYPE:External perimeter +G1 F2400 +G1 X126.55 Y105 E.05891 +G1 X126.55 Y106.55 E.05891 +G1 X123.45 Y106.55 E.11782 +G1 X123.45 Y103.45 E.11782 +G1 X126.49 Y103.45 E.11554 +M73 P27 R0 +M73 Q27 S0 +M204 P4000 +G1 X126.549 Y103.846 F18000 +G1 E-.7 F2100 +G1 X124.836 Y104.09 Z.23 F18000 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;TYPE:Solid infill +;WIDTH:0.570672 +G1 F2400 +G1 X125.704 Y104.958 E.05387 +G1 X125.704 Y105.704 E.03274 +G1 X124.296 Y104.296 E.08738 +G1 X124.296 Y105.042 E.03274 +G1 X125.164 Y105.91 E.05387 +M204 P4000 +M106 S127.5 +;LAYER_CHANGE +;Z:0.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.4 +M201 X3999.96 Y3999.96 + +G1 E-.7 F2100 +G1 X125.164 Y105.91 Z.2 F18000 +M73 Q28 S0 +G1 X126.368 Y103.632 Z.4 F9625.247 +;AFTER_LAYER_CHANGE +;0.4 +M74 W0.0364665 + +M104 S225 ; set temperature +G1 X126.368 Y103.632 F18000 +M73 P28 R0 +G1 Z.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y106.368 E.09261 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y103.632 E.09261 +G1 X126.308 Y103.632 E.09058 +G1 X126.775 Y103.225 F18000 +M73 P29 R0 +;TYPE:External perimeter +G1 F1200 +G1 X126.775 Y106.775 E.12016 +G1 X123.225 Y106.775 E.12016 +M73 Q29 S0 +G1 X123.225 Y103.225 E.12016 +G1 X126.715 Y103.225 E.11813 +G1 X126.774 Y103.621 F18000 +M73 P30 R0 +M73 Q30 S0 +G1 E-.7 F2100 +G1 X123.978 Y103.978 Z.449 F18000 +G1 Z.4 F720 +G1 E.7 F1500 +;TYPE:Solid infill +G1 F1200 +G1 X126.022 Y103.978 E.06919 +G1 X126.022 Y106.022 E.06919 +G1 X123.978 Y106.022 E.06919 +G1 X123.978 Y104.182 E.06228 +G1 X124.385 Y104.385 E.01539 +G1 X125.615 Y104.385 E.04163 +G1 X125.615 Y105.615 E.04163 +M73 P31 R0 +M73 Q31 S0 +G1 X124.385 Y105.615 E.04163 +G1 X124.385 Y104.589 E.03473 +G1 X124.794 Y104.794 E.01549 +;WIDTH:0.454205 +G1 X125.206 Y104.794 E.01409 +G1 X125.206 Y105.206 E.01409 +G1 X124.794 Y105.206 E.01409 +G1 X124.794 Y104.998 E.00711 +M106 S255 +;LAYER_CHANGE +;Z:0.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.6 +M201 X3999.96 Y3999.96 + +G1 E-.7 F2100 +G1 X124.794 Y104.998 Z.4 F18000 +G1 X126.368 Y103.632 Z.6 F7921.103 +;AFTER_LAYER_CHANGE +;0.6 +M74 W0.0405131 + +G1 X126.368 Y103.632 F18000 +G1 Z.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y106.368 E.09261 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y103.632 E.09261 +M73 P32 R0 +M73 Q32 S0 +G1 X126.308 Y103.632 E.09058 +G1 X126.775 Y103.225 F18000 +;TYPE:External perimeter +G1 F1200 +G1 X126.775 Y106.775 E.12016 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y103.225 E.12016 +M73 P33 R0 +M73 Q33 S0 +G1 X126.715 Y103.225 E.11813 +G1 X126.774 Y103.621 F18000 +G1 E-.7 F2100 +G1 X123.978 Y106.022 Z.664 F18000 +G1 Z.6 F720 +G1 E.7 F1500 +;TYPE:Solid infill +G1 F1200 +G1 X123.978 Y103.978 E.06919 +G1 X126.022 Y103.978 E.06919 +M73 P34 R0 +G1 X126.022 Y106.022 E.06919 +M73 Q34 S0 +G1 X124.182 Y106.022 E.06228 +G1 X124.385 Y105.615 E.01539 +G1 X124.385 Y104.385 E.04163 +G1 X125.615 Y104.385 E.04163 +G1 X125.615 Y105.615 E.04163 +G1 X124.589 Y105.615 E.03473 +G1 X124.794 Y105.206 E.01549 +;WIDTH:0.454205 +G1 X124.794 Y104.794 E.01409 +G1 X125.206 Y104.794 E.01409 +G1 X125.206 Y105.206 E.01409 +G1 X124.998 Y105.206 E.00711 +;LAYER_CHANGE +;Z:0.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.8 +M201 X3999.95 Y3999.95 + +G1 E-.7 F2100 +M73 P35 R0 +G1 X124.998 Y105.206 Z.6 F18000 +M73 Q35 S0 +G1 X126.368 Y103.745 Z.8 F7642.212 +;AFTER_LAYER_CHANGE +;0.8 +M74 W0.0445597 + +G1 X126.368 Y103.745 F18000 +G1 Z.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y104.381 E.02153 +G1 X126.293 Y104.418 E.00283 +G1 X126.176 Y104.691 E.01005 +G1 X126.168 Y105.176 E.01642 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.832 Y105.176 E.00491 +G1 X123.826 Y104.591 E.0198 +G1 X123.695 Y104.294 E.01099 +G1 X123.632 Y104.263 E.00238 +G1 X123.632 Y103.745 E.01753 +G1 X123.787 Y103.832 E.00602 +G1 X124.243 Y103.832 E.01544 +G1 X124.502 Y103.775 E.00898 +G1 X124.616 Y103.632 E.00619 +G1 X125.372 Y103.632 E.02559 +G1 X125.441 Y103.751 E.00466 +G1 X125.597 Y103.814 E.00569 +G1 X126.213 Y103.832 E.02086 +M73 P36 R0 +G1 X126.316 Y103.774 E.004 +M73 Q36 S0 +G1 X126.316 Y103.774 F18000 +G1 X126.341 Y103.352 +;TYPE:External perimeter +G1 F1200 +G1 X126.388 Y103.326 E.00182 +G1 X126.416 Y103.225 E.00355 +G1 X126.775 Y103.225 E.01215 +G1 X126.775 Y104.587 E.0461 +G1 X126.591 Y104.709 E.00747 +G1 X126.575 Y105.176 E.01582 +G1 X126.61 Y105.29 E.00404 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.379 Y104.55 E.00464 +G1 X123.225 Y104.477 E.00577 +G1 X123.225 Y103.225 E.04238 +G1 X123.584 Y103.225 E.01215 +G1 X123.612 Y103.326 E.00355 +M73 P37 R0 +G1 X123.787 Y103.425 E.00681 +G1 X124.312 Y103.413 E.01778 +G1 X124.444 Y103.225 E.00778 +M73 Q37 S0 +G1 X125.543 Y103.225 E.0372 +G1 X125.643 Y103.398 E.00676 +G1 X125.695 Y103.419 E.0019 +G1 X126.213 Y103.425 E.01753 +G1 X126.289 Y103.382 E.00296 +G1 X126.657 Y103.225 F18000 +G1 X126.022 Y104.182 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G2 X126.022 Y105.751 I1.328 J.784 E.05571 +G1 X126.022 Y106.022 E.00917 +G1 X124.178 Y104.178 E.08827 +G2 X124.783 Y103.978 I.084 J-.761 E.02225 +G1 X125.204 Y103.978 E.01425 +G2 X125.736 Y104.178 I.499 J-.519 E.01978 +G1 X125.822 Y104.178 E.00291 +G1 X123.978 Y106.022 E.08827 +G3 X124.178 Y105.344 I2.325 J.317 E.02402 +M73 P38 R0 +G2 X124.12 Y104.348 I-3.037 J-.324 E.03392 +M73 Q38 S0 +G1 X124.049 Y104.221 E.00492 +;LAYER_CHANGE +;Z:1 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1 +M201 X3999.95 Y3999.95 + +G1 E-.7 F2100 +G1 X124.049 Y104.221 Z.8 F18000 +G1 X126.368 Y103.684 Z1 F8946.585 +;AFTER_LAYER_CHANGE +;1 +M74 W0.0482789 + +G1 X126.368 Y103.684 F18000 +G1 Z1 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y104.381 E.02359 +G1 X126.293 Y104.418 E.00283 +G1 X126.192 Y104.618 E.00758 +G1 X126.168 Y105.176 E.01891 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.829 Y104.619 E.02376 +G1 X123.704 Y104.304 E.01147 +G1 X123.632 Y104.263 E.0028 +G1 X123.632 Y103.685 E.01956 +G1 X123.935 Y103.832 E.0114 +G1 X124.462 Y103.827 E.01784 +G1 X124.749 Y103.713 E.01045 +M73 P39 R0 +G1 X124.79 Y103.632 E.00307 +M73 Q39 S0 +G1 X125.2 Y103.632 E.01388 +G1 X125.241 Y103.713 E.00307 +G1 X125.505 Y103.824 E.00969 +G1 X126.07 Y103.832 E.01913 +G1 X126.314 Y103.711 E.00922 +G1 X126.314 Y103.711 F18000 +G1 X126.194 Y103.358 +;TYPE:External perimeter +G1 F1200 +G1 X126.232 Y103.344 E.00137 +G1 X126.272 Y103.225 E.00425 +G1 X126.775 Y103.225 E.01703 +G1 X126.775 Y104.587 E.0461 +G1 X126.617 Y104.665 E.00596 +G1 X126.583 Y104.732 E.00254 +G1 X126.575 Y105.176 E.01503 +G1 X126.61 Y105.29 E.00404 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.382 Y104.554 E.00447 +G1 X123.225 Y104.477 E.00592 +G1 X123.225 Y103.225 E.04238 +M73 P40 R0 +G1 X123.733 Y103.225 E.0172 +M73 Q40 S0 +G1 X123.773 Y103.345 E.00428 +G1 X123.935 Y103.425 E.00612 +G1 X124.476 Y103.404 E.01833 +G1 X124.589 Y103.225 E.00717 +G1 X125.402 Y103.225 E.02752 +G1 X125.514 Y103.404 E.00715 +G1 X126.003 Y103.425 E.01657 +G1 X126.138 Y103.378 E.00484 +G1 X126.507 Y103.225 F18000 +G1 X126.022 Y104.182 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G2 X126.022 Y105.751 I1.327 J.784 E.05572 +G1 X126.022 Y106.022 E.00917 +G1 X124.175 Y104.175 E.08841 +G2 X125.012 Y103.978 I.123 J-1.352 E.02962 +G2 X125.822 Y104.178 I.673 J-.987 E.02885 +G1 X123.978 Y106.022 E.08827 +G3 X124.178 Y105.344 I2.327 J.318 E.02402 +M73 P41 R0 +M73 Q41 S0 +G1 X124.174 Y104.587 E.02562 +G2 X124.042 Y104.221 I-.909 J.122 E.01327 +;LAYER_CHANGE +;Z:1.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.2 +M201 X3999.94 Y3999.94 + +G1 E-.7 F2100 +G1 X124.042 Y104.221 Z1 F18000 +G1 X126.336 Y103.65 Z1.2 F8889.382 +;AFTER_LAYER_CHANGE +;1.2 +M74 W0.0520099 + +G1 X126.336 Y103.65 F18000 +G1 Z1.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y103.636 E.00118 +G1 X126.368 Y104.382 E.02525 +G1 X126.293 Y104.419 E.00283 +G1 X126.177 Y104.683 E.00976 +G1 X126.168 Y105.176 E.01669 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.832 Y104.679 E.02173 +G1 X123.757 Y104.385 E.01027 +G1 X123.632 Y104.264 E.00589 +M73 P42 R0 +G1 X123.632 Y103.632 E.02139 +G1 X123.977 Y103.823 E.01335 +M73 Q42 S0 +G1 X124.084 Y103.832 E.00363 +G1 X124.691 Y103.811 E.02056 +G1 X124.978 Y103.632 E.01145 +G1 X125.22 Y103.782 E.00964 +G1 X125.357 Y103.823 E.00484 +G1 X125.927 Y103.832 E.0193 +G1 X126.281 Y103.675 E.01311 +G1 X126.281 Y103.675 F18000 +G1 X126.063 Y103.364 +;TYPE:External perimeter +G1 F1200 +G1 X126.078 Y103.358 E.00055 +G1 X126.129 Y103.225 E.00482 +G1 X126.775 Y103.225 E.02187 +G1 X126.775 Y104.587 E.0461 +G1 X126.608 Y104.678 E.00644 +G2 X126.61 Y105.29 I1.658 J.299 E.02083 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +M73 P43 R0 +M73 Q43 S0 +G1 X123.425 Y105.176 E.00907 +G1 X123.4 Y104.581 E.02016 +G1 X123.225 Y104.477 E.00689 +G1 X123.225 Y103.225 E.04238 +G1 X123.881 Y103.225 E.0222 +G1 X123.933 Y103.358 E.00483 +G1 X124.048 Y103.422 E.00445 +G1 X124.531 Y103.425 E.01635 +G1 X124.679 Y103.362 E.00544 +G1 X124.733 Y103.225 E.00498 +G1 X125.26 Y103.225 E.01784 +G1 X125.315 Y103.362 E.005 +G1 X125.41 Y103.418 E.00373 +G1 X125.927 Y103.425 E.0175 +G1 X126.008 Y103.389 E.003 +G1 X126.372 Y103.224 F18000 +G1 X126.022 Y104.178 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G2 X126.022 Y105.751 I1.355 J.786 E.05578 +G1 X126.022 Y106.022 E.00917 +G1 X124.177 Y104.177 E.08832 +G2 X124.997 Y104.043 I.222 J-1.214 E.02868 +G2 X125.822 Y104.178 I.616 J-1.175 E.02879 +G1 X123.978 Y106.022 E.08827 +M73 P44 R0 +G3 X124.178 Y105.344 I2.327 J.318 E.02402 +M73 Q44 S0 +G2 X124.065 Y104.224 I-2.535 J-.309 E.03842 +G1 X124.063 Y104.222 E.0001 +;LAYER_CHANGE +;Z:1.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.4 +M201 X3999.94 Y3999.94 + +G1 E-.7 F2100 +G1 X124.063 Y104.222 Z1.2 F18000 +G1 X126.21 Y103.659 Z1.4 F8391.09 +;AFTER_LAYER_CHANGE +;1.4 +M74 W0.0557451 + +G1 X126.21 Y103.659 F18000 +G1 Z1.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y103.632 E.00543 +G1 X126.368 Y104.383 E.02542 +G1 X126.291 Y104.421 E.00291 +G1 X126.17 Y104.744 E.01168 +G1 X126.168 Y105.176 E.01462 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +M73 P45 R0 +M73 Q45 S0 +G1 X123.832 Y105.321 E.01281 +G2 X123.793 Y104.465 I-6.942 J-.115 E.02902 +G1 X123.7 Y104.3 E.00641 +G1 X123.632 Y104.267 E.00256 +G1 X123.632 Y103.632 E.02149 +G1 X123.736 Y103.632 E.00352 +G1 X124.045 Y103.803 E.01195 +G1 X124.232 Y103.832 E.00641 +G1 X124.871 Y103.8 E.02166 +G1 X124.998 Y103.689 E.00571 +G1 X125.125 Y103.8 E.00571 +G1 X125.321 Y103.832 E.00672 +G1 X125.909 Y103.819 E.01991 +G1 X126.157 Y103.687 E.00951 +G1 X126.157 Y103.687 F18000 +G1 X125.929 Y103.359 +;TYPE:External perimeter +G1 F1200 +G1 X125.986 Y103.225 E.00493 +G1 X126.775 Y103.225 E.02671 +G1 X126.775 Y104.587 E.0461 +G1 X126.594 Y104.702 E.00726 +G2 X126.61 Y105.29 I1.966 J.241 E.01998 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +M73 P46 R0 +M73 Q46 S0 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.381 Y104.552 E.00455 +G1 X123.225 Y104.477 E.00586 +G1 X123.225 Y103.225 E.04238 +G1 X124.03 Y103.225 E.02725 +G1 X124.091 Y103.368 E.00526 +G1 X124.17 Y103.415 E.00311 +G1 X124.712 Y103.422 E.01835 +G1 X124.857 Y103.312 E.00616 +G1 X124.878 Y103.225 E.00303 +G1 X125.119 Y103.225 E.00816 +G1 X125.139 Y103.312 E.00302 +G1 X125.256 Y103.414 E.00525 +G1 X125.784 Y103.425 E.01788 +G1 X125.879 Y103.386 E.00348 +G1 X126.244 Y103.223 F18000 +G1 X126.022 Y104.144 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G2 X125.825 Y104.709 I2.893 J1.327 E.02028 +G2 X126.022 Y105.751 I1.686 J.22 E.0365 +G1 X126.022 Y106.022 E.00917 +G1 X124.17 Y104.17 E.08865 +G2 X124.995 Y104.125 I.288 J-2.269 E.02812 +M73 P47 R0 +M73 Q47 S0 +G2 X125.825 Y104.175 I.576 J-2.658 E.02826 +G1 X123.978 Y106.022 E.08841 +G3 X124.178 Y105.344 I2.325 J.317 E.02402 +G2 X124.12 Y104.344 I-3.709 J-.286 E.03401 +G1 X124.026 Y104.178 E.00646 +;LAYER_CHANGE +;Z:1.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.6 +M201 X3999.94 Y3999.94 + +G1 E-.7 F2100 +G1 X124.026 Y104.178 Z1.4 F18000 +G1 X126.063 Y103.637 Z1.6 F8001.666 +;AFTER_LAYER_CHANGE +;1.6 +M74 W0.0594874 + +G1 X126.063 Y103.637 F18000 +G1 Z1.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.065 Y103.632 E.00018 +G1 X126.368 Y103.632 E.01026 +G1 X126.368 Y104.385 E.02549 +G1 X126.288 Y104.425 E.00303 +G1 X126.198 Y104.6 E.00666 +G1 X126.168 Y105.176 E.01952 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +M73 P48 R0 +M73 Q48 S0 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.816 Y104.541 E.02641 +G1 X123.705 Y104.305 E.00883 +G1 X123.632 Y104.27 E.00274 +G1 X123.632 Y103.632 E.0216 +G1 X123.958 Y103.632 E.01103 +G1 X124.125 Y103.776 E.00746 +G1 X124.38 Y103.832 E.00884 +G1 X124.881 Y103.829 E.01696 +G1 X125 Y103.754 E.00476 +G1 X125.058 Y103.82 E.00297 +G1 X125.211 Y103.832 E.00519 +G1 X125.835 Y103.8 E.02115 +G1 X126.032 Y103.687 E.00769 +G1 X126.032 Y103.687 F18000 +G1 X125.783 Y103.357 +;TYPE:External perimeter +G1 F1200 +G1 X125.842 Y103.225 E.00489 +G1 X126.775 Y103.225 E.03158 +G1 X126.775 Y104.587 E.0461 +G1 X126.615 Y104.667 E.00606 +G1 X126.585 Y104.726 E.00224 +G2 X126.61 Y105.29 I2.218 J.183 E.01916 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +M73 P49 R0 +M73 Q49 S0 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.176 E.00907 +G1 X123.407 Y104.596 E.01964 +G1 X123.225 Y104.477 E.00736 +G1 X123.225 Y103.225 E.04238 +G1 X124.179 Y103.225 E.03229 +G1 X124.295 Y103.406 E.00728 +G1 X124.84 Y103.424 E.01846 +G1 X125 Y103.237 E.00833 +G1 X125.14 Y103.421 E.00783 +G1 X125.64 Y103.425 E.01692 +G1 X125.737 Y103.389 E.0035 +G1 X126.101 Y103.224 F18000 +G1 X126.022 Y104.111 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G3 X125.867 Y104.492 I-.62 J-.03 E.01419 +G2 X126.022 Y105.751 I1.678 J.433 E.04396 +G1 X126.022 Y106.022 E.00917 +G1 X124.144 Y104.144 E.0899 +G2 X125 Y104.161 I.481 J-2.785 E.02909 +M73 P50 R0 +M73 Q50 S0 +G2 X125.855 Y104.145 I.373 J-2.982 E.02904 +G1 X123.978 Y106.022 E.08985 +G3 X124.178 Y105.344 I2.325 J.317 E.02402 +G2 X124.095 Y104.288 I-2.617 J-.327 E.0361 +G1 X123.993 Y104.115 E.0068 +;LAYER_CHANGE +;Z:1.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.8 +M201 X3999.93 Y3999.93 + +G1 E-.7 F2100 +G1 X123.993 Y104.115 Z1.6 F18000 +G1 X125.91 Y103.634 Z1.8 F7551.422 +;AFTER_LAYER_CHANGE +;1.8 +M74 W0.0632498 + +G1 X125.91 Y103.634 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X125.911 Y103.632 E.00008 +G1 X126.368 Y103.632 E.01547 +G1 X126.368 Y104.387 E.02556 +G1 X126.285 Y104.43 E.00316 +G1 X126.18 Y104.667 E.00877 +G1 X126.168 Y105.176 E.01723 +G1 X126.274 Y105.519 E.01215 +G1 X126.368 Y105.569 E.0036 +G1 X126.368 Y106.368 E.02705 +G1 X123.632 Y106.368 E.09261 +M73 P51 R0 +M73 Q51 S0 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.829 Y104.613 E.02397 +G1 X123.71 Y104.312 E.01096 +G1 X123.632 Y104.273 E.00295 +G1 X123.632 Y103.632 E.0217 +G1 X124.117 Y103.632 E.01642 +G1 X124.217 Y103.746 E.00513 +G1 X124.529 Y103.832 E.01095 +G1 X125.042 Y103.832 E.01736 +G1 X125.613 Y103.821 E.01933 +G1 X125.879 Y103.698 E.00992 +G1 X125.884 Y103.688 E.00038 +G1 X125.884 Y103.688 F18000 +G1 X125.622 Y103.345 +;TYPE:External perimeter +G1 F1200 +G1 X125.699 Y103.225 E.00483 +G1 X126.775 Y103.225 E.03642 +G1 X126.775 Y104.587 E.0461 +G1 X126.59 Y104.711 E.00754 +G2 X126.61 Y105.29 I2.123 J.217 E.01967 +G1 X126.775 Y105.377 E.00631 +G1 X126.775 Y106.775 E.04732 +G1 X123.225 Y106.775 E.12016 +M73 P52 R0 +M73 Q52 S0 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.384 Y104.556 E.00439 +G1 X123.225 Y104.477 E.00601 +G1 X123.225 Y103.225 E.04238 +G1 X124.327 Y103.225 E.0373 +G1 X124.425 Y103.396 E.00667 +G1 X124.957 Y103.425 E.01803 +G3 X125.042 Y103.425 I.042 J.023 E.00353 +G1 X125.582 Y103.407 E.01829 +G1 X125.589 Y103.395 E.00047 +G1 X125.952 Y103.227 F18000 +G1 X126.022 Y104.112 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G3 X125.894 Y104.423 I-.439 J.001 E.01168 +G2 X126.022 Y105.751 I1.584 J.517 E.04646 +G1 X126.022 Y106.022 E.00917 +G1 X124.049 Y104.049 E.09445 +M73 P53 R0 +M73 Q53 S0 +G2 X124.521 Y104.178 I.526 J-.997 E.0167 +G1 X125.504 Y104.178 E.03327 +G2 X125.927 Y104.073 I-.023 J-.994 E.01487 +G1 X123.978 Y106.022 E.0933 +G3 X124.178 Y105.344 I2.327 J.318 E.02402 +G2 X124.124 Y104.358 I-3.695 J-.292 E.03352 +G1 X123.994 Y104.115 E.00933 +;LAYER_CHANGE +;Z:2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2 +M201 X3999.93 Y3999.93 + +G1 E-.7 F2100 +G1 X123.994 Y104.115 Z1.8 F18000 +G1 X125.761 Y103.632 Z2 F7050.553 +;AFTER_LAYER_CHANGE +;2 +M74 W0.0670353 + +G1 X125.761 Y103.632 F18000 +G1 Z2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y103.632 E.02055 +G1 X126.368 Y104.322 E.02336 +G1 X126.284 Y104.365 E.00319 +M73 P54 R0 +M73 Q54 S0 +G1 X126.169 Y104.691 E.0117 +G1 X126.168 Y105.2 E.01723 +G1 X126.279 Y105.595 E.01389 +G1 X126.368 Y105.641 E.00339 +G1 X126.368 Y106.368 E.02461 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.811 Y104.521 E.02709 +G1 X123.715 Y104.319 E.00757 +G1 X123.632 Y104.277 E.00315 +G1 X123.632 Y103.632 E.02183 +G1 X124.275 Y103.632 E.02176 +G1 X124.318 Y103.715 E.00316 +G1 X124.52 Y103.811 E.00757 +G1 X124.677 Y103.832 E.00536 +G1 X125.354 Y103.832 E.02292 +G1 X125.686 Y103.734 E.01172 +G1 X125.725 Y103.678 E.00231 +G1 X125.725 Y103.678 F18000 +G1 X125.487 Y103.351 +;TYPE:External perimeter +G1 F1200 +G1 X125.556 Y103.225 E.00486 +G1 X126.775 Y103.225 E.04126 +G1 X126.775 Y104.521 E.04387 +G1 X126.614 Y104.603 E.00612 +M73 P55 R0 +M73 Q55 S0 +G1 X126.584 Y104.664 E.0023 +G2 X126.612 Y105.361 I3.308 J.215 E.02366 +G1 X126.775 Y105.446 E.00622 +G1 X126.775 Y106.775 E.04499 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.386 Y104.559 E.00427 +G1 X123.225 Y104.477 E.00612 +G1 X123.225 Y103.225 E.04238 +G1 X124.476 Y103.225 E.04234 +G1 X124.557 Y103.386 E.0061 +G1 X124.677 Y103.425 E.00427 +G1 X125.277 Y103.425 E.02031 +G1 X125.452 Y103.395 E.00601 +G1 X125.814 Y103.224 F18000 +G1 X125.858 Y104.468 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +M73 P56 R0 +M73 Q56 S0 +G2 X126.022 Y103.978 I-.611 J-.477 E.01783 +G1 X123.978 Y106.022 E.09785 +G1 X126.022 Y106.022 E.06919 +G1 X123.978 Y103.978 E.09785 +G1 X123.978 Y104.089 E.00376 +G1 X124.145 Y104.424 E.01267 +;LAYER_CHANGE +;Z:2.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.2 +M201 X3999.92 Y3999.92 + +G1 E-.7 F2100 +G1 X124.145 Y104.424 Z2 F18000 +G1 X125.654 Y103.632 Z2.2 F6609.862 +;AFTER_LAYER_CHANGE +;2.2 +M74 W0.0705597 + +G1 X125.654 Y103.632 F18000 +G1 Z2.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y103.632 E.02417 +G1 X126.368 Y104.182 E.01862 +G1 X126.247 Y104.288 E.00545 +G1 X126.168 Y104.589 E.01053 +G1 X126.168 Y105.383 E.02688 +M73 P57 R0 +M73 Q57 S0 +G1 X126.29 Y105.749 E.01306 +G1 X126.368 Y105.788 E.00295 +G1 X126.368 Y106.368 E.01963 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G1 X123.832 Y104.679 E.02173 +G1 X123.738 Y104.352 E.01152 +G1 X123.632 Y104.277 E.0044 +G1 X123.632 Y103.632 E.02183 +G1 X124.361 Y103.632 E.02468 +G1 X124.406 Y103.719 E.00332 +G1 X124.584 Y103.806 E.00671 +G1 X124.76 Y103.832 E.00602 +G1 X125.316 Y103.828 E.01882 +G1 X125.606 Y103.716 E.01052 +G1 X125.624 Y103.681 E.00133 +G1 X125.624 Y103.681 F18000 +G1 X125.384 Y103.353 +;TYPE:External perimeter +G1 F1200 +G1 X125.45 Y103.225 E.00487 +G1 X126.775 Y103.225 E.04485 +G1 X126.775 Y104.387 E.03933 +G1 X126.617 Y104.466 E.00598 +G1 X126.575 Y104.589 E.0044 +G2 X126.616 Y105.505 I4.532 J.257 E.03109 +M73 P58 R0 +M73 Q58 S0 +G1 X126.775 Y105.584 E.00601 +G1 X126.775 Y106.775 E.04031 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.76 E.01899 +G1 X123.393 Y104.57 E.00652 +G1 X123.225 Y104.477 E.0065 +G1 X123.225 Y103.225 E.04238 +G1 X124.559 Y103.225 E.04515 +G1 X124.642 Y103.387 E.00616 +G1 X124.701 Y103.416 E.00223 +G1 X125.248 Y103.425 E.01852 +G1 X125.346 Y103.393 E.00349 +G1 X125.708 Y103.224 F18000 +G1 X125.824 Y103.99 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G3 X125.252 Y104.178 I-.61 J-.891 E.02065 +G3 X124.183 Y103.992 I-.269 J-1.616 E.03743 +M73 P59 R0 +M73 Q59 S0 +G1 X123.978 Y103.978 E.00696 +G1 X126.022 Y106.022 E.09785 +G1 X123.978 Y106.022 E.06919 +G1 X126.022 Y103.978 E.09785 +G1 X125.845 Y104.375 E.01471 +;LAYER_CHANGE +;Z:2.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.4 +M201 X3999.92 Y3999.92 + +G1 X125.845 Y104.375 Z2.2 F18000 +G1 X126.868 Y105.006 Z2.4 F4873.872 +;AFTER_LAYER_CHANGE +;2.4 +M74 W0.074219 + +G1 X126.868 Y105.006 F18000 +G1 Z2.4 F720 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1200 +G1 X126.769 Y105.006 E.0028 +G1 E-.7 F2100 +G1 X126.769 Y105.006 F18000 +G1 X125.808 Y103.632 Z2.429 +G1 Z2.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +M73 Q60 S0 +G1 X126.368 Y103.632 E.01896 +G1 X126.368 Y104.038 E.01374 +M73 P60 R0 +G1 X126.307 Y104.067 E.00229 +G1 X126.185 Y104.312 E.00926 +G1 X126.168 Y104.895 E.01974 +G1 X126.209 Y105.006 E.00401 +G1 X126.173 Y105.041 E.0017 +G1 X126.168 Y105.521 E.01625 +G1 X126.305 Y105.906 E.01383 +G1 X126.368 Y105.936 E.00236 +G1 X126.368 Y106.368 E.01462 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y105.723 E.02183 +G1 X123.715 Y105.681 E.00315 +G1 X123.832 Y105.321 E.01281 +G2 X123.806 Y104.503 I-10.046 J-.092 E.02771 +G1 X123.713 Y104.316 E.00707 +G1 X123.632 Y104.275 E.00307 +G1 X123.632 Y103.632 E.02176 +G1 X124.212 Y103.632 E.01963 +G1 X124.251 Y103.71 E.00295 +G1 X124.467 Y103.813 E.0081 +G1 X124.679 Y103.832 E.0072 +G1 X125.466 Y103.828 E.02664 +G1 X125.773 Y103.702 E.01123 +G1 X125.781 Y103.686 E.00061 +G1 X125.781 Y103.686 F18000 +G1 X125.536 Y103.355 +;TYPE:External perimeter +G1 F1200 +G1 X125.599 Y103.225 E.00489 +G1 X126.775 Y103.225 E.03981 +G1 X126.775 Y104.253 E.0348 +M73 P61 R0 +M73 Q61 S0 +G1 X126.621 Y104.326 E.00577 +G1 X126.581 Y104.408 E.00309 +G1 X126.575 Y104.895 E.01649 +G1 X126.672 Y104.951 E.00379 +;WIDTH:0.41646 +G1 X126.769 Y105.006 E.00346 +G1 X126.673 Y105.048 E.00325 +;WIDTH:0.449999 +G1 X126.577 Y105.091 E.00356 +G1 X126.575 Y105.521 E.01456 +G1 X126.621 Y105.649 E.0046 +G1 X126.775 Y105.723 E.00578 +G1 X126.775 Y106.775 E.03561 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.523 E.04238 +G1 X123.386 Y105.441 E.00612 +G1 X123.425 Y105.321 E.00427 +G1 X123.425 Y104.679 E.02173 +G1 X123.385 Y104.558 E.00431 +G1 X123.225 Y104.477 E.00607 +G1 X123.225 Y103.225 E.04238 +G1 X124.416 Y103.225 E.04031 +G1 X124.496 Y103.384 E.00602 +G1 X124.618 Y103.425 E.00436 +G1 X125.397 Y103.425 E.02637 +G1 X125.494 Y103.392 E.00347 +M73 P62 R0 +M73 Q62 S0 +G1 X125.857 Y103.224 F18000 +G1 E-.7 F2100 +G1 X124.144 Y104.432 Z2.437 F18000 +G1 Z2.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G3 X123.978 Y103.978 I.674 J-.503 E.0166 +G1 X125.974 Y105.974 E.09555 +G3 X125.834 Y105.005 I1.453 J-.704 E.03367 +G3 X125.902 Y104.098 I1.832 J-.319 E.0311 +G1 X123.978 Y106.022 E.0921 +G1 X125.838 Y106.022 E.06296 +;LAYER_CHANGE +;Z:2.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.6 +M201 X3999.92 Y3999.92 + +G1 E-.7 F2100 +M73 P63 R0 +M73 Q63 S0 +G1 X125.838 Y106.022 Z2.4 F18000 +G1 X125.021 Y103.121 Z2.6 F11136.775 +;AFTER_LAYER_CHANGE +;2.6 +M74 W0.0778736 + +G1 X125.021 Y103.121 F18000 +G1 Z2.6 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1200 +G1 X125.021 Y103.2 E.00223 +G1 X125.021 Y103.23 E.00085 +G1 X125.021 Y103.23 F18000 +G1 X125.965 Y103.639 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X125.968 Y103.632 E.00026 +G1 X126.368 Y103.632 E.01354 +G1 X126.368 Y103.894 E.00887 +G1 X126.325 Y103.913 E.00159 +G1 X126.188 Y104.168 E.0098 +G1 X126.168 Y104.322 E.00526 +G1 X126.188 Y104.925 E.02042 +G1 X126.277 Y105.007 E.0041 +G1 X126.188 Y105.089 E.0041 +G1 X126.168 Y105.659 E.01931 +G1 X126.322 Y106.065 E.0147 +G1 X126.368 Y106.086 E.00171 +G1 X126.368 Y106.368 E.00955 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y106.302 E.00223 +G1 X123.832 Y105.821 E.01763 +G1 X123.832 Y104.179 E.05558 +G1 X123.77 Y103.911 E.00931 +G1 X123.652 Y103.745 E.00689 +G1 X123.632 Y103.632 E.00388 +G1 X124.051 Y103.632 E.01418 +G1 X124.072 Y103.68 E.00177 +M73 Q64 S0 +G1 X124.314 Y103.81 E.0093 +M73 P64 R0 +G1 X124.922 Y103.832 E.02059 +G1 X125.021 Y103.796 E.00357 +G1 X125.053 Y103.828 E.00153 +G1 X125.546 Y103.832 E.01669 +G1 X125.765 Y103.791 E.00754 +G1 X125.936 Y103.69 E.00672 +G1 X125.936 Y103.69 F18000 +G1 X125.687 Y103.356 +;TYPE:External perimeter +G1 F1200 +G1 X125.748 Y103.225 E.00489 +G1 X126.775 Y103.225 E.03476 +G1 X126.775 Y104.12 E.03029 +G1 X126.627 Y104.185 E.00547 +G1 X126.582 Y104.271 E.00329 +G1 X126.575 Y104.771 E.01693 +G1 X126.775 Y105.007 E.01047 +G1 X126.582 Y105.192 E.00905 +G1 X126.575 Y105.659 E.01581 +G1 X126.626 Y105.795 E.00492 +G1 X126.775 Y105.861 E.00552 +G1 X126.775 Y106.775 E.03094 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y106.024 E.02542 +G1 X123.365 Y105.966 E.00513 +G1 X123.425 Y105.821 E.00531 +G1 X123.425 Y104.179 E.05558 +G1 X123.365 Y104.034 E.00531 +M73 P65 R0 +M73 Q65 S0 +G1 X123.225 Y103.976 E.00513 +G1 X123.225 Y103.225 E.02542 +G1 X124.273 Y103.225 E.03547 +G1 X124.341 Y103.374 E.00554 +G1 X124.422 Y103.418 E.00312 +G1 X124.922 Y103.425 E.01693 +G1 X124.972 Y103.328 E.00369 +;WIDTH:0.41646 +G1 X125.021 Y103.23 E.0034 +G1 X125.06 Y103.327 E.00325 +;WIDTH:0.449999 +G1 X125.098 Y103.424 E.00353 +G1 X125.546 Y103.425 E.01516 +G1 X125.643 Y103.389 E.0035 +G1 X126.007 Y103.224 F18000 +G1 E-.7 F2100 +G1 X124.32 Y104.159 Z2.634 F18000 +G1 Z2.6 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G1 X125.027 Y104.166 E.02393 +G2 X125.846 Y104.126 I.299 J-2.268 E.02791 +G1 X124.178 Y105.822 E.08052 +G1 X124.178 Y104.178 E.05565 +G1 X125.905 Y105.905 E.08267 +M73 Q66 S0 +G1 X125.95 Y106.022 E.00424 +M73 P66 R0 +G1 X124.111 Y106.012 E.06225 +;LAYER_CHANGE +;Z:2.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.8 +M201 X3999.91 Y3999.91 + +G1 E-.7 F2100 +G1 X124.111 Y106.012 Z2.6 F18000 +G1 X126.084 Y103.61 Z2.8 F11462.502 +;AFTER_LAYER_CHANGE +;2.8 +M74 W0.0815626 + +G1 X126.084 Y103.61 F18000 +G1 Z2.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.395439 +G1 F1200 +G1 X126.105 Y103.589 E.00087 +;WIDTH:0.364268 +G1 X126.411 Y103.589 E.00818 +G1 X126.411 Y103.705 E.0031 +;WIDTH:0.395439 +G1 X126.306 Y103.865 E.00561 +;WIDTH:0.42661 +G1 X126.202 Y104.025 E.00609 +;WIDTH:0.449999 +G1 X126.168 Y104.188 E.00564 +G1 X126.18 Y104.764 E.0195 +G1 X126.33 Y105.007 E.00967 +G1 X126.181 Y105.25 E.00965 +G1 X126.168 Y105.798 E.01855 +G1 X126.368 Y106.237 E.01633 +G1 X126.368 Y106.368 E.00443 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y106.044 E.01097 +M73 Q67 S0 +G1 X123.683 Y106.021 E.00189 +M73 P67 R0 +G1 X123.832 Y105.621 E.01445 +G1 X123.832 Y104.783 E.02837 +G1 X123.8 Y104.185 E.02027 +;WIDTH:0.448873 +G1 X123.678 Y103.98 E.00805 +;WIDTH:0.44111 +G1 X123.628 Y103.956 E.00184 +;WIDTH:0.440966 +G1 X123.628 Y103.628 E.01086 +G1 X123.909 Y103.628 E.0093 +G1 X123.929 Y103.674 E.00166 +;WIDTH:0.448873 +G1 X124.162 Y103.807 E.00906 +;WIDTH:0.449999 +G1 X124.379 Y103.832 E.00739 +G1 X124.941 Y103.811 E.01904 +G1 X125.026 Y103.72 E.00421 +G1 X125.112 Y103.811 E.00424 +G1 X125.187 Y103.826 E.00259 +G1 X125.695 Y103.832 E.0172 +G1 X125.922 Y103.776 E.00791 +;WIDTH:0.42661 +G1 X126.013 Y103.682 E.00417 +;WIDTH:0.395439 +G1 X126.042 Y103.653 E.0012 +G1 X126.042 Y103.653 F18000 +G1 X125.84 Y103.358 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X125.897 Y103.225 E.0049 +G1 X126.775 Y103.225 E.02972 +G1 X126.775 Y103.986 E.02576 +G1 X126.635 Y104.044 E.00513 +G1 X126.583 Y104.133 E.00349 +G1 X126.575 Y104.641 E.0172 +G1 X126.668 Y104.812 E.00659 +G1 X126.775 Y104.843 E.00377 +G1 X126.775 Y105.172 E.01114 +G1 X126.668 Y105.203 E.00377 +G1 X126.587 Y105.304 E.00438 +G1 X126.575 Y105.798 E.01673 +G1 X126.634 Y105.941 E.00524 +G1 X126.775 Y106 E.00517 +G1 X126.775 Y106.775 E.02623 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.823 E.03222 +M73 P68 R0 +M73 Q68 S0 +G1 X123.375 Y105.755 E.00557 +G1 X123.425 Y105.621 E.00484 +G1 X123.425 Y104.379 E.04204 +G1 X123.375 Y104.245 E.00484 +G1 X123.225 Y104.177 E.00557 +G1 X123.225 Y103.225 E.03222 +G1 X124.131 Y103.225 E.03067 +G1 X124.197 Y103.373 E.00549 +G1 X124.333 Y103.425 E.00493 +G1 X124.835 Y103.418 E.01699 +G1 X124.986 Y103.225 E.00829 +G1 X125.026 Y103.225 E.00135 +G1 X125.217 Y103.418 E.00919 +G1 X125.695 Y103.425 E.01618 +G1 X125.791 Y103.387 E.00349 +G1 X126.156 Y103.223 F18000 +G1 E-.7 F2100 +G1 X125.898 Y106.01 Z2.849 F18000 +G1 Z2.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G1 X124.04 Y106.022 E.06289 +G1 X124.076 Y105.924 E.00353 +M73 Q69 S0 +G1 X125.822 Y104.178 E.08358 +G2 X125.901 Y105.007 I1.772 J.25 E.02845 +M73 P69 R0 +G2 X125.822 Y105.822 I1.837 J.59 E.02793 +G1 X124.142 Y104.151 E.08021 +G3 X124.178 Y105.647 I-10.184 J.994 E.0507 +G1 X124.146 Y105.733 E.00311 +;LAYER_CHANGE +;Z:3 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3 +M201 X3999.91 Y3999.91 + +G1 E-.7 F2100 +G1 X124.146 Y105.733 Z2.8 F18000 +G1 X126.275 Y103.679 Z3 F10943.52 +;AFTER_LAYER_CHANGE +;3 +M74 W0.0852567 + +G1 X126.275 Y103.679 F18000 +G1 Z3 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.485096 +G1 F1200 +G1 X126.188 Y103.871 E.00775 +;WIDTH:0.46487 +G1 X126.168 Y104.054 E.00646 +;WIDTH:0.449999 +G1 X126.177 Y104.616 E.01903 +G1 X126.368 Y105.008 E.01476 +G1 X126.22 Y105.26 E.00989 +G1 X126.178 Y105.397 E.00485 +G1 X126.168 Y105.936 E.01825 +M73 Q70 S0 +G1 X126.362 Y106.368 E.01603 +G1 X123.632 Y106.368 E.09241 +M73 P70 R0 +G1 X123.632 Y105.83 E.01821 +G1 X123.706 Y105.793 E.0028 +G1 X123.832 Y105.421 E.01329 +G1 X123.832 Y104.989 E.01462 +G1 X123.799 Y104.38 E.02064 +G1 X123.69 Y104.187 E.0075 +G1 X123.632 Y104.16 E.00217 +G1 X123.632 Y103.632 E.01787 +G1 X123.707 Y103.632 E.00254 +G1 X123.973 Y103.792 E.01051 +G1 X124.19 Y103.832 E.00747 +G1 X124.773 Y103.818 E.01974 +G1 X125.031 Y103.659 E.01026 +G1 X125.291 Y103.818 E.01032 +G1 X125.844 Y103.832 E.01872 +;WIDTH:0.46487 +G1 X126.093 Y103.787 E.00888 +;WIDTH:0.485096 +G1 X126.223 Y103.709 E.00557 +G1 X126.223 Y103.709 F18000 +G1 X125.989 Y103.358 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.046 Y103.225 E.0049 +G1 X126.775 Y103.225 E.02468 +G1 X126.775 Y103.852 E.02122 +G1 X126.644 Y103.901 E.00473 +G1 X126.584 Y103.994 E.00375 +G1 X126.575 Y104.51 E.01747 +G1 X126.632 Y104.65 E.00512 +G1 X126.775 Y104.712 E.00528 +G1 X126.775 Y105.304 E.02004 +G1 X126.632 Y105.365 E.00526 +G1 X126.592 Y105.424 E.00241 +M73 Q71 S0 +G1 X126.575 Y105.936 E.01734 +G1 X126.643 Y106.088 E.00564 +G1 X126.775 Y106.139 E.00479 +G1 X126.775 Y106.775 E.02153 +M73 P71 R0 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.623 E.03899 +G1 X123.383 Y105.545 E.00596 +G1 X123.425 Y105.421 E.00443 +G1 X123.425 Y104.641 E.0264 +G1 X123.378 Y104.448 E.00672 +G1 X123.225 Y104.377 E.00571 +G1 X123.225 Y103.225 E.03899 +G1 X123.988 Y103.225 E.02583 +G1 X124.047 Y103.366 E.00517 +G1 X124.118 Y103.412 E.00286 +G1 X124.641 Y103.425 E.01771 +G1 X124.806 Y103.34 E.00628 +G1 X124.844 Y103.225 E.0041 +G1 X125.219 Y103.225 E.01269 +G1 X125.257 Y103.34 E.0041 +G1 X125.349 Y103.412 E.00395 +G1 X125.844 Y103.425 E.01676 +G1 X125.94 Y103.387 E.00349 +G1 X126.305 Y103.224 F18000 +G1 E-.7 F2100 +G1 X124.182 Y106.022 Z3.061 F18000 +G1 Z3 F720 +M73 P72 R0 +M73 Q72 S0 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G1 X125.847 Y106.022 E.05636 +G1 X125.822 Y105.822 E.00682 +G1 X124.178 Y104.178 E.0787 +G2 X125.031 Y104.085 I.249 J-1.675 E.02936 +G2 X125.822 Y104.178 I.571 J-1.436 E.02727 +G1 X123.978 Y106.022 E.08827 +G1 X124.182 Y106.022 E.00691 +;LAYER_CHANGE +;Z:3.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.2 +M201 X3999.9 Y3999.9 + +G1 E-.7 F2100 +G1 X124.182 Y106.022 Z3 F18000 +G1 X126.19 Y103.791 Z3.2 F11091.904 +;AFTER_LAYER_CHANGE +;3.2 +M74 W0.088802 + +G1 X126.19 Y103.791 F18000 +G1 Z3.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +M73 Q73 S0 +G1 X126.168 Y104.007 E.00735 +G1 X126.182 Y104.51 E.01703 +G1 X126.28 Y104.731 E.00818 +M73 P73 R0 +G1 X126.368 Y104.777 E.00336 +G1 X126.368 Y105.24 E.01567 +G1 X126.28 Y105.285 E.00335 +G1 X126.189 Y105.48 E.00728 +G1 X126.168 Y106.075 E.02015 +G1 X126.314 Y106.368 E.01108 +G1 X123.632 Y106.368 E.09078 +G1 X123.632 Y105.617 E.02542 +G1 X123.723 Y105.57 E.00347 +G1 X123.832 Y105.221 E.01238 +G1 X123.832 Y104.779 E.01496 +G1 X123.727 Y104.436 E.01214 +G1 X123.632 Y104.371 E.0039 +G1 X123.632 Y103.645 E.02457 +G1 X124.012 Y103.831 E.01432 +G1 X124.5 Y103.832 E.01652 +G1 X124.753 Y103.777 E.00876 +G1 X124.923 Y103.632 E.00756 +G1 X125.151 Y103.632 E.00772 +G1 X125.174 Y103.683 E.00189 +G1 X125.467 Y103.823 E.01099 +G1 X125.993 Y103.832 E.01781 +G1 X126.131 Y103.804 E.00477 +G1 X126.131 Y103.804 F18000 +G1 X126.127 Y103.414 +;TYPE:External perimeter +;WIDTH:0.570578 +G1 F1200 +G1 X126.174 Y103.413 E.00206 +G1 X126.235 Y103.285 E.00622 +;WIDTH:0.569467 +G1 X126.715 Y103.285 E.02102 +G1 X126.715 Y103.678 E.01721 +;WIDTH:0.570578 +G1 X126.567 Y103.763 E.00749 +M73 Q74 S0 +G1 X126.57 Y103.815 E.00229 +;WIDTH:0.530385 +G1 X126.572 Y103.868 E.00215 +;WIDTH:0.490192 +G1 X126.575 Y103.92 E.00194 +;WIDTH:0.449999 +G1 X126.595 Y104.467 E.01853 +G1 X126.775 Y104.581 E.00721 +G1 X126.775 Y105.436 E.02894 +G1 X126.612 Y105.52 E.00621 +M73 P74 R0 +G1 X126.587 Y105.568 E.00183 +G1 X126.575 Y106.075 E.01717 +G1 X126.656 Y106.237 E.00613 +G1 X126.775 Y106.277 E.00425 +G1 X126.775 Y106.775 E.01686 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y105.423 E.04576 +G1 X123.389 Y105.338 E.00625 +G1 X123.425 Y105.221 E.00414 +G1 X123.425 Y104.955 E.009 +G1 X123.39 Y104.664 E.00992 +G1 X123.225 Y104.577 E.00631 +G1 X123.225 Y103.225 E.04576 +G1 X123.846 Y103.225 E.02102 +G1 X123.895 Y103.356 E.00473 +G1 X124.048 Y103.425 E.00568 +G1 X124.584 Y103.407 E.01815 +G1 X124.702 Y103.225 E.00734 +G1 X125.372 Y103.225 E.02268 +G1 X125.44 Y103.375 E.00557 +M73 Q75 S0 +G1 X125.621 Y103.425 E.00636 +;WIDTH:0.490192 +G1 X125.805 Y103.421 E.00684 +;WIDTH:0.530385 +G1 X125.99 Y103.417 E.0075 +;WIDTH:0.570578 +G1 X126.067 Y103.416 E.00338 +G1 X126.445 Y103.285 F18000 +G1 E-.7 F2100 +G1 X125.039 Y104.005 Z3.228 F18000 +G1 Z3.2 F720 +M73 P75 R0 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F1200 +G1 X125.152 Y104.078 E.00455 +G2 X125.828 Y104.178 I.605 J-1.748 E.02326 +G1 X125.856 Y104.619 E.01496 +G1 X126.022 Y104.948 E.01247 +G3 X125.849 Y105.433 I-1.641 J-.312 E.0175 +G1 X125.829 Y106.022 E.01995 +G1 X123.978 Y106.022 E.06265 +G3 X124.034 Y105.704 I.552 J-.067 E.01109 +G1 X124.178 Y105.174 E.01859 +G1 X124.163 Y104.642 E.01801 +G1 X123.978 Y104.171 E.01713 +G2 X124.813 Y104.117 I.229 J-2.945 E.02842 +G1 X124.857 Y104.096 E.00165 +G1 X125.026 Y104.478 E.01414 +G1 X125.435 Y104.569 E.01418 +G1 X125.581 Y105.008 E.01566 +G1 X125.462 Y105.283 E.01014 +G1 X125.435 Y105.615 E.01127 +G1 X124.488 Y105.615 E.03205 +M73 Q76 S0 +G1 X124.585 Y105.172 E.01535 +G1 X124.571 Y104.596 E.0195 +M73 P76 R0 +G1 X124.829 Y104.53 E.00901 +G1 X125.125 Y104.982 E.01829 +;WIDTH:0.586528 +G2 X125.107 Y105.048 I-.063 J.019 E.01525 +;LAYER_CHANGE +;Z:3.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.4 +M201 X3999.9 Y3999.9 + +G1 E-.7 F2100 +G1 X125.107 Y105.048 Z3.2 F18000 +G1 X126.168 Y103.831 Z3.4 F6300.572 +;AFTER_LAYER_CHANGE +;3.4 +M74 W0.0927406 + +G1 X126.168 Y103.831 F18000 +G1 Z3.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.168 Y104.248 E.01411 +G1 X126.23 Y104.517 E.00934 +G1 X126.368 Y104.619 E.00581 +G1 X126.368 Y105.399 E.0264 +G1 X126.247 Y105.469 E.00473 +G1 X126.168 Y105.77 E.01053 +G1 X126.168 Y106.213 E.015 +G1 X126.255 Y106.368 E.00602 +G1 X123.632 Y106.368 E.08879 +G1 X123.632 Y103.693 E.09055 +G1 X123.906 Y103.832 E.0104 +M73 Q77 S0 +G1 X124.358 Y103.832 E.0153 +G1 X124.624 Y103.771 E.00924 +M73 P77 R0 +G1 X124.744 Y103.632 E.00622 +G1 X125.339 Y103.632 E.02014 +G1 X125.396 Y103.736 E.00401 +G1 X125.581 Y103.815 E.00681 +G1 X126.108 Y103.829 E.01784 +G1 X126.295 Y103.364 F18000 +;TYPE:External perimeter +;WIDTH:0.473996 +G1 F1200 +G1 X126.353 Y103.237 E.005 +G1 X126.763 Y103.237 E.0147 +G1 X126.763 Y103.575 E.01212 +G1 X126.632 Y103.631 E.00511 +;WIDTH:0.47007 +G1 X126.574 Y103.787 E.00591 +;WIDTH:0.452967 +G1 X126.591 Y104.327 E.01842 +;WIDTH:0.449999 +G1 X126.775 Y104.45 E.00749 +G1 X126.775 Y105.568 E.03784 +G1 X126.59 Y105.693 E.00756 +G1 X126.575 Y106.213 E.01761 +G1 X126.674 Y106.388 E.00681 +G1 X126.775 Y106.416 E.00355 +G1 X126.775 Y106.775 E.01215 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y103.225 E.12016 +G1 X123.703 Y103.225 E.01618 +M73 P78 R0 +M73 Q78 S0 +G1 X123.741 Y103.342 E.00416 +G1 X123.906 Y103.425 E.00625 +G1 X124.424 Y103.414 E.01754 +G1 X124.56 Y103.225 E.00788 +G1 X125.524 Y103.225 E.03263 +G1 X125.636 Y103.405 E.00718 +G1 X126.142 Y103.425 E.01714 +;WIDTH:0.470069 +G1 X126.245 Y103.391 E.00385 +G1 X126.614 Y103.237 F18000 +G1 E-.7 F2100 +G1 X123.978 Y106.022 Z3.467 F18000 +G1 Z3.4 F720 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F1200 +G1 X123.978 Y104.178 E.06242 +G2 X124.651 Y104.132 I.154 J-2.662 E.02289 +G1 X124.913 Y103.978 E.01029 +G1 X125.17 Y103.978 E.0087 +G1 X125.219 Y104.031 E.00244 +G1 X125.466 Y104.14 E.00914 +G1 X125.833 Y104.169 E.01246 +G2 X125.901 Y104.614 I1.087 J.06 E.01535 +M73 Q79 S0 +G1 X126.016 Y104.782 E.00689 +G1 X126.022 Y105.223 E.01493 +G1 X125.941 Y105.31 E.00402 +G1 X125.828 Y105.726 E.01459 +G1 X125.822 Y106.022 E.01002 +M73 P79 R0 +G1 X124.182 Y106.022 E.05551 +G1 X124.385 Y105.615 E.01539 +G1 X124.385 Y104.583 E.03493 +G2 X124.987 Y104.425 I-.119 J-1.683 E.02119 +G1 X125.454 Y104.553 E.01639 +G1 X125.464 Y104.608 E.00189 +G1 X125.615 Y104.937 E.01225 +G3 X125.436 Y105.615 I-3.91 J-.668 E.02377 +G1 X124.589 Y105.615 E.02867 +G1 X124.772 Y105.228 E.01449 +;WIDTH:0.40871 +G1 X124.772 Y104.916 E.00949 +G1 X125.013 Y104.84 E.00769 +G1 X125.147 Y104.867 E.00416 +G1 X125.196 Y104.985 E.00389 +G1 X125.135 Y105.228 E.00762 +G1 X124.975 Y105.228 E.00487 +;LAYER_CHANGE +;Z:3.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.6 +M201 X3999.9 Y3999.9 + +G1 E-.7 F2100 +G1 X124.975 Y105.228 Z3.4 F18000 +G1 X126.368 Y103.632 Z3.6 F8041.528 +;AFTER_LAYER_CHANGE +;3.6 +M74 W0.0966924 + +G1 X126.368 Y103.632 F18000 +G1 Z3.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y106.368 E.09261 +G1 X123.632 Y106.368 E.09261 +M73 P80 R0 +M73 Q80 S0 +G1 X123.632 Y103.632 E.09261 +G1 X126.308 Y103.632 E.09058 +G1 X126.775 Y103.225 F18000 +;TYPE:External perimeter +G1 F1200 +G1 X126.775 Y106.775 E.12016 +G1 X123.225 Y106.775 E.12016 +M73 Q81 S0 +G1 X123.225 Y103.225 E.12016 +M73 P81 R0 +G1 X126.715 Y103.225 E.11813 +G1 X126.774 Y103.621 F18000 +G1 E-.7 F2100 +G1 X123.978 Y103.978 Z3.649 F18000 +G1 Z3.6 F720 +M73 Q82 S0 +G1 E.7 F1500 +;TYPE:Solid infill +G1 F1200 +G1 X126.022 Y103.978 E.06919 +G1 X126.022 Y106.022 E.06919 +M73 P82 R0 +G1 X123.978 Y106.022 E.06919 +G1 X123.978 Y104.182 E.06228 +G1 X124.385 Y104.385 E.01539 +G1 X125.615 Y104.385 E.04163 +G1 X125.615 Y105.615 E.04163 +G1 X124.385 Y105.615 E.04163 +G1 X124.385 Y104.589 E.03473 +M73 Q83 S0 +G1 X124.794 Y104.794 E.01549 +;WIDTH:0.454205 +G1 X125.206 Y104.794 E.01409 +M73 P83 R0 +G1 X125.206 Y105.206 E.01409 +G1 X124.794 Y105.206 E.01409 +G1 X124.794 Y104.998 E.00711 +;LAYER_CHANGE +;Z:3.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.8 +M201 X3999.89 Y3999.89 + +G1 E-.7 F2100 +G1 X124.794 Y104.998 Z3.6 F18000 +G1 X126.368 Y103.632 Z3.8 F7921.031 +;AFTER_LAYER_CHANGE +;3.8 +M74 W0.100739 + +G1 X126.368 Y103.632 F18000 +G1 Z3.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X126.368 Y106.368 E.09261 +G1 X123.632 Y106.368 E.09261 +G1 X123.632 Y103.632 E.09261 +G1 X126.308 Y103.632 E.09058 +M73 Q84 S0 +G1 X126.775 Y103.225 F18000 +M73 P84 R0 +;TYPE:External perimeter +G1 F1200 +G1 X126.775 Y106.775 E.12016 +G1 X123.225 Y106.775 E.12016 +G1 X123.225 Y103.225 E.12016 +G1 X126.715 Y103.225 E.11813 +M73 Q85 S0 +G1 X126.774 Y103.621 F18000 +M73 P85 R0 +G1 X126.207 Y104.573 +M204 P2000 +;TYPE:Top solid infill +;WIDTH:0.428043 +G1 F1200 +G1 X125.427 Y103.793 E.03532 +M204 P4000 +G1 X125.427 Y103.793 F18000 +G1 X124.883 Y103.793 +M204 P2000 +G1 F1200 +G1 X126.207 Y105.117 E.05996 +M204 P4000 +G1 X126.207 Y105.117 F18000 +G1 X126.207 Y105.662 +M204 P2000 +G1 F1200 +G1 X124.338 Y103.793 E.08464 +M204 P4000 +G1 X124.338 Y103.793 F18000 +G1 X123.793 Y103.793 +M204 P2000 +G1 F1200 +G1 X126.207 Y106.207 E.10932 +M204 P4000 +G1 X126.207 Y106.207 F18000 +M73 P86 R0 +M73 Q86 S0 +G1 X125.662 Y106.207 +M204 P2000 +G1 F1200 +G1 X123.793 Y104.338 E.08464 +M204 P4000 +G1 X123.793 Y104.338 F18000 +G1 X123.793 Y104.883 +M204 P2000 +G1 F1200 +G1 X125.117 Y106.207 E.05996 +M204 P4000 +G1 X125.117 Y106.207 F18000 +G1 X124.573 Y106.207 +M204 P2000 +G1 F1200 +G1 X123.793 Y105.427 E.03532 +M204 P4000 +;LAYER_CHANGE +;Z:4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4 +M201 X3999.89 Y3999.89 + +G1 X123.793 Y105.427 Z3.8 F18000 +G1 X123.992 Y106.438 Z4 F4277.994 +;AFTER_LAYER_CHANGE +;4 +M74 W0.104665 + +G1 X123.992 Y106.438 F18000 +G1 Z4 F720 +;TYPE:External perimeter +;WIDTH:0.500812 +G1 F1200 +G1 X123.941 Y106.415 E.00213 +;WIDTH:0.543929 +G1 X123.887 Y106.392 E.00245 +;WIDTH:0.587046 +G1 X123.834 Y106.368 E.00263 +G1 X123.832 Y105.972 E.01792 +;WIDTH:0.582954 +G1 X123.9 Y105.907 E.00422 +;WIDTH:0.538636 +G1 X123.968 Y105.843 E.00385 +;WIDTH:0.494318 +G1 X124.036 Y105.779 E.0035 +;WIDTH:0.449999 +G1 X125.083 Y105.768 E.03544 +G1 X124.319 Y104.813 E.0414 +;WIDTH:0.472785 +G1 X123.783 Y104.161 E.03017 +M73 Q87 S0 +;WIDTH:0.507579 +G1 X123.718 Y104.027 E.00575 +M73 P87 R0 +G1 X123.718 Y103.679 E.01345 +;WIDTH:0.524205 +G1 X123.727 Y103.591 E.00354 +G1 X123.783 Y103.572 E.00237 +;WIDTH:0.486019 +G1 X123.839 Y103.552 E.00219 +;WIDTH:0.447833 +G1 X123.895 Y103.533 E.00199 +;WIDTH:0.409647 +G1 X123.951 Y103.514 E.0018 +;WIDTH:0.37146 +G1 X126.049 Y103.514 E.05731 +;WIDTH:0.409647 +G1 X126.105 Y103.533 E.0018 +;WIDTH:0.447833 +G1 X126.161 Y103.552 E.00199 +;WIDTH:0.48602 +G1 X126.217 Y103.572 E.00219 +;WIDTH:0.524206 +G1 X126.273 Y103.591 E.00237 +G1 X126.28 Y104.069 E.01913 +;WIDTH:0.511099 +G1 X126.165 Y104.15 E.00548 +;WIDTH:0.480549 +G1 X126.049 Y104.232 E.00517 +;WIDTH:0.449999 +G1 X124.841 Y104.232 E.04089 +G1 X125.666 Y105.245 E.04422 +;WIDTH:0.459646 +G1 X126.168 Y105.854 E.02735 +;WIDTH:0.494254 +G1 X126.209 Y105.92 E.00292 +;WIDTH:0.528861 +G1 X126.25 Y105.986 E.00314 +;WIDTH:0.541746 +G1 X126.243 Y106.401 E.01722 +G1 X126.184 Y106.422 E.0026 +;WIDTH:0.499175 +G1 X126.125 Y106.443 E.00238 +;WIDTH:0.456603 +G1 X126.066 Y106.464 E.00215 +;WIDTH:0.414032 +G1 X126.007 Y106.486 E.00194 +;WIDTH:0.37146 +G1 X124.102 Y106.486 E.05204 +;WIDTH:0.414578 +G1 X124.048 Y106.462 E.00183 +G1 X123.825 Y106.13 F18000 +G1 E-.7 F2100 +G1 X126.035 Y105.042 Z4.043 F18000 +G1 Z4 F720 +M73 Q88 S0 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.464061 +G1 F1200 +G1 X125.712 Y104.646 E.0179 +G1 X126.125 Y104.641 E.01446 +M73 P88 R0 +G1 X126.361 Y104.417 E.01139 +G1 X126.361 Y105.442 E.03589 +G1 X126.073 Y105.089 E.01595 +G1 E-.7 F2100 +G1 X126.073 Y105.089 F18000 +G1 X123.634 Y105.77 Z4.044 +G1 Z4 F720 +G1 E.7 F1500 +;WIDTH:0.453747 +G1 F1200 +G1 X123.634 Y104.611 E.03959 +G1 X124.232 Y105.359 E.03271 +G1 X123.905 Y105.392 E.01123 +G1 X123.669 Y105.721 E.01383 +G1 E-.7 F2100 +G1 X126.738 Y103.262 Z4.069 F18000 +G1 Z4 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.524206 +G1 F1200 +M73 Q89 S0 +G1 X126.738 Y103.503 E.00964 +G1 X126.744 Y104.109 E.02425 +;WIDTH:0.511099 +G1 X126.76 Y104.48 E.01446 +;WIDTH:0.480549 +G1 X126.775 Y104.85 E.01347 +;WIDTH:0.48943 +G1 X126.755 Y105.418 E.0211 +;WIDTH:0.528861 +G1 X126.736 Y105.986 E.02296 +M73 P89 R0 +;WIDTH:0.541746 +G1 X126.729 Y106.729 E.03082 +G1 X126.549 Y106.75 E.00752 +;WIDTH:0.499175 +G1 X126.368 Y106.772 E.00692 +;WIDTH:0.456603 +G1 X126.187 Y106.793 E.00627 +;WIDTH:0.414032 +G1 X126.007 Y106.814 E.00559 +;WIDTH:0.37146 +G1 X124.102 Y106.814 E.05204 +;WIDTH:0.414906 +G1 X123.995 Y106.793 E.00337 +;WIDTH:0.458352 +G1 X123.888 Y106.771 E.00377 +;WIDTH:0.501798 +G1 X123.781 Y106.749 E.00417 +;WIDTH:0.545244 +G1 X123.674 Y106.727 E.00456 +;WIDTH:0.588689 +G1 X123.567 Y106.706 E.00495 +G1 X123.294 Y106.706 E.01239 +G1 X123.291 Y105.972 E.03331 +;WIDTH:0.582954 +G1 X123.269 Y105.713 E.01167 +;WIDTH:0.538636 +G1 X123.247 Y105.455 E.01067 +;WIDTH:0.494318 +G1 X123.225 Y105.197 E.00972 +;WIDTH:0.478789 +G1 X123.239 Y104.612 E.02121 +;WIDTH:0.507579 +G1 X123.254 Y104.027 E.02261 +;WIDTH:0.524205 +G1 X123.262 Y103.262 E.03062 +G1 X123.434 Y103.243 E.00693 +;WIDTH:0.486019 +G1 X123.606 Y103.224 E.00638 +;WIDTH:0.447833 +G1 X123.779 Y103.205 E.00586 +;WIDTH:0.409647 +G1 X123.951 Y103.186 E.00528 +;WIDTH:0.37146 +G1 X126.049 Y103.186 E.05731 +;WIDTH:0.409647 +G1 X126.161 Y103.205 E.00346 +M73 Q90 S0 +;WIDTH:0.447833 +G1 X126.273 Y103.224 E.00382 +;WIDTH:0.48602 +G1 X126.385 Y103.243 E.00419 +;WIDTH:0.524206 +G1 X126.497 Y103.262 E.00455 +G1 X126.678 Y103.262 E.00724 +G1 X126.739 Y103.657 F18000 +M486 S-1 +G1 E-.7 F2100 +M107 +;TYPE:Custom +; Filament-specific end gcode +G1 Z5 F720 ; Move print head up +M104 S0 ; turn off temperature +M140 S0 ; turn off heatbed +M107 ; turn off fan +G1 X241 Y170 F3600 ; park +M73 P90 R0 +G1 Z27 F300 ; Move print head up +M73 P93 R0 +M73 Q93 S0 +G4 ; wait +M572 S0 ; reset PA +M593 X T2 F0 ; disable IS +M593 Y T2 F0 ; disable IS +M84 X Y E ; disable motors +; max_layer_z = 4 +M73 P100 R0 +M73 Q100 S0 +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl","polygon":[[127.000,107.000],[123.000,107.000],[123.000,103.000],[127.000,103.000]]}]} +; filament used [mm] = 55.25 +; filament used [cm3] = 0.13 +; filament used [g] = 0.16 +; filament cost = 0.00 +; total filament used [g] = 0.16 +; total filament cost = 0.00 +; total filament used for wipe tower [g] = 0.00 +; estimated printing time (normal mode) = 1m 7s +; estimated printing time (silent mode) = 1m 8s +; estimated first layer printing time (normal mode) = 19s +; estimated first layer printing time (silent mode) = 19s + +; prusaslicer_config = begin +; arc_fitting = emit_center +; autoemit_temperature_commands = 1 +; avoid_crossing_curled_overhangs = 0 +; avoid_crossing_perimeters = 0 +; avoid_crossing_perimeters_max_detour = 0 +; bed_custom_model = +; bed_custom_texture = +; bed_shape = 0x0,250x0,250x210,0x210 +; bed_temperature = 60 +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n +; between_objects_gcode = +; binary_gcode = 0 +; bottom_fill_pattern = monotonic +; bottom_solid_layers = 3 +; bottom_solid_min_thickness = 0.5 +; bridge_acceleration = 1500 +; bridge_angle = 0 +; bridge_fan_speed = 100 +; bridge_flow_ratio = 1 +; bridge_speed = 50 +; brim_separation = 0.1 +; brim_type = outer_only +; brim_width = 5 +; chamber_minimal_temperature = 0 +; chamber_temperature = 0 +; color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change +; colorprint_heights = +; compatible_printers_condition_cummulative = "printer_notes=~/.*MK4S.*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*HF_NOZZLE.*/";"printer_model=~/(MK4S|MK4SMMU3|MK3.9S|MK3.9SMMU3)/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.5 and printer_notes=~/.*HF_NOZZLE.*/" +; complete_objects = 0 +; cooling = 1 +; cooling_tube_length = 5 +; cooling_tube_retraction = 91.5 +; default_acceleration = 4000 +; default_filament_profile = "Prusament PLA @HF0.4" +; default_print_profile = 0.20mm SPEED @MK4S HF0.4 +; deretract_speed = 25 +; disable_fan_first_layers = 1 +; dont_support_bridges = 0 +; draft_shield = disabled +; duplicate_distance = 6 +; elefant_foot_compensation = 0.2 +; enable_dynamic_fan_speeds = 0 +; enable_dynamic_overhang_speeds = 1 +; end_filament_gcode = "; Filament-specific end gcode" +; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +; external_perimeter_acceleration = 4000 +; external_perimeter_extrusion_width = 0.45 +; external_perimeter_speed = 200 +; external_perimeters_first = 0 +; extra_loading_move = -2 +; extra_perimeters = 0 +; extra_perimeters_on_overhangs = 0 +; extruder_clearance_height = 14 +; extruder_clearance_radius = 45 +; extruder_colour = "" +; extruder_offset = 0x0 +; extrusion_axis = E +; extrusion_multiplier = 1 +; extrusion_width = 0.45 +; fan_always_on = 1 +; fan_below_layer_time = 17 +; filament_abrasive = 0 +; filament_colour = #FF8000 +; filament_cooling_final_speed = 3.5 +; filament_cooling_initial_speed = 10 +; filament_cooling_moves = 2 +; filament_cost = 25.4 +; filament_density = 1.24 +; filament_deretract_speed = nil +; filament_diameter = 1.75 +; filament_infill_max_crossing_speed = 200 +; filament_infill_max_speed = 0 +; filament_load_time = 10.5 +; filament_loading_speed = 10 +; filament_loading_speed_start = 50 +; filament_max_volumetric_speed = 22 +; filament_minimal_purge_on_wipe_tower = 15 +; filament_multitool_ramming = 0 +; filament_multitool_ramming_flow = 10 +; filament_multitool_ramming_volume = 10 +; filament_notes = "" +; filament_purge_multiplier = 81.25% +; filament_ramming_parameters = "250 100 39.871 39.871| 0.05 39.9547 0.45 39.9547 0.95 39.9547 1.45 39.9547 1.95 39.9547 2.45 39.9547 2.95 39.9547 3.45 39.9547 3.95 39.9547 4.45 39.9547 4.95 39.9547" +; filament_retract_before_travel = nil +; filament_retract_before_wipe = nil +; filament_retract_layer_change = nil +; filament_retract_length = nil +; filament_retract_length_toolchange = nil +; filament_retract_lift = nil +; filament_retract_lift_above = nil +; filament_retract_lift_below = nil +; filament_retract_restart_extra = nil +; filament_retract_restart_extra_toolchange = nil +; filament_retract_speed = nil +; filament_settings_id = "Generic PLA @MK4S HF0.4" +; filament_shrinkage_compensation_xy = 0% +; filament_shrinkage_compensation_z = 0% +; filament_soluble = 0 +; filament_spool_weight = 0 +; filament_stamping_distance = 45 +; filament_stamping_loading_speed = 29 +; filament_toolchange_delay = 0 +; filament_travel_lift_before_obstacle = nil +; filament_travel_max_lift = 0.6 +; filament_travel_ramping_lift = 1 +; filament_travel_slope = 1 +; filament_type = PLA +; filament_unload_time = 8.5 +; filament_unloading_speed = 100 +; filament_unloading_speed_start = 100 +; filament_vendor = Generic +; filament_wipe = nil +; fill_angle = 45 +; fill_density = 15% +; fill_pattern = grid +; first_layer_acceleration = 500 +; first_layer_acceleration_over_raft = 0 +; first_layer_bed_temperature = 60 +; first_layer_extrusion_width = 0.5 +; first_layer_height = 0.2 +; first_layer_speed = 40 +; first_layer_speed_over_raft = 30 +; first_layer_temperature = 230 +; full_fan_speed_layer = 3 +; fuzzy_skin = none +; fuzzy_skin_point_dist = 0.8 +; fuzzy_skin_thickness = 0.3 +; gap_fill_enabled = 1 +; gap_fill_speed = 120 +; gcode_comments = 0 +; gcode_flavor = marlin2 +; gcode_label_objects = firmware +; gcode_resolution = 0.008 +; gcode_substitutions = +; high_current_on_filament_swap = 0 +; host_type = prusalink +; idle_temperature = 70 +; infill_acceleration = 4000 +; infill_anchor = 2 +; infill_anchor_max = 12 +; infill_every_layers = 1 +; infill_extruder = 1 +; infill_extrusion_width = 0.45 +; infill_first = 0 +; infill_overlap = 15% +; infill_speed = 250 +; inherits_cummulative = ;;"Original Prusa MK4S HF0.4 nozzle" +; interface_shells = 0 +; ironing = 0 +; ironing_flowrate = 15% +; ironing_spacing = 0.1 +; ironing_speed = 15 +; ironing_type = top +; layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n +; layer_height = 0.2 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 2500,2500 +; machine_max_acceleration_extruding = 4000,2500 +; machine_max_acceleration_retracting = 1200,1200 +; machine_max_acceleration_travel = 4000,2500 +; machine_max_acceleration_x = 4000,2500 +; machine_max_acceleration_y = 4000,2500 +; machine_max_acceleration_z = 200,200 +; machine_max_feedrate_e = 100,100 +; machine_max_feedrate_x = 300,160 +; machine_max_feedrate_y = 300,160 +; machine_max_feedrate_z = 40,40 +; machine_max_jerk_e = 10,10 +; machine_max_jerk_x = 8,8 +; machine_max_jerk_y = 8,8 +; machine_max_jerk_z = 2,2 +; machine_min_extruding_rate = 0,0 +; machine_min_travel_rate = 0,0 +; max_fan_speed = 100 +; max_layer_height = 0.3 +; max_print_height = 220 +; max_print_speed = 250 +; max_volumetric_extrusion_rate_slope_negative = 0 +; max_volumetric_extrusion_rate_slope_positive = 0 +; max_volumetric_speed = 0 +; min_bead_width = 85% +; min_fan_speed = 70 +; min_feature_size = 25% +; min_layer_height = 0.07 +; min_print_speed = 20 +; min_skirt_length = 4 +; mmu_segmented_region_interlocking_depth = 0 +; mmu_segmented_region_max_width = 0 +; multimaterial_purging = 140 +; notes = +; nozzle_diameter = 0.4 +; nozzle_high_flow = 0 +; only_one_perimeter_first_layer = 0 +; only_retract_when_crossing_perimeters = 0 +; ooze_prevention = 0 +; output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +; overhang_fan_speed_0 = 0 +; overhang_fan_speed_1 = 0 +; overhang_fan_speed_2 = 0 +; overhang_fan_speed_3 = 0 +; overhang_speed_0 = 15 +; overhang_speed_1 = 25 +; overhang_speed_2 = 50 +; overhang_speed_3 = 70% +; overhangs = 1 +; parking_pos_retraction = 92 +; pause_print_gcode = M601 +; perimeter_acceleration = 4000 +; perimeter_extruder = 1 +; perimeter_extrusion_width = 0.45 +; perimeter_generator = arachne +; perimeter_speed = 250 +; perimeters = 2 +; physical_printer_settings_id = +; post_process = +; prefer_clockwise_movements = 0 +; print_settings_id = 0.20mm SPEED @MK4S HF0.4 +; printer_model = MK4S +; printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4S\nPG\nHF_NOZZLE\nNO_TEMPLATES +; printer_settings_id = School Prusa MK4S HF0.4 nozzle +; printer_technology = FFF +; printer_variant = HF0.4 +; printer_vendor = +; raft_contact_distance = 0.15 +; raft_expansion = 1.5 +; raft_first_layer_density = 80% +; raft_first_layer_expansion = 3.5 +; raft_layers = 0 +; remaining_times = 1 +; resolution = 0 +; retract_before_travel = 1.5 +; retract_before_wipe = 80% +; retract_layer_change = 1 +; retract_length = 0.7 +; retract_length_toolchange = 0 +; retract_lift = 0.2 +; retract_lift_above = 0 +; retract_lift_below = 219 +; retract_restart_extra = 0 +; retract_restart_extra_toolchange = 0 +; retract_speed = 35 +; seam_position = aligned +; silent_mode = 1 +; single_extruder_multi_material = 0 +; single_extruder_multi_material_priming = 0 +; skirt_distance = 6 +; skirt_height = 1 +; skirts = 0 +; slice_closing_radius = 0.049 +; slicing_mode = regular +; slowdown_below_layer_time = 6 +; small_perimeter_speed = 170 +; solid_infill_acceleration = 4000 +; solid_infill_below_area = 0 +; solid_infill_every_layers = 0 +; solid_infill_extruder = 1 +; solid_infill_extrusion_width = 0.45 +; solid_infill_speed = 250 +; spiral_vase = 0 +; staggered_inner_seams = 0 +; standby_temperature_delta = -5 +; start_filament_gcode = "M572 S{if nozzle_diameter[0]==0.4}0.036{elsif nozzle_diameter[0]==0.5}0.026{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.015{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.08{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" +; start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "[printer_model]" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U6.0.4+14924\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; set extruder temp for bed leveling\nM109 T0 R{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; wait for temp\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; support_material = 0 +; support_material_angle = 0 +; support_material_auto = 1 +; support_material_bottom_contact_distance = 0 +; support_material_bottom_interface_layers = 0 +; support_material_buildplate_only = 0 +; support_material_closing_radius = 2 +; support_material_contact_distance = 0.2 +; support_material_enforce_layers = 0 +; support_material_extruder = 0 +; support_material_extrusion_width = 0.36 +; support_material_interface_contact_loops = 0 +; support_material_interface_extruder = 0 +; support_material_interface_layers = 5 +; support_material_interface_pattern = auto +; support_material_interface_spacing = 0.2 +; support_material_interface_speed = 50% +; support_material_pattern = rectilinear +; support_material_spacing = 2 +; support_material_speed = 120 +; support_material_style = snug +; support_material_synchronize_layers = 0 +; support_material_threshold = 40 +; support_material_with_sheath = 0 +; support_material_xy_spacing = 80% +; support_tree_angle = 40 +; support_tree_angle_slow = 25 +; support_tree_branch_diameter = 2 +; support_tree_branch_diameter_angle = 5 +; support_tree_branch_diameter_double_wall = 3 +; support_tree_branch_distance = 1 +; support_tree_tip_diameter = 0.8 +; support_tree_top_rate = 30% +; temperature = 225 +; template_custom_gcode = +; thick_bridges = 0 +; thin_walls = 0 +; thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 480x240/QOI, 640x480/PNG +; thumbnails_format = PNG +; toolchange_gcode = +; top_fill_pattern = monotoniclines +; top_infill_extrusion_width = 0.42 +; top_one_perimeter_type = none +; top_solid_infill_acceleration = 2000 +; top_solid_infill_speed = 100 +; top_solid_layers = 5 +; top_solid_min_thickness = 0.7 +; travel_acceleration = 4000 +; travel_lift_before_obstacle = 0 +; travel_max_lift = 1.5 +; travel_ramping_lift = 1 +; travel_slope = 1 +; travel_speed = 300 +; travel_speed_z = 12 +; use_firmware_retraction = 0 +; use_relative_e_distances = 1 +; use_volumetric_e = 0 +; variable_layer_height = 1 +; wall_distribution_count = 1 +; wall_transition_angle = 10 +; wall_transition_filter_deviation = 25% +; wall_transition_length = 100% +; wipe = 0 +; wipe_into_infill = 0 +; wipe_into_objects = 0 +; wipe_tower = 1 +; wipe_tower_acceleration = 0 +; wipe_tower_bridging = 10 +; wipe_tower_brim_width = 2 +; wipe_tower_cone_angle = 25 +; wipe_tower_extra_flow = 250% +; wipe_tower_extra_spacing = 110% +; wipe_tower_extruder = 0 +; wipe_tower_no_sparse_layers = 0 +; wipe_tower_rotation_angle = 0 +; wipe_tower_width = 60 +; wipe_tower_x = 180 +; wipe_tower_y = 140 +; wiping_volumes_matrix = 0 +; wiping_volumes_use_custom_matrix = 0 +; xy_size_compensation = 0 +; z_offset = 0 +; prusaslicer_config = end diff --git a/server/xyz-cali-cube_MK4.gcode b/server/xyz-cali-cube_MK4.gcode new file mode 100644 index 00000000..a32cec3c --- /dev/null +++ b/server/xyz-cali-cube_MK4.gcode @@ -0,0 +1,28674 @@ +; generated by PrusaSlicer 2.8.1+win64 on 2024-10-22 at 17:38:37 UTC + + +; +; thumbnail_QOI begin 16x16 676 +; cW9pZgAAABAAAAAQBAD+VFRUf6uIpoiUiDB//oxeMLl4/nNsZpgeO5yIwKWIpIgwqIgRKH+a4f6yZB +; b+0WkAwP7AciP+h3psO52IwAqtiBE3DsD+iGE5Nf6/YACcTDXAny/+pnlL/mlpaZiIoogOq4g7QP6k +; YyE1/rldABI1w/7DdSb+gXp0/llZWR3A/m9iVP7CZws1EjXE/sBhADWgL/6WfGICLP5tUDL+p1QA/s +; xnADXCm02ilv6zWgAcNf6aTQD+ZUkt/oWFhSz+cVQ2/odEAKfx/sNiADXB/sBgABA1Ev6CQQD+cDgA +; /nRYPC+jiP5zVjgewKK2/rFZAP7MZwA1wQ4d/o9IAB0M/ltbW6mI/nJUNx6lwzce/plNAP7IZAAU/o +; 5HAB2jpf6KRQAd/mdLLwrA/nBSNR43o6UewDf+djsAHcAsHcD+aU0x/mVlZQr+blEzHsCcXCueWh4d +; wKK2o5UdPv5rTzP+a2trJP5tYFP+f0gRHlYewR3A/phMAB0C/nFEFv5kWU8KsYiQiJ7U/nROKCvAHs +; AdOwId/m1OLg0ZCqOIIJiIGf58a1r+gkcLHsAdHv5wPAf+aFM+GcAgNwrACbGIgogu/npLHR4d/nFD +; Fv5iXlsZCsE6CraIQJGIIBnA/nJVN549NyAoChkNNwAAAAAAAAAB +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 313x173 29684 +; cW9pZgAAATkAAACtBAD+T09PxH/Ko4j+kJCQr4j+YWFhj4h/z3/FrYj+ra2t/n19fQPHEtR/xaSI/p +; mZmaOIBiHNf8cd/oKCgv6jo6P+a2trMNh/yf6AgID+29vb/v39/ZSI/p2dnf5eXl4/1Tf+n5+fk4j+ +; XV1dP+Iwooj+hISE/qqqqv50dHQw1yT+sbGx/o2Njf5ZWVkwxCHYEsMw/ouLi72I/mxsbBLKFsIlzr +; +I/qurq/53d3cSNMx/xv5+fn4YNwPGEtMhyv54eHj+rq6u/nJycjAhyDDJroj+m5ublYj+W1tbMNM/ +; 0AL+uLi4/vb29gj+xcXFNj/TAToBHT/ms4j+p6enh4gKMNQ1/re3twk/MMch2BLDuIj+q6ur/oWFhQ +; 4SyBbBf880LP6dnZ2ZiAo0ygPFDv6ioqIHA8YS0yHNN/6lpaWLiAohxDDKf/55eXn+pKSk/nd3dzDQ +; P9L+bVpG/qtjG/58XDs/DiX+5ubmuIiKiP6MjIwKP88s/pWVlRn+YmJiP+mkiP6Pj4+3iP5qamo/wF +; XPOyCYiAowzCHWVcMs/paWlquIAhLHnIh/zzTD/n5+fv6oqKj+bm5uAzTGA8Yn/rS0tP5oaGgDxRLS +; IdL+hoaG/qmpqSswITDMN/6RkZGpiP5gYGAwzH/W/pNfK/7RaQDAnx7+jF4w/l5XUZ8et4j+yMjI/v +; r6+sD+tbW1/mZmZj/Ns4g6/n9/f/5aWlo/7L6IOv6Dg4MdP8IwyruI/ru7u/54eHgOMM8h1hLDPf6s +; rKz+enp6IRLFJc5/xhX+pqamgogONMMDxhL+lpaWqIgwA8QS0iHVs4j+rq6uIiwwy7qI/qOjoxP+Vl +; ZWMMk/2P5lWEv+umUQNcP+smQVKT8KBv7v7++wiP7b29v+f39/LD/KLSAcP++piP6ampo3/mNjYz/F +; MMQ//piYmDr+YWFhMNMh1lXCBv6goKCWiP5dXV0SxCXNNMgSOLaI/mVlZTTAA8cz/q+vr/51dXUDxB +; LSIdUwwQ4/q4j+Y2NjMMiliP6Hh4e7iD4wx3/b/oRdNjXGnx7+jF4w/lZWVsAb/tbW1v78/PyYiBH+ +; YGBglojHq4j+o6OjiYj+XFxcP/ImOv55eXk/yDAg/rW1tQ/+WFhYMNch1RLBIQD+qqqqBRLDJcs0zL +; mIOv59fX0wA8Ye/qmpqf5bW1sDwxLSIdQwxiM2Lg4wxbGI/p2dnSksMMQ/3aHy/qNiIDXJ/rJkFTk/ +; JP6wsLD+9fX1qYj+zMzM/nNzcw4/xP57e3s6/nFxcR0/9C/+paWljoj+XV1dP8b+iYmJ/rS0tP5paW +; k/MNoh1VXBPv6qqqqAiCwSwSXKNM4d/piYmKOI/l5eXgPDqIj+p6enLQPDEtIh1DDJGf6hoaGWiCgw +; wqKI/nx8fBEjMMM/3/5tWkb+wmcLNcufHv59XDs/Djz+4uLiF46I/pOTk/5dXV0OwaSI/pmZmR3+YG +; BgDtQ/4aOI/oqKir6I/m9vbz/DKDodCj/CMNoh1FXBDv6RkZER/mdnZxLAJch/zgPCGysyEgPAMgX+ +; Y2NjA8MS0SHTMM4T/qysrP5ubm4/MBX+lpaWwDcwwH/i/pthJv7RaQDP/pxhJv5fWFIOHP7CwsL++P +; j4o4j+vb29/mpqag4nCf57e3v+WlpaDt8/2Tb+qqqqLSw/wP52dnb+ubm5Mg4/xjDZIdQSwTL+ra2t +; JjAlxzTOA8Q3/qKiogc3/p2dnR0SA8IS0SHTMNEC/qmpqTwu/qOjo/59fX0/5P5tWkb+umUQNdH+um +; YQ/m5aRw47/pmZmf7r6+u0iIKIKwIvDug/06eI/paWlgL+aWlp/qCgoKaIBpeIyzDYIdMSwRn+nZ2d +; DSXFNM4DyB3+29vb/oyMjAPCEtEh0zDUGQG5iP51dXU/4w7+jF8xNdSfHgv+X1hSDhD+1dXVF5uI/q +; +vr/5jY2MO7T/QGv7g4OD+oaGh/llZWT/OVdgh0xI/HjolxDTNA8kpuIj+a2tr/qurq/6CgoIOEtEh +; 0jDWtYj+oKCgMYyI/rCwsDksP9wOwgj+q2MbNdf+smQW/mZZTBP+qqqqNv7y8vIm/tPT0/54eHiAiA +; 7wP8r+j4+PBf5ycnIkl4gGP9Aw1yHRroj+rq6u/vX19aeIJcI0zgPIq4gJ/oGBgQPAP/6Tk5OsiDOR +; iM4h0jDWo4gEEf5ubm4/wKSI/pycnMD+YGBgP9YOxv51W0H+yWgFNdmjL/6viGA3Dv6Dg4P+3d3d/v +; 39/ZOI/pqamv5fX18O8z/Eq4j+sbGxgIj+WlpaPx0PK/5zc3M/0TDWIc4wG/7S0tIXk4ggJcF/zX/J +; Kv6urq4VA8ESwP50dHQJLjASyiHSMNY/Av6ampqZiP5dXV0/w/58fHw2Iw4/0A7J/pxhJjXd/qNiIf +; 5fWFIOr4j+u7u7/vf396aI/sPDw/5ubm4O9j/+fX19/ri4uP5vb28OP8K0iA2FiP5bW1s/0TDWIcuo +; iP6cnJz+7u7uF/7Y2Nj+gICAISU0zgPIP/6goKCRiAPBf8OpiP6fn5+WiCgSxyHSf9U/wA7+eHh4Ef +; 55eXk/xiT+p6enFig/yw7L/m5aR/7CZwv+0WkA3/66ZhD+fV08Diz+k5OT/ufn5yY+GhkO8wr+pKSk +; FTcOP8QKNCAcP9Iw1lXINv7ExMT+/Pz8moj+tra2/mRkZCHAnYjNA8m6iP61tbUrA8F/xhP+qamp/m +; 5ubhLEIdIw1D/CNxatiDM/yf6KioocKz/GDs7+jF8xNeKfHgv+X1hSDrmI/svLy/76+vpV/rOzsy8O +; 8QX+vLy8/nt7eywOwz/D/nZ2djoT/lhYWD/SMNUhxSw4/uTk5CYRBw4hwTTMA8n+k5OTFT8DwH/JsY +; gcHiwSwCHSMNQ/wyf+oqKiAA4/yyv+sLCwE/5aWlo/wQ7PCP6yZBY15Rn+ZllMDij+o6Oj/vDw8Cb+ +; 2dnZ/n19ff5ZWVkO7v6WlpYYEQ7IVcGqiP6cnJx//mJiYj/TMNUhwrGIEC4b/sXFxSchwzTKA8kk/q +; +vrz0DwBLMMP6QkJACLyHRMNQ/wzv+hYWFvYgrP84s/piYmKmI/mNjYw7Q/oVeNjXonx7+jV8x/lhY +; WMD+fn5+/tnZ2f79/f2WiDP+YWFhHcdV4iD+tLS0/oqKiv5aWloOzD8OEzr+d3d3P9Qw1CE//n9/f/ +; 7Y2Nj+////j4j+np6e/lxcXCHENMkDyf6EhIQ6GQPAEs+8iP6srKz+fHx8DiHNMNM/xS8oBzs/zg7B +; H/6wsLAugogOzf6jYiE16/6zZRb+YFlSnx8RAf719fWpiP7Kysr+c3NzHdAO1x7+tra2/mxsbA7QP7 +; KI/qampouI/l1dXT/UMNIV/qSkpP7x8fEI/tPT0yr+VFRUxp2IyAPIHf6lpaWGiAPAf88hwQr+m5ub +; Vf5fX18hyjDTP8UOORH+dXV1P80OxQYRkogGDsr+blpH/sJnCzXtnx7+fl08HSz+jY2N/uTk5BeMiP +; 6RkZEoHdUOzij+qqqqCigO06OIOLuI/m1tbYmI1TDPv4j+ysrKCJeI/q+vr/5iYmIhx52IxgPJv4j+ +; srKy/mVlZQPAEs8hxP5+fn46/nNzczAhxjDTP8YV/pSUlKSIFT/LDsoA/qysrP5ubm4OyP6UYCs18f +; 6cYib+YFlSnx+0iP7FxcUMooj+urq6/mpqah3aDse+iP67u7v+dnZ2LA7Wu4g6Dyw/1TDLpogh/unp +; 6SaAiDwOMFXHNMUDyCH+mpqafyEDEs8hx62I/qWlpQf+W1tbIcMw0z/HI/6kpKT+gICAP8oOzq+I/q +; ysrC0ZDsT+blpH/rpmEP7RaQDzEf5vW0cdGf6cnJz+7e3tJv7e3t7+g4ODCh3cDsEs/p6enquIJA7Z +; qIj+mJiYqYj+ZGRkP9UwySsqDMD+v7+/KzDCIcY0wwPJs4j+srKy/nJycgPAEs4hyjAavIgrIcAw0j +; /Ipoj+ioqKJC8/yQ7Rf/6UlJSyiP5mZmYOwv6MXzE19p8e/o1fMR3A/nh4eP7U1NT+/Pz8mYj+qqqq +; Ah3dsoj+t7e3MQqciNz+fX19Cf58fHw/1jDFHQ/+3t7e/v///4uI/piYmAowxFXFNMIDyf6Kioq8iC +; wDEs8hzbeICRMsMNA/ybOI/qCgoAssP8cO1hQF/nt7e/5aWlqe8v6rYxs11f6tVwCipjXg/rNlFv5n +; Wk3+WFhYqYj+ra2t/vPz8yb+0NDQ/nd3dywd2v6Ojo7+srKy/mhoaB3CDtsg/qOjo5OIBpeI1jDCM/ +; 6rq6sBqIj+zs7OHzDHVcQ0wQPIGf6qqqoxA8ASziHQLP6Xl5eniP5jY2MwzT/JHf5/f3/+pKSk/nBw +; cD/Hf9kZ/p+fn/6njXL+yWgGNdX+lEoA/nA4AP6HRAD+mU0A/sxnADXfnx7+fl08/lhYWMAP/t/f37 +; +IkIj+mJiY/l9fX5mI16mINoeIGR3HDtgsPCv+cnJyDlXVMD/+eXl5JReTiP6oqKgGMMohwjQDyT02 +; JAPAEs4hzzDCPRguPzDJP8oz/piYmJ2I/l5eXj/FDtx//pxiJjXV/r9gAP58PgCaLsAewKK2/rpdAD +; XgPf5gWVKfH7CI/r+/v/74+Pg5/sHBwf5ubm4d1f58fHz+ubm5Iywdyw7Xtoj+qampGgoOP9OoiP6b +; m5v+7e3tF/7b29v+g4ODP1XMIcEDyDD+n5+flYgDwH/Of88wxauI/qGhoZKINzDHP8r+dnZ2ESo/xQ +; 7bHcD+b1tH/rpmEDXV/qBQAP5wOADC/oxGAB7B/qdUAP7MZwA13xE6/lhYWKKI/paWlv7p6em2iIaI +; PBkd0aOI/qOjo8AVHdBV1Sj+lJSUsYgNDsBV0LmI/sLCwv77+/uciAz+Z2dnMM9VwAPHuIj+tbW1/m +; xsbAPAf84hzzDID/6pqaknMMQ/yij+jo6OsIj+Y2NjP8QO2h3C/o1fMTXV/r9gAP6CQQD+cDgAwQAS +; oqb+lUsAHsH+sVkANeCfHg7+YFlSnx8j/s7Ozio9/rCwsP5mZmYdzzb+vLy8/n9/f/5aWlod01XV/n +; h4eP6srKz+gYGBHQ4/zTsp/uLi4r2Ih4j+kpKSOzDRIUDG/pCQkCQ/A8B/zSHPMMs+/qurq/6Hh4c7 +; MMA/y7eIJA8dP8IO2h3DEP6zZRY11f6nVAD+cDgAwv6aTQA1wZ5aDv6MRgCeWg414iH+Z1pN/lhYWK +; eI/qenpxQm/tbW1v58fHw7Hcz+lJSUu4j+ZmZmHdhV06uI/p6enp2I/mFhYQ7AVcov/rKysv729vYq +; /sjIyP5ycnI/wjDQA8QG/q6urjkDwBLOf88wzR3+k5OTr4j+Z2dnP8osIv6jo6P+a2trP8IO2R3F/o +; VeNzXVnVv+jkcA/nA4AMGjtf65XQA1xP6+YAD+kEkAATXjnx/+hl84LMD+gYGB/tvb2/79/f2ViP6g +; oKAkl4jJrYj+tLS0/o6Ojhkd2w7SHf6EhIT+q6ur/nZ2dg7BP8YO/n9/f/7W1tb+////kIj+oaGh/l +; 5eXpiIxDDPA8P+gICA/q2trTcDwH/NIc8w0D+8iBg5HT/HIP6cnJwl/ltbWz/BDtgdx/6kYyH+0WkA +; 1f65XQA+nVvB/pRKADXHHP6jUgA15Tf+YFpTLC/+t7e3/vb29gj+x8fH/nJycizDVcL+hISE/ri4uP +; 5vb28sHd8O0bKI/qenpyX+XV1dDsA/xKqIM/7w8PAI/tbW1v5/f3/+VlZWxzDOA8EO/qOjo4qIA8ES +; zSHOMNE/waiI/p+fn5qI/mFhYT/EDv56enr+paWl/nd3dz/BDtcdyP5vW0f+wmcLNdX+mk0A/nA4AM +; L+rVcANcn+sVkA/shkADXl/rpmEf5+Xj3+WVlZO/6RkZH+5ubmF4uIFjcswzf+qKioloj+X19fmYjj +; DtAKFj7+a2trDsE/wb2I/snJyf78/PyZiP6zs7P+ZWVlP8pVzAPANjL+Z2dnA8ASziHOMNA/xf6BgY +; H+q6ur/nNzcw4/wQb+k5OTp4gkP8AO1x3J/pRgLDXV/r9gAP58PgCaLsEA/sVjADXK/rpdAKXDNeaf +; H/6VYSz+YFpTLDYt/vr6+sD+t7e3/mlpaSzBu4j+vLy8/np6egoswlXiDtABOv6FhYUsDsCliP6Tk5 +; P+5+fnuIiDiCkdP8wwy0D+l5eXpogwA8ASzSHPMM8/yK6I/qioqDj+XV1dP7qI/qSkpP6CgoL+V1dX +; Pw7WHcr+b1tH/rpmEDXV/qdUAP5wOADC/qBQADXMmR80Nej+s2UW/m9cSCykiP6goKD+7u7uJv7c3N +; wTKP6cnJwJ/mRkZCzIVeBVz6mI/pmZmaeIIKWIDP74+Ph//sPDw/5tbW0/zzDK/rCwsAEDwRLNf84w +; 0D/KDha/iIiIroj+aGhojogO1h3L/o1fMTXVFP6IRAD+cDgAwf6CQQD+v2AANc7+p1QANekC/o1gMv +; 5ZWVnA/nt7e/7W1tb+/Pz8mIiAiP6SkpL+XFxcLMxV31XP/n9/f/7AwMC/iP7///+NiP6cnJwomYjR +; MMn+oqKiCpeIwH/Nf84wzz/NDSe8iDH+WlpaDtUdy6Hx/qRjIf7RaQDV/q1XAP5wOADC/ppNADXQ/q +; xWADXr/rNlFv5oW04sqoj+z8/PDCb+zs7O/nZ2djsszlXeDswC/qmpqQEXiogTDsE/0TDIG/6srKwy +; Esx/zjDPP80d/n5+fv6lpaUjioj+mpqapIgCDtEdzTr+wmcLNdX+lEoA/nA4AMGjtS810Sc17J8f/o +; RkQ/6urq47Kf7h4eG9iI+I/paWlv5fX18s0FXcDsr+eXl5BwiWiP6tra2DiLqI/nBwcA7BVdEwxwOx +; iBWTiD+diMkhzjDPP82riP6Xl5dVBpiIwf59fX0Y/nd3dx0OzR3O/pxiJjXV/r9gAP58PgCaLsEA/s +; VjADXS/qdUADXu/qZsMf5iW1Ussoj+wsLC/vj4+Dn+v7+//m1tbSzSVdtVxqeI/pmZmf7s7OwX/t3d +; 3f6Hh4cdDjYr/omJif5aWloOwFXRMMZAwB0x/qurq/5nZ2cSxyHNMM8/zr+IEQgOxAIRjogGDsodzv +; 5vW0f+umYQ/tFpANX+oFAA/nA4AML+p1QANdT+sVkA/shkADXu/rpmEf5vXEj+WVlZGf6ZmZn+6+vr +; tIiDiB7+XV1dnIjSVdtVw7eI/sDAwP76+vo9/r29vf5qamoOwjf+lpaWrYg+DsBV0TDFEsK4iP6oqK +; geEsUhzTDPP80oKRX+ZWVlDsf+ioqKHDYOxx3P/o1fMf7RaQDV/r9gAAD+cDgAwQASNdUPNDXvnx/+ +; jWAy/mBaUywQ/tHR0f77+/uciP6tra3+ZmZmLNRV2VXAOwv+4eHhJomI/paWlhkOxf56enr+rKys/n +; 9/fx0OwFXQMMRAw6iI/pCQkLKI/l9fXxLBIc4wzj/Otoj+oaGhh4gsDsk6GP6GhoYZDsMdz6Hx/qtk +; GzXV/qdUAP5wOADCBDXXmR+ZHzXx/rNlFv5oW04sqIj+qqqq/vLy8q2I/tTU1P57e3s7LNRV2C/+sb +; Gx/vX19aaI/svLy/51dXWCiMisiBWZiP5gYGAOwD/QMMMSxH/+dHR0/qysrP56enoSIc4wzj/No4j+ +; goKC/qSkpP5ubm4OzDv+lZWVMz4OwB3Q/nZcQv7JaAU11J1b/pRKAP5wOADBo7X+s1oANdn+rFYANf +; KfH/6GXzgswP6Dg4P+3d3d/v39/ZOI/p2dnSQs1VXULP5+fn7+1NTU/v7+/gX+paWlFQ7KHR4rAQ7A +; P9BVwhLGFSibiCybiMswzz/Nroj+m5ubmIj+XV1dDs/+d3d3/q6urv58fHz+WlpaQM/+nGImNdX+v2 +; AAH5ouwf6IRAAUNdr+p1QANfT+pGMhHCyuiP67u7v+9/f3CP7FxcX+cXFxLNZV0ST+oKCg/u/v7xf+ +; 2NjYE/5YWFhVzTr+qKiohogZDj/QMMFAxw7+f39/Cf5sbGyIiMkwzj/NDgwgDA7RHST+oaGhl4j+YW +; Fhl4jM/m9bR/7CZws11QT+cDgAwv6nVAA13P6sVgA19f66ZhH+fl49LKKI/pOTk/7n5+e4iD44/l5e +; XizWVc67iP7Hx8f+/Pz8moj+tra2/mdnZx3BVc0K/pGRkbWI/mpqag4/0FXAQMYhwbSIIDgwIcYwzj +; /NNzSriP5iYmIO0R3C/oSEhP6rq6syHcr+jV8xNdX+v2AA/oJBAP5wOADBAP7FYwA13f6xWQA19gL+ +; jWAyHCy3iP7Ly8v++vr6Vf61tbUNLNdVyig0/uXl5SaFiCUKHcNVzr+IOiIsP9AwQMUhwzv+i4uLvI +; j+YmJiIcQwzj/NuYj+o6OjMR0O0B3FDTopNx3GofH+s2UWNdX+p1QA/nA4AMIENd/+tVsA/shkADX3 +; IRYsNwL+7+/vJv7a2tr+gICACizXVcexiP64uLguo4j+xcXFFB3GVc4k/pubm6OI/mNjYz/QEsN/xj +; AU/qqqqjUhwTDOP82kiA+9iP5qamoO0B3IOwMv/mtrax3E/oVeN/7RaQDWmi7+djsAnVvAPv65XQA1 +; 4P6+YACdezX4ny/+jmEzLMD+fn5+/tjY2AiWiP6lpaX+YmJiLNdVxDsi/tra2iaOiP6fn5/+X19fHc +; kOzv6BgYH+q6ur/nh4eD/PEsIhyQb+mZmZpIj+XFxcMM4/zbGINwf+W1tbDs8dzBT+rq6uBAodwf6k +; YyE12J1b/o5HACM14gH+tVsANfr+s2UW/mFbVJ8vq4j+s7Oz/vX19amI/svLy/51dXU7VdcdwauI/q +; enp/7y8vII/tPT0/59fX0dzA7OsIgvA/5dXV0/zZ2IwX/LP/57e3v+ra2t/nFxcTDMP8wd/nx8fP6l +; paUQDtAdzqaI/p2dnX/+ZGRk/m9bRws1/cP+sVkANfufL/5/Xz47wDj+4+PjF42I/pOTkwYs1x09/s +; zMzP79/f2XiP6wsLD+ZWVlHc4Ozjv+jIyMvIj+bm5uiIjMnYjAIc0wsYj+oqKiAx0wyD/Nqoj+lZWV +; oogVl4jPHdL+gICA/ruHU/7RaQD9xf6sVgA1/f6dYicrO7OIAAyiiP68vLwJLNUG/peXl/7q6uomgI +; j+ioqKOx3QVc8U/qurq/6Hh4csP8oSIc1/wTseOj4wxj/NvYj+pKSk/n9/fw7QHdL+b1tH/rpmEDX9 +; xv6nVAA1/cD+u2YR/nBdSTujiP6cnJz+7Ozss4iBiP6FhYX+XFxcLNK1iP6+vr4MVf7AwMD+bGxsHd +; NVz6eI/peXlzMgP8khzTDEJ/6pqakAMMQ/zBn+i4uLtogvkYjPHdP+allI/sxnAKK2/cf+sVkA/shk +; ADXbnVv+lksABRw13Z8v/mxYQzvAvoj+09PT/vz8/JqI/qurqyAszxk8/t/f3yaKiP6ZmZk3m4jAHd +; MO0P58fHz+rKysOQ4/xyHLMMc3/pSUlDP+X19fMME/zLSI/qCgoAs7Ds8d0yz+alE4/pVLAP7DYgA1 +; /cb+tVsAATXa/r9gAP58PgCbTf6HRAD+mU0A/shkADXbEv6CQQD+ZEszO8GpiP6tra3+8/PzJv7S0t +; L+eXl5OyzML/6urq7+9fX1p4j+zs7O/nh4eCzDHdMO0K2IM5WIBj/GQMowyaKIPRg9MD/LHf6AgID+ +; pKSk/m9vbw7PHdMswf5qUTgeorb+sVkA/tFpAP3FKDQ12f6aTQD+cDgAwCsewDcbNdn+p1QAHcD+ZE +; szO8P+hoaG/t/f37+IkYj+mpqa/mBgYCzJf/59fX0DFyP+qampAizFVdNV0B0t/qqqqv5ycnI/xSHJ +; MMwC/p+fn5iIOz/JAv6ZmZmbiP5eXl6ZiM8d0n/DEB7B/plNAP7IZAA1/cOeWv6nVAA11xIA/nA4AM +; H+hEIApMQewf6jUgA11hT+jkcAHcE9O8SviP6+vr7++Pj4Of7CwsL+b29vLMeoiP6enp7+7u7usIj+ +; 29vb/oWFhTssx1XTVdA/tYj+qampKTs/w0DIMM0/OyIY/mxsbD/GDv53d3f+paWl/nt7ew7PHdMsxB +; AewqK2GzX9w/6sVgA11v6nVAD+cDgAwgQ1Af6eUAAewBs11TH+djsAHcL+ZEszO8UZ/paWlv7p6ekm +; hoga/l1dXSzEI/7FxcX++/v7nIj+urq6/mpqaizLVdJV0FWliP6Tk5OyiP5oaGg/wiHHMM0/wjr+qK +; ioKQ4/wyj+j4+Pr4j+Y2NjDs8d0izGEB7EOP7MZwA1/cEnNdQU/o5HAP5wOADBPjE1wRz+tVsAATXV +; BB3EPTvHuYj+zc3NKp2I/rKysv5nZ2cswSgW/uTk5CaGiBIZLM1V0g7PP8H+d3d3/qurq/6BgYEOP8 +; AhxjDNP8QoFreI/mNjYz/BuIgzDx0Ozx3SLMcQHsWn8f6+YAA1/cD+rFYANdMx/nY7AJ1bwf6USgA1 +; 2/6/YAD+fD4AHcX+ZEszO8gVL/7x8fEm/tjY2P5+fn4Kroj+tbW1/vf39yr+ycnJ/nR0dCzQVdIOzz +; /BJP6dnZ1AJD8hxTDNP8YOAf6srKz+f39/O/6FhYW+iP5ra2sOzx3SLMn+alE4HsaitgX+zGcANfwF +; /shkADXR/ppNAP5wOADC/q1XADXb/qdUAB3HPTvK/oGBgf7a2tr+/f39lYj+tLS0/tjY2P7///+QiD +; MkLNJV0VXQP8EOIv6rq6v+dnZ2IcQwzT/JrYgJuoj+lZWV/ltbWw7PHdIsyhAeyP6ZTQD+yGQANfv+ +; vmAAwDXPEv58PgCaLsH+gkEA/sVjADXanVv+iEQAHcg9/lpaWsusiP68vLz+/Pz8JsCAiAQs1VXRDs +; 8/w7KI/rKysv5UVFTDf80/yQ7+enp6/qioqI+IGP5xcXGGiM4d0SzMEB7JNwU1+pkfmR81zv6nVAD+ +; cDgAwv6gUAA12/6tVwAdyj07y72I/svLy/7+/v4IF46I/pGRkf5fX18s1VXRVc8/wR0E/re3tyHCMM +; 0/yQb+k5OTpogVP7OI/qWlpY2I/llZWQ7LHdEszRAey/6ZTQD+zGcANfn+p1QANcydW/6IRAD+cDgA +; wQD+v2AANdv+lEoAHcs9O8kV/paWlv7p6ekmg4j+mpqa/sjIyP76+voq/rm5uf5qamos1VXRVc9VJP +; 6goKCpiP5iYmKSiMEwzT/Ju4j+pKSkBA4/DsCmiP6Kioo6/mdnZw7IHdEszxAezDf+ul0ANfgnNcsv +; /nA4AML+mk0ANdsSHx3MPf5aWlrIs4j+vLy8/vn5+cD+w8PD/m9vbzukiP6fn5/+7u7uJv7d3d3+g4 +; ODGSzUVdAOz7+I/rW1tf6Li4s/wCHBMMw/yAr+iYmJuIg+Pw7EFDr+hISEDsYd0SzQ/mpROB7O/qdU +; AP7MZwA19v6sVgA1yiP+cDgAwaO1/rNaADXbJR3OPTvGGf6IiIgoJoyI/pycnP5gYGA7wv57e3v+1d +; XV/vz8/JmI/qmpqf5kZGQs01XRVcymiAP+tLS0Kz/BIcB/zD/IsogGGiw/DsYV/peXlyQVl4jDHdEs +; 0f5qUTgez6fx/sNiADX1JzXIEv58PgCaLsEA/stmADXaEgAdzz07xayI/qysrP709PQ5/tHR0So7xR +; H+r6+vASb+z8/P/nd3dzss0lXRDsq1iP6urq6KiDs/wiEwzD/Hooj+f39/IP5xcXE/wA7ILConLg7B +; HdAs0xAe0KK2BTX0Bf7IZAA1xiX+cDgAwv6nVAA12w4d0T07xP58fHz+0NDQCJWI/q2trf5lZWU7yD +; z+4eHhF4+I/peXlxUs0lXQVcg7/oaGhv64uLguDsE/wTDMP8cz/piYmJ2I/l5eXj/ADssv/qGhoZWI +; Cp2I0CzTO/5rUjge0v6ZTQD+yGQANfL+vmAAnXs1xBIA/nA4AMEAEjXanVv+lEoAHdL+ZEwzO8KniB +; n+7e3tF/7d3d08CjvKsYj+wcHB/vj4+KSI/sDAwP5ubm4s0lXQDsYg/qampsAVDsJVwVXLP8e/iP6k +; pKT+fHx8P8EOzaWI/oaGhv6tra0JHc4s0zvA/mtSOB7TorY0/tFpAPEB/rVbADXD/qdUAP5wOADC/p +; pNADXbEh8d0wIKwDu3iBP++/v7Pf69vb0JO82jiP6ZmZn+6urqtYiEiDwonIjRVdBVwx3+e3t7/ra2 +; tv6FhYUOxT/AMMo/xij+jY2NBv5kZGQ/wQ7PHSf+qqqqGh3MLNI7whge1f6nVAD+zGcANfD+sFkANc +; GdW/6ORwD+cDgAwT7+s1oANdsEHdX+ZEwzCqOI/o6Ojv7i4uImiIj+l5eXNzvQu4gW/vv7+5yINv5m +; ZmYs0FXQVcKoiP6YmJi4iP5nZ2cOxj/AMMk/xgn+oaGhhoj+WFhYP8EOzx3BFQOziBEdyCzTO8MY/o +; dEANan8f7DYgA17/6wWQA1wP65XQD+djsAnVvBIzXbEv6CQQAd1v5kTDP+aWlp/rOzs/729vYq/svL +; y/53d3c706eI/qmpqf7y8vIm/tXV1TkKLM9V0A7Auoj+srKygogsDsg/Vck/xCwi/qSkpAk/wg7PHc +; Msv4j+rq6u/n5+fh3GLNM7xBge16K2/rFZAP7MZwA17f6wWAA1BP5wOADC/q1XADXb/qdUAB3Y/oNq +; Uv7W1tb+/v7+koj+pqam/mRkZAo71SL+3Nzc/v39/ZSI/p6eniQsz1XPpIgp/re3t/5xcXGGiMpVMM +; g/xBH+m5ublogZP8MOzR3HETcK/l1dXR3DLNI7xv51XEMe2f6ZTQD+yGQApcPs/rNaAP58PgCaLsEA +; /sVjADXanVv+jkcAHdn+v6aOF/7Y2Ngi/ltbW8I71a2I/rq6uv739/cI/sbGxv5xcXEszlXODf6qqq +; qWiCgOzDDHP8MO/nl5eSD+eHh4P8QOzh3ICv6CgoIn/nBwcB3BLNI7xSj+l5eX/pyDaR7aorb+tVsA +; Nev+rFYA/nA4AMH+oFAANdsQ/nY7AB3a/rifh/62trb+ampqCsQ71Qr+k5OT/ubm5heKiAf+Xl5eLM +; 1Vyyz+gICA/re3tzX+V1dXzjDGP8MGNKqIJD/EDs4dyzr+p6enNDss0jvFCf6tra0T/m5VPB7c/qNS +; AByitun+sVkA/nA4AAD+v2AANdv+lEoAHdz+dl5FN52IxzvVtoj+ysrK/vr6+sAf/mlpaSzMVcqqiP +; 6enp6uiP5lZWUdwA7NMMU/w7mI/qOjoyIOP8QOzh3NpogHvIj+aGhoLM87xv6Kioo+/mxsbDv+a1I4 +; Ht2n8f66XQA16BIENdsS/nw+AJouzaOlHc3+ZEwzCslV1Qb+oqKi/u/v7yb+2tra/oGBgQosy1XIvY +; j+tbW1/o+Pjx3CDs0wxD/CCv6Hh4czDT/FDs4dzyy7iP6srKz+hISELM1/xRH+paWlFv5gYGA7wBj+ +; h0QA3qK2/rFZAP7MZwA15p1bNdsl/nA4AM7+hUIA/p9QAB3NAgrKO9Us/n19ff7X19f+/Pz8mIj+pq +; am/mNjYyzLVcWmiP6RkZH+tra2/m5ubh3EVcwwxD/BsYj+np6ej4g7P8UOzh3PLMEC/pubm6aI/mFh +; YSzKO8UICf50dHSHiDvBGB7g/plNAP7IZAA1/cOaLgD+cDgAziL+w2IA/no9AB3N/mRMMwrLO9UsEf +; 6ysrL+9fX1F/7Nzc3+dXV1OyzJVcS0iP6urq6NiBkdxQ7MMMM/wB0I/qSkpP5zc3M/xg7OHc8sw6KI +; /n5+fv6vr6/+dnZ2LMg7xKSI/pqamsD+Y2Njl4jDGB7horYFNf3B/q1XAP5wOADO/oBAAP6zWgAo/q +; RSAB3O/mRMMwrMVdUswP6MjIz+4uLivIiOiCEGLMlVwTv+hYWF/ri4uP56enodyA7LMMI/wBX+lpaW +; fwaXiMd/zR3PLMawiBGSiBksxTvEtoj+ra2t/n5+fv5dXV07xP5rUjge4/6ZTQD+zGcAorb7nVv+lE +; oA/nA4AM4j/sNiAMH+ej0AHc4CCs071CzBs4j+xMTE/vn5+Sr+vb29CSzIVcCtiBGkiP5iYmIdyQ7L +; MMF/wDIRFz/IDs0dzyzIpYg8JwkswzvDCv6QkJAR/mlpaTvGGB7korb+ul0ANfkS/nw+AJouzv6kUg +; Aowf6pVQAdzwIKzVXVLMGjiP6ampr+7OzsJoKI/oaGhhksxxv+tra2PP5YWFjLDsswwRn+i4uLtYgg +; P8gOzR3PLMu4iP6rq6s8LMF/wz4NPAY7xxge5v6nVAD+zGcANfb+mk0A/nA4AM+jpSjC/no9AB3PAg +; rOVdQsw76I/tLS0iqbiP6srKwgLMSniP6Wlpa9iP5qamqOiM1VyjDAK/6goKAtHUDIDs0dzyzNqIj+ +; lpaWEf5kZGQ7w/6Dg4P+qqqqBQo7yBge56fxKDXzEv6CQQD+cDgA0P6kUgAowf6pVQAd0AIKz1XULM +; MzOv7z8/Mm/tPT0/55eXk7LMG4iP6xsbGGiAqdiM5Vyh3+gYGB/qSkpP5ubm6IiMkOzh3OLM87Gzb+ +; fX19O8CmiP6goKCXiDM7yhge6Df+sVkAHDXw/qdUAP5wOADRo6Uowv6AQAAd0AIK0FXTLMX+hYWF/t +; 7e3r+Ikoj+m5ub/mBgYJyICz3+dXV1LMBVzlXK/peXl5yI/l1dXT/Kf80dzizROz7+oaGhmYj+eHh4 +; /q2trRsZQMv+a1I5HuoB/shkAKXD7Z1b/o5HAP5wOADS/qRSAP7DYgDB/rNaAB3R/mRMM/5bW1vQVd +; Qsxa+I/ry8vP739/cI/sbGxoyIk4gVLMFVzw7JBz/LDs0dzizRO8EV/rOzs/7U1NQXO8wKHx7rorb+ +; tVsANev+uV0A/nY7AJ1b0qOlKML+gEAAHdECCtFV0yzGCv6Wlpb+8PDwJjYa/l1dXSzCVc4OyRGQiA +; 5VyA7NHc8s0DvBsIgJF5eI/q+vryUKO8kKwB8e7f6jUgAcorbo/ppNAP5wOADU/qRSAP7DYgDB/rNa +; AB3SAgrSVdMsxTP+nZ2dvoi0iP76+vpVMv5nZ2cswVXODsk7/oaGhgn+ZmZmP8cOzR3OLNA7wS2/iP +; 5tbW07wKeI/pGRkbiI/mdnZzvHCsEfHu6n8f66XQA15f6/YAD+fD4Ami7Uo6X+w2IAwv6KRQAd0gIK +; 0lXTVcS8iP61tbX+kpKSOwYRBSb+2NjYFwosVc4OyT/Atog6AD/FDs0dzizQO8CpiBGNiBU7wgq8iP +; 6tra0iO8UKwv5rUjke7zf+sVkA/sxnADXi/qdUAP5wOADW/p9QACjB/rNaAB3T/mRMMwrTO9MswaWI +; Jf63t7f+cHBwLML+f39//tnZ2QiWiP6ioqL+YmJilojPVcg/waeI/pWVlREGP8IOzR3Of9A7wP56en +; r+rKys/nZ2dgo7xauI/p6enqKIJDvCCsP+a1I5HvH+mU0A/shkADXfFP6IRAD+cDgA1qOl/sNiAML+ +; ikUAHdMCCtNV01XAs4gYN/5eXl4sxCD+tbW1/vb29qiI/snJyf5zc3MsVc0OyD/CHb6INh8/wQ7MHc +; 4s0DujiP6ZmZmjiP5kZGQ7yCj+gYGBNv51dXU7wArE/mtSOR7yorb+sVkANd3+rVcA/nA4ANgi/sNi +; AMExHdQCCtQ70gr+hISE/rm5uf5+fn7+WVlZxzsH/uTk5LqIjIg0/l5eXpqIzA7IP8QzFZiILA7Nf8 +; 4szzsnGCb+XV1dO8uxiP6mpqYwKArEHx70/plNABw12v6USgD+cDgA2KOl/sNiAML+j0gAHdQC/ltb +; W9RV0SD+oqKiqIj+ZGRkLMq0iP7Gxsb++fn5ooj+urq6/mpqao6Iyw7IP8U7BP6wsLA6DssdzizPO/ +; 6NjY0vKzvOpogp/q2trToKwx8e9aK2/rpdADXXEv58PgCaLtkiKMGbPR3VAgrVO8+/iP62trYpLM2k +; iP6dnZ3+7e3tsoj+3t7e/oODgwqdiMkOyD/HK/6oqKgpDskdzizPIC8aBjvPCsC5iP6srKwtCsL+a1 +; I5HtGitv6nVAD+vmAA/qxWADce3w4cNdT+oFAA/nA4ANqjpf7DYgDC/o9IAB3VAv5bW1vVVc0kMP61 +; tbX+bW1tOyzPDP7U1NT+/Pz8mYj+qamp/mNjY5WIyVXHP8YOwKaIBw3+YmJiDscdzizOBP6rq6v+cn +; JyCjvPCsIR/pmZmauI/mRkZArAHx7P/p5QAP7DYgA1wg/+lUsAHt8rKDXREv6CQQD+cDgA2/6ZTQAo +; wZ1rHdb+ZEwz/ltbW9Y7y7aI/rCwsIqI/l1dXZ2IwVXPM/6urq7+9PT0Jv7Q0ND+d3d3LFXHDsc/xg +; 7BHSM2/n5+fg7FHc4szaWI/p2dnZuIMzvQCsSiiP59fX0F/nt7ewr+a1I5Hs+itv6xWQA1w55a/qdU +; AB7fNwU1zw7+cDgA3KOl/sNiAML+j0gAHdYC/ltbW9ZVyaOI/omJif64uLj+eHh4O8Msz1XA/oeHh/ +; 7g4OC+iJCI/peXlwYdxlXHP8UOxCQZf/5cXFwOwx3NLM0j/q2trSoZO9AKx66I/qOjo5aI/m9WPf6H +; RADR/plNAP7IZAA1ww/+kEkAHt/+mU0A/shkADXLFP6ORwD+cDgA3f6ZTQD+w2IAwZ1rHdf+ZUw0/l +; tbW9Y7yK6I/qioqC8zO8RVz1XBHP6/v7/++Pj4Of7AwMD+bW1tHcUOxz/EDsY7CP6xsbH+cHBwDsEd +; zizLCv6Tk5OviD470grIBi3+o4lwHtI3NP7RaQDDnlr+nlAAHt83NDXJMf52OwCdW92jpSjC/o9IAB +; 3X/ntjSiSaiNZVxQr+fn5+/ri4uP6GhoY7xizPVcI7/peXl/7p6em2iIWILRkdw1XHP8QOyLCI/qen +; pwMdzyzLr4j+qqqqD/5dXV070grL/nVcQh7U/qdUAP7MZwA1w/6xWQA3Ht8OHDXG/ppNAP5wOADfAf +; 7DYgDBnWsd2P6KclkrGI6I1TvEqIj+m5ubFP5paWk7xyzQVcMj/s7Ozv77+/udiDYgHcJVxz/DDsoZ +; /omJiTr+ZmZmHcwsy/6FhYX+qKio/m5ubizAO9F/zP5rUjke1afxKDXDKCse3ysoNcP+v2AA/oJBAP +; 5wOADfo6UxKMH+j0gAHdgM/nFxcf6zs7Mi/l5eXp2I01XDuoj+tLS0MBk7ySzPVcQGPv7x8fEm/tbW +; 1v58fHw7HcAOx1XDDssdt4j+ra2t/oWFhR3LLMkk/qKiopKI/mBgYCzAO9EKzR8e1qK2Bf7MZwA1wh +; z+p1QAHt83BRw1wA7+cDgA4f6ZTQAowZ1rHdkMGQb+nZ2dpIgvlYjSO8EG/o6Ojv64uLj+c3NzO8ss +; z1XG/oGBgf7b29v+/f39lYj+n5+fFR1Vxz/CDs0dFf6YmJiriP5fX1+ZiMgsyb6ICS6EiCzAO9IKzR +; 8e2AH+yGQANcEx/no9AB7h/plNAP7CYQD+iEQA/nA4ANlGHcWjpTGl08H+j0gAHdkMGcBV/n19ff6y +; srL+eXl5GQrRVbGICSQGO8wsz1XHrYg9/vb29qeI/sbGxv5xcXEdDsY/wg7MHcE7DP6wsLD+dXV1Hc +; csxxn+l5eXpoj+ZGRkLMI70QrO/mtSOR7ZN/61WwA1/ppNAP5wOACiph7imi4d251LHcT+j0gA/sNi +; AMGdax3a/mVMNBnAVcARDY6I/mJiYgrPGSL+ubm5EzvOLM9VyCz+kJCQ/ubm5riIi4gHGZuIxVXBDs +; 0dwyAClYg7QMQsx7OIGP6BgYEZLMI70QrPHx7bmz2ZL8Ciph7imi4d3DlCHcIxKMH+lEoAHdr+ZUw0 +; GcEKwRoY/nBwcArNICSriP5mZmY7zyzPVcq2iP7IyMj++vr6wP63t7f+aGhoDsQ/wQ7MHcUZ/oSEhP +; 6wsLD+ampqHcMsxgu8iDosxDvRCs8fHtv+dzwAnFzAMx7iHB3dnlqcbBId/o9IACjBnWsd2wwZwVXC +; sYj+sbGx/oiIiP5fX18Kyr6I/ra2tv6Pj48KO9Asz1XLGf6goKD+7u7uJv7b29v+gICAOw7CP8AOzR +; 3HCf6qqqoLHcEsxRH+paWlOAYsxDvRf9D+a1I5/odEANv+dzwAnFzAMx7iHB3eEptNEv6zWgAowQEd +; 2/5lTDQZwQrDooj+mZmZIP5paWkKx6aI/pOTky42CjvRVc9Vzf57e3v+1tbW/vz8/JiILyQOwVXADs +; wdyaeIAz7+YmJiHSzE/n5+fv6srKwyOyzFO9AK0R/+h0QA2/53PACcXMCiph7imi4d4f6fUAD+vl8A +; o6UKHdwMGcFVxbuI/rKysv58fHyBiArENgWMiP5eXl4KwDvRLM9VzQ6riAUBJv7Nzc3+dXV1HQ4/wA +; 7MHcosHwU5LMIo/pubm0Azl4jGO9F/0f5rUjke2wacXMCiph7iHB3i/oVCAP65XQD+mU0AHdwM/lxc +; XMIKxaeIApiI/mNjY5iIwSgt/rm5uSoKwjvRLM9VzVXBPP7h4eG9iI6I/pSUlCg/DswdzQL+np6eQC +; gst4gYOYCILMc70ArSHx7bBpxcwKKmHsen8TT+jEYAHtYcHeOjpcAd3P5lTDQZwgrH/oaGhv6vr68y +; GQ0voogRCsNV0SzPVc0OwrKI/sLCwv74+PikiP69vb0/DswdzSwZNSOMiBX+aGhoLMg70QrSHx7bBp +; xcwDMexv6xWQD+0WkAwJ5a/p5QAB7Vmi4d/cUMGcIKyK6I/qysrJeItog8CsU70SzPVc0Owzv+mJiY +; /uvr6yYOzX/NLMCuiBSriAMsyTvQCtP+a1I5HtsGnFzAMx7Eorb+vmAANcMFNx7Tmi4d/cX+ZUw0Gc +; IKyKeI/piYmP7MzMyIiP5ra2sKxTvRLM9VzQ7FAP7w8PAOzB3NLMD+g4OD/qmpqf5vb2+RiAe8iP5m +; ZmYsxjvRCtMfHtsGnFzAoqYexafx/sNiADXD/qxWAB7Tmi4d/cUMGcIKxzL+s7OzhYj+XV1dtYj+sr +; Ky/oGBgf5eXl4KwzvRLM9VzQ7EAP62trb+gICADswdzRX+n5+fMBUswDu4iP6vr68iLMU70ArU/mxS +; OR7b/nc8AJxcwDMexqK2/rFZAP7MZwCitsEc/pBJAB7Smi4d/cUMGcIKxaSI/o2Njf64uLgQCsEG/p +; 6enn/+ZWVlCsI70SzPVc0OwhX+paWlnIj+Y2NjDswdzbyICf54eHgKLMOpiP6bm5sk/l9fXyzCO9F/ +; 1P5sUzke2/53PACcXMCiph7I/plNAP7IZAA1wTQe0hwd3f6MRgAd4wwZwgrEr4j+q6urmYj+YmJiCs +; T+f39/FP53d3cZCsA70SzPVc0OwS7+tbW1/oKCgiwOzR3LOzCqiP5lZWUsxgr+fHx8FP50dHQswTvR +; CtQnHtv+dzwAnFzAoqYeyTf+tVsANcH+mU0AHtEcHdyn8f67XgD+qFQA/nU6AB3hDBnCCsIZBP65ub +; n+hISECscg/qmpqYqI/mFhYQo70SzPVc0OO/6YmJiyiP5qamoOz3/KKzoTKCzJPv6lpaWSiAosO9AK +; 1BknHtsGnFzAMx7L/qNSAP7MZwA1Dzce0JouHdz+pFIA/rxeADyeWv6DQQAd4P5lTDQZwgrBqoj+n5 +; +fr4gNCsr+jY2NOv5ubm6MiNEszlXOs4gyByiaiM8dyv6Hh4e/iDoszCge/q+vrys70ArUGf5sUzn+ +; h0QA2/53PACcXMAzHsyn8TQ1/qdUAB7QHB3bKv69XwA/wDz+eT0AHeAMGcIKwC4f/pKSkhkKzLKI/r +; KysgD+Xl5eO88szlXN/oyMjCP+c3NzhIjRHciqiP6jo6MWNx0szraICf6JiYk7zgrVGSce2wacXMCi +; ph7NorYF/shkADcezxwd2/6mUwAHwD/+oFAAHc+jtcAdzgwZwhUD/re3t/5xcXEKzyj+mpqaAv5nZ2 +; eTiM4szlXLrIj+qampkogkDtIdx/56enoJ/nR0dCwdwCzPJP6WlpaxiP5jY2M7zH/UGcAnHtv+dzwA +; nFzAMx7P/plNAKfxHs+aLh3apdP+v2AAVsAHKh3O/pRKADX+uV0A/nw+AB3NDBnBsoj+r6+vj4j+X1 +; 9fnIjROz3+sbGxGxk7yyzPVcr+fn5+EBcsQNMdxRn+mJiYooj+YmJilojCLM5/wAo9FBs7ywrUGcD+ +; bFM5Htv+dzwAnFzAoqYe4hwd2v6jUgD+wWEAVkaZD/5wOADO/q1XADXBnVv+mk0AHcwMGaKI/oaGhv +; 66urom/ltbW9M7wKiI/qWlpZSIMzvKVc9VyCj+np6ePg0dDtMdxRj+ra2t/n19fQodwizPO8KriP6h +; oaGaiCg7yX/UGcAnHtsGnFzAoqYe4hwd2aXT/sFhAMFa/o5HAP5wOADM/oJBAP7FYwA1wxL+djsAHc +; sM/mdnZ/6kpKSniP5mZmYK0zvD/oeHhxj+cXFxO8ksz1XHuIj+tLS0PBkdwA7THcT+jY2NEQ0dxCzP +; O8Mo/oODg/6ysrL+bm5uO8cK1BnB/mxTOv6HRADbBpxcwKKmHuIcHdn+o1IA/sFhAMGda/5wOADM/q +; dUADXEBB3N/n5mTf63t7f+jY2NGQrUO8SviP6urq4L/l9fXzvHLM5VxiwDNgUdwVXUHcIg/qampjwo +; HcUszjvGsoj+qqqqFjvGCtQZwLKI/qmYh/6FRwce2v53PACcXMAzHuKaLh3Yo6X+wWEAwgL+cDgAyg +; ASNcMSAB3O/qSWiCeOiMAK1FXFCjCyiP5paWk7xizOVcWuiP6tra2JiP5gYGAdwg7UHcH+gICAKwUs +; HcYszjvHFf6QkJArLzvECtQZ/pGRkQEJ/mRYS/56Sx0e2QacXMAzHuKaLh3Y/qNSAP7BYQDBnWv+cD +; gAyx/+s1oA/tFpAML+p1QAHc7+glQn/o6HgP5fX18ZwQrUO8cyIxcZO8Msz1XE/oSEhAH+enp6LB3D +; VdMdwKaI/p2dnZmIFR3HLM87yAq5iP6wsLD+gYGBO8MK0yAjhogVGcCe1P5xUC7+hEUGHtf+dzwAnF +; zAMx7iHB3Xo6X+wWEAwv6ORwD+cDgAzKO1/qBQAP7LZgDAAh3O/pN3Wz0ZwgrUVcmliP6goKAo/mNj +; YzvCLM9VwqiI/qOjo8AgHcUO0x26iP6srKz+eXl5Ox3ILM87yqqIKKOIBjvACtP+f39//rq6uv51dX +; WIiBnD/mdWRf5/SBEe1v53PACcXMAzHuIcHdc4/sFhAMGda/5wOADP/ohEAP52OwAdzf6DVSj+loyB +; /mRkZBnDCtRVy/6BgYEFAQo7wCzOVcIQ/rW1tf6FhYUKHcUO0yz+k5OTFSAdyizPO8soF/6ysrIyOw +; rRN/6mpqZV/mNjYxnGLv50Tige1QacXMAzHuIcHdajpf7BYQDC/pNKAP5wOADf/pN0VP6Hh4cZxQrU +; VcysiCsW/mBgYCzPVcAK/peXlwn+bW1tHcdV0rCI/qmpqTEZHcsszjvOr4j+qKiojYgZCs+3iP6+vr +; 4EKBnJ/mdWRf6CRwv+h0QA0wacXMAzHuKaLh3WOP7BYQDBnWv+cDgA3qlc/pJ9aP5qamoZxQrVVc4W +; u4j+a2trLM5Vsoj+srKy/pGRkf5eXl4dyA7RAP6np6f+bGxsHc0szjvPpYj+i4uL/q6urisKzf6Xl5 +; cF/mlpaQrAGcou/npLHR7S/nc8AJxcwDP+jEYAHuGaLh3Vo6X+wWEAwv6YTAD+cDgA3f6BVCb+iYJ7 +; KBnGCtRV0LSIIxMZLMtVPCP+dnZ2HcpVzxX+oqKiNP5eXl4dzizOO9AKtoj+rq6uLQrKrYgQFv5fX1 +; 8KwRnM/m9RNP6ERQYe0P53PACcXP6USgA1/r5gAP6QSQAe4Bwd1f6eTwD+wWEAwT/+ej0Amz3c/pZ9 +; ZQEZyArUO9AsGf6bm5sk/mVlZSzJAv6oqKiWiAKViMoOzx/+q6urAR0OHc0szzvRf6eI/pmZmayIAg +; rH/oeHh/65ubn+cXFxGQrCGc3+ZFdL/n9IEf6HRADPBv6tVwA1wZ5a/qdUAB7fmi6aPtSjpf7BYQDC +; OP5wOADb/oNVKP6QhXv+YWFhGcgK1FXRVcH+enp6/rGxsf53d3c7LMb+fHx8/ra2tv6AgIA7QMtVzT +; v+lpaWpogzDsEdzSzPO9EKwCi+iP6ysrI9CsQV/qqqqpWIJArEf86e1P50TigEHs2m4v7MZwCitsMP +; /oxGAB7dmi6aPtQa/sFhAML+ej0Amz3a/pFyUhMZygrUO9EswjM+MBUswyj+nJycrYj+ampqjojNDs +; y0iP6srKz+f39/Ow7CHc0szzvRCsKsiP6kpKSWiP5eXl4KwbyI/r29vSooCsV/0P5nVkX+gkcLHs2i +; tv66XQA1w55a/p5QAB7cmi6aPtOjpf7BYQDCOP5wOADZqVz+k4FwPhnKCtVV0CzFLQn+bm5uLMG2iP +; 60tLQp/l5eXpqIzQ7M/pGRkbGI/mhoaA7EHc0szzvRCsOkiP6GhoYjGBn+np6er4j+ZmZmCscZ0Z7U +; /ndNIh7O/qdUAP7MZwA1w/6xWQAe25oumj7T/phMAP7BYQDCFJs92P6FWzH+iYaCGcwK1DvRLMawiP +; 6urq4LBv6QkJD+sbGx/nJycizAVc1VzP7b29sjDsUdzSzPO9F/xTb+uLi4sIj+iIiI/l5eXgrIGdP+ +; bFM5BB7Np/H+w2IANcIoHtuaLpo+0qOl/rdcAKXTwTj+cDgA2P6bgmr+cHBwGcwK1VXQLMgK/qGhof +; 7Q0NAz/mFhYSzAVc0Ozf7///+IiP6SkpIsDsMdzSzPO9EKxf6Ojo7+t7e3ioipiC8KyBnUIf56Sx0e +; zaK2/rFZADXBKB7bHJo+0jn+wWEAwv56PQCbPdb+glQn/pqTjBAPBhnLCtQ70SzIE/62trb+kJCQPf +; 59fX0KLFXNDsxV/sTExP79/f2aiP66urovDsIdzSzPO9EKwwL+r6+vi4gVGbuI/rGxsf5/f38KxxnV +; ntT+dE4oBB7N/plNAP7IZAA1KB7bHJo+0aOl/rdcAKXTwf6oVAD+cDgA1v6Ob08qGaWI/p2dnaSIDR +; nJCtVV0SzGBiSkiP5mZmYsBiSZiP5hYWGXiM0OzD8G/pycnP7w8PAIgIgTDsEdzSzPf9EKwv58fHwb +; /nZ2doaICsGqiP6fn59V/l9fXwrFGdf+Z1ZF/oJHCx7Norb+ul0ApcMe2xyaPtH+jkcA/sFhAML+f0 +; AA/nA4ANSqS/6ZjoQgGcL+gICA/rCwsDmBiBnGCtVV0SzGu4gQ/oeHhxkswv6Dg4P+rq6u/nFxcSxV +; yg7MP8AOHf55eXn+09PT/v///5OI/qmpqQYOHc0szzvRCsAo/qSkpKOI/mNjYwrEo4j+gYGB/rOzs/ +; 5ycnIKxRnXLv50Tij+h0QAzv6ZTQAe25oumj7R/rdcAKXTwf6yWQD+cDgA1P6Oa0j+iYmJGcWriP6n +; p6ePiP5kZGQZxQrVO9EsxAo/J/5vb28sxFUg/qqqqik3HckOzFXADsER/qysrP739/ekiP7Q0ND+dH +; R0Hc0szzvRCjb+vLy8IigKxzoriYgZCsMZ2f5nVkX+hEUGHuocmj7Q/o5HAP7BYQDC/olFAP5wOADT +; /pR/av5sbGwZyP6MjIz+rKys/nNzcxnDCtVV0SzEsIgFg4gVLMVVwCz+kJCQuIgcHccOzT/ADsIKD/ +; 7i4uK9iIyI/piYmAqdiMsszzvREr6IHArKFf6NjY0J/mhoaArCGdqe1P56Sx0e6Zoumj7Q/rdcAKXT +; wSb+cDgA0jD+i4R9NxnKNv6vr68L/mBgYBnACtU70SzELf6zs7P+eHh4LMZVw7aIFDUKHcUOzD/BDs +; S0iBf+/Pz8nIj+wsLC/mpqah3KLM87z6yI/rS0tAMGCswZt4g2DwrBGdz+b1E0BB7nHJo+zwL+wWEA +; wgL+cDgA0f6RdVkfGc0G/piYmKyIOhkK1TvRLMKpiD6aiP5lZWUsx1XEGf6cnJyiiP5jY2Mdww7NP8 +; EOxaeI/pSUlP7s7OyyiIOICx3JLM87ziIbI4qICs8C/pubm6iIM5mIwBnc/mRXS/5/SBEe5hyaPs/+ +; wmEAoZefacAm/nA4AND+g1Uo/pKIfTMZ0L6IBf5/f38oCtM70SzCG/62trYiCizHVcf+fHx8Bf51dX +; UsHcEOzFXCDsYdEP7Nzc3+////lYgFMx3HLM87zDf+p6enmYj+YWFhOwrRKP59fX3+srKy/nZ2dgoZ +; 3S7+dE4oHuUcmj7O/pRKAP7RaQBWnEydS/6ORwD+cDgAz/6Sc1P+hISEGdOniP6jo6OYiP5lZWWWiN +; E70izAKP6ampqwiDosyVXIMz6LiAYdDs0/wg7HHTP+pqam/vT09KiI/tbW1v57e3sdxizPf8u6iP69 +; vb3+fX19GTvBCtKuiC+TiDcZ3v5nVkX+gkcLHuOaLpo+zv7FYwA1wf67XgAdzqlc/pSDcf5paWkZ0w +; rBDxgfCtBV0VXAJ/60tLQH/l5eXizJVcv+iYmJ/qqqqv5sbGwOzFXDDscdwDv+gICA/tvb2yaPiBUo +; HcQszzvK/pqamrSI/mZmZpSIw3/TBv6IiIgF/m1tbRneLv53TSIe4poumj7NI/7RaQDB/qBQAB3O/o +; ZcMv6MiIUoGdMKw6+I/qysrAf+YWFhCs070SzAB/6ysrL+dHR0LMtVzCs2/oSEhAoOyj/DDscdwg3+ +; t7e3/vr6+sD+yMjI/m5ubh3DLNA7x6+I/ra2tgsoO8QK1bWI/q2trf6Li4sZ3jf+iXBX/o5PDx7gmi +; 6aPs0zNf6/YAD+gkEAHc7+lHxj/nFxcRnUCsUo/pSUlLSIJwrLO9Ig/qqqqpGI/mNjYyzLVc47/piY +; mKqI/mRkZA7IP8QOx3/DKAf+6enptYiIiP6QkJAsHcIszzvGGi7+bW1tO8Z/1qeI/paWlg0gGduriC +; v+8vLy/ubazf6ZZzQe35oumj7M/o5HADX+p1QAHc7+glUn/pKLhP5gYGAZ1ArIMjYiNwrIO9L+gYGB +; /rW1tf5+fn47LMtVzlXBAf6wsLD+eHh4LA7GVcQOxx3FFP7Gxsb+/f39mYj+uLi4IB3BLM87xCQJj4 +; gGO8h/1igM/rKysv59fX0Z2Sr+zs7O/v39/ZeI/rGxsf5rZ2P+dE4o/oRFBh7dHJo+zP65XQD+lEoA +; Hc7+kHBRFxnVCsok/p6enqKI/mdnZwrHO9AGFaeI/mhoaCzNVc0Ow6eI/qKiopaIBg7EP8UOx3/GqI +; j+n5+f/vLy8gj+29vb/oGBgR3ALM87w7+I/ru7u/53d3eEiDvJCtcZq4j+oaGhnIj+X19fGdWmiP6a +; mpr+6+vrJoCI/o2NjTcZwP5nVkX+gkcLHtwcmj7LpuLAHc2qS/6WiHr+ZmZmGdUKzQQ2DBkKxFXQuI +; gQC/5dXV0szVXODsX+hISECScOwj/GDscdxzv+fHx8/tbW1iaSiP6np6f+X19fLM87wRkkqIj+Y2Nj +; O8sK1xnABiL+s7Oz/nFxcRnTFP7AwMAbQP7BwcH+b29vGcOe1P50Tige25oumj7b/o9sSSkZ1grPrI +; j+p6enjIgzCsI7zwohBRQszlXODseviP6rq6v+iYmJGQ7AVcYOxx3JIAX++Pj4o4gH/nNzcyzPO7KI +; /rq6ujEZO80K1xnBGP6qqqqHiBnQBin+4ODgJouI/pubm/5hYWEZxv5nVkX+hEUGHtkcmj7a/peCbR +; gZ1lXS/o6Ojjr+cHBwCsBVz6+I/q+vr4eIJCzPVc4OyB0DPi+QiMcOyB3JGTz+5OTkJoqI/peXlwos +; zSX+srKy/mlpaTvPCtgZwSQlOg0ZzRz+sbGx/vX19aeI/s7Ozv57e3sZyf5hWVD+eksdHtiaLpo+2D +; D+jYZ/NxnWCtS0iP6vr6/+h4eHNzvO/oaGhv60tLQqOyzPVc5Vy7iI/rCwsP58fHwsP8UOyB3LGDX+ +; /Pz8PTX+aWlpLMoCI4OINzvRCtcZwii4iBQxGcoo/oCAgP7U1NT+/v7+k4j+qqqqLxnLLv5vUTQEn8 +; PWmi6aPtf+knZa/nh4eBnXCtYG/pmZmaqI/mlpaTvLqIj+paWlVf5mZmY7Vc9Vzg7NCv6dnZ1A/mBg +; YD/EDsgdzAb+mJiY/u7u7heCiP6Hh4csyP5/f3/+ubm5MjvTf9gZw6mIKKWIJBnHqIj+oKCgJxf+29 +; vbHv5dXV0Zzv5kV0v+f0gR/odEANWaLpo+1f6DVij+lYqA/mNjYxnWVdg7wCr+r6+v/n19fRk7yL2I +; /ra2tv6FhYUoOyzQVc4OzD/BCDb+cXFxDj/CDsgdzSw9/tDQ0P7///+ViP6urq7+YmJiLMQo/qWlpZ +; 2I/mBgYCzAO9MK2BnDN/5/f3/+s7OzEBnFEP7GxsY5m4gbCRnRntT+dE4oHtQcmj7U/pNzVP6GhoYZ +; 1wrYO8KqiP6kpKSUiAI7xSj+mZmZs4gYO8BV0FXODs0/wqyIPoeIGT/BDsgdziyriP6pqan+9fX1p4 +; j+1NTU/nl5eSzCFP69vb3+fn5+CizBO9MK2RnEroj+qKioj4goGcEV/pKSkv7l5eW6iIeIMAYZ1P5n +; VkX+gkcLHtKaLpo+0qlc/pF8Z/5qamoZ1wrYVcX+iIiICf5zc3M7wxj+s7OzNBU7wCzQVc5VzT/FGg +; 3+aWlpP8AOyR3OLBki/t7e3iaOiP6dnZ3+XV1dLP6Xl5cn/mZmZizEO9MK2RnEFQs2/mtraxmwiP63 +; t7f+9/f3o4j+ycnJ/nV1dRnXntT+eksdHtGaLpo+0f6BVCb+iIF6/l1dXRnWCtk7xzr+ra2tGgY7wC +; ky/nd3dzvBLNBVzlXNP8crBTU7DskdzizBOv66urr++/v7QDyPiDgZLMU70wraGcW2iDKOiP7Z2dn+ +; ////kIj+o6OjERna/m9RNP6ERQYezxyaPtD+ln1l/nNzcxnXCtk7yRn+lpaWuYj+eXl5/qqqqpOIET +; vBVdBVzw7NVcgs/piYmKeIDsl/zizCpYj+kpKS/vLy8iaIiBY7LMY70wraGcQv/qysrP739/cm/tnZ +; 2f6BgYEZ3f5kV0v+f0gRHs4cmj7O/oVXKv67sKb+Z2dnGdYK2jvM/pqamv7e3t4k/l5eXpyIwVXRVc +; 4OzT/LDQ7JHc8swaaIK3/+y8vL/v7+/piI/rW1tf5lZWUsxTvUCtoZwgz+zMzM/v39/ZiI/sPDw5aI +; /np6ehnentT+dE4o/oRFBh7MHJo+zf6RcVIxAAX+dnZ2GdMK2zvLNyisiP5wcHD+np6efyA7LNFVzg +; 7NP8qoiDqMiP5XV1fKHc4swB/+vLy8/nh4eDszAv7z8/MI/tnZ2f5/f3/+WVlZxTvTCtsZM/6Xl5f+ +; 6enptoiCiAc3qogCmYj+X19fnYje/mdWRf6CRwsey5oumj7LqVz+koBvPhnAsYj+p6enj4j+Xl5eGd +; AK21XLFP61tbX+jIyMBjvA/oODg/6tra0fOyzPVc5VzlXJ/nd3dwz+cnJyPw7KHc47KDr+Y2NjLMEK +; /n9/f/7Y2Nj+////kYj+pKSkBizDO9QK2rSI/r29vf75+fnA/sPDwxQZwQYA/rKysjYZ3p7U/ndNIh +; 7KHJo+yv6KZD3+iIWBGcOliDgY/mxsbBnNCts7ywoDv4j+cnJyO8OtiP6oqKiIiBUszVXPDs0/yB3+ +; oKCgpYg3l4jADsodzbGI/ri4uAAZLMQ+/rOzswx//szMzP5xcXEswzvUCteiiP6JiYn+3t7eJoyI/p +; 2dnTMZxLKI/qysrAcZ3/5sUzkEHsiaLpo+yf6TemI2Gca5iBg8GcoK3DvLrogYjIgzO8NVwDsWuoj+ +; bm5uLMtVzw7NP8i0iP65ubk1HTDBDssdyzgy/mlpaSzHpIj+i4uL/ubm5iaJiP6VlZUKLME71QrVrI +; j+rq6uAaiI/tHR0f58fHwKwBnFMwO4iD4Z3iH+eksdHseaLpo+x/6CVCf+j4iBBhnIqYj+mpqaqogg +; GcYK3VXLMf61tbX+fX19CjvDLMM2JzEoLMhVzw7NP8j+kJCQv4ggP8BVwQ7LHckkNoiIKB3ALMi3iC +; L+/f39moj+vb29/mhoaCzBO9QK1P59fX00/v39/ZWICf5mZmYKwhnFKLqI/rKysv6BgYEZ3p7U/nRO +; KP6ERQYexRyaPsb+lHhc/np6ehnLooj+fn5+/rCwsDkZwwreO8ok/qOjo6OIPjvELMUo/pqamhX+Zm +; ZmLMZVz1XNP8ck/rGxsSX+WlpaP8Awwg7LHcj+e3t7DP5zc3MsHcEsyaeI/pubm/7w8PAIgYgALMA7 +; 1X/RM/6dnZ3+7e3tsYj+3d3dPBkKxRnFIP6fn5/AFRne/mdWRf6CRwsexByaPsSzH/6YjYP+ZGRkGc +; 6uiAKWiP5fX18K31XLu4j+tbW1/oeHh/5dXV07xFXI/n19fTYbOyzDVc8Ozj/G/n5+fj3+bm5uDj/A +; MMMOzB3FCgJ/FR3ELMk7G/7T09MmlIj+q6urMyw71QrPt4gi/vv7+5yI/r29vf5tbW0KyRnEBv6BgY +; H+s7Oz/nNzcxnentT+dE4o/odEAMMcmj7D/o1qR/6IiIgZ0RUtNv5vb28K3TvKGf6Xl5e3iDY7xSzK +; q4j+pKSkkYgkLMFVz1XOP8U7EZuIKD/BVcQOzB3Etoj+vLy8/n9/fzsdxSzLIAn+9vb2Kv7S0tL+d3 +; d3LDvVCsykiP6Pj4/+4+PjvIiIiP6WlpYGCswZxK+I/qmpqYyIKBne/mdWRf6ERQYewZoumj7C/pN+ +; aToZ0ArCtoj+q6urBxkK2VXLsYj+srKygYgVO8UszTz+q6urFCxVzw7OVcW6iP67u7sbHT/CVcQOzR +; 3CEryI/mZmZh3ILMsZD/7h4eEmjYj+m5ubKDvVf8or/rS0tB+liP7Ly8suCtEZwqWI/oyMjP6tra0r +; Gd6e1P56Sx0ewByaPsD+gVQm/oqDfP5eXl4ZzgrGM/6UlJSziP5nZ2cK1zvLPP6zs7P+eHh4O8Ysz7 +; KIGDwom4jNDs4/xf6Xl5e1iDM/wzDFoojNHcCsiDIWGR3KLMyziAgqQP7ExMQJO9UKxxn+goKC/tfX +; 1/7+/v6SiP6lpaX+Y2NjCtQZwreINv6Hh4cZ3/5vUTQEHJo+/pd+Zv51dXUZzQrKGRs2/oGBgQrUO8 +; sR/qioqJiI/mVlZTvFLNIK/pWVla+I/mhoaJCIzFXNP8QgATwsP8NVxg7O/oODg/64uLgnLB3LLM2m +; iP6VlZX+7OzsF4SIKTvVCsWpiP6kpKQFrYj+2NjYIgrZGcCoiP6bm5upiAIZ3v50Z1v+lGo//pGHfC +; QZy1XPPhVAJArQO8z+fHx8/ra2thMZO8Us0lXBvoj+rq6u/n19fTsdyQ7NP8Qe/ra2tv5ra2s/xFXH +; DswZ/qenpzf+Xl5eHc4szTsQ/s3NzReXiCMgO9UKwruI/snJyf78/PwQ/ra2tv5paWkK3Si/iCP+eH +; h4Gdso/oSEhP65ubkiGckK1DciNjIKzjvLN/6bm5uwiP5ra2s7xizSHcOoiP6hoaGZiP5iYmIdxw7N +; P8MZ/qmpqTsZP8QwyD8OyruI/ry8vP55eXksHc8szwL+pqam/vT09KiI/tfX1wj+Wlpa1X8VIf7n5+ +; e4iISI/pCQkCgK4A3+paWllYg3GdiqiCQYDRnFCtuziP6oqKghKArKVcy1iP60tLQHBjvFLNMdxiL+ +; q6ur/nR0dB3FVc4/wr+I/rq6uhAOP8QwyQAAAAAAAAAB +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 440x240 43548 +; cW9pZgAAAbgAAADwBAD+T09PyX/Q/n5+fv7R0dH+iYmJ/lFRUSXANNZ/yLuI/tLS0v6enp7+VlZWnI +; jKf9x/yf6AgID+0tLS/oyMjP5VVVUh0zDMP/6bm5v+1dXV/np6ev5VVVXjP8x//ouLi/7p6em1iH+a +; iP6zs7P+Xl5eP+C2iP7Ly8uBiP5fX18/8DDBuYj+zMzM/qampv5YWFgw4reIA/6qqqr+XV1dMMYh4h +; LHGBovPxLPVcEWyCXSBv64uLhANzTVA8gh/paWlv7V1dX+c3NzA8oS3CHMroj+vLy8mIj+YWFhIc9/ +; zrGI/sjIyIuIJDDef9SyiC3+/f39Jhf+29vb/nZ2dj/e/pGRkf7Y2Nj+g4ODP/Yo/qSkpCknMOD+lZ +; WV/tzc3P5/f3/+VVVVyyHhEsaniBH+ysrKCRLQAxbGJdUD/pCQkP7Pz8/+eXl5/lFRUdIDya6I/sXF +; xY2I/llZWQPJEtsh0f6SkpL+0dHR/nt7eyHLMND+iIiIHf6Kioo/MNo/16HyP8CliP6bm5v+8fHxro +; jAlYj+oaGhLD/aM/6/v79ALz/5/n19ff7V1dX+k5OTDj9V2xX+xsbGloj+Y2NjMM4h4VXG/nx8fP7U +; 1NQD/lRUVBLPnIjEf9Z/wbmIAIGIPzTOf8kSIv7X19f+hISEEgPIEtsh1An+yMjI/qWlpTshxjDRpo +; g9q4gNMNc/2qHy/qtjG6Hh/mVYSz/Bv4gdF39A/szMzP5ra2s/2P5/f38O/pWVlf5YWFg/+6uI/rW1 +; tayIMz/CVdb+gYGB/t3d3f6QkJD+V1dXMNIh4FXFFRCqiCQSzhbDf9U0xD/+oaGh/sfHxwk0zAPJLD +; IA/mFhYQPIEtoh2B0vLSchwjDTDP7U1NT+mpqaLDDTP97+fFw7/sloBaHywDv+hF02P8EG/q2trf75 +; +fmmiMCOiP6NjY0OP9SkiP6urq4p/m9vbz/9wB3+jIyMIQQOP8Qw0B0yu4g6MNZV4FXEMDj+09PT/n +; 9/f/5TU1PNFsEl1jTH/nd3d/7Pz8/+kpKS/lJSUjTIA8r+c3Nz/tXV1f6VlZX+VVVVA8cS2iHc/nl5 +; ef7R0dH+lJSUPzDTPxH+0tLS/nR0dDDRP+H+o2IgNcQoOT/BMf7k5OQXf5uI/rq6ujM/0ruIFi/+XF +; xcP/3DKx6IiAo/xzDLFP7X19f+oqKiCjDZVeASxLaIADYdEsuciMAl1X/KrIgjqYj+YmJiNMV/yjD+ +; np6e/tPT0/5tbW0Dx3/aId8G/re3tyr+ZWVlMNE6/szMzP6rq6v+Xl5eMM0/5P5tWkb+wmcLNcb+um +; UQ/nxcOz/BIP6/v78IoojAgoj+fX19P9D+mpqa/tbW1gj+VlZW/cYK/pycnP7Q0NAyP8owxv6dnZ3+ +; 2tra/nl5eTDeId5VxB0oBxQSyhZ/1DTOPP7R0dH+gICA/lFRUcIDyyAp/qmpqR0DxhLaId4ww/6Li4 +; v+1NTU/oKCgjDP/pCQkP7Y2Nj+g4ODMMs/5/6TXyv+0WkAyZ8e/oxeMDmfHsAs/pWVlf7u7u4mwJeI +; /qioqAo/zLCI/sTExJKIAj/9yf52dnb+09PT/pqamv5YWFg/y1XBICkQFTDhVd5VxB/+0dHROyESyJ +; 2I1H/QIP7AwMCMiCw0A8oS/oqKiv7Y2Nj+fHx8A8YS2iHdMMeziP7Dw8MJNzDLBv6/v7+diCAwyT/o +; /mVYS/66ZRD+0WkAzP6yZBUpP8G5iP7R0dEXfxf+0tLS/nBwcD/K/oeHhx04/ldXVz/9ywb+rq6uLf +; 5nZ2c/zP6IiIj+3d3dCz8w5CHeVcM3JzEgEscl0n/TIf6bm5v+y8vLFIGIyamIKqOI/l1dXQPFEtkh +; 3jDKDv6enp7+zc3NMjDJ/n9/f/7W1tb+lJSU/lhYWDDGf+v+hF02Nc+fHv6MXjD+VlZWwSj+pqam/v +; b29qmIwJGI/paWlg5VxigQAP5sbGwOzj/8Dv6FhYX+1dXVHg4/yKaIKqyIDT/BVeVV3RLEAP7U1NT+ +; h4eHEsYl0TTUA8AU/szMzP6bm5v+VFRUA8b+eXl5/tfX1/6NjY0hA8US2CHdMM8BB/6cnJwdMMUd/q +; 2trf7Nzc3+bm5uMMU/7KHy/qNiIDXS/rJkFf5eWFGfL8H+fn5+/uDg4L6IJp2I/sHBwf5mZmYOxP54 +; eHj+09PT/p+fn/5bW1sO3z/uL/7AwMCYiP5eXl4/xv54eHj+2dnZ/pubm/5aWlo/xTDkId1VwyD+vr +; 6+EP5bW1sSxCXPNNUDwgr+rKysNS8Dwz/+pqamBw0DxRLYId0w0qeI/rCwsAT+aWlpMMO7iP7Q0ND+ +; pKSkGTDCP+/+bVpG/sJnC/7RaQDUnx7+fV08DsEC/ri4uP77+/smwIeI/oSEhA7BHf6jo6P+09PT/n +; d3dw7rP+WkiP6VlZX+0tLSDA4/wg7+p6en/tXV1QE/ylXjVd1Vwj/+l5eXJS4Swv6Dg4P+UFBQzjTU +; f8YT/tLS0h4SVcAr/tHR0f6hoaE/A8QS2CHcMNcx/tPT0/6Kioo/MD/+mJiY/tfX1/58fHwwwT/x/p +; thJv7RaQDY/pxhJv5fWFKfHsCiiP6Ojo7+6+vrs4gmmYj+sLCwN62I/snJyQX+YWFhDvM/4BT+zs7O +; /qKioiw/wDo0GDc/zTDjIdxVw72I/s3NzQIwo4j+tra2/vz8/CXMf9QDybCI/ru7u5mIJP6SkpL+1t +; bW/nZ2dv5SUlLEEtgh3DDaIP6+vr6ViAX+x8fHj4gzMH/yKP66ZRA12v66ZhD+blpHDsE6Gv7+/v4m +; F4GIVf6GhoYO+z/bqIg+Kf5tbW3+k5OT/t7e3iI/0jDiIdxVwhn+qKioB7SIFyYlyzTUA8sh/p2dnf +; 7v7+8HGQPDEtgh2zDeP/69vb3+9vb2/qurqw4/8Q7A/oxfMTXdnx4L/l9YUg7AGf6enp7+8/PzJsCV +; iDcsDv3CP9f+h4eH/vX19YeI/mdnZz/WMOEh2xLBIf6hoaH++/v7JsABJck01APMEv6AgID+19fX/p +; aWlv7IyMj+pKSk/lZWVgPBEtch2zDfCv61tbWwiP6BgYH+ysrKAgo/63/DCP6rYxv+0WkA4P6yZBb+ +; ZllMDsD+fX19/t7e3jMXJkA8Kw79xj/R/n9/f/7c3Nz+m5ubG6SI/mFhYT/XMOEh2hI+/tHR0f7+/v +; 5/F4qI/sbGxiXIf9QDzA7+r6+vLf5jY2MDHf6lpaUP/mtraxLXIdsw4P52dnb+09PTKP5bW1s/pIj+ +; qqqqvYgYP+UOx/51W0H+yWgFNeI7/odgOf6rq6sHFCT+sbGx/vn5+SbAjIj+ioqKDv3JP8sd/rCwsP +; 7R0dEnPx3+j4+P/tTU1P6AgIAOP9cw4VXY/oyMjP7y8vImwJuI/qOjow7+kJCQ/lBQUMZ/1APNvogS +; /pmZmTADwv58fHz+0NDQ/pCQkCFV0yHbMN8/wP6hoaH+09PTLj/D/n19ff7S0tL+kZGRDj/fDsr+nG +; Em/tFpAOb+xoVD/mZgWQ7AHf6Hh4cvFyYb/ri4uP5hYWEO/cs/xgX+1dXV/qampv5dXV0/wraI/srK +; yoKI/lpaWj/ZVd8h1aiI/r6+vv79/f0mVYWIH/5TU1PBnYjFf9R/zDD+m5ub/tTU1AUDwxLArYgQpY +; j+YmJiEtB/2jDfP8EcGoOIFT/FM/65ubnA/mVlZT/ZDs3+blpH/sJnC/7RaQDo/rpmEP59XTwOwT7+ +; w8PDCCbABv56enoO/c4/wf6ampr+3Nzc/n19fT/Fpoj+n5+fB/5xcXE/2jDfVdL+e3t7/uXl5RcmQA +; z+WlpaIRLBJcQ00wPNsYg8hIgdA8ISxP6NjY3+0dHR/n9/fxLOIdkw3z/COP7Y2NgeP8kW/tPT0/6A +; gIA/1A7Q/oxfMf7RaQDrnx4L/l9YUg7Ao4j+mJiY/vDw8K+IwJaIL/5bW1sO/c2uiBo9Mz/I/nl5ef +; 7U1NT+l5eX/lhYWD/aMN9Vzj8r/vv7+ybAj4j+hoaGIcJVwCXCf9QDzBIe/tjY2P6AgIASA8ESxxwi +; iIj+WVlZEsoh2TDeP8OoiP68vLyjiP5mZmY/y7WI/sbGxoOINz/Of9II/rJkFv7RaQDuGf5mWUwOwb +; uI/tTU1Bd/F/7Q0ND+b29vDv3L/oaGhv7e3t7+jY2N/lhYWA7BP8cV/rGxsQAgP9sw3lXMuYj+19fX +; FyYXB/5kZGQhxZyIwTTTA807PamI/l9fXwPCEsmkiP6fn5/+ysrK/nFxcRLHIdkw3j/E/n19ff7W1t +; b+l5eXLJ2IzR3+oqKi/szMzCM/yn/U/oVeNjXxnx7+jF8xDsGoiCv+9/f3JsCQiP6Tk5MdDv3HpYj+ +; ubm5sYg6DsY/xR3+iIiI/tXV1f6FhYUOP9tV3iHIMP6VlZUQJsCZiP6ampr+VVVVIcYlwDTTA80f/t +; fX1/6QkJAhA8F/zf51dXUH/piYmDASwyHZMN0/xX8r/s/Pz/5xcXE/0f53d3cW/pmZmf5ZWVk/xA7X +; /qNiITX0/rJkFv5fWFIOwf6BgYH+4uLiFyYq/r+/vyAO/cUf/tnZ2f6fn58KDso/xLKI/sTExJGIKD +; /cMN0hxiT+x8fH/v39/aKIF/7d3d0UIcg00wPNP/6ioqL+0NDQ/mpqagPBEtCqiP6vr6+wiP5mZmYS +; wCHZMN0/xriIB/6oqKj+Xl5eP9M3/rOzsyb+aGhoP8AO2P5uWkf+wmcLNfafHv59XTwOwSA5/vz8/K +; OIwIWIEw79w/6kpKT+2NjYLg7PP8KkiP6YmJj+0dHRHw4/3DDdVcME/uvr6ybACP6xsbH+WFhYIcmd +; iNIDzQ0WEQ4DwRLTD/7T09MPIdgw3D/I/pWVlf7X19f+gICAP9Z/Lf7U1NT+h4eHDtj+lGAr/tFpAP +; r+nGIm/mBZUh3AO/6SkpL+7OzsFyaYiP6tra0oHc8O7LKIFhQVDtM/wb6IJQb+WVlZP91V3FXAHf60 +; tLQ5JsCLiP6AgIAhyzTQf80h/o6Ojv7X19f+eXl5A8F/1iD+vr6+lIgoIdQw3D/IrIgxlIgCP9V/wg +; 3+wcHBj4gkDtQy/rpmEP7RaQD8Ef5vW0cdwbWI/s7Ozv7+/v4mF/7W1tb+dHR0HdgO4f6Pj4/+3t7e +; Hg7YVamI/qqqqv7Kysr+ampqP94w272INxcmCP7FxcUGIcw0zwPNKP7AwMCZiP5cXFwDwRLVIcE//p +; iYmAcfIdEw3D/JE/7X19f+kJCQ/lhYWD/Tf8Yd/pubm/7Pz8/+eHh4DtL+jF8x/tFpAP3Bnx7+jV8x +; HcGmiP6ioqL+9PT0JsCTiAosHd1V2Ab+wMDAIv5nZ2cO3P6Dg4P+1dXV/oyMjB0/3lXXP/6dnZ3++P +; j4JsCWiP6Tk5MwIc00zQPO/n19ff7X19f+ioqK/lNTUwPAEtYhxLyIGv6hoaH+V1dXIc0w2z/KpIj+ +; s7Ozt4j+bGxsP9IOy7yI/szMzCT+W1tbDs6h8v6rYxv+0WkA/cT+s2UW/mdaTR3B/nt7e/7e3t4XJj +; n+xsbGHB3iDtH+fX19/tvb2/6YmJg7Dt6tiP68vLzAFT/fMNQvFv79/f0mF/7W1tYJiIjPNMwDzQ7+ +; q6urCyADwRLVIceniP6pqakxKyHKMNt/y76I/tLS0v6goKAZP9AOzxn+ra2tAP5tbW0OzP51W0H+yW +; gFNf3GO/5+XTz+WFhYwQL+tLS0/vr6+ibAioj+iYmJHeYOyh3+ra2t/tPT0/5ycnIO4aOI/pKSkhL+ +; fX19Dj/eMNI8/vDw8CbAnIj+qKioPzDAIc40y3/NJ/7S0tIoP5yIwH/VIcv+f39//tLS0jgwIccw2j +; /LDij+1dXV/nl5eT/QDtP+gYGB/tPT0/6Pj48dDsn+nGEmNf3K/pxiJv5gWVIdwCz+i4uL/ujo6Bd/ +; moj+tra2/mFhYR3pDsS4iP7U1NT+qqqq/l5eXg7kJyn+qKioLD/fVc4ZOf78/PwmF4eI/np6ejDDVc +; 00yX/NIf6Xl5f+1dXVIwPBf9Uhza6I/ri4uMAkIcR/2j/Msoj+ycnJFCQ/zn/XEf68vLybiP5lZWUO +; xv5uWkf+umYQNf3MEf5vW0cdwbKI/sfHx/79/f0mwP7c3Nz+eXl5HewO/piYmBn+gICADuemiP6jo6 +; P+zc3NNj/gMMsb/uTk5BcmCP69vb3+XFxcMMYhyzTIA80kDxQsA8AS1SHQMP6RkZH+0NDQ/n19fSHB +; MNo/zf6Kior+2NjY/omJiQ4/zA7c/pOTk/7S0tL+f39/DsT+jF8x/tFpAP3Pnx7+jV8xEJ8fwBn+m5 +; ub/vHx8a6IwJaIAv5bW1sd6ayI/sjIyJOIEZOI6v58fHz+1NTU/pWVlR0/31XIP/6np6f++/v7JsAU +; /oqKijDJVcqdiMd/zH/+hYWF/tfX1/6Dg4MSA8AS1SHTCQ+CiP5aWlow2T/NKP66urqpiP5paWk/zH +; /fJy0+Nw7AofL+smQWNd6aLv6mUwD+zGcANe3+s2UWBx3BvYj+19fXFyZAOP5tbW0d5/6EhIT+3d3d +; /pGRkSwdxFXmq4j+tbW1rYgRDj/fMMUJ/tTU1BcmFyX+Z2dnMMtVyZ2IxQPNLAGwiCQDwRLUIdYsAv +; 7Jycn+cHBwMNY/zv57e3v+1dXV/pqamv5aWlo/yg7jO/6mpqb+ysrK/nFxcf6FXjY13/6tVwD+cDgA +; p/H+lUsA/rpdADXtnx7+hV43/lhYWMEk/q2trf74+PgmwI+I/pCQkCwd46SIHzj+bW1tHckO5Cz+jI +; yMMP6Dg4MdDj/eVcI//pKSkv709PQmwJqI/p+fnz8wziHHnYjEf83+dHR0/tXV1f6UlJT+VVVVA8AS +; 1H/XMMH+eXl5/tDQ0P6Wlpb+VlZWMNI/zg7+pqam/tHR0QE/yg7n/np6ev7Yl1U13/6USgD+cDgAwC +; qlw6K2/rFZABw17f6rZBv+YFlSnx/B/oSEhP7k5OQXJpyI/r29vf5kZGQd4b2I/tnZ2f6ioqL+XV1d +; Hc0O47OID4uIGQ7AP94wq4j+w8PD/v39/SYXgogBMNEhxp2Iw3/MMAb+0tLSCYaIwRLUIdYwxKuI/r +; Ozs6qI/mZmZjDPP88YOP6rq6v+X19fP8gO6B3+b1tH/sJnCzXe/r9gAP58PgCaLsGn8R7B/plNAP7I +; ZAA17f66ZhD+fl08/lhYWME+/r+/v/78/PyjiMCDiP5/f38d3/6goKD+2traGx3SDuEZ/pubm/7R0d +; H+dHR0DsE/3P6AgID+6enpF39A/ra2tv5aWlow0yHFnYjBA80vOA0OA8B/1CHWMMj+ioqK/tLS0jEw +; zD/PDv6SkpL+2NjY/oODgz/IDuZ/wv6UYCz+0WkA3/6aTQD+cDgAwyoewjf+sVkANe2fHv6UYCz+YF +; lSnx/Ao4j+lZWV/u7u7ibAl4j+q6urKB3bsIj+zs7Ohoj+YmJiHdYO4L+I/tLS0v6cnJz+WVlZDsE/ +; 2Cz+sLCwOSbAjYgi/lVVVdYhxDTAf8wSGv7Y2Nj+e3t7A8ES1CHWMMoN/sHBwQUoMMg/0KuIBJqIID +; /HDuV/w/5vW0cRNd7+v2AA/oJBAP5wOADDAP64XQD+kEkAHsP+nlAANe7+s2UWOv5YWFjBuIj+0dHR +; FyYX/tTU1P5zc3Md2Sn+3t7eCywd2g7eqYj+ra2tPP5paWkOwT/Wu4j+3NzcFyYXPP5jY2Mw2FXDnY +; gDzKqI/r29vcAoA8F/1CHVMM0d/pycnDj+dXV1MMU/0f6AgID+19fX/pOTkx0/xn/kHcX+jV8x/tFp +; AN/+p1QA/nA4AMT+oFAANcCeWv6jUgAewzXvO/6NXzH+WFhYwQb+pqam/vX19SbAkoj+mJiYLFXVpo +; j+vr6+D/5paWkd31XcHf6FhYX+1dXV/omJiR0OwT/T/pqamv739/cmwJeI/peXlz/BVdkhwQPMG/7X +; 19cpIQPAf9Qh1jDQMv7Nzc3+np6eHTDBP9Es/q+vr72I/m5ubj/Ff+QdxqHx/qRjITXenVv+jkcA/n +; A4AMP+gkEA/r9gADXD/rpdAP6MRgAewf7IZAA18P6zZRb+aFtO/lhYWMAs/n5+fv7g4OC+iCY5/sPD +; ww0d0/58fHz+2tra/pubm/5bW1sd4g7cr4j+v7+/mogGDsI/zyA4/v39/aKIF/7Z2dn+b29vP8Qw2F +; XAA8oO/qioqDg+A8F/1CHVMNMo/qysrCIrMD/RvIj+0dHR/qOjoyg/xA7jHcg6/sJnCzXe/rldAP52 +; OwCdW8P+mk0ANcYB/p5QAB7A/rpdADXxnx/+fl49/llZWcGriP63t7f++/v7JsCJiP6Ghob+WFhY0C +; z+qqqq/tbW1v51dXUd5w7ao4j+lZWV/tPT0xsdDsFVzf6Hh4f+7u7uJsCdiAkdP8ZV2CEDybqI/tHR +; 0f6goKA/A8F/0yHVMNf+g4OD/tPT0xo/zw7+mpqa/tbW1v58fHz+VlZWxA7iHcr+nGImNd8E/nA4AM +; M+/rNaADXInlr+rFYAN/61WwA18/6cYif+YFpTLMB//o6Ojv7q6uoXJpmI/rKysv5gYGAswVXKtoj+ +; 0tLSGBUd6g7auoj+zs7O/qSkpCwOwj/JpogM/vz8/CYXiYj+fn5+P8kw2J2IxyH+k5OT/tXV1f51dX +; UDwX/UIdV/1z/AIP67u7ubiCQ/zC8tjYgzP8MO4h3KOv66ZhA13v6/YAD+fD4Ami7D/ohEAP7LZgA1 +; y/66XQD+p1QANfT+umYR/m9cSCzBCf7Kysr+/v7+Jhf+2tra/nd3dyzGVcP+lZWV/t3d3f6EhIQd7l +; XZp4j+pqamKf5tbW0Owz/G/nh4eP7i4uIXJgj+wMDANz/MVdYDxhX+xMTEkIj+WlpaA8F/03/VMNc/ +; ww7+lZWV/tDQ0P57e3s/yg/+2NjY/oyMjA4/wg7hf8z+jV8x/tFpAN/+p1QA/nA4AMQONc7+mU0ANf +; UC/o1gMhwswDf+n5+f/vLy8ibAlYj+oKCgCizHqoj+xcXFmoj+Z2dnkYjxVdn+f39//tXV1f6SkpId +; DsI/ww7+o6Oj/vr6+qWIwJSI/o+Pjz/PVdWdiMQS/oKCgv7X19cPEgPBEtMh1TDXP8Y2LS87P8YZ/r +; e3t6+I/mpqaj/Cf+AdzaHx/qtkGzXeFP6IRAD+cDgAw/6CQQD+v2AANc/+nlAANff+s2UW/mhbTizB +; Pf7a2toXJgj+y8vLCSzFEyj+lJSUCizBVfEO2KyI/ri4uKiI/mJiYg7DP8C1iP7S0tIXJhf+09PTKz +; /RMNSdiMMd/rCwsLeIMwPCf9N/1DDXP8kZLx42P8T+eHh4/tTU1P6dnZ0KP8EO4B3O/nZcQv7JaAWh +; 8t7+rVcA/nA4AMT+mk0ANdEfNfifH/6GXzgswTMF/vn5+SbAjYj+jY2NLMIZ/rOzsyX+cHBwLMhV7g +; 7XLP6Ojo4h/oGBgR0Owv6QkJD+8/PzJsAb/qOjow4/0zDTrogDwb+I/tPT0/6Xl5cwA8F/03/VMNY/ +; zf59fX3+0dHR/pOTk/5XV1c/wA7+oqKi/tPT0/52dnY/wQ7fHdD+nGImNd/+lEoA/nA4AMOjtf65XQ +; A10v6nVAD+yGQANfn+pGMhHCzAf/6IiIj+5ubmFyYb/rq6uv5kZGQsuoj+19fX/qampv5fX18szFXt +; Vde1iP7JycmFiAoOBv7BwcH+/f39JheEiP54eHg/1jDS/tLS0hMw/pycnBI2g4jCf9Mh1DDXP88C/r +; a2tqeI/mZmZjr+zMzMJ/5gYGA/wA7fHdD+b1tH/sJnCzXe/r9gAP58PgCaLsMjNdT+sVkApNQ1+v66 +; ZhH+fl49LMGviP7Dw8P+/f39JsCBiP6srKz+3Nzc/n19fSzSVesO1qaI/p6env7Pz8/+l5eX/ufn5x +; cmQP65ubn+XFxcP9hV0S/+ysrKtYj+sbGxHQPCf9Mh1DDWP9J//o+Pj/7e3t6liC0/wA7fHdH+jV8x +; Nd/+oFAA/nA4AMT+rVcANdX+vmAA/qxWADX7nx/+jWAyHCzAGf6YmJj+7+/vJsA5FCgs1VXqVdUs/s +; TExAgmwI+I/oeHhw7AP9owzy3+2NjYFLyI/nJycgPBEtIh1TDWP9OpiP6+vr4io4iHiCgO3X/SEP66 +; ZhA13v6/YAAA/nA4AMMA/sVjADXWAf6jUgA1/f6zZRb+aFtOLMH+oaGh/vj4+KaIJhf+0dHR/nFxcS +; zXVekO0rmI/tnZ2RcmF5yI/nd3dw7DP9kwzv7AwMD+Xl5eAyP+y8vL/pycnD+diNJ/1DDWP9T+fn5+ +; /tbW1v6Wlpb+WVlZO/6goKAp/nR0dA7aHdP+hV43/tFpAN/+p1QA/nA4AMQlNdk4Nf3AAv6NYDI3/r +; u7uwsy/qmpqf739/cmwJGI/pWVlTss2FXoVc4d/piYmP729vYmwC7+m5ubLf7V1dX+hoaG/lhYWA7C +; VdlVzSEDwTsJBP5nZ2cS0CHUMNU/1Cz+q6ur/s/Pz/5wcHA/DsH+d3d3FhkdDtYd1P6kYyE13p1b/o +; 5HAP5wOADDAP6/YAA12v6ZTQA1/cL+1og5/qOdligswP6BgYH+4uLivIgmOf7BwcE+LNpV5g7MrYgL +; CCYX/tzc3CMOwD4ik4j+Xl5eDsI/2TDMA8J/wP6BgYE0/ouLixLOIdMw1j/UBRb+p6enNw7Eqoj+sL +; CwsYgrDtMd1P5vW0f+wmcLNd7+uV0A/nY7AJ1bw/6aTQA13P6eUAD+yGQANf3Cnx/+fl49LMGtiCo5 +; JsCHiP6EhIQs21XmDskx/uzs7CbAOf6xsbE7DsKkiP6Xl5f+0tLS/nd3dx1Vwj/YVcudiMESwrCIKp +; iIBhLKIdQw1T/UDv6Xl5f+19fX/n9/f/5XV1fIHv7T09P+iYmJDtAd1f6UYCw13wT+cDgAwz7+rVcA +; Nd3+sVkAp+E1/cT+nGIn/mBaUyzACv6RkZEJF389/rCwsP5gYGAs3FXlDsWliP62trY5JsCKiP6BgY +; EOxryI/tDQ0P6hoaEsDsI/2DDKA8B/xCH+k5OTJf56enr+U1NTyCHUMNU/1BH+xcXFkogRDsqwiP6+ +; vr6WiP5hYWEOzB3VOv66ZhA13v6/YAD+gkEA/nA4AMMA/sVjADXe/rVbAKK2Nf3F/rpmEf5vXEgswb +; WI/s3Nzf7+/v4mF/7Y2Nj+dnZ2LN1V5A7DH/7g4OAXJgj+xMTEJA7IqIj+qamp/svLy/5ra2sOwj/Y +; MMkDf8e6iP7Hx8f+pKSkHRLFIdMw1j/U/oODg/7X19f+j4+PHVXMLP6ZmZn+z8/P/nl5eQ7JHdb+jV +; 8xNd/+p1QA/nA4AMQONeCZHw41/cafH/6NYDL+WVlZwQb+oaGh/vT09CbAlIj+nZ2dCizdVeRVHf6g +; oKD++fn5JsCWiP6UlJQdDssE/tXV1QcdDsE/2FXIEskd/qamph7+bGxsEsN/0zDVP9QK/rS0tLWICQ +; 7QI/7Ly8sRCg7FHdah8f6rZBs13p1b/ohEAP5wOADDAP6/YAA14Z5a/qNSADX9yP6zZRb+aFtOLMH+ +; e3t7/t3d3RcmOf7IyMg6LN5V4rGIJQgmF/7V1dUYDs6tiP67u7uiiP5gYGAOwT/YVcdAy/56enol/p +; OTk/5UVFRVIdQw1T/Uv4j+09PT/p+fnxkO0jf+qqqqvIj+bm5uDsId1/52XEL+yWgFNd7+rVcA/nA4 +; AMT+mk0ANeT+nlAANf3JAv6GXzgswaqIAf76+vomwIuI/oqKiizfVd/+jY2N/vHx8SbAnIj+p6enLA +; 7Qo4j+kpKS/tTU1P5+fn4dDsA/2DDGQMysiP62trakiAKRiNIw1T/UDv6fn5/+1NTU/nl5eQ7W/oGB +; gf7S0tL+kZGRHdn+nGImNd/+lEoA/nA4AMOjtf6zWgA15f6jUgA1/cv+pGMh/mBaUyzAO/6Li4v+6O +; jotogmmoguAizgVdqniP6+vr7+/f39JlWHiP57e3sO1Cf+y8vL/qqqqjsOwT/XMMUSzX/+i4uLAwQh +; 0DDVP9SyiP7KysqHiP5iYmIO2K6I/rm5uRsgHdX+b1tH/sJnCzXe/r9gAP58PgCaLsP+iEQA/stmAD +; XmOP7IZAA1/cv+umYR/nddQyzBsYgPCCbA/t7e3v56enos4FXY/n19ff7l5eUXJkD+vb29/l5eXh3A +; DtSmiP6ioqL+zc3N/nBwcA7BP9YwxUDNf8C1iP7Dw8OHiBkhzTDUP9X+jIyM/tjY2P6JiYkO2X/ALP +; 6SkpL+0tLS/oCAgB3T/o1fMf7RaQDf/qBQAP5wOADE/qdUADXo/rFZAKTUNf3Mnx/+jWAy/mBaUyzA +; KP6bm5v+8fHxJsCWiP6lpaUZLOBV1Cwr/vv7+ybAkYj+i4uLHcRV1P57e3v+1NTU/paWliwOwFXWMM +; QSzH/CP/6fn5/+zMzM/nFxcSHLMNQ/1Df+u7u7pogNDtgdxCcPhYgoHc+h8f6zZRY13v6/YAD+gkEA +; /nA4AMMA/sVjADXpNJ5aNf3OIRYswbyI/tbW1hcmQP7Pz8/+b29vLOFV0TY/FyYX/tDQ0P5paWkdxg +; 7Uqoj+s7OzsYj+ZWVlDsBV1jDDQMt/xRA4/pqamg4hxzDVP9T+fHx8/tXV1f6ZmZn+WlpaDtgdxhn+ +; o6Oj/srKyv50dHQdzf6FXjc13/6nVAD+cDgAxP6aTQA165kfDjX9z58f/o1gMv5ZWVnBJP6tra3++P +; j4JsCPiP6SkpI7LOFVzSz+lZWV/vX19SbAPQYsHchV1Cz+ioqK/tXV1f6EhIQdDj/WVcISyiHHqYj+ +; sLCwr4j+Z2dnIcUw1D/UHf6oqKj+0dHR/nNzcw7YHcr+e3t7Jf6ZmZn+WVlZHcr+pGMhNd6dWyP+cD +; gAw6O1/rldADXt/p5QADX90f6zZRb+YVpULMH+hISE/uPj4xd/nIj+vr6+/mZmZiziVcqriA8IJlWB +; iP51dXUdyw7VHP7FxcWOiCgOP9YwwRLIf8sx/tLS0v6IiIghwzDUP9S4iP7Ozs4r/l9fXw7XHc0CMq +; 2IHB3H/m9bR/7CZwv+0WkA3/6tVwA+nVvCIzXvHzX90p8v/n9fPjsswK6I/r6+vv78/PwmwIWI/oGB +; gSziVcj+g4OD/urq6ibACP61tbUZHc4O1Bn+mpqaNP51dXUOVdYwwBLHIc2yiBeTiAYhwDDUP9MO/p +; SUlP7Y2NgTDth/0P6Li4v+09PT/oeHhx3F/pRgLDXi/r9gAP6CQQD+cDgAwP6tVwA18B81/dOfL/6d +; Yif+YVtUny/AGSH+7e3tJsCYiP6tra3+Xl5eLOJVxBkyOSbAjIgxHdEO1RAD/p2dnSw/1TDAQMZ/zz +; D+l5eXFv54eHgw1D/TrIj+wsLCmIggkojXHdOyiP7BwcGRiCQdwf5vW0f+umYQNeSdW/6gUAD+xWMA +; NfH+o1IA/shkADX91P67ZhH+cF1JO8G2iP7Q0ND+/v7+Jhf+1dXV/nR0dCziHcIB/t3d3RcmF/7IyM +; j+ZGRkHdMO1Qb+rKysvYgcP9VVEsUh0r2IPP6ioqIsMNA/1P6BgYH+19fX/pKSkixA1h3Wo4j+nJyc +; /s7Ozj0d/o1fMTX93v6xWQCk1DX91Z8v/o5hMzvBpoj+pKSk/vX19SbAk4j+mpqa/lpaWizj/p2dnf +; 74+PgmwJeI/peXlywd1VXVHTH+1dXV/oqKih0/1BLEIdJ/wDv+qqqqu4j+bGxsMM4/0zv+sbGxuogn +; Dtcd2b2I/s3GwP6ycS81/d/+vmAAmS81/df+s2UW/mlcTzvB/n5+fv7f39+/iCY5AP5qamos4K+IBw +; gmF/7Y2Nj+cHBwiIjYDtYg/r6+vpyI/l9fXz/TEsN/0jDD/n5+fv7R0dH+kJCQPzDLP9O9iP7S0tIz +; /l1dXQ7WHdv+b1tH/sJnCzX94JtN/qdUADX92J8v/n9fPjvBIP63t7cqJsCKiP6IiIgs3v6Kior+7+ +; /vJsA5/qysrDsd2lXWO/6Tk5P+09PT/nx8fA5V0Z2IwiHSMMWtiAxVAjDJP9IO/pubm/7W1tb+e3t7 +; Dtcd2/6cYic1/eP+nlAANeqeWjXr/p1iJ/5hW1Q7wH/+jY2N/urq6rSIJpqI/rS0tCQs2gb+u7u7/v +; z8/CYXiYj+f39/LB3cDte4iP7Nzc3+pqamLJ2I0BLBIdIwxz/+j4+PAyYwxj/TsYj+yMjIATMO1h3c +; /qxfEDX95P6ZTQA16J1b/ohEAP6YTAD+yGQANev+qF0RO8KyiDwIoogX/tvb2/55eXks2P57e3v+4+ +; PjFyYI/sDAwBUswh3bDtc3/qWlpf7MzMwnP88SwH/SMMoJAISI/lxcXDDDP9P+iIiI/tjY2P6Li4v+ +; WFhYDtYd2yzA/olIBv6xWQD+zGcAorb94h8BNeb+rVcA/nA4AMD+hkMAo7X+tVsANen+p1QAHTvDpI +; j+np6eIybAlYj+oqKiGSzUO/6mpqb++vr6JsAj/o+PjyzFVdtV2P59fX3+1dXV/pOTkx0/zZ2IIdJ/ +; zCz+o6Oj/srKyv5wcHAwwT/SKD2siP5qamoO1h3bLML+hEUGn8P+mU0A/shkADX94f6xWQABNeX+lE +; oA/nA4AMH+hkMAHsAfHDXlFP6IRAAdwDvFvoj+2dnZFyZAKf5tbW2MiNIY/tTU1BcmF/7T09MJLMdV +; 2w7Yq4j+tra2qogzP8xA0jDPPf7Ozs7+mJiYDj/SDP7V1dX+nJyc/ltbWw7Wf9sswwQewKK2Bf7RaQ +; D94P61WwCitjXj/r9gAP58PgCaLsIWHsGlwzQ14/6tVwAdwjvGqYj+sLCw/vn5+SbAjoj+j4+POyzP +; /pOTk/709PQmwJuI/qOjozssyVXbDtc/Hf6NjY3+1NTU/oGBgQ4/ykDRf9EG/rOzs6uIPj/PDhEDEA +; 7WHdssxf6ERQYewv6ZTQAcNf3eD/6nVAA14v6gUAD+cDgAxP6KRgAewjf+rFYAHDXgIx3DO8j+h4eH +; /uXl5bmIJhv+vLy8/mVlZSzMM/7Dw8MIJheEiP55eXkszVXaVdc/wCv+yMjIh4gKP8kh0DDTP/6IiI +; j+09PT/oaGhj/NCSmBiBUO1h3aLMcEn8PDpcM0Nf3dm03+o1IANeD+v2AA/oJBAP5wOADDAP7FYwBG +; /p5QAB7Cp/EcNd4S/nw+AB3EO8kNEwgmwIKI/n9/fyzK/oGBgf7o6OgXfwj+ubm5/l5eXizPVdoO1z +; /ACv6dnZ3+z8/P/nJycg4/xyHPMNM/wbOI/sHBwTYVP8kO/pGRkf7Z2dn+hYWFDtcd2n/I/oRFBh7E +; orb+p1QA/sxnADX93P6eUAA13w7+cDgAxCU1wRwONx4ONd8lHcY7yqOI/peXl/7v7+8mwJeI/qqqqv +; 5eXl4sxqOIBf77+/smwI6I/oeHhyzSVdpV1z/B/nd3d/7T09P+mpqaHT/GIc4w0j/EHf6bm5sWLj/H +; Ff6/v79A/mZmZg7WHdosygSfw8b+mU0A/sNiADX92/6eUAA13RT+lEoA/nA4AMOjtf6/YAA1xP6+YA +; DANd4S/oJBAB3H/lpaWsy4iP7T09MXfxcSIyzEuoj+29vbFyYXKf5nZ2cs1FXaDtY/wgb+rq6uuYg+ +; P8UhzTDSP8e+iP7Ly8v+oKCgOz/E/n9/f/7W1tb+lZWVLA7WHdosywSfw8eitv6xWQA1/do4/shkAD +; Xb/r9gAP58PgCaLsP+lEoANef+p1QAHck7zaeI/qioqP729vapiMCSiP6Xl5c7LMA7/pqamh8mwJiI +; /pubm/5aWlos1lXaVdZVwg7+hoaG/tXV1Q8OP8MhzH/SP8mniP6tra0i/mtraz/BLBgH/m9vbw7XHd +; kszQQeyf6ZTQD+yGQANf3YBaTUNdr+mk0A/nA4AMT+rVcANeadWyMdyjvP/oGBgf7h4eEXJp2IE/5p +; aWk+Kf79/f0mF/7b29syLNlV2Q7XP8OwiP7BwcGWiP5eXl4/wSwhyzDSP8z+goKCA/6Pj48Ouoj+0N +; DQ/qWlpSgO1h3aLM7+hEUGHsqitjQ1/df+tVsAwDXYEgD+cDgAwwAzNeYS/nw+AB3LO9Av/rq6uiom +; wIqIpogmwDn+sLCwGSzbVdkO1j/EO/6WlpYDPQ62iP7IyMghyjDSf84v/r6+vhquiP5+fn4/DtYd2X +; /QBB7M/qdUAP7MZwA1/dWZHw411w7+cDgAxP6gUAA15/6aTQAdzTvRGf6QkJD+7e3tJsKLiBMs3lXZ +; VdY/xRT+0NDQDL2I/n9/fyHJMNJ/z66ILQuqiP59fX0O1h3ZLNH+hEUGHs2n8Sg1/dQc/p5QADXVnV +; v+jkcA/nA4AMMAEjXmEgAdzjvSv4j+5eXluojC/t3d3f53d3c7LN5V2Q7WP8StiP7CwsIDnYgJiIjI +; MNI/z/6EhIT+19fX/o+Pjx23iP7Hx8f+pqam/l1dXZqI0h3ZLNMEn8PON/6xWQAcNf3THzXU/rldAP +; 52OwCdW8P+mk0ANef+p1QAHdA70H/+o6Oj/vn5+SbAmIhAJsCUiP6fn58KLN5V2VXVP8P+f39//tHR +; 0RYd/oCAgP7U1NT+VFRUxzDSP84K/rW1tbKIOj/ADgovPP5wcHAO0B3ZLNT+hEUGn8PQ/plNAP7IZA +; Clw/3SHzXT/ppNAP5wOADDPv6zWgA15hT+jkcAHdE7zwkD/v7+/n8X/tXV1TasiP7c3NwXJkA8CSze +; VdgO1j/ACv6srKy5iP5sbGw/wa2IIcZ/0j/O/nZ2dv7T09P+np6eCj/ADsL+fHx8Jf6WlpYdDs0d2C +; zW/oRFBh7Rorb+tVsANf3ROAE10P6/YAD+fD4Ami7D/ohEABQ15jE+HdI7zv6QkJD+8vLyJsCciP6n +; p6cKO8AR/rOzs/76+vomwIyI/oyMjDss3VXYDtVVvIgaFQo/w0DFMNI/zQ4V/tTU1D0/wQ7Eq4j+t7 +; e3pYj+ZmZmDssd2CzXBB7TOBw1/c/+sVkApNQ1z/6gUAD+cDgAxP6nVAA15/6aTQAd1DvMp4j+wMDA +; CCYXhoj+fHx8O8MK/oqKiv7n5+cXJhv+ubm5AizdVdhV0x3+mZmZ/tDQ0P55eXk/xUDEMNI/zbOI/s +; rKyoWI/mBgYD/ADscdKf7T09P+hISEDskd2H/Y/oRFBh7UKzT+0WkA/c7+vmAAm001zRT+gkEA/nA4 +; AMMA/sVjADXm/r9gAB8d1TvL/oCAgP7m5uYXJgj+vLy8FTvGsIj+xcXFCCbAgIj+fHx8LN1V1w7SsY +; gTjogkP8YhwzDSP83+jY2N/tjY2P6IiIg/wQ7KtIgiioj+X19fDsV/2CzaBB7VN/6xWQD+zGcAorb9 +; zJkf/qdUADXM/q1XAP5wOADE/ppNADXn/qBQAB3XO8miiP6srKwqJsCRiP6Li4s7yaOI/pqamgUmwJ +; eI/qenpygs21XYVdD+h4eH/tPT0/6JiYkOwD/GIcIw0j/MqIj+vLy8pIj+Z2dnP8EOzCz+n5+f/s3N +; zf51dXUOwx3YLNv+hEUGn8PX/plNAP7IZAClw/3M/plNADXL/pRKAP5wOADDo7X+uV0ANeYz/oJBAB +; 3Y/lpaWsi3iP7Y2NgXJhf+0NDQKzvMuoj+1dXVFyYXJf5wcHAs21XYDs03MqyIDQ7CP8UhwX/SP8z+ +; fX19/tXV1f6YmJgsP8EOz/53d3c4/p2dnTsOwH/YLNs7BB7Yorb+sVkANf3L/plNADXJ/r9gAP58Pg +; CaLsMjNecvHdo7xgr+mJiY/vX19aqIwJmIBgo7zqiI/qurqz0mwJCI/pSUlDss2lXXDsz+eHh4/s/P +; z/6ampr+WlpaDsM/xUDAf9I/yx3+qampJf5ycnI/wg7RqIj+sbGxE/5ra2sd1yzcO8AEHtoB/sxnAD +; X9yf6eUAAcNcf+mk0A/nA4AMQvNef+lEoAHdsKO8SsiP7IyMj+/f39JheAiP52dnY70v6Dg4P+4+Pj +; FyYq/r+/v/5nZ2cs2lXXDsks/qGhoTj+c3NzDsY/xCHAMNE/y7iIB/6oqKj+Xl5eP8IO1P6Ghob+09 +; PTKR3VLNs7wv6ERQYe26K2NDX9yP6nVAD+yGQANcX+v2AAAP5wOADDAP7FYwA15hL+fD4AHdwKwTvB +; /oWFhf7r6+u0iMAI/rW1tf5eXl471D7+vb29/vz8/CbAEf6CgoIs2VXYVce1iP7GxsaEiAYOxz/EIT +; DRP8oO/pWVlf7X19f+gICAP8N/1R2wiP6+vr6WiP5jY2Md0izbO8MEHt3+p1QAHKK2/cYFNDXEDv5w +; OADE/qBQADXn/ppNAB3eCsI3/ra2tjkmwDoAO9cZ/pOTk/7t7e0mwJiI/q6urgYs2FXXVcUd/o6Ojv +; 7T09MTDso/w1XSP8msiP7Dw8OWiP5kZGQ/ww7VHcL+l5eX/tHR0f58fHwd0CzbO8T+hEUGHt6n8f7D +; YgA1/cX+vmAAGzXCnVv+jkcA/nA4AMP+gkEA/r9gADXmEgAd3wrBvIj+39/fv4gmQC3+ZWVlO9q2iB +; YXJhf+1tbW/nV1dSzYVddVwzP+urq6wCAOy1XDMNE/yRP+19fX/pGRkR0/w3/VHcS6iAv+paWl/l1d +; XR3NLNo7xgQe36K2/rFZABw1/cObTf6sVgA1wTH+djsAnVvD/ppNADXn/qdUAB3hChn+oKCg/vj4+C +; bAl4j+l5eXCjvcpoj+o6Oj/vT09CbAk4j+m5ubOyzXVdZVwv59fX3+0dHR/pOTkywOzT/CMNA/yDv+ +; s7OzC/5tbW0/xA7VHcYo/qqqqr2I/m9vbx3KLNs7x/6ERQafw+H+mU0A/shkADX9w/6wWAA1wP6aTQ +; D+cDgAwz7+rVcANeYU/o5HAB3i/mtra/7Q0ND+/f39Jhf+2NjY/nFxcYqIwDve/n19ff7e3t4XJp2I +; /sbGxv5qamos1lXXDgr+qqqqLf5ubm4Ozz/CMM8/yL2IAyQZP8UO1B3J/n9/fzQSLB3HLNo7yf6ERQ +; Ye4qK2/rVbADX9wv6nVAD+v2AA/nw+AJouwwD+xWMANeYxPh3j/vDw8K+IwDn+q6urGQrCO96riP62 +; trb++vr6JsCKiDws1lXWFAv+o6OjKJqI0T/BMM4/xw7+nJyc/tXV1f56eno/xg7UHcsgDH/+ZmZmHc +; Us2jvJtoj+h0gIHuT+o1IA/sxnADX9wP6kUgD+cDgAxP6nVAA15/6aTQAd5SYXiIj+f39/CsVV3gop +; /unp6Rd/moj+tbW1/mJiYizVVdMs/paWlgP+fHx8/ldXV9NVwVXNP8exiP7JycmJiP5hYWE/xg7UHc +; 0s/pCQkP7T09MTHcMs2jvJ/pSUlP7Z2dn+i0wNHuWn8f66XQA1/f6dTgD+cDgAwgD+v2AANeYS/nw+ +; AB3m/v39/f7AwMAzCsc737GI/sjIyAgmwP7c3Nz+eXl5gIjUVdI+NZOI/mNjYw7UP8EwzD/H/omJif +; 7Y2Nj+ioqKDj/GDtR/0CcPhYj+X19fHcAs2TvJrIj+wcHBnIj+aWlp/oRFBh7mN/6xWQD+zGcAorb7 +; /qtWAP5wOADB/ppNADXn/qdUAB3o/o+PjwrLO96kiP6dnZ3+8fHxJsCWiP6kpKT+XFxcLNNV0P6FhY +; X+09PTKSwdwFXUP8AwzD/Fp4j+ubm5Iv5paWk/xw7UHdIK/qOjo/7MzMz+dHR0LNk7yRP+2NjY/paW +; lhk7/oRFBh7o/plNAP7IZAA1+v6vWAD+cDgAo7X+s1oANeadW/6IRAAd6QrNO98u/tjY2BcmQP7Nzc +; 0nLNJVzjf+sbGxIv5ra2sdwg7UVcAwyz/F/np6ev7U1NT+mpqa/lpaWj/HDtQd1Rv+z8/P/pubm/5b +; W1ss1jvINxQp/nJycjvBBB7porYFNfkx/o5HAP7LZgA15v6tVwD+cDgA1aXTHdMKzjvfM/6urq7++f +; n5JsCPiCU7LNFVzC7+zs7O/p2dnRkdxA7UPzDKP8QO/qWlpf7R0dH+dHR0P8gO1B3WLDP+tLS0rIj+ +; ampqLNQ7yLuINP6np6cVO8IEHuv+mU0A/sxnAKK2951bNef+lEoA/nA4ANX+pFIA/oBAAB3TCs9V4P +; 6Ghob+5eXlFyaciP69vb3+ZWVlLNBVyiz+n5+fFv52dnYdxg7UPzDJP8QJKf6rq6v+X19fP8gO1B3W +; LMI8IQss0jvIKP7W1tb+f39/O8QEHuw3/rpdAP7RaQD94P6/YAD+fD4Ami7U/oBAADGaPh3UCtA73i +; zADf7AwMD+/f39oojAg4j+f39/LNBVyLOI/sXFxYiI/mFhYR3IDtNVMMg/xP6RkZH+2NjY/oODgz/K +; DtN/1izEOv7BwcGRiP5jY2MszjvIK/7GxsaRiP5mZmY7xf6ERQYe7v6nVAAcorb93f6gUAD+cDgA1Q +; H+w2IAwP6AQAAd1ArRO94swBn+lpaWJ7GIwJeI/qqqqv5eXl4szlXGLP6MjIwh/oaGhh3KVdQwyH/C +; Ff7AwMCciCA/yg7THdYsxjv+nJyc/tDQ0BsszDvIC/7Z2dn+j4+PCjvGBB7vp/Eop/H92hL+gkEA/n +; A4ANT+gEAA/rNaACjAEB3VCtI73lXBuIj+0dHRFyYXIf5zc3MszlXEqoj+uLi4pYj+Z2dnHcsO1DDH +; P8L+f39//tbW1v6UlJQdQMoO0x3WLMm9iP7MzMz+oqKi/l1dXSzJO8cVLq+I/m5ubjvIBB7worYF/t +; FpAP3Y/qdUAP5wOADV/pRKAP7DYgDC/opFAB3VCtM73VXCFf6np6f+9vb2qYjAkoj+mJiYOyzMVcP+ +; fHx8NP6WlpYKHc1V0zDGf8Es/q+vrzgnP8sO1B3VLMsGGLiIJyzHO8cb/tTU1P6goKD+Xl5eO8kEHv +; IB/shkADX91J1b/o5HAP5wOADUo6X+pFIAKMIQHdYK1FXdLMP+f39//uDg4Bd/nYj+w8PDDSzMVcAK +; Pv7Ly8v+cXFxHc8O0zDFP8EUNBH+XV1dP8sO1B3VLM7+g4OD/tPT0/6RkZE7LMQ7xgr+pqamEv55eX +; k7ywQe86K2/rpdADX90v6/YAD+djsAnVvV/qRSAP7DYgDD/opFAB3WCtRV3VXEID0qJsCIiP6GhoYs +; y1W4iP7JyckvBh3RDtIwxT/A/pmZmf7X19f+fHx8P81/0x3Vf9CuiP68vLw9/mZmZizCO8YnGoaI/m +; NjYzvMBB71/qdUABw1/c/+mk0A/nA4ANal0yjDMR3XCtU73SzEChb+6+vrFyaZiP6ysrIkLMn+k5OT +; /tPT0/6AgID+WFhY01XSMMQ/IP7Hx8eOiP5iYmI/zQ7THdUs0jv+lJSUEjUswDvG/pKSkv7Z2dn+iI +; iIO87+hEUGn8P2Kyin8f3M/r9gAP6CQQD+cDgA1/6kUgAow/6KRQAd1wrWVdxVxrSIGhcmF/7a2tr+ +; d3d3LMYv/r6+vpmIIJOI1A7SMMM//oWFhf7Y2Nj+jIyMDj/NDtN/1SzVuIj+yMjIgYj+YGBgmojEqo +; gXooj+ampqO88EHvc3/rFZAP7MZwCitv3J/qdUAP5wOADYo6UowzEd2ArXVdwsxqWI/p+fn/7y8vIm +; wJWI/qCgoAoswxP+0tLS/pCQkDssVdVV0TDCCv62trawiCs/zg7THdUs16SI/qenpxoyO8L+gICA/t +; fX1/6ZmZn+XFxcO9AEHvn+mU0A/shkAKXD/cadW/6ORwD+cDgA2f6kUgD+w2IAw/6PSAAd2P5bW1vX +; VdxVyAz+2tra/v7+/iZA/srKygkswDc2DxgswVXVVdH+l5eXPzD+d3d3/tTU1P6dnZ0KP89/0x3ULN +; h/wP59fX3+0NDQ/pmZmRl//q6urgf+dHR0O9IEHvqitv61WwA1/cT+rVcA/nA4ANqjpf7DYgDDMR3Z +; CthV3FXIM/6xsbH++fn5JsCNiCmKiDgV/l5eXizCVdUO0f6zs7OsiD4DHz/QDtMd1SzXO8IR/re3t6 +; yIJf6qqqokO9IKBB78/qNSABw1/cH+lEoA/nA4ANv+pFIAKMP+j0gAHdn+W1tb2VXbLMk7/oiIiP7m +; 5uYXfyqViAAsxVXVDtC0iP7d3d2tiP6Ojo4/0A7THdUs1zvE/pqamv7g4OBV/omJif5aWlrRCsEEHv +; 2n8TQ1/BL+fD4Ami7bo6X+w2IAw51rHdr+W1tb2VXcLMqwiP7MzMwmwRX+fHx8LMRV1Q7Q/tjY2P6G +; hoaEiP7BwcGPiP5dXV0/zQ7Tf9Us1zvDDf7ExMSViP5nZ2eniDEF/mNjYzvOCsL+hEUGHv3Aorb+sV +; kA/sxnADX5/qBQAP5wOADd/qRSAP7DYgDD/o9IAB3a/ltbW9pV2yzK/oqKiv7V1dUy/vDw8CbAl4j+ +; p6en/l1dXSzCVdUO0K+IP8As/pubm/7Ozs7+dXV1P8wO0h3VLNc7w/6Hh4f+2NjY/pKSkv5bW1s7wB +; n+n5+f/s/Pz/54eHg7zArDBJ/D/cL+mU0A/sNiADX2Ev6CQQD+cDgA3aOlKMOdax3b/ltbW9tV2yzH +; JP62traqiCssuoghFyYX/tHR0f5wcHAswlXUDtA/w76I/s3NzQYdP8kO0x3ULNc7wqWI/rS0tLWIBT +; vEPTj+oKCg/l1dXTvJCsQEHv3DN/6xWQD+0WkA9P6nVAD+cDgA3/6kUgD+w2IAw/6PSAAd2/5bW1vb +; Vdssxv57e3v+0NDQ/pqamhkswaeI/qmpqf739/cmwJCI/pSUlCzBVdVVzz/EBv6srKwx/mlpaT/HDt +; Md1CzXf8I9/tPT0/6jo6P+Xl5eO8Yk/rCwsCIYO8cKxQQe/cX+mU0A/sxnADXwnVsj/nA4AN+jpSjD +; nWsd3P5bW1vcVdsswxn+pKSkOP50dHQsxRP+4uLiFyaciP7AwMD+Z2dnLFXVDs8/xv6Dg4P+1NTU/o +; yMjD/GDtId1SzWO8L+oqKiITk7yv6Hh4cS/o+PjzvFCsYEn8P9xqK2/rpdAP7RaQDu/r9gAP58PgCa +; LuD+pFIAKMP+j0gAHdz+W1tb3FXbLMK2iP7Hx8eCiCQsxy/+vLy8/vz8/CbAhogiLFXUDs8/xy/+u7 +; u7nIgkP8MO0x3ULNc7wLKI/sjIyIuI/mRkZDvMK/6+vr6YiC87wgrHBJ/D/cj+p1QA/sxnADXr/ppN +; AP5wOADho6Uow51rHd3+W1tb3VXaLMA7/pGRkSEiLMoKA/7s7OyyiCaYiP6urq7+Xl5emojTVc8/yB +; 3+lZWV/tLS0v56eno/wQ7THdQs1zvAFv7Z2dkLO88K/piYmAP+f39/O8AKyP6ERQYe/cmn8Sg16BIA +; /nA4AOL+pFIAKMMjHd0K3VXbq4j+vb29nYg+kojNtYgHF38X/tfX1xCDiNJVzz/KNi3+p6enOz8O0h +; 3ULNc7qYgqp4j+bGxsO9IB/srKyj4VCsgEHv3Korb+sVkAHDXl/qdUAP5wOADjo6Uow51rHd7+W1tb +; 3lXZ/oCAgAP+k5OTCizPBv6hoaH+9PT0JsCUiP6cnJw7QNFVzj/Lpoj+pqamPDYO0h3ULNY7/n19ff +; 7W1tb+nJyc/lxcXDvUBv6qqqo8/nJycgrHBB79zP6ZTQD+yGQApcPiFP6ORwD+cDgA5P6fUAAow/6U +; SgAd3greO9ekiP6srKwtNizT/nt7e/7d3d0XJjn+x8fH/mpqao6I0A7OVc0IA/6UlJQO0B3ULNYZ/q +; qqqiUQO9YKwP6AgIAD/peXlxkKxQSfw9yith7sN/61WwD+0WkA4P65XQD+djsAnVvko6X+w2IAw51r +; Hd/+W1tb31XVuoj+zMzM/qSkpAY7LNSqiP60tLT++vr6JsCLiP6JiYn+WFhYz1XOP80OAv61tbWoiC +; AOzR3ULNa3iAf+ra2t/mJiYjvWCsKriP66urqiiP5paWkKxAQe2Df+p1QA/rpdADUo/p5QAB7t/qNS +; AP7MZwA13f6aTQD+cDgA5iIow/6ZTQAd3wrfO9MK/pmZmQP+fX19O8FV1Tv+i4uL/ujo6Bcmmoj+tr +; a2/mFhYZeIzVXOP8wOwv6Ojo7+09PT/oKCgg7MHdQs1TD+2dnZ/oSEhDvYCsT+kJCQ/tTU1A8Kw/6E +; RQYe1afx/qxWACg1wxz+p1QANx7sKzQ12v6/YAD+fD4Ami7mo6Uow51r/no9AB3f/ltbW99V0rCI/s +; LCwpGIIDvCVdZVK/7Gxsb+/f39JsD+3d3d/nl5eR3MVc4/zA7DtYj+xMTEioj+XV1dDskd1CzULxM5 +; /mhoaDvYCsY2DycCCsEEHtU3NDXG/r5gAP6QSQAe7Df+sVkAHDXX/qdUAP5wOADoIv7DYgDDAR3gCu +; A70P6Hh4f+09PT/o2NjQo7wyzXVRn+m5ub/vHx8SbAlogR/ltbWx3KVc4/yw7FCv6fn5/+zc3N/nR0 +; dA7HHdQs1P6Dg4P+2NjYMAo72ArIo4j+o6Oj/s7Ozv53d3cKwASfw9f+p1QAHDXFHA4e7f6ZTQD+yG +; QANdSdW/6IRAD+cDgA6KOlKMOda/56PQAd4P5bW1vgVc4VMhMJO8Ys1lXBvYj+1tbWFyYIB/5ubm4d +; ylXNVcsOxy7+z8/P/pycnB0OxR3ULNI3/rKysrmI/nFxcTvZCssqFv6enp7+XV1dBB7Yp/H+w2IANc +; YP/oxGAB7sNwU10v6tVwD+cDgA6v6ZTQAow/6kUgAd4f5cXFwK31XNDBb+nZ2d/l5eXjvHLNZVwiQY +; /vj4+CbAj4j+kZGRLB3IDs0/yg7JJP6wsLAT/mlpaQ7DHdQs0hD+0dHR/qWlpf5fX1872QrNqYj+tL +; S0EwQe2aK2/rFZAP7MZwA1xRz+nlAAHu3+mU0AHDXP/pRKAP5wOADqo6X+w2IAw51r/no9AB3h/l1d +; XUDgVcoK/qGhoRYuO8ks1lXE/oSEhP7j4+MXJpyI/r29vf5lZWUdxw7NP8kOzP6Hh4f+1NTUPA7CHd +; Ms0v6enp7+1tbW/n5+fiw72QrQ/oqKiv6NTw+V1NsB/shkADXGBf6MRgAe7Df+ul0ANcz+v2AA/nw+ +; AJou6/6ZTQD+w2IAw/6kUgAd4v6/v786Ct9VybSI/sbGxhgzO8os1lXFroj+vr6+/vz8/KOIwAL+gI +; CAHcYOzVXJDs0NF5eIFQ4d1CzQsYj+x8fHj4ggLMA72QrR/oRGBp7U3Df+tVsA/tFpAMYo/pVLAB7t +; /qdUABw1yf6gUAD+cDgA7KOlKMQUHeL+jo6O/tXV1TwZCt1Vxwr+j4+PIf6GhoY7zCzWVcYK/pSUlP +; 7t7e0mwJiI/qysrP5dXV0dxFXNP8gOzyz+mJiY/tDQ0Az+WFhY0yzQ/oyMjP7Z2dn+jY2NO1XAO9kK +; 0gQe3v6jUgAcNcUc/qdUAB7tK/7DYgA1xhIA/nA4AO0BKMP+pFIAHeMZsYj+xcXFjIggCt1VxRH+ur +; q6ooj+aWlpO84s1VXINiX+/v7+Jhf+1NTU/nNzcx3DVc0/yA7Ru4ga/qWlpQod0SzOFf64uLgAGCzB +; O9kK0wQe3ys0Ncb+vmAA/pBJAB7sNwU1xP6nVAD+cDgA7qOl/rldACjD/no9AB3jGcB//qCgoCX+en +; p6CtxVxP5+fn4D/peXlxk7zyzWVcimiP6kpKT+9fX1JsCSiP6ZmZn+WVlZHcFVzT/HDtMG/qmpqS0Y +; Hc8szv57e3sh/p+fnxkswjvYCtQEHuCitgX+zGcANcU0/opGAB7tAf7IZAA1wJ1b/o5HAP5wOADv/p +; lNAP7DYgDD/qRSAB3kGcEKvYj+zs7OJP5fX18K2lXCpIj+qqqqGiM70SzWVcr+fX19/t/f37+IJp2I +; /sTExA0dwA7NP8cO0h3B/oCAgP7T09P+kZGRHc0szQr+pqamA/53d3cswzvZCtQEHuIB/shkAKXDw/ +; 6nVAD+cDgA/oRCAB7uorb+ul0AWj4d76OlMSjD/no9AB3kGcIKpoj+sbGxs4g2CtlVwSMaL/5gYGA7 +; 0izWVcuriB/++/v7JsCJiP6GhoYdVc0/xg7SHcOtiP65ubmiiP5kZGQdyyzMJxqEiDMswzvZCtUEHu +; M3BTXAnVv+iEQA/nA4AMALHvAd6EYdxv6PSAAow/6kUgAd5RnCVcEP/tTU1P6QkJAZCtdVCv6Wlpb+ +; 09PT/oCAgDvULNZVzCz+jY2N/urq6hcmmoj+s7Oz/l9fXw7MVcYO0h3ELP6RkZESNR3JLMz+k5OT/t +; nZ2Q/+WVlZxTvYCtYEHuX+mU0A/q5XAP5wOADCCx7wHeidS6K2HcX+uV0AKMP+ej0AHeUZwgrCroj+ +; wMDAl4j+Z2dnCtatiDUf/mZmZjvVLNZVzjr+ycnJFyYX/tra2v53d3cOyz/FDtIdxyf+xsbGCf5dXV +; 0dxyzKEf6/v7/AHCzFf9kK1gQe5v52OwCdW8ILHvAd6Zkfoacdw/6PSAD+w2IAw/6pVQAd5v5cXFzD +; VcP+mJiY/tPT0/6AgIAK1P6FhYUSJQo71izWVc+liP6dnZ3+8vLyrYjAlYj+oKCgOw7JP8UO0h3IGf +; 6jo6Ma/nNzcx3FLMo1/tfX1/6Xl5cKLMY72ArXBJ/D5j6dW8ILHvAd6ptNnUun8R3BMaXTwxQd5hnD +; CsQyGv6np6ckCtCliP6xsbG0iCc72CzWVdEu/tnZ2RcmQBo6jIjIP8QO0h3LG/7R0dH+mpqa/llZWR +; 3DLMgZ/q6urr+IMizHf9kK1wQe5v52OwCdW8ILHvAd6xIQpcMd/oBAACjD/qlVAB3n/lxcXMMKxQb+ +; q6urPDIKzj0H/qGhoQYKO9ks1VXSqYg2DCbAjYj+jY2NHQ7GP8QO0h3Mq4j+srKyrogNHcEsyDL+0N +; DQ/qioqBUsyDvYCtj+hEUGHuY+nVvCCx7wHe2eWhL+pFIAKMP+gEAAHecZwwrH/n9/f/7T09P+l5eX +; /l1dXQrKGf6enp4lGwrBVdks1VXU/oaGhv7l5eUXf5uI/rq6ugKUiMU/xA7RHc4s/oqKiiH+h4eHHc +; Asx/6ampr+19fX/n9/fyzJf9gK2f6ERQafw+Y+nVvC/oRCAB7wHe+jpf6pVQD+w2IAwRkd6P5cXFzE +; CscvG6OI/mpqagrIsYj+xMTEjIgRCsJV2SzVVdNVwLCI/sHBwf79/f2iiMCBiP58fHwOxD/DDtId0L +; OIBJKI/mBgYCzFr4gxlIgvLMo72ArZ/oRFBh7m/nY7AJ1bwgse8B3x/pRKAP6+XwAoNR3oGcRVyf6Q +; kJAwDwrG/oyMjCH+ioqKCsQ72SzVVdMOwTv+lpaW/u/v7ybAl4j+qKioCg7CVcMO0h3RCv6cnJz+z8 +; /P/nh4eCzD/oeHh/7Y2NglOyzKO9gK2gSfw+Y+nVvC/oRCAB7LpcMe4h3yNf6kUgAd6f5cXFzECsqz +; iP7Hx8eHiAIKwgL+t7e3qIg6CsVV2SzVVdMOw7qIAxd/FwP+cHBwDsE/wg7SHdMsvIj+zc3N/qKiog +; osBv61tbUe/m5ubizMO9gK2gSfw+Y+nVvCCx7K/qxWAP7RaQD+vmAA/oxGAB7gHf3hGcQKy6KI/qOj +; owc9CsD+fX19NP6ampr+Xl5eCsZV2SzVVdMOxDf+p6enHybAkYj+lZWVKP6lpaU/wg7SHdMswCT+ra +; 2tuYj+i4uL/tPT0/6hoaEoLMw72ArbBB7m/nY7AJ1bwgseyKXD/sNiADXBnlr+nlAAHt8d/eEZxArN +; v4j+0NDQJKiIOP51dXUKyTvYLNVV0w7G/n9/f/7h4eEXf52Iioj+sbGxP8EO0h3TLMI7/sHBwf719f +; X+q6urLM472Arb/oRFBh7mPp1bwgsex/6jUgA1xf6xWQD+jEYAHt0d/eEZxArOvoj+7e3tl4j+dXV1 +; Cso72CzVVdMOx6yI/rq6uv79/f0mwImIP8EO0h3TLMEJPImI/nNzc/68vLwb/mRkZCzLO9gK3AQe5j +; 6dW8ILHscFNcYoNx7cHf3hGcQKzBn+k5OT/tXV1SKmiDD+jY2NGQrIO9gs1VXTDseviCot/uzs7Bcm +; P8EO0R3ULMD+j4+P/tnZ2Tz+WVlZwAr+lZWVA/5+fn4syjvYCtwEHuY+nVvCCx7I/plNAP7MZwA1xQ +; Ue3B394RnECsusiP6+vr6biP5oaGgKwLCI/sHBwZOILwrHO9gs1VXTDsb+goKCJRodtIj+zMzM/v7+ +; /j/ADtId0yypiDkEKyzDuIgtgYj+XV1dLMc72Ard/oRFBh7m/nY7AJ1bwgseyTf+ul0ANcSeWv6VSw +; Ae2x394RnECsoT/tPT0/6Tk5MZCsP+m5ub/tHR0f59fX3+W1tbxTvYLNZV0w7Ep4j+q6urtYj+a2tr +; DsEZ/qCgoD/ADtId0wj+1tbW/pqamgosxTf+pqam/srKyhQsxn/YCt0EHuY+nVvC/oRCAB7L/qdUAB +; yitsP+ul0AHtsd6aK2HfMZxArIpYj+rq6uLf5wcHAKxrmIKSAGCsM72CzVVdRVwzL+ysrK/p2dnTsO +; xFXADtEd0gr+q6ur/s/PzwEsyf5+fn4D/peXlyzFO9cK3RkEHuY+nVvC/oRCAB7MK/7DYgA1wx8e2h +; 3pp/H+qFQA/nk9AB3xGcQKx7uI/s3Nzf6jo6MVCsgV/q2trS0UCsI72CzVVdNVwiz+m5ub/srKyhAO +; xj8O0h3RBQc6FSzLrIj+tra2qYgNLMI72H/dGQQe5j6dW8L+hEIAHs2itv6xWQA1wptNNx7ZHen+n1 +; AA/rteAJ5aHh3wGcQKxRn+m5ubAwj+W1tbzBP+09PT/pSUlArBO9gs1VXTDsGyiCYYFQ7HPw7SHdD+ +; lpaW/tjY2BMszjv+jo6O/tTU1P6FhYUswTvYCtwZwAQe5j6dW8ILHs/+mU0A/shkADXB/qxWAB7ZHe +; gR/rxeADzB/ppNADYd7hnECsSwiP7Dw8OQiP5mZmYKzqyIORsNCjvYLNVV0w7A/omJiSX+hISEHQ7I +; VQ7RHc8g/sLCwpmIPizRtIj+xMTEjIgGO9gK3RnA/oRFBh7m/nY7AJ1bwgse0Dc0NcCeWv6QSQAe2B +; 3o/qBQAP68XgDBWsA3He4ZxArD/oqKiiH+jY2NGQrR/pOTkyH+g4ODCjvXLNVV0zP+srKyG/5nZ2cO +; 3h3O/oSEhP7Y2NgSLFUs0qSI/qCgoP7Ozs7+d3d3O9cK3RnABB7m/nY7AJ1bwv6EQgAe0v6nVAAcNT +; Qe2B3nNv69XwDAP8GeWh3vGcQKwTP+tbW1ExgK1LWILYOI/mFhYTvWLNVV0v55eXn+zc3N/paWlixA +; 3x3MKCO4iP5vb28dwSzUPRb+n5+fCjvVCtwZwQQe5j6dW8L+hEIAHtMr/sNiADX+mU0AHtcd5/6hUA +; AHwVbA/pFJAB3vGcQKwP57e3v+0NDQ/p2dnf5fX18K1qOI/qampin+dXV1O9Us1VXQGf6ioqIPFA7g +; HcwQ/tHR0f6kpKT+XV1dHcEs1qqI/rCwsLSICTvTCt0ZwQQe5j6dW8ILHtSitv6xWQCk1Dce1h3mor +; b+vl8AwQfAnWs2HdX+lEoA/stmACMd1RnEKP6jo6P+zs7OLoSI2Tsq/tHR0f6bm5sZO9Ms1FXQGP7D +; w8OEiCgO4R3L/p+fn/7V1dX+fHx8HcMs1TvB/oeHhzD+jY2NO9J/3BnCBJ/D5v52OwCdW8ILHtYBo6 +; Ue1h3m/qNSAP7AYQBSWsBa/qBQAB3V/q1XADXB/rldAD4d0xnDs4j+x8fHhoj+Y2NjmIjaO8AC/ra2 +; tqmI/mtrazvRLNVVz/6RkZH+zs7OFx0O4h3JsogejYgCHcQs1TvCK/6+vr6ZiAI70ArcGcIEn8PmPp +; 1bwgse8B3lo6X+wWEAwUZWwP56PQCbPdP+gkEA/sVjADXDnVv+jkcAHdIZwjQh/oaGhv5bW1vbVcP+ +; i4uLMP6JiYkKO88s1VXNIC4fIB3ADuIdyP6MjIz+2dnZGv5ZWVkdxSzVO8MZ/pmZmQM5O84K3RnCBB +; 7mPp1bwgse8B3lOP7BYQDCVv6hUQD+cDgA0yU1xhQAHdEZwKqI/ry8vMArCtw7xDoijoj+ZGRkO85V +; 1VXM/oCAgBb+jo6OLB3BDuEdx6iI/rm5uSI6HcYs1TvGuYj+y8vL/qenpyg7zArdGcIEHub+djsAnV +; vCCx7wHeSjpf7BYQDE/n9AAP5wOADRABL+0WkAxjE+HdIZ/oCAgP7T09P+lpaW/l5eXgrdVcUK/p6e +; nv7Q0ND+enp6O80s1VXKpoj+qampE/5tbW0dww7hHcYqMP6dnZ0KHccs1TvHJBz+ycnJ/nBwcDvLCt +; wZwwQe5j6dW8ILHvAd5P6eTwD+wWEAw/6tVwD+cDgA0f6aTQD+0WkAxwQd1P6srKy+iP5ycnIZwArd +; O8e8iP7Nzc3+oaGhNzvLLNRVyiP+yMjI/qCgoBkdxA7hHcQ7/qenpyUQHcks1TvJ/oGBgRL+lJSUO8 +; kK3RnBf/6urq7+nnBBHub+djsAnVvC/oRCAB7wHeOjpf7BYQDEMv5wOADPPi81xv6/YAD+fD4AHdT+ +; hFcp/qampv5iYmIZwQrdVcimiP6vr6+2iP5vb287ySzVVcg7/piYmP7MzMw9gIjFDuIdw7aIKYGI/m +; FhYR3KLNU7yq2I/ri4uKWIPjvHCt0ZwCP+1NTUGP5mYl7+dE4o/oRFBh7k/nY7AJ1bwgse8B3j/phM +; AP7BYQDD/rJZAP5wOADQAv7LZgA1xf6nVAAd1f6Tc1T+gICAGcMK3DvL/oWFhSH+kJCQCjvHLNVVx7 +; CI/r29vZOIMx3GVeEdw/6Tk5P+2NjY/oSEhP5YWFjLLNU7zAr+kpKSIf6Dg4M7xgrdGf6ZmZn+3t7e +; MRnB/mdWRf6CRwse4z6dW8ILHvAd4qOl/sFhAMT+iUUA/nA4ANH+gkEA/rldAP7RaQDCFP6IRAAd1K +; tc/paIev5nZ2cZxArcVcw+Fy7+ZmZmO8ZV1VXG/oeHh/7R0dEeHcgO4R3BEf7AwMAIDR3MLNU7zraI +; /sbGxoiI/l9fXzvECtw+LRf+aWlpGcOe1P50Tij+h0QA4v52OwCdW8ILHvAd4v6YTAD+wWEAw5s9/n +; A4ANM+/qdUABSjtf6tVwAd1f6Sb0wlKBnECt07zj/+09PT/oCAgDvFLNRVxST+r6+vCBwdyVXhHcA1 +; /tfX1/6VlZU7Hc0s1TvPpIj+o6OjOBA7wwrb/oaGhv7e3t7+lJSUKBnG/mdWRQQe4D6dW8ILHvAd4a +; OlJqXTw/6ORwD+cDgA1f6IRACm4h3V/pmEb/5vb28ZxgrdVc+3iP7Kysr+qKio/mBgYDvCLNVVxL+I +; /szMzP6ZmZk7HclV4hn+r6+vvIgFHc9/1TvR/nt7ezT+nJycCjvACtqjiP63t7cWFIuIyZ7U/npLHR +; 7fPp1bwv6EQgAe8B3hAv7BYQDDnWv+cDgA7f6DVSj+kYqDFRnHCtw70SgN/srKyv5ycnI7wSzVVcIK +; /qCgoC3+c3NzHcsO4SP+0NDQ/qampjcd0CzVO9IR/rOzs7CIOjsK2buI/tnZ2f6lpaUkGcz+b1E0BB +; 7dPp1bwv6EQgAe8B3go6UmpdPDAv5wOADs/pd7XxsZyArdVdP+fX19/tLS0v6Xl5cKO1XVVcG0iP7C +; wsKIiAYdzFXg/pqamv7W1tYIHdEs1X/UCv6Li4v+1dXV/oqKigrYFf7b29sI/ltbW8AZzf5kV0v+f0 +; gRHtw+nVvC/oRCAB7wHeAC/sFhAMOda/5wOADr/oZZK/6YjYP+ZWVlGckK3TvUqogMpIgcLNRVwCwH +; Fv6BgYEdzQ7fsIj+xcXFkYgRHdIs1TvWCf7BwcGUiP5jY2MK1LCI/s7OzomI/mZmZgrBGc6e1P50Ti +; ge2/52OwCdW8ILHvAd4Cal08P+jkcA/nA4AOr+l3hY/omJiRnLCtw71/6Ojo4w/oWFhSzTVREQPf5n +; Z2cdzg7e/oiIiP7Y2NgH/lhYWNQs1TvXKP6cnJw0GwrS/oyMjP7e3t7+jY2N/lxcXArCGdD+Z1ZF/o +; JHCx7Z/nY7AJ1bwf6USgD+p1QA/oxGAB7vHd8C/sFhAMM//nA4AOmrXP6ZiHb+a2trGcsK3VXYCf7F +; xcUnMyzR/n19fRb+kpKSLFXPVdFVDsiniB8ACQ7AHdMs1TvYCruI/s3Nzf6kpKQoCs4kF6uI/m1tbQ +; rEGdEu/npLHR7YPp1bwP6tVwD+0WkAwP6+YAD+kEkAHu4d3yal08P+k0oA/nA4AOj+hlwy/pKPizcZ +; zArcO9gswAr+oKCg/s7Ozv53d3cszjf+pqamvoj+b29viYjQDtI/Dsf+eHh4/tPT0/6fn58KDsEd0y +; zVO9gKwDP+rKysu4g2CswI/tvb2/6enp7+X19fnIjFGdP+b1E0/oRFBp/D1j6m4v7FYwA1wp5a/qdU +; AB7t/nA4AN4C/sFhAMOda/5wOADo/pqCaf50dHQZzQrdO9hVwi4WNxksywX+x8fHESibiNFV0lUOxR +; 3+o6OjEi4Owx3TLNU72ArC/oSEhDD+kZGRCskZ/qqqqv7X19c9CscZ1CH+f0gR/odEANX+nU8ANcYP +; /oxGAB7r/nA4AN4mpdPDOf5wOADm/oVXKv6Rh3z+Y2NjGc4K3TvYLMOniP6ysrKwiP5sbGwsyDv+lZ +; WVOCr+WFhY01XRP8AOxDr+ysrKhogkDsMd1CzVO9gKw66I/ru7u8D+ZmZmCsY2Ev6vr68CCscZ1p7U +; /nROKAQe0zf+sVkA/sxnADXFHP6eUAAe6v5wOADd/o5HAP7BYQDDnWv+ej0Amz3l/pR1Vf6Dg4MZzw +; rdO9gsxv6Hh4f+1NTUKTssxa6IKjL+ZGRklIjTDtJVwB4dDsH+j4+P/tnZ2f6GhoYOxR3Tf9Y72ArE +; GTAS/oCAgArEMP7e3t4PCsoZ1/5nVkX+gkcL/odEANT+mU0A/shkAKXDxgX+jEYAHuj+cDgA3Sal08 +; P+o1IA/nA4AOSrXP6Zi33+aGhoGdAK3VXYLMcN/sDAwJSIESzDMf7R0dEaLMBV0w7SP8D++fn5/rCw +; sCTA/r29vaKIDQ7GHdMs1TvYCse3iP7IyMiEiAYKwBH+xcXFnIj+ampqCssZ2C7+d00iHtQ3/rVbAP +; 7RaQDGmR/+lUsAHuf+cDgA3P6JRQD+wWEAxBSbPeP+k3BN/pOTk/5dXV0Z0ArdO9gsyv6ZmZk0/n19 +; ff5ZWVnAJP6tra2ziDoswFXUDtE/wf7///9V/tzc3MD+l5eXLEDHHdMs1TvYCsiliP6mpqb+zMzM/n +; R0dK+I/t3d3f6Xl5f+XV1dCsx/2v5sUzn+hEUGHtQfHKK2xZkfHuf+cDgA3P63XACl08M4/nA4AOP+ +; m4Zx/nBwcBnSCt1V2CzLI/7Ly8v+paWl/np6ehr+nZ2dCizBVdNV0j/B/vT09KuIwAH+np6eGQ7HHd +; Ms1TvYCsr+goKC/u/v75uI/nNzcwrOGdv+ZFdLJR7UpcP+ul0ANcSZHx7n/nA4ANv+iUUA/sFhAMQU +; mz3h/opcL/6lnpckGdIK3TvYLM0V/tXV1b6I/o2NjSzDVdMO0j/B/tjY2K+IFyYXGjoOxn/TLNU72A +; rJuYj+19fXGKuIqYj+ampqCs0Z3C7+dE4o/oRFBh7Torb+rFYAHDXCmR8e5/5wOADb/rJZAKjww/6j +; UgD+cDgA4f6YfGD+mJiY/tTU1P6Wlpb+XV1dGdEK3TvYLMw6/sDAwBgPIf6UlJQ7LMBV01XSVcL+f3 +; 9/Bv6xsbH++Pj4p4gXjYj+jo6OLA7EHdMs1TvZCsf+nZ2d/tzc3P5/f38Kfwcw/oiIiArMGd7+Z1ZF +; /n9IER7U/plNAP7DYgCn8cEoHuf+cDgA2v6EQgD+wWEAxP56PQCbPd/+h1os/pqQhf5mZmYZDf65ub +; mliCsZzwrdVdksy/6Li4v+0NDQMSzArIgqVf5nZ2eRiNQO0j/CDsEx/ufn57iIwJyI/ri4uP5jY2MO +; wx3TLNU72QrFroj+zMzMDC8KwrOI/sPDw4+I/mJiYgrKGd8u/nROKB7UN/6xWQA1wCge5/5wOADaCK +; jww/6oVAD+cDgA3/6YeVn+jIyMGcP+k5OT/tPT0w8ZzQreO9gsygL+s7OzqIgNLMP+kJCQ/tTU1P6D +; g4P+WFhY0w7RP8MOwrCI/sLCwv78/PwmVYKI/n5+fg7CHdMs1TvZCsT+iYmJ/t7e3iUZCsSjiP6fn5 +; /+z8/P/nl5eQrJGeH+Z1ZF/oJHCx7UAf7IZAAoHuf+cDgA2ajw/sFhAMQy/nA4AN2sS/6VgGv+bW1t +; GcW1iP7GxsaJiDMZywrdVdksyf57e3v+zs7O/pWVlTssxRj+x8fHhIj+YGBgmIjQDtI/ww7DHf6Wlp +; b+8fHxJsCXiP6mpqY3DsAd0yzVO9kKwhX+u7u7sIgnCsi9iBb+oaGh/l1dXQrHGeIu/npLHf6HRADU +; orb+rFYAHuf+cDgA2RH+wWEAwwj+cDgA3f6BVCb+joeA/l9fXxnHFf6kpKT+zc3NPRnJCt472CzIKB +; EP/nFxcSzHVTv+o6Oj/szMzP50dHQdzw7SVcMOxRQS/v7+/n8X/tHR0f5wcHAOHdMs1TvZCsG/iP7a +; 2tokBgrKqYg2DxgKxxnjAwQe/f5wOADYpdP+wWEAxP6JRQD+cDgA3P6chGv+d3d3Gcv+fHx8/tDQ0P +; 6dnZ3+XV1dGcZV3lXZLMe2iP7FxcWCiP5eXl4syFXBDCX+mpqaCh3MDtI/xA7GGf6pqakfJsCPiDAK +; HdIs1X/ZCsAv/tjY2BsKzv6Hh4f+1dXV/o+PjwrGGeT+ZFdL/n9IEf6HRAD8/nA4ANj+o1IA/sFhAM +; ObPf5wOADb/oZYK/6UiX/+ZGRkGc2qiP6zs7MTGBnFCt472FXGO/6Tk5MH/n5+fizJVcMV/rS0tKyI +; /mpqah3LDtI/xA7IF/7h4eG+iMBANS8d0SzWO9gYNCMRCtAr/r29vZyIIArEGeWe1P50TigEHvr+cD +; gA1xT+wWEAxP6ORwD+cDgA2v6Wdlf+hYWFGdH+jIyM/tTU1CkZwwreO9gsxq2IDBAgLMpVxTwhPCwd +; yA7SP8UOyayI/ry8vP76+vqliFWGiAAd0CzWO9f+kZGR/t7e3jwK0yj+mJiY/tPT0/5+fn4Kwxnn/m +; dWRf6CRwse+f5wOADXOP7BYQDDnWv+cDgA2atc/paEc/5paWkZ07CI/sHBwRAgGcAK3lXZLMX+goKC +; Jf6Ojo47LMpVxxz+wsLCj4gzHccO0lXFDsodB/7t7e0mwJqI/q6urv5hYWEdzizWO9UkE6KI/mpqag +; rWuYga/qmpqf5eXl4KwhnnLv53TSIe+P5wOADWptL+wmEAWsP+jkcA/nA4ANj+hFow/o+LiDcZ1Tf+ +; nJycJf58fHwK3zvYLMSniP6qqqq4iP5ubm4szFXILP6bm5sl/nl5eR3FDtI/xg7MtYj+zMzM/v39/S +; ZV/tjY2P52dnaCiM0s1jvU/oCAgP7c3Nz+mZmZKEDYJP6pqan+ysrK/nJycgrBf+n+bFM5/oRFBh72 +; /nA4ANb+rVcA/s1nAJs9RsGda/5wOADY/ph/ZyMZ2bqI/s3Nzf6lpaU3Ctw72VXDAQv+oKCg/l1dXS +; zMVcsBKf6ioqIZHcMO0lXGDs07M/709PQmwJKI/pycnP5cXFwdyyzWO9IK/q+vryH+dHR0O8EK2f6B +; gYEh/peXlwrAGer+ZFdL/npLHR71/nA4ANWjtf7RaQDAVpxMnUvAAv5wOADW/oRWKf6Wj4j+YmJiGd +; sz/qysrLuI/nFxcQraO9kswgr+mpqaKT0szlXMKP6tra24iP5ubm4dwVXSP8cOzR3APf7c3NwXJgj+ +; yMjI/mpqah3KLNZ/0RT+1dXV/qurqyQ7wgraDf64uLiniP5paWkKf+ou/nROKP6ERQafw/P+cDgA1f +; 6tVwA1wp5q/r1fAB3W/pJyU/5/f38Z3ArB/oSEhP7U1NT+kpKSGQrYO9kswSv+vr6+kogzLM5Vz/6B +; gYES/pCQkB3ADtI/xw7NHcGoiP60tLT++fn5pohVi4j+jIyMLB3ILNY70P6ZmZn+3d3d/oKCgjvECt +; sZNCH+hYWFGez+Z1ZF/oJHCx7y/nA4ANQ+NcOdW/6CQQAd1Ktc/pWHef5nZ2cZ3ArDroj+u7u7wA0K +; 1lXZVcE8/tHR0f6Hh4c7LM9V0K2I/ry8vJuIIA7RP8gOzR3DHv7p6em2iMAbEP5jY2MdyCzVO86siP +; 7JycmUiD47xQrdtYgPiogkGeue1P50Tij+h0QA8f5wOADU/q1XADXCLx3V/pJvTBb+XV1dGdwKxRn+ +; lZWV/tLS0hMK1DvZLMAz/rGxsQj+ampqLNBV0/6Tk5MD/n9/fw7QP8gOzR3EsYj+xcXF/vz8/KOIF4 +; CI/nx8fB3HLNU7zf6GhoY3/pKSkhk7xwrdBv6ioqL+zs7O/nd3dxns/mdWRf6FRgYe7/5wOADTo7U1 +; wv6USgAd1f6Yg24nGd0KyLeILQn+YGBgCtFV2iz+eXl5OP6ZmZkKQNBV1FUnLYCI/l5eXg7NP8kOzR +; 3FLP6ampr+8vLyJsCViP6jo6M3HcUs1n/KNz0H/m9vbzvJCt/+e3t7/tDQ0P6fn58oGeko/peXl/7t +; 6eX+rH5QHu7+cDgA0y81wP6/YAD+fD4AHdT+glUn/pCJghUZ3QrKFf6mpqYa/nZ2dgrQO9koMy3+dH +; R0LNJV01XBO/6lpaUL/nFxcQ7MP8kOzR3Hu4j+1dXVFyYX/s/PzzYdxCzWO8kf/tnZ2QL+X19fO8oK +; 4KqI/rKysrGI/m1tbRnnqoj+xsbG/v39/SYX/ruegP6ERgYe7P5wOADSPv7RaQDA/qBQAB3V/pZ6Xv +; 55eXkZ3grN/n5+fjT+mpqaGQrNVdknIoeIFZmI0lXUDsP+enp6NP6Xl5csQMk/yg7NHcgo/q2trf73 +; 9/cmwI6IAzsdwizWO8j+oaGh/tra2jn+WlpazArgGcD+ioqKMCkZ5f6FhYX+6urqtIgmCD0V/mRXS/ +; 5/SBEe6/5wOADS/qdUABL+gkEAHdT+hlkr/peMgiAZ3grPLxCriP5ra2sKyzvZ/pGRkRb+gYGBLNRV +; 01XFqoj+tra2p4j+Z2dnDsg/yg7NHcoE/uTk5CbAnYgILx3BLNY7xrCIFhD+ZGRkO84K4BnAsIg1Hy +; AZ4Qb+tLS0OSbAjYj+iIiIGcGe1P50Tige6v5wOADRo7X+p1QAHdX+l3dYLRnfCtL+jo6O/tTU1C0K +; yTvYrIj+t7e3Pf5nZ2cs1FXUVcf+i4uLIf6FhYUOxj/LDs4dyiD+v7+//vv7+6SIF4WIEx3ALNc7xP +; 6NjY3+3t7eGgo7zwrgGcGiiP6bm5v+0tLS/nx8fBnfu4j+3d3dFyYX/svLy/5oaGgZxP5nVkX+gkcL +; Huj+cDgA0aO1HdSrXP6Zh3Y6Gd9V1LKIIiP+Y2NjCsZV2P6AgIAWNDss1FXUDskcIouI/mBgYA7DP8 +; wOzh3LLP6SkpL+7+/vJsCZiP6rq6sVHSzWO8IV/r+/v6iI/mtrazvRCuEZwrqI/s3Nzf6mpqYGGdt/ +; /p6env739/eoiMAu/pubmygZxp7U/ndNIv6HRADn/nA4AOf+hVsx/pGOiv5eXl4Z31XWN/6fn5/+zs +; 7ODArFO9aliP6oqKi8iP5wcHAs1lXTDssd/p6engcfDsI/zA7OHc23iBb+/f39Jhcw/nR0dCzWO8H+ +; fX19/tvb2/6cnJwoO9N/4X/CAv6srKy8iBQZ2ToW/v39/SYX/tra2gEZyv5sUzn+hEUGHuX+cDgA5v +; 6agWkBGeAK2b2IBzMoCsJV1riILf6jo6MoLNZV1A7Nvog4NzsOP80Ozh3OGS/+9fX1JsCRiP6ampoZ +; LNR/wP6rq6v+1tbWHzvVCuEZxP6EhIQw/pOTkxnX/ouLi/7u7u4mwJ2I/q+vr/5eXl4ZzP5kV0v+fE +; oXHuT+cDgA5P6FVyr+mJGK/mNjYxngCtsCNraI/m5ubgrAO9UK/peXlzj+e3t7OyzWVdRVzyg2tIj+ +; a2tri4jNDs4d0Bv+3t7eJsAIABws07aI/tPT0/6tra3+YmJiO9cK4RnEHP66urqiiP5oaGgZ0zM5/v +; z8/CYXioj+g4ODGc8u/nROKAQe4v5wOADj/pN0VBMZ4Qre/oeHh/7U1NT+j4+PCjvUHP68vLyWiBE7 +; LNZV1Q7RMRIpDj/LDs4d0TP+uLi4/vr6+iYXiYj+iYmJOyzQ/paWljcxO9kK4hnEKP6UlJQSIhnR/n +; 19ff7j4+MXJkD+w8PDERnS/mdWRSv+h0QA4f5wOADhq1z+mIp8/mhoaBngCuGviP69vb2ciP5mZmaU +; iNL+hoaG/tHR0Qs7wCzXVdQO0lUg/r6+vpaIMz/KDs4d0iwa/uvr6ybAGyP+Y2NjLM0CAJqIDTvbCu +; EZxraILRj+YGBgGc0o/qenpxuliMCUiP6Tk5MZ1S7+dE4oHuD+cDgA4P6TcE3+kZGRKBngCuI7Cv6Y +; mJj+0dHR/n9/fzvPM/6urq6xiP5sbGw7wCzXVdQO0z/B/pWVlTT+fHx8P8kOzx3Ts4gt/v39/aKIVf +; 7c3Nz+enp6LMv+g4OD/t3d3TAKLMA72grif8YV/qWlpf7MzMz+dXV1Gcu0iP7U1NQXJhchNhnY/mdW +; RQQe3v5wOADf/pqFcAUZ4QriVcK5iP7Kysr+qamp/l5eXjvMvYgp/pycnP5cXFw7wVXWVdUO0j/DNv +; 7Jycn+pKSkGT/HDs8d1Dv+np6e/vPz8ybAMv6hoaH+Xl5eLMcZ/rOzs72IBSzCO9sK4hnH/n5+fv7S +; 0tL+m5ubGcn+lJSU/vPz8ybAm4j+pqam/l1dXRna/mFZUP56Sx0e3f5wOADd/oNVKP6Si4QVGeBV4z +; vEFf6oqKg8IzvJKP6fn58LHzvCLNZV1Q7SVcU7Db+I/m5ubj/GDs8d1Swf/tjY2P7+/v4mFyn+bW1t +; LMW6iP7X19cv/l9fXyzDO9sK4xnHqoj+tbW1rYg6GcWoiP7Dw8MIJheFiDkZ3Z7U/m9RNASfw9v+cD +; gA3P6Xe18qGeEK4zvHNf7S0tL+lpaWCjvGCf7BwcGLiP5hYWE7wizXVdRV01XHOTQSHT/EDs8d1SzA +; Bv6wsLD++Pj4JheOiP6QkJAKLML+np6e/tzc3P59fX0sxjvbCuN/yP6NjY3+1dXV/oqKihnD/oODg/ +; 7o6OgXJkD+u7u7JBng/mRXS/5/SBEe2v5wOADa/odZLP6Zj4QvGeEK41XJL/63t7emiP5paWk7xDj+ +; 0NDQMTvDLNdV1A7TP8kk/ri4uKKI/mVlZT/DDtAd1CzCMf7m5uYmwJyIGyAsr4j+zc3NPSAsxzvbCu +; QZyLKIEwH+Y2NjGaKIBf78/PwmwI+ICxnjntT+dE4oHtn+cDgA2f6YeVkaGeEK5DvM/pCQkP7T09P+ +; hYWFO8EgARsNO8Ms11XVDtI/zP6NjY3+1NTU/oGBgT/BAg7QHdQsww3+wcHBOSYXg4j+oqKi/t7e3j +; g7LMk72wrkGcgG/p+fn/7Q0NAW/tra2hcmF/7Pz88rGeb+Z1ZF/oJHCx7X/nA4ANesS/6Uf2r+bGxs +; GeEK5FXOtIj+xcXFjIj+YWFhvYgW/pWVlQo7wyzXVdUO0z/NtIgAhoj+Xl5e/oKCgv7V1dUO0B3VLM +; T+lpaW/vDw8CbAQBAVLMo72wrlGckQCCbAmIj+nZ2d/l1dXRnontT+eksdHtb+cDgA1v6GWCv+j4iB +; /l9fXxngCuU70Df+pqam/uHh4Y+IIzvEVddV1Q7TVc8d/qWlpf7k5OQ0DQ7QHdUsxf6SkpL++fn5pY +; gmF/7T09MjLMk73ArlGcYc/svLywgmFwwLGez+b1E0/oRFBh7U/nA4ANX+nIRrGwA2Gd5V5jvRtoj+ +; xsbGATT+n5+fCjvCLNhV1A7TP9C+iDT+qqqqJf6ampoO0R3ULMT+p6en/tjY2P58fHz+qamp/vb29i +; bAkIj+l5eXGSzIf9wK5RnE/oiIiP7s7OwmwAj+s7OzC/7W1tb+kJCQGez+ZFdL/n9IER7T/nA4ANP+ +; hVgq/pOIfgIZLf7U1NT+kZGRGdwK5TvRCv6VlZUH/n19fTsC/rCwsLKI/mxsbDvALNhV1VXTVc8GFi +; ODiCj+srKyDtEd1SzBCf7R0dEF/mJiYizA/n5+fiQmwEATDSzHO9wK5xnAFT3+/Pz8JsCLiP6FhYUZ +; wK+I/r29vRv+Z2dnGesu/nROKAQe0f5wOADS/pV2VjEZwjr+v7+/PQ0Z2ArmO9ENGxD+ZWVlO8L+iY +; mJIf6Li4s7LNdV1Q7TP84N/sjIyIKINzDCDtEd1SzA/pKSkv7e3t7+hoaGLMOriP67u7v++vr6pYhV +; h4j+h4eHOyzGO9wK576IJBcmQP7FxcX+ZWVlGcIo/peXlxI1Gez+Z1ZF/oJHC/6HRADQ/nA4ANCrXP +; 6Vg3L+aWlpGcQo/piYmBL+gYGBGdUK51XRMf7Q0NA4CjvEsIj+v7+/l4gCLNVV1VXTP84a/tbW1gT+ +; VlZWMMMO0h3UqIgTwA0sxTv+jo6O/u3t7bKIwJqIBTMsxTvdCuQZ/qKiogwmwJaI/paWlhnGt4j+ys +; rKgYgVGesu/ndNIh7P/nA4AM/+jGY//o2KhigZx7mIC/6oqKj+YWFhGdEK6DvQJP6srKy1iP5tbW07 +; xiwK/pqamiX+e3t7LNNV1Q7TVc2oiP66urp/ET8wxA7SHdM1/tzc3P6Xl5cKLMgYKQgmF/7Z2dn+eH +; h4gYjFf90K4hj+0tLSFyYX/tfX1/5xcXGLiMgkDf7Ly8syGez+bFM5/oRFBh7N/nA4AM7+l35mFBnK +; Ff6qqqoLARnOCuk70BALBig7xizCARr+paWlKCzQVdVV1D/M/np6ehL+kpKSHT8wxQ7SHdE7Bf7S0t +; L+cnJyHSzKCv6hoaH+9PT0JsCTiP6enp7+Xl5eLMQ73Arh/pCQkP7x8fEmwJyI/qmpqSgKGco1Ev6Y +; mJgZ7P5kV0v+eksdHsz+cDgAzP6DVij+lI2G/mFhYRnNNQP+mJiYKBnKCuk70Bn+nJycGj07xyzEFS +; u9iP5wcHAszlXWDtM/yx0NGhg/wTDFDtMdzwX+1dXV/qmpqf5fX18dwCzMPf7b29sXJhcL/mxsbCzD +; O91/3qeI/r+/vwiiiBeHiP5+fn4Kw3/JrIj+t7e3qYj+ampqGeue1C/+hEUGn8PK/nA4AMv+mX1hFx +; nQPv65ubmkiCsZxwrqO9CxiP6/v7+QiDM7xyzH/oKCghL+kpKSOyzLVdYO0z/LCSn+o6OjGT/BVcYO +; 0x3O/pqamv7c3Nz+f39/HcIszST+s7Oz/vn5+aaIVYyIB/5aWloswjvdCtz+gICA/ubm5hcmQP6+vr +; 4kCsZ/yf6QkJD+1dXVDxns/mdWRf6CRwseyf5wOADJth/+nJGHPhnTJf7U1NT+h4eHGcQK61XQ/oqK +; ijQeO8gsya2I/rm5uaKILyzJVdYO0z/L/pOTkzAbP8JVxw7THcytiP7JycmRiCAdxCzOHv7o6OgmwJ +; yI/re3txEswTveCtkZ/qysrDkmwJGI/o2NjQrJGcmziACMiDMZ657U/nROKP6HRADI/nA4AMj+kW5L +; BxnWtIj+xsbGiIgRGcAK6zvQEf6ysrKqiP5qamo7yCzLOxL+0tLSEyzHVdYO0z/KM/7BwcGTiCQ/wz +; DHDtQdyv6FhYX+3d3dJTsdxSzPsIj+xcXF/vz8/CZVgYj+fn5+LME73grXt4j+19fX/v7+/iYXNP5s +; bGwKzBnIo4j+oaGh/s7Ozj0Z7P5nVkX+hEUGHsb+cDgAx/6Xgm3+bW1tGdg3Agf+d3d3CutV0P57e3 +; v+zc3N/piYmBk7yCzONg+GiDcsxFXWVdRVyTX+1tbW/ouLiw5VwzDIPw7THcgZ/ri4uCkYHcgszzv+ +; mZmZ/vLy8ibAloj+paWlBpqIwDveCtX+l5eXECbAmYj+oKCgGQrPGci+iP7Q0NAV/l1dXRnrntT+ek +; sdHsX+cDgAxf6CVCf+j4iBBhnZCsAbFv6fn583Cuc70DcC/sfHx/5zc3M7ySzQKP6kpKT+y8vL/nR0 +; dCzCVdZV1D/IO/6wsLAAHD/EVck/DtQdxr6I/tnZ2SQoHcl/0QH+1dXVFyYX/tDQ0P5wcHAsO98K0q +; uIHggmF4KI/nd3dwrUGcapiBSziBgZ7P5vUTQEn8PD/nA4AMT+nYVs/nh4eISI2ArEqIj+s7Ozr4gY +; CuU70DYxhYj+X19fO8gs1Co0/pqamv5aWlosVdYO1D/IvIgl/pycnDs/xFXKPw7UHcX+oqKi/tnZ2f +; 55eXkdzH/RNwn+9/f3JsCPiP6UlJT+XFxcO98K0P6FhYX+6urqJsAI/ra2tgYK1xnG/omJif7V1dX+ +; jo6OGez+ZFdL/n9IER7C/nA4AML+hlgr/pWKgP5kZGQZ1grJPP7U1NT+jY2NCuI70f6SkpIWNTvJVd +; YCMq2I/mlpaY+I1Q7UP8j+nJyc/tLS0v51dXU/xVXLP8AO1B3CHBYy/mJiYh3OLNL+gYGB/uPj47yI +; wDn+v7+/PjvfCs03/rW1tTmjiMCNiP6GhoYK23/FsIj+v7+/mIggGeue1P50Tij+hEUGHsD+cDgAwf +; 6Wd1f+h4eHGdUKzQn+wcHBEP5mZmYK3zvQPv64uLhVPjvJLNn+i4uL/tPT0/6IiIj+WFhY0w7UP8cv +; /sbGxoiIBj/GMMs/wA7UHcE4/t7e3i0sHc8s0y/+vr6+/vv7+6SIVYaIADvfCsu7iDcXJhf+ycnJLw +; rfGcMo/pqamgP+fX19Gez+Z1ZF/oJHC/6HRAD+cDgAq1z+l4Z0/mpqahnTCtEZ/pubmzQI/ltbW9xV +; 0f6BgYEl/pCQkAo7ySzZVcAr/sHBwZGI/mFhYR3QDtQ/xy3+1tbW/oSEhP5WVlbHMMw/wQ7UBv6/v7 +; +miBwd0izTO/6SkpL+7u7usYjAmYj+ra2tMzveCsgZ/p6enj0mwJeI/pmZmQrkGcK5iCn+p6enBhnr +; ntT+hFowf/6QjIn+Xl5eGdAK17uI/svLy/6mpqYGCtg70aaI/qmpqbqINjvJVdodwgr+nJyc/s7Ozv +; 54eHgdzg7UP8Yo/re3t6iI/mVlZT/HMM0/wQ7T/nx8fP7b29v+mpqa/ltbWx3TLNU2B/79/f2iiBf+ +; 1tbWH4SI3wrFKxYIJhf+2dnZ/nJycgrpfzP+qqqqPP5xcXEZ6Qb+q6urGv5zc3MZzgrcFRi6iBQK1j +; vRMv7IyMj+oqKiNzvJVdodxQH+zc3NMwodyw7VVcU9Ev6VlZUsP8dVzj/BDtEdOv7V1dUyHdYs1Rn+ +; paWl/vX19SbAkoj+m5ubNzvef8P+i4uL/u/v7ybAnYgYGQrt/oKCgiH+lZWVGecB/svLyy/+YmJiGc +; sK4v6Dg4P+0tLSMBkK0jvRGf6ZmZkpGzvKVdodxxX+rKysAP5tbW0dyQ7VP8QO/qSkpAf+b29vP8hV +; zz/CDs8YEjoGDh3XLNYb/t3d3SbACA/+a2trO94KwCT+vLy8/vz8/CYXiYj+gYGBCvCuiP65ubmkiP +; 5oaGgKGeJ//peXlxL+gICAGcgK6A3+u7u7VQ0KzzvSsIgIlIj+ZGRkO8os2h3KMRL+jo6OHcdV1T/E +; OhovKD/IMNAAAAAAAAAAAQ== +; thumbnail_QOI end +; + +; +; thumbnail_QOI begin 480x240 48260 +; cW9pZgAAAeAAAADwBAD+Tk5OyH/Tf9D+fn5+/tHR0f6JiYn+UVFRJcA01n/Iu4j+0tLS/p6env5WVl +; aciMp/3H/J/oCAgP7S0tL+jIyM/lVVVSHTMMw//pubm/7V1dX+enp6/lVVVeM/zH/+i4uL/unp6bWI +; f5qI/rOzs/5eXl4/4LaI/svLy4GI/l9fXz/wMMG5iP7MzMz+pqam/lhYWDDit4gD/qqqqv5dXV0wxi +; HiEscYGi8/Es9Vzx3+ubm5/vz8/CYXh4gHxhbUJdIG/ri4uEA3NNUDyCH+lpaW/tXV1f5zc3MDyhLc +; IcyuiP68vLyYiP5hYWEhz3/OsYj+yMjIi4gkMN5/1LKILf79/f0mF/7b29v+dnZ2P97+kZGR/tjY2P +; 6Dg4M/9ij+pKSkKScw4P6VlZX+3Nzc/n9/f/5VVVXLIeESxqeIEf7KysoJEtADzB/+4uLiF38I/r29 +; vTsHxRbTJdUD/pCQkP7Pz8/+eXl5/lFRUdIDya6I/sXFxY2I/llZWQPJEtsh0f6SkpL+0dHR/nt7ey +; HLMND+iIiIHf6Kioo/MNo/16HyP8CliP6bm5v+8fHxJsCViP6hoaEsP9qsiP6/v78ILz/5/n19ff7V +; 1dX+k5OTDj9V2xX+xsbGloj+Y2NjMM4h4VXG/nx8fP7U1NQD/lRUVBLPVckSEf76+vqliMAUCwPAB8 +; R/0n/Wf8G5iACBiD80zgPJEiL+19fX/oSEhBIDyBLbIdQJ/sjIyP6lpaU7IcYw0aaIPauIDTDXP9qh +; 8v6rYxuh4f5lWEs/wb+IHRcmQP7MzMz+a2trP9j+f39/Dv6VlZX+WFhYP/uriP61tbWsiDM/wlXW/o +; GBgf7d3d3+kJCQ/ldXVzDSIeBVxRUQqogkEtADxg3+09PTF38X/tHR0f5lZWUDwQfCFtN/1X/EP/6h +; oaH+x8fHCTTMA8ksMgD+YWFhA8h/2iHYHS8tJyHCMNMM/tTU1P6amposMNM/3v58XDv+yWgFofLAO/ +; 6EXTY/wQb+ra2t/vn5+SbAjoj+jY2NDj/UpIj+rq6uKf5vb28//cAd/oyMjCEEDj/EMNAdMruIOjDW +; VeBVxDA4/tPT0/5/f3/+U1NT0APDEv6Pj4/+8/PzrIjADP6fn58SA8KciMF/0iXWNMf+d3d3/s/Pz/ +; 6SkpL+UlJSNMgDyv5zc3P+1dXV/pWVlf5VVVUDxxLaIdz+eXl5/tHR0f6UlJQ/MNM/Ef7S0tL+dHR0 +; MNE/4f6jYiA1xCg5P8Ex/uTk5BcmG/66urozP9K7iBYv/lxcXD/9wyseiIgKP8cwyxT+19fX/qKiog +; ow2VXgEsS2iAA2HRLQVcAZ/sHBwQgmF4KIMgPEB3/TJdV/yqyII6mI/mJiYjTFA8ow/p6env7T09P+ +; bW1tA8d/2iHfq4j+t7e3KiAw0Tr+zMzM/qurq/5eXl4wzT/k/m1aRv7CZws1xv66ZRD+fFw7P8Eg/r +; +/vwiiiMCCiP59fX0/0P6ampr+1tbWCP5WVlb9xgr+nJyc/tDQ0DI/yjDG/p2dnf7a2tr+eXl5MN4h +; 3lXEHSj+zs7OFBLQCP7n5+cXJkD+tra2/ldXVwPFFtN/1DTOPP7R0dH+gICA/lFRUcIDyyAp/qmpqR +; 0DxhLaId4ww/6Li4v+1NTU/oKCgjDP/pCQkP7Y2Nj+g4ODMMs/5/6TXyv+0WkAyZ8e/oxeMDmfHsAs +; /pWVlf7u7u4mwJeI/qioqAo/zLCI/sTExJKIAj/9yf52dnb+09PT/pqamv5YWFg/y1XBICkQ/mBgYD +; DhVd5VxB/+0dHROyESzDD+ra2t/vz8/CbAjYgTA8cW0X/Vf9Ag/sDAwIyILDQDyhL+ioqK/tjY2P58 +; fHwDxhLaId0wx7OI/sPDwwk3MMsG/r+/v52IIDDJP+j+ZVhL/rplEP7RaQDM/rJkFSk/wbmI/tHR0R +; d/F/7S0tL+cHBwP8r+h4eHHTgOP/3LBv6urq4t/mdnZz/M/oiIiP7d3d0LPzDkId5VwzcnMSASyruI +; /tra2hcmF/7KysoVkojIFtAl1H/TIf6bm5v+y8vLFAPJqYgqo4j+XV1dA8US2SHeMMoO/p6env7Nzc +; 0yMMn+f39//tbW1v6UlJT+WFhYMMZ/6/6EXTY1z58e/oxeMP5WVlbBKP6mpqb+9vb2qYjAkYj+lpaW +; DlXGKBAA/mxsbA7OP/wO/oWFhf7V1dUeDj/IpogqrIgNP8FV5VXdEsQA/tTU1P6Hh4cSyP6Xl5cfJs +; CYiP6Wlpb+VFRUEgPIFs4l1TTUA8AU/szMzP6bm5shA8b+eXl5/tfX1/6NjY0hA8US2CHdMM8BB/6c +; nJwdMMUdGP7Nzc0nMMV/7KHy/qNiIDXS/rJkFf5eWFGfL8H+fn5+/uDg4L6IJp2IBP5mZmYOxP54eH +; j+09PT/p+fn/5bW1sO3z/uL/7AwMCYiP5eXl4/xv54eHj+2dnZ/pubm/5aWlo/xTDkId1VwyD+vr6+ +; EP5bW1sSxDMaCCZVLP5tbW0SwwPGFs0l1DTVA8IK/qysrDUvA8M//qampgcNA8US2CHdMNKniP6wsL +; AE/mlpaTDDu4j+0NDQ/qSkpBkwwj/v/m1aRv7CZwv+0WkA1J8e/n1dPA7BAv64uLj++/v7JsCHiP6E +; hIQOwR3+o6Oj/tPT0/53d3cO6z/lpIj+lZWV/tLS0gwOP8IO/qenp/7V1dUBP8pV41XdVcI//peXly +; UuEsL+g4OD/uzs7CbAOf6srKwwEsVVxRbLf9U01APGE/7S0tIeElXAK/7R0dH+oaGhPwPEEtgh3DDX +; Mf7T09P+ioqKPzA//piYmP7X19f+fHx8MME/8f6bYSb+0WkA2P6cYSb+X1hSnx7Aooj+jo6O/uvr6x +; cmmYj+sLCwN62I/snJyQX+YWFhDvM/4BT+zs7O/qKioiw/wDo0/q2trTc/zTDjIdxVw72I/s3NzQIw +; o4j+tra2/vz8/CYXioj+fX19EsgDxBbKJdR/1APJsIj+u7u7mYgk/pKSkv7W1tb+dnZ2/lJSUsQS2C +; HcMNog/r6+vpWIBf7Hx8ePiDMwf/Io/rplEDXa/rpmEP5uWkcOwToa/v7+/iYXgYhV/oaGhg77P9uo +; iD4p/m1tbf6Tk5P+3t7eIj/SMOIh3FXCGf6oqKgHtIgXJkAEGRLLA8IWySXUNNQDyyH+nZ2d/u/v7w +; cZA8MS2CHbMN4//r29vf729vb+q6urDj/xDsD+jF8xNd2fHgv+X1hSDsAZ/p6env7z8/MmwJWINywO +; /cI/1/6Hh4f+9fX1h4j+Z2dnP9Yw4SHbEsEh/qGhof77+/smwAH+j4+PEs4DwZ2IxyXUNNQDzBL+gI +; CA/tfX1/6Wlpb+yMjI/qSkpP5WVlYDwRLXIdsw3wr+tbW1sIj+gYGB/srKygIKP+t/wwj+q2Mb/tFp +; AOD+smQW/mZZTA7A/n19ff7e3t4zFyZAPCsO/cY/0f5/f3/+3Nzc/pubmxukiP5hYWE/1zDhIdoSPv +; 7R0dH+/v7+fxeKiP7Gxsb+X19fEs8DwBbGJdR/1APMDv6vr68t/mNjYwMd/qWlpQ/+a2trEtch2zDg +; /nZ2dv7T09Mo/ltbWz+kiP6qqqq9iBg/5Q7H/nVbQf7JaAU14jv+h2A5/qurqwcUJP6xsbH++fn5Js +; CMiP6KiooO/ck/yx3+sLCw/tHR0Sc/Hf6Pj4/+1NTU/oCAgA4/1zDhVdj+jIyM/vLy8ibAm4j+o6Oj +; Dv6QkJAS/n19ff5TU1PPA52IxX/Tf9QDzb6I/tPT0/6ZmZkwA8L+fHx8/tDQ0P6QkJAhVdMh2zDfP8 +; D+oaGh/tPT0y4/wwj+0tLS/pGRkQ4/3w7K/pxhJv7RaQDm/saFQ/5mYFkOwB3+h4eHLxcmG/64uLj+ +; YWFhDv3LP8YF/tXV1f6mpqb+XV1dP8K2iP7KysqCiP5aWlo/2VXfIdWoiP6+vr7+/f39JlWFiB/+U1 +; NTwbeILQkOEs4Ww3/Uf9R/zDD+m5ub/tTU1AUDwxLArYgQpYj+YmJiEtB/2jDfP8EcGoOIFT/FM/65 +; ubnA/mVlZT/ZDs3+blpH/sJnC/7RaQDo/rpmEP59XTwOwT7+w8PDCCbAgIj+enp6Dv3OP8H+mpqa/t +; zc3P59fX0/xaaI/p+fnwf+cXFxP9ow31XS/nt7e/7l5eUXJkAM/lpaWiESwqaI/qCgoP7Nzc3+b29v +; Es0WwiXUNNMDzbGIPISIHQPCEsT+jY2N/tHR0f5/f38SziHZMN8/wjj+2NjYHj/J/o+Pj/7T09P+gI +; CAP9QO0P6MXzH+0WkA658eC/5fWFIOwKOI/piYmP7w8PCviMCWiC/+W1tbDv3NrogaPTM/yP55eXn+ +; 1NTU/peXl/5YWFg/2jDfVc4//qqqqv77+/smwI+I/oaGhiHCVcP+eHh4/tLS0v6WlpYhEsuciMEl03 +; /Uf8wSHv7Y2Nj+gICAEgPBEsccIoiILBLKIdkw3n/DqIj+vLy8o4j+ZmZmP8u1iP7GxsaDiDc/zn/S +; CP6yZBb+0WkA7hn+ZllMDsG7iP7U1NQXfxf+0NDQNg79y/6Ghob+3t7e/o2Njf5YWFgOwT/Hqoj+sb +; GxACA/2zDeVcy5iP7X19cXJhcH/mRkZCHFEsKsiBSxiBESyhZ/1DTTA807/ri4uKmIBgPCEsmkiP6f +; n5/+ysrK/nFxcRLHIdkw3j/E/n19ff7W1tb+l5eXLJ2IzR3+oqKi/szMzCM/yn/U/oVeNjXxnx7+jF +; 8xDsGoiCv+9/f3JsCQiP6Tk5MdDv3HpYj+ubm5sYg6DsY/xR3+iIiI/tXV1f6FhYUOP9tV3iHIMP6V +; lZUQJsCZiP6ampr+VVVVIcdVwiEt/tTU1DESySXUNNMDzR/+19fX/pCQkP5UVFQDwRLN/nV1dQf+mJ +; iYMBLDIdkw3T/Ffyv+z8/P/nFxcT/R/nd3dxb+mZmZ/llZWT/EDtf+o2IhNfT+smQW/l9YUg7B/oGB +; gf7i4uIXJir+v7+/IA79xR/+2dnZ/p+fnwoOyj/Esoj+xMTEkYgoP9ww3SHGJP7Hx8f+/f39oogX/t +; 3d3RQhyhLDs4j+wcHBkoj+WlpaEsediNI00wPNP/6ioqL+0NDQ/mpqagPBEtCqiP6vr6+wiC8SwCHZ +; MN0/xriIB/6oqKj+Xl5eP9M3Mib+aGhoP8AO2P5uWkf+wmcLNfafHv59XTwOwSA5/vz8/KOIwIWI/o +; KCgg79w/6kpKT+2NjYLg7PP8KkiP6YmJj+0dHRHw4/3DDdVcP+gYGB/uvr6ybACP6xsbH+WFhYIc0S +; wg7+mpqaFv51dXUSxp2I0X/TA80NFhEOA8ES0w/+09PTDyHYMNw/yP6VlZX+19fX/oCAgD/Wfy3+1N +; TU/oeHhw7Y/pRgK/7RaQD6/pxiJv5gWVIdwKKI/pKSkv7s7OwXJpiI/q2trSgdzw7ssogWFBUO0z/B +; voj+0NDQBv5ZWVk/3VXcVcAd/rS0tDkmwIuI/oCAgCHQVcMjFgYhEsSdiM8003/NIf6Ojo7+19fX/n +; l5eQPBEtYg/r6+vpSIKCHUMNw/yKyIMZSIAj/Vf8IN/sHBwY+IJA7UMv66ZhD+0WkA/BH+b1tHHcG1 +; iP7Ozs7+/v7+Jhf+1tbW/nR0dB3YDuH+j4+P/t7e3h4O2FWpiP6qqqr+ysrK/mpqaj/eMNu9iDcXJg +; j+xcXFBiHSEsOpiP6qqqq8iA0SwyXONNMDzSj+wMDAmYgZA8ES1SHBP/6YmJgHHyHRMNw/yRP+19fX +; /pCQkP5YWFg/03/GHf6bm5v+z8/P/nh4eA7S/oxfMf7RaQD9wZ8e/o1fMR3Bpoj+oqKi/vT09CbAk4 +; gKLB3dVdgG/sDAwCL+Z2dnDtz+g4OD/tXV1f6MjIwdP95V1z/+nZ2d/vj4+CbAloj+k5OTMCHUVcQT +; /tTU1P6Li4v+VFRUEsGdiM000gPO/n19ff7X19f+ioqKEgPAEtYhxLyI/svLy/6hoaH+V1dXIc0w2z +; /KpIj+s7Ozt4j+bGxsP9IOy7yI/szMzCT+W1tbDs6h8v6rYxv+0WkA/cT+s2UW/mdaTR3B/nt7e/7e +; 3t4XJjkPHB3iDtH+fX19/tvb2/6YmJg7Dt6tiP68vLzAFT/fMNQvFv79/f0mF/7W1tYJiIjXEsQC/r +; q6usD+XV1dEsAlzDTSA80O/qurqwsgA8ES1SHHp4j+qampMf5qamohyjDbf8u+iP7S0tL+oKCgGT/Q +; Ds8Z/q2trQD+bW1tDsz+dVtB/sloBTX9xjv+fl08/lhYWMEC/rS0tP76+vomwIqI/omJiR3mDsod/q +; 2trf7T09P+cnJyDuGjiP6SkpIS/n19fQ4/3jDSPP7w8PAmwJyI/qioqD8wwCHYVcM/A/7S0tL+enp6 +; EiXKNNN/zSf+0tLS/p2dnT+ciMAS1SHL/n9/f/7S0tI4MCHHMNo/yw4o/tXV1f55eXk/0A7T/oGBgf +; 7T09P+j4+PHQ7J/pxhJjX9yv6cYib+YFlSHcAs/ouLi/7o6OgXf5qI/ra2tv5hYWEd6Q7EuIj+1NTU +; /qqqqv5eXl4O5Ccp/qioqCw/31XOGTn+/Pz8JheHiBsww1XYVcQJCw0lyTTSf80h/peXl/7V1dUjA8 +; ES1SHNroj+uLi4wCQhxH/aP8yyiP7JyckUJD/Of9cR/ry8vJuI/mVlZQ7G/m5aR/66ZhA1/cwR/m9b +; Rx3Bsoj+x8fH/v39/SbA/tzc3P55eXkd7A7+mJiYGf6AgIAO56aI/qOjo/7Nzc02P+Awyxv+5OTkFy +; YI/r29vf5cXFwwxiHXEsQ7AiXINNIDzSQPFCwDwBLVIdAw/pGRkf7Q0ND+fX19IcEw2j/N/oqKiv7Y +; 2Nj+iYmJDj/MDtz+k5OT/tLS0v5/f38OxP6MXzH+0WkA/c+fHv6NXzEQnx/AGf6bm5v+8fHxrojAlo +; gC/ltbWx3prIj+yMjIk4gRk4jq/nx8fP7U1NT+lZWVHT/fVcg//qenp/77+/smwBT+ioqKMMlV11XF +; uoiDiMZ/0n/MEv6FhYX+19fX/oODgxIDwBLVIdMJD4KI/lpaWjDZP80o/rq6uqmI/mlpaT/Mf98nLT +; 43DsCh8v6yZBY13pou/qZTAP7MZwA17f6zZRYHHcG9iP7X19cXJkA4GB3n/oSEhP7d3d3+kZGRLB3E +; VeariP61tbWtiBEOP98wxQn+1NTUFyYX/tDQ0P5nZ2cwy1XXEsQ4/peXlxKdiMJ/0gPNLAGwiCQDwR +; LUIdYsAv7Jycn+cHBwMNY/zv57e3v+1dXV/pqamv5aWlo/yn/jO/6mpqb+ysrK/nFxcf6FXjY13/6t +; VwD+cDgAp/H+lUsA/rpdADXtnx7+hV43/lhYWMEk/q2trf74+PgmwI+I/pCQkCwd46SIHzj+bW1tHc +; kO5Cz+jIyMMP6Dg4MdDj/eVcI//pKSkv709PQmwJqI/p+fnz8wziHWEsOniP6wsLCtiDOOiMB/0n/N +; /nR0dP7V1dX+lJSU/lVVVQPAEtR/1zDB/nl5ef7Q0ND+lpaW/lZWVjDSP84O/qampv7R0dEBP8oO5/ +; 56enr+2JdVNd/+lEoA/nA4AMAqpcOitv6xWQAcNe3+q2Qb/mBZUp8fwf6EhIT+5OTkFyaciAj+ZGRk +; HeG9iP7Z2dn+oqKi/l1dXR3NDuOziA+LiBkOwD/eMKuI/sPDw/79/f0mF4KIATDRIdYSwp2If/6Dg4 +; P+0dHRMf5RUVHSf8wwBv7S0tIJhojBEtQh1jDEq4j+s7Ozqoj+ZmZmMM8/zxg4/qurq/5fX18/yA7o +; Hf5vW0f+wmcLNd7+v2AA/nw+AJouwafxHsH+mU0A/shkADXt/rpmEP5+XTz+WFhYwT7+v7+//vz8/K +; OIwIOI/n9/fx3f/qCgoP7a2tobHdIO4Rn+m5ub/tHR0f50dHQOwT/c/oCAgP7p6ekXf0D+tra2/lpa +; WjDTIdZVwSXBs4j+vr6+kIj+W1tblojOA80vOA0OA8AS1CHWMMj+ioqK/tLS0jEwzD/PDv6SkpL+2N +; jYIj/IDuZ/wv6UYCz+0WkA3/6aTQD+cDgAwyoewjf+sVkANe2fHv6UYCz+YFlSnx/ACv6VlZX+7u7u +; JsCXiP6rq6soHduwiP7Ozs6GiP5iYmId1g7gv4j+0tLS/pycnP5ZWVkOwT/YLP6wsLA5JsCNiCL+VV +; VV1iHWEsAlwDTAf/6WlpYH/nR0dDTMA8wSGv7Y2Nj+e3t7A8ES1CHWMMoN/sHBwQUoMMh/0KuIBJqI +; ID/HDuV/w/5vW0cRNd7+v2AA/oJBAP5wOADDAP64XQD+kEkAHsP+nlAANe7+s2UWOv5YWFjBuIj+0d +; HR/v7+/iYX/tTU1P5zc3Md2Sn+3t7eCywd2g7eqYj+ra2tPP5paWkOwT/Wu4j+3NzcFyYXPAIw2FXW +; VSV/w72ILf6enp4wNMkDzKqI/r29vcAoA8ES1CHVMM0d/pycnDj+dXV1MMU/0f6AgID+19fX/pOTkx +; 0/xn/kHcX+jV8x/tFpAN/+p1QA/nA4AMT+oFAANcCeWv6jUgAewzXvO/6NXzH+WFhYwQb+pqam/vX1 +; 9SbAkoj+mJiYLFXVpoj+vr6+D/5paWkd31XcHf6FhYX+1dXV/omJiR0OwT/T/pqamv739/cmwJeI/p +; eXlz/BVdkh1TTFpoj+qampuogNNMcDzBv+19fXKSEDwH/UIdYw0DL+zc3N/p6enh0wwT/RLP6vr6+9 +; iCc/xX/kHcah8f6kYyE13p1b/o5HAP5wOADD/oJBAP6/YAA1w/66XQD+jEYAHsH+yGQANfD+s2UW/m +; hbTv5YWFjALP5+fn7+4ODgvogmOSINHdP+fHx8/tra2v6bm5v+W1tbHeIO3K+I/r+/v5qIBg7CP88g +; OP79/f2iiBf+2dnZ/m9vbz/EMNhV1J2Ix/59fX3+0NDQ/o2NjQM0wwPMDv6oqKj+zc3NPgPBf9Qh1T +; DTKP6srKwiKzA/0byI/tHR0f6jo6MoP8QO4x3IOv7CZws13v65XQD+djsAnVvD/ppNADXGAf6eUAAe +; wP66XQA18Z8f/n5ePf5ZWVnBq4j+t7e3/vv7+ybAiYj+hoaG/lhYWNAs/qqqqv7W1tb+dXV1HecO2q +; OI/pWVlf7T09MbHQ7BVc3+h4eH/u7u7ibAnYgJHT/GVdgh052IyK+IDJyIBjTBA8y6iP7R0dH+oKCg +; PwPBf9Mh1TDX/oODg/7T09MaP88O/pqamv7W1tb+fHx8/lZWVsQO4h3K/pxiJjXfBP5wOADDPv6zWg +; A1yJ5a/qxWADf+tVsANfP+nGIn/mBaUyzAf/6Ojo7+6urqFyaZiP6ysrL+YGBgLMFVyraI/tLS0hgV +; HeoO2rqI/s7Ozv6kpKQsDsI/yaaIDP78/PwmF4mI/n5+fj/JMNgh0p2IyX/+jo6O/tHR0f57e3v+UV +; FRA8sh/pOTk/7V1dX+dXV1A8F/1CHVf9c/wCD+u7u7m4gkP8wvLY2IMz/DDuIdyjr+umYQNd7+v2AA +; /nw+AJouw/6IRAD+y2YANcv+ul0A/qdUADX0/rpmEf5vXEgswQn+ysrK/v7+/iYX/tra2v53d3csxl +; XD/pWVlf7d3d3+hISEHe5V2aeI/qampin+bW1tDsM/xv54eHj+4uLiFyZA/sDAwDc/zFXXVdGdiMu5 +; iP7FxcUvLAPJFf7ExMSQiP5aWloDwX/TIdUw1z/DDv6VlZUl/nt7ez/KD/7Y2Nj+jIyMDj/CDuF/zP +; 6NXzH+0WkA3/6nVAD+cDgAxA41zv6ZTQA19QL+jWAyHCzAN/6fn5/+8vLyJsCViP6goKAKLMeqiACa +; iP5nZ2eRiPFV2f5/f3/+1dXV/pKSkh0Owj/DDv6jo6P++vr6pYjAlIj+j4+PP89V1yHQNMw//qKioi +; 0YhYjGEv6CgoL+19fXDxIDwRLTIdUw1z/GNi0vOz/GGf63t7eviCs/wn/gHc2h8f6rZBs13hT+iEQA +; /nA4AMP+gkEA/r9gADXP/p5QADX3/rNlFv5oW04swT3+2traFyYI/svLywksxRMo/pSUlAoswVXxDt +; isiP64uLioiP5iYmIOwz/AtYj+0tLSFyYX/tPT0ys/0TDXVc80yn/C/nZ2dv7Ozs7+lJSU/lRUVAPD +; Hf6wsLC3iDMDwn/TIdQw1z/JGS8eNj/E/nh4eP7U1NT+nZ2dCj/BDuAdzv52XEL+yWgFofLe/q1XAP +; 5wOADE/ppNADXR/p5QADX4nx/+hl84LMEzBf75+fkmwI2I/o2NjSzCGf6zs7Ml/nBwcCzIVe4O1yz+ +; jo6OIf6BgYEdDsL+kJCQ/vPz8ybAG/6jo6MOP9Mw11XONMkDxKuI/rOzs6iI/mNjYwPBv4j+09PT/p +; eXlzADwX/TIdUw1j/N/n19ff7R0dH+k5OT/ldXVz/ADv6ioqL+09PT/nZ2dj/BDt8d0P6cYiY13/6U +; SgD+cDgAw6O1/rldADXS/qdUAP7IZAA1+f6kYyEcLMB//oiIiP7m5uYXJhv+urq6/mRkZCy6iP7X19 +; f+pqam/l9fXyzMVe1V17WI/snJyYWICg4G/sHBwf79/f0mF4SI/nh4eD/WMNchzZ2IyAPGf/6Hh4f+ +; 0tLSEzD+nJyc/tPT0zaDiMJ/0yHUMNc/zwL+tra2p4j+ZmZmOv7MzMwn/mBgYD/ADt8d0P5vW0f+wm +; cLNd7+v2AA/nw+AJouwyM11P6xWQCk1DX6/rpmEf5+Xj0swa+I/sPDw/79/f0mwIGI/qysrP7c3Nz+ +; fX19LNJV6w7WKP6enp7+z8/P/peXl/7n5+cXJkD+ubm5/lxcXD/YVdchzJ2IxwPJL/7Kysq1iP6xsb +; EdA8J/0yHUMNY/0n/+j4+P/t7e3qWILT/ADt8d0f6NXzE13/6gUAD+cDgAxP6tVwA11f6+YAD+rFYA +; NfufH/6NYDIcLMAZ/piYmP7v7+8mwDkUKCzVVepV1Sz+xMTECCbAj4geDsA/2jDWIcs0xgPJEi3+2N +; jYFLyI/nJycgPBEtIh1TDWP9OpiP6+vr4io4iHiCgO3X/SEP66ZhA13v6/YAAA/nA4AMMA/sVjADXW +; Af6jUgA1/f6zZRb+aFtOLMH+oaGh/vj4+KaIJhf+0dHR/nFxcSzXVekO0rmI/tnZ2RcmF5yI/nd3dw +; 7DP9kw1lXKnYjFA8kKDKeI/l5eXgMj/svLy/6cnJw/nYjSIdQw1j/U/n5+fv7W1tb+lpaW/llZWTv+ +; oKCgKf50dHQO2h3T/oVeN/7RaQDf/qdUAP5wOADEJTXZODX9wAL+jWAyNyoLMv6pqan+9/f3JsCRiP +; 6VlZU7LNhV6FXOHf6YmJj+9vb2JsAu/pubmy3+1dXV/oaGhv5YWFgOwlXZVdYhyTTEA8n+d3d3/tfX +; 1/6Pj48hA8E7CQT+Z2dnEtAh1DDVP9Qs/qurq/7Pz8/+cHBwP3/BLhYZHQ7WHdT+pGMhNd6dW/6ORw +; D+cDgAwwD+v2AANdr+mU0ANf3C/taIOf6jnZYoLMD+gYGB/uLi4ryIJjn+wcHBPizaVeYOzK2ICwgm +; F/7c3NwjDsA+IpOI/l5eXg7CP9kw1SHJNMMDyD/+o6Oj/tDQ0P5paWkDw3/A/oGBgf7R0dH+i4uLEs +; 4h0zDWP9QFFv6np6c3DsSqiP6wsLCxiCsO0x3U/m9bR/7CZws13v65XQD+djsAnVvD/ppNADXc/p5Q +; AP7IZAA1/cKfH/5+Xj0swa2IKjkmwIeI/oSEhCzbVeYOyTH+7OzsJsA5/rGxsTsOwqSI/peXl/7S0t +; L+d3d3HVXCP9hV1SHInYjBf8kcJf6jo6MOA8MSwrCIKpiIBhLKIdQw1T/UDv6Xl5f+19fX/n9/f/5X +; V1fIHv7T09P+iYmJDtAd1f6UYCw13wT+cDgAwz7+rVcANd3+sVkAp+E1/cT+nGIn/mBaUyzACv6RkZ +; EJF389/rCwsP5gYGAs3FXlDsWliP62trY5JsCKiP6BgYEOxryIJf6hoaEsDsI/2DDVIcediMADyCH+ +; kJCQ/tbW1i4DxH/EIf6Tk5P+0NDQ/np6ev5TU1PIIdQw1X/UEf7FxcWSiBEOyrCI/r6+vpaI/mFhYQ +; 7MHdU6/rpmEDXe/r9gAP6CQQD+cDgAwwD+xWMANd7+tVsAorY1/cX+umYR/m9cSCzBtYj+zc3N/v7+ +; /iYX/tjY2P52dnYs3VXkDsMf/uDg4BcmCP7ExMQkDsioiP6pqan+y8vL/mtraw7CP9gw1SHGnYgDyD +; f+wcHBl4gKA8R/x7qI/sfHx/6kpKQdEsUh0zDWP9T+g4OD/tfX1/6Pj48dVcws/pmZmf7Pz8/+eXl5 +; Dskd1v6NXzE13/6nVAD+cDgAxA414JkfDjX9xp8f/o1gMv5ZWVnBBv6hoaH+9PT0JsCUiP6dnZ0KLN +; 1V5FUd/qCgoP75+fkmwJaI/pSUlB0Oy/6BgYH+1dXVBx0OwT/YVdVVxQPI/n5+fv7X19ctEgPDEsod +; /qamph7+bGxsEsMh0zDVP9QK/rS0tLWICYuI0CP+y8vLEQoOxR3WofH+q2QbNd6dW/6IRAD+cDgAww +; D+v2AANeGeWv6jUgA1/cj+s2UW/mhbTizB/nt7e/7d3d3+/v7+Jjn+yMjIOizeVeKxiCUIJhf+1dXV +; GA7OrYj+u7u7ooj+YGBgDsE/2FXVVcQDxg7+ra2tPBEDxH/N/np6eiX+k5OTIVUh1DDVP9S/iP7T09 +; P+n5+fGQ7SN/6qqqq8iP5ubm4Owh3X/nZcQv7JaAU13v6tVwD+cDgAxP6aTQA15P6eUAA1/ckC/oZf +; OCzBqogB/vr6+ibAi4j+ioqKLN9V3/6NjY3+8fHxJsCciP6np6csDtCjiP6SkpL+1NTU/n5+fh0OwD +; /YMNVVw0DFvYgS/pubmzADxH/PrIj+tra2pIgCIdIw1T/UDv6fn5/+1NTU/nl5eQ7W/oGBgf7S0tL+ +; kZGRHdn+nGImNd/+lEoA/nA4AMOjtf6zWgA15f6jUgA1/cv+pGMh/mBaUyzAO/6Li4v+6Ojotogmmo +; guAizgVdqniP6+vr7+/f39JlWHiP57e3sO1Cf+y8vL/qqqqjsOwT/XMNRVw0DDIf6YmJj+1NTU/nJy +; cgPFEtF//ouLi/7S0tIEIdAw1T/Usoj+ysrKh4j+YmJiDtiuiP65ubkbIJOI1f5vW0f+wmcLNd7+v2 +; AA/nw+AJouw/6IRAD+y2YANeY4/shkADX9y/66ZhH+d11DLMGxiA8IJsD+3t7e/np6eizgVdj+fX19 +; /uXl5RcmQP69vb3+Xl5eHcAO1KaI/qKiov7Nzc3+cHBwDsE/1jDVVcJAwrCIHoiILAPFf9IhwLWI/s +; PDw4eIGSHNMNQ/1f6MjIz+2NjY/omJiQ7Zf8As/pKSkv7S0tL+gICAHdP+jV8x/tFpAN/+oFAA/nA4 +; AMT+p1QANej+sVkApNQ1/cyfH/6NYDL+YFpTLMAoCv7x8fEmwJaI/qWlpRks4FXULCv++/v7JsCRiP +; 6Li4sdxFXU/nt7e/7U1NT+lpaWLA7AVdYw1VXBQMAS/oWFhf7Y2NgEEgPFEtIhwj/+n5+f/szMzP5x +; cXEhyzDUP9Q3/ru7u6aIDQ7Yf8QnD4WIKB3PofH+s2UWNd7+v2AA/oJBAP5wOADDAP7FYwA16TSeWj +; X9ziEWLMG8iP7W1tYXJkD+z8/P/m9vbyzhVdE2PxcmF/7Q0ND+aWlpHcYO1KqI/rOzs7GI/mVlZQ7A +; VdYw1VXAAyz+tbW1IhUDxn/SIcX+dXV1OP6ampoOIccw1T/U/nx8fP7V1dX+mZmZ/lpaWg7YHcYZ/q +; Ojo/7Kysr+dHR0Hc3+hV43Nd/+p1QA/nA4AMT+mk0ANeuZHw41/c+fH/6NYDL+WVlZwST+ra2t/vj4 +; +CbAj4j+kpKSOyzhVc0s/pWVlf719fUmwD0GLB3IVdQs/oqKiv7V1dX+hISEHQ4/1lXUIcAB/tXV1Q +; MhQMYS0iHHqYj+sLCwr4j+Z2dnIcV/1D/UHf6oqKj+0dHR/nNzcw7YHcr+e3t7Jf6ZmZn+WVlZHcr+ +; pGMhNd6dW/6USgD+cDgAw6O1/rldADXt/p5QADX90f6zZRb+YVpULMH+hISE/uPj4xd/nIj+vr6+/m +; ZmZiziVcqriA8IJlWBiP51dXUdyw7VHP7FxcWOiCgOP9Yw1FU0/mtrawPHEtEhyzH+0tLS/oiIiCHD +; MNQ/1LiI/s7Oziv+X19fDtcdzQIyrYgcHcf+b1tH/sJnC/7RaQDf/q1XAD6dW8IjNe8fNf3Sny/+f1 +; 8+OyzAroj+vr6+/vz8/CbAhYj+gYGBLOJVyP6Dg4P+6urqJsAI/rW1tRkdzg7UGf6ampo0/nV1dQ5V +; 1jDUDpuIxxLRIc2yiBeTiAYhwDDUP9MO/pSUlP7Y2NgTDth/0P6Li4v+09PT/oeHhx3F/pRgLDXi/r +; 9gAP6CQQD+cDgAwP6tVwA18B81/dOfL/6dYif+YVtUny/AGSH+7e3tJsCYiP6tra3+Xl5eLOJVxBky +; OSbAjIgxHdEO1RD+0tLS/p2dnSw/1TDUnYjHf9F/zzD+l5eXFv54eHgw1D/TrIj+wsLCmIggkojXHd +; OyiP7BwcGRiCQdwf5vW0f+umYQNeSdW/6gUAD+xWMANfH+o1IA/shkADX91P67ZhH+cF1JO8G2iP7Q +; 0ND+/v7+Jhf+1dXV/nR0dCziHcIB/t3d3RcmF/7IyMj+ZGRkHdMO1Qb+rKysvYgcP9VV0gYDxhLRId +; K9iDz+oqKiLDDQP9T+gYGB/tfX1/6SkpIsQNYd1qOI/pycnP7Ozs49Hf6NXzE1/d7+sVkApNQ1/dWf +; L/6OYTM7waaI/qSkpP719fUmwJOI/pqamv5aWlos4/6dnZ3++Pj4JsCXiP6Xl5csHdVV1R0x/tXV1f +; 6KioodP9RV0P56enol/lJSUsUS0SHSMMA7/qqqqruI/mxsbDDOP9M7/rGxsbqIJw7XHdm9iP7NxsD+ +; snEvNf3f/r5gAJkvNf3X/rNlFv5pXE87wf5+fn7+39/fv4gmOQD+ampqLOCviAcIJhf+2NjY/nBwcI +; iI2A7WIP6+vr6ciP5fX18/1DDNLP6np6c8/m1tbQPEEtF/0jDD/n5+fv7R0dH+kJCQPzDLP9O9iP7S +; 0tIz/l1dXQ7WHdv+b1tH/sJnCzX94JtN/qdUADX92J8v/n9fPjvBIP63t7cqJsCKiP6IiIgs3v6Kio +; r+7+/vJsA5/qysrDsd2lXWO/6Tk5P+09PT/nx8fA5V0zDLJzz+o6OjCjCdiMN/0SHSMMWtiAxV/mNj +; YzDJP9IO/pubm/7W1tb+e3t7Dtcd2/6cYic1/eP+nlAANeqeWjXr/p1iJ/5hW1Q7wH/+jY2N/urq6r +; SIJpqI/rS0tCQs2gb+u7u7/vz8/CYXiYj+f39/LB3cDte4iP7Nzc3+pqamLJ2I0jDJP/6Tk5P+0tLS +; /nt7ezDBnYjCf9Eh0jDHP/6Pj4/+0tLSJjDGP9OxiP7IyMgBMw7WHdz+rF8QNf3k/plNADXonVv+iE +; QA/phMAP7IZAA16/6oXRE7wrKIPAiiiBf+29vb/nl5eSzYKv7j4+MXJgj+wMDAFSzCHdsO1zf+paWl +; /szMzCc/0jDHr4j+vr6+lYgkMMKdiMES0X/SMMoJAISI/lxcXDDDP9P+iIiI/tjY2P6Li4v+WFhYDt +; Yd2yzA/olIBv6xWQD+zGcAorb94h8BNeb+rVcA/nA4AMD+hkMAo7X+tVsANen+p1QAHTvDpIj+np6e +; IybAlYj+oqKiGSzUO/6mpqb++vr6JsAj/o+PjyzFVdtV2P59fX3+1dXV/pOTkx0/0VXF/oGBgf7S0t +; L+jIyMPzDDnYjAf9Eh0jDMLP6jo6P+ysrK/nBwcDDBP9IoPayI/mpqag7WHdsswv6ERQafw/6ZTQD+ +; yGQANf3h/rFZAAE15f6USgD+cDgAwf6GQwAewB8cNeUU/ohEAB3AO8W+iP7Z2dn+/v7+JkD+zMzMGI +; yI0hj+1NTUFyYX/tPT0wksx1XbDtiriP62traqiDM/0DDDpoj+r6+vtIj+aWlpMMUDf9F/0jDPPf7O +; zs7+mJiYDj/SDP7V1dX+nJycCg7Wf9sswwQewKK2Bf7RaQD94P61WwCitjXj/r9gAP58PgCaLsIWHs +; GlwzQ14/6tVwAdwjvGqYj+sLCw/vn5+SbAjoj+j4+POyzP/pOTk/709PQmwJuI/qOjozssyVXbDtc/ +; Hf6NjY3+1NTU/oGBgQ4/z1XBv4j+zc3N/p2dnSwwxkDRf9Iw0QYyq4g+P88O/qSkpP7S0tIQDtYd2y +; zF/oRFBh7C/plNAP7MZwA1/d4P/qdUADXi/qBQAP5wOADE/opGAB7CN/6sVgAcNeAjHcM7yP6Hh4f+ +; 5eXluYgmG/68vLz+ZWVlLMwzIggmF4SI/nl5eSzNVdpV1z/AK/7IyMg2Cj/QGf7Pz88QMMgS0CHSMN +; M//oiIiP7T09P+hoaGP80JKYGIFQ7WHdosxwSfw8OlwzQ1/d2bTf6jUgA14P6/YAD+gkEA/nA4AMMA +; /sVjAEb+nlAAHsKn8Rw13hL+fD4AHcQ7yQ0TCCbAgoj+f39/LMr+gYGB/ujo6Bd/CP65ubn+Xl5eLM +; 9V2g7XP8AK/p2dnRb+cnJyDj/MsogiGAYwyUDPIdIw0z/Bs4j+wcHBNhU/yQ7+kZGR/tnZ2f6FhYUO +; 1x3af8j+hEUGHsSitv6nVAD+zGcANf3c/p5QADXfDv5wOADEJTXBHA43Hg413yUdxjvKo4j+l5eX/u +; /v7ybAl4j+qqqq/l5eXizGo4gF/vv7+ybAjoj+h4eHLNJV2lXXP8H+d3d3/tPT0/6ampodP8r+iYmJ +; Eg8wy0DOIdIw0j/EHf6bm5sWLj/HFf6/v79A/mZmZg7WHdosygSfw8b+mU0A/sNiADX92/6eUAA13R +; T+lEoA/nA4AMOjtf6/YAA1xP6+YADANd4S/oJBAB3H/lpaWsy4iP7T09MXfxcSIyzEuoj+29vbFyYX +; Kf5nZ2cs1FXaDtY/wgb+rq6uuYg+P8cG/ra2tggvPzDLQM0h0jDSP8e+iP7Ly8v+oKCgOz/E/n9/f/ +; 7W1tb+lZWVLA7WHdosywSfw8eitv6xWQA1/do4/shkADXb/r9gAP58PgCaLsP+lEoANef+p1QAHck7 +; zaeI/qioqP729vapiMCSiP6Xl5c7LMA7/pqamh8mwJiI/pubm/5aWlos1lXaVdZVwg7+hoaG/tXV1Q +; 8OP8T+eXl5Fv6WlpYsnYjBVcpAzCHSMNI/yaeI/q2trSL+a2trP8EsGAf+b29vDtcd2SzNBB7J/plN +; AP7IZAA1/dgFpNQ12v6aTQD+cDgAxP6tVwA15p1bIx3KO8/+gYGB/uHh4RcmnYgT/mlpaT4p/v39/S +; YX/tvb2zIs2VXZDtc/w7CI/sHBwZaI/l5eXj/BLBEa/nBwcD/EMMkSyyHSMNI/zP6CgoID/o+Pjw66 +; iP7Q0ND+paWlKA7WHdoszv6ERQYeyqK2NDX91/61WwDANdj+v2AAAP5wOADDADM15hL+fD4AHcs70C +; /+urq6KibAioimiCbAOf6wsLAZLNtV2Q7WP8Q7/paWlgM9DraI/sjIyP6mpqYomYjFMMlAyiHSMNI/ +; zrCI/r6+vhquiP5+fn4/DtYd2X/QBB7M/qdUAP7MZwA1/dWZHw411w7+cDgAxP6gUAA15/6aTQAdzT +; vRGf6QkJD+7e3tJsKLiBMs3lXZVdY/xRT+0NDQiYi9iP5/f3/+VlZWyDDIEskh0jDSP8+uiC0Lqoj+ +; fX19DtYd2SzR/oRFBh7Np/EoNf3UHP6eUAA11Z1b/o5HAP5wOADDAP6/YAA15hIAHc470r+I/uXl5b +; qIwv7d3d3+d3d3OyzeVdkO1j/ErYj+wsLCA52ICT/IMMhAyH/SMNI/z/6EhIT+19fX/o+Pjx23iP7H +; x8f+pqam/l1dXZqI0h3ZLNMEn8PON/6xWQAcNf3THzXU/rldAP52OwCdW8P+mk0ANef+p1QAHdA70H +; /+o6Oj/vn5+SbAmIhAJsCUiP6fn58KLN5V2VXVP8P+f39//tHR0RYd/oCAgP7U1NQWHT/HMMcSyH/R +; MNI/zgr+tbW1sog6P8AOCi/+ycnJ/nBwcA7QHdks1P6ERQafw9D+mU0A/shkAKXD/dIfNdP+mk0A/n +; A4AMM+/rNaADXmFP6ORwAd0TvPCQP+/v7+fxf+1dXVNqyI/tzc3BcmQDwJLN5V2A7WP8AK/qysrLmI +; /mxsbD/BrYj+ubm5pYj+YGBgP8ZVxxLHIdEw0j/O/nZ2dv7T09P+np6eCj/ADsL+fHx8Jf6WlpYdDs +; 0d2CzW/oRFBh7Rorb+tVsANf3ROAE10P6/YAD+fD4Ami7D/ohEABQ15jE+HdI7zv6QkJD+8vLyJsCc +; iP6np6cKO8AR/rOzs/76+vomwIyI/oyMjDss3VXYDtVVvIga/qCgoAo/wywl/tTU1P5/f38OP8Uwxk +; DGf9Ew0j/NDhX+1NTUPT/BDsSriP63t7eliP5mZmYOyx3YLNcEHtM4HDX9z/6xWQCk1DXP/qBQAP5w +; OADE/qdUADXn/ppNAB3UO8yniP7AwMAIoohVhoj+fHx8O8MK/oqKiv7n5+cXJhsMAizdVdhV0x3+mZ +; mZ/tDQ0P55eXk/xwn+ysrKgYj+WVlZP8QwxhLFf9Ew0j/Ns4gLhYj+YGBgP8AOxx0p/tPT0/6EhIQO +; yR3YLNj+hEUGHtQrNP7RaQD9zv6+YACbTTXNFP6CQQD+cDgAwwD+xWMANeb+v2AAHx3VO8v+gICA/u +; bm5hcmCP68vLwVO8awiP7FxcUIJsCAiP58fHws3VXXDtKxiBOOiCQ/yaaI/qCgoAf+cHBwP8QwxUDE +; IdEw0j/N/o2Njf7Y2Nj+iIiIP8EOyrSIIoqI/l9fXw7Ff9gs2gQe1Tf+sVkA/sxnAKK2/cyZH/6nVA +; A1zP6tVwD+cDgAxP6aTQA15/6gUAAd1zvJGf6srKwqJsCRiP6Li4s7yaOI/pqamgUmwJeI/qenpygs +; 21XYVdD+h4eH/tPT0/6JiYkOwD/LDP7U1NT+l5eXHT/CMMVAw3/RMNI/zKiI/ry8vKSI/mdnZz/Bf8 +; ws/p+fn/7Nzc3+dXV1DsMd2Czb/oRFBp/D1/6ZTQD+yGQApcP9zP6ZTQA1y/6USgD+cDgAw6O1/rld +; ADXmM/6CQQAd2P5aWlrIt4j+2NjYFyYX/tDQ0Cs7zLqI/tXV1RcmFyX+cHBwLNtV2A7NNzKsiA0Owj +; /Lqoj+sbGxs4j+ZGRkP8JVxBLCIdEw0j/M/n19ff7V1dX+mJiYLD/BDs/+d3d3OP6dnZ07DsB/2Czb +; OwQe2KK2/rFZADX9y/6ZTQA1yf6/YAD+fD4Ami7DIzXnLx3aO8YK/piYmP719fWqiMCZiAYKO86oiP +; 6rq6s9JsCQiP6UlJQ7LNpV1w7M/nh4eP7Pz8/+mpqa/lpaWg7DP8wdLTD+hISEDj/AVcRAwn/QMNI/ +; yx3+qampJf5ycnI/wg7RqIgUE/5ra2sd1yzcO8AEHtoB/sxnADX9yf6eUAAcNcf+mk0A/nA4AMQvNe +; f+lEoAHdsKO8SsiP7IyMj+/f39JheAiP52dnY70v6Dg4P+4+PjFyYq/r+/v/5nZ2cs2lXXDsks/qGh +; oTj+c3NzDsY/zT7+xMTEkIgZP8AwwxLBIdEw0T/LuIgH/qioqP5eXl4/wg7U/oaGhv7T09MpHdUs2z +; vC/oRFBh7borY0Nf3I/qdUAP7IZAA1xf6/YAAA/nA4AMMA/sVjADXmEv58PgAd3ArBO8H+hYWF/uvr +; 67SIwAj+tbW1/l5eXjvUPv69vb3+/Pz8JsCFiP6CgoIs2VXYVce1iP7GxsaEiAYOxz/OO/6YmJj+0d +; HR/nV1dQ4ww0DAIdEw0T/KDv6VlZX+19fX/oCAgD/Df9V/sIj+vr6+loj+Y2NjHdIs2zvDBB7d/qdU +; AByitv3GBaTUNcQO/nA4AMT+oFAANef+mk0AHd4Kwjf+tra2OSbAOgA71xn+k5OT/u3t7SbAmIj+rq +; 6uBizYVddVxR3+jo6O/tPT0xMOyj/PMv7R0dH+np6eHZ2IwkAh0TDSP8msiP7Dw8OWiP5kZGQ/ww7V +; HcL+l5eXNP58fHwd0CzbO8T+hEUGHt6n8f7DYgA1/cX+vmAAGzXCnVv+jkcA/nA4AMP+gkEA/r9gAD +; XmEgAd3wrBvIj+39/fv4gmQC3+ZWVlO9q2iBYXJhf+1tbWECzYVddVwzP+urq6wCAOy1XQqIj+qqqq +; C/5paWkwwSHRMNI/yRP+19fX/pGRkR0/w3/VHcS6iAv+paWl/l1dXR3NLNo7xgQe36K2/rFZAP7MZw +; A1/cObTf6sVgA1wf65XQD+djsAnVvD/ppNADXn/qdUAB3hChn+oKCg/vj4+CbAl4j+l5eXCjvcpoj+ +; o6Oj/vT09CbAk4j+m5ubOyzXVdZVwv59fX3+0dHR/pOTkywOzT/QDhP+1dXVGg5AIdAw0j/IO/6zs7 +; ML/m1tbT/EDtUdxigrvYj+b29vHcos2zvH/oRFBp/D4f6ZTQD+yGQANf3D/rBYADXA/ppNAP5wOADD +; Pv6tVwA15hT+jkcAHeL+a2tr/tDQ0P79/f0mF/7Y2Nj+cXFxiojAO97+fX19/t7e3hcmnYj+xsbG/m +; pqaizWVdcOCv6qqqot/m5ubg7PP9KtiP68vLxV/l9fXyHPMNI/yL2IAyQZP8UO1B3J/n9/fzQSLB3H +; LNo7yf6ERQYe4qK2/rVbADX9wv6nVAD+v2AA/nw+AJouwwD+xWMANeYxPh3j/vDw8K+IwJ2I/qurqx +; kKwjveq4j+tra2/vr6+ibAiog8LNZV1hQL/qOjoyiaiNE/0h3+kpKS/tPT0yHPMNE/xw7+nJyc/tXV +; 1f56eno/xg7UHcsgDH/+ZmZmHcUs2jvJtoj+h0gIHuT+o1IA/sxnADX9wP6kUgD+cDgAxP6nVAA15/ +; 6aTQAd5SYXiIj+f39/CsVV3gop/unp6Rd/moj+tbW1/mJiYizVVdMs/paWlv7S0tL+fHx8/ldXV9NV +; 01UYIc4w0T/HsYj+ycnJiYj+YWFhP8YO1B3NLP6QkJASEx3DLNo7yf6UlJT+2dnZ/otMDR7lp/H+ul +; 0ANf3+nU4A/nA4AMIA/r9gADXmEv58PgAd5v79/f3+wMDAMwrHO9+xiP7IyMgIJsD+3Nzc/nl5eYCI +; 1FXSPjWTiP5jY2MO1D/TMMD+rKysLJuIyzDRP8f+iYmJ/tjY2P6KiooOP8YO1H/QJw+FiAYdwCzZO8 +; msiP7BwcGciP5paWn+hEUGHuY3/rFZAP7MZwCitvv+q1YA/nA4AMH+mk0ANef+p1QAHej+j4+PCss7 +; 3qSI/p2dnf7x8fEmwJaI/qSkpP5cXFws01XQ/oWFhf7T09MpLB3AVdQ/0jDA/p+fn/7Ly8v+cXFxIc +; kw0j/Fp4j+ubm5Iv5paWk/xw7UHdIK/qOjo/7MzMz+dHR0LNk7yRP+2NjY/paWlhk7/oRFBh7o/plN +; AP7IZAA1+v6vWAD+cDgAo7X+s1oANeadW/6IRAAd6QrNO98u/tjY2BcmQP7Nzc0nLNJVzjf+sbGxIv +; 5ra2sdwg7UVdMwVf52dnb+z8/P/pmZmTAhxzDRP8X+enp6/tTU1P6ampr+WlpaP8cO1B3VGxb+m5ub +; /ltbW0DWO8g3FCn+cnJyO8EEHumitgU1+TH+jkcA/stmADXm/q1XAP5wOADVpdMd0wrOO98z/q6urv +; 75+fkmwI+IJTss0VXMLv7Ozs7+nZ2dGR3EDtQ/0jBVwKuI/q+vr7GI/mZmZiHFMNE/xA7+paWl/tHR +; 0f50dHQ/yA7UHdYsM/60tLQ1/mpqaizUO8i7iDT+p6enFTvCBB7r/plNAP7MZwCitvedWzXn/pRKAP +; 5wOADV/qRSAP6AQAAd0wrPVeD+hoaG/uXl5RcmnIj+vb29/mVlZSzQVcos/p+fnxYfHcYO1D/SMCHB +; MAASDyHDMNE/xLaIKf6rq6v+X19fP8gO1B3WLMI8/tTU1Ass0jvIKP7W1tb+f39/O8QEHuw3/rpdAP +; 7RaQD94P6/YAD+fD4Ami7U/oBAADGaPh3UCtA73izADf7AwMD+/f39oojAg4j+f39/LNBVyLOI/sXF +; xYiI/mFhYR3IDtNV0jBVwy/+vr6+lIj+XV1dIcAw0T/E/pGRkf7Y2Nj+g4ODP8oO03/WLMQ6/sHBwS +; P+Y2NjLM47yCv+xsbGkYgvO8X+hEUGHu7+p1QAHKK2/d3+oFAA/nA4ANUB/sNiAMD+gEAAHdQK0Tve +; LMAZ/paWliexiMCXiP6qqqr+Xl5eLM5Vxiz+jIyM/tTU1P6GhoYdylXUVdJAxA7+mJiYFv52dnYhMN +; E/whX+wMDAnIggP8oO03/WLMY7/pycnP7Q0NAbLMw7yAv+2dnZ/o+Pjwo7xgQe76fxKKfx/doS/oJB +; AP5wOADU/oBAAP6zWgAowBAd1QrSO95VwbiI/tHR0f7+/v4mF/7U1NT+c3NzLM5VxKqI/ri4uKWI/m +; dnZx3LDtQ/0kDGvIga/qKiog4wzz/C/n9/f/7W1tb+lJSUHUDKDtMd1izJH/7MzMwz/l1dXSzJO8cV +; Lq+I/m5ubjvIBB7worb+sVkA/tFpAP3Y/qdUAP5wOADV/pRKAP7DYgDC/opFAB3VCtM73VXCFf6np6 +; f+9vb2qYjAkoj+mJiYOyzMVcP+fHx8NP6WlpYKHc1V01XSQMeoiP6pqakP/mpqajDNP8EsNjgnP8sO +; 1B3VLMsGGLiIJyzHO8cb/tTU1P6goKD+Xl5eO8kEHvIB/shkADX91J1b/o5HAP5wOADUo6X+pFIAKM +; IQHdYK1FXdLMP+f39//uDg4Bd/nYj+w8PDDSzMVcAKPhr+cXFxHc8O1D/RQMYwwf5/f3/+09PT/o6O +; jjDLP8EUNBH+XV1dP8sO1B3VLM7+g4ODEv6RkZE7LMQ7xgr+pqamEv55eXk7ywQe86K2/rpdADX90v +; 6/YAD+djsAnVvV/qRSAP7DYgDD/opFAB3WCtRV3VXEID0qpIjAiIj+hoaGLMtVuIj+ycnJLwYd0Q7T +; P9EhxjDCrog9fyQwyT/A/pmZmf7X19f+fHx8P81/0x3Vf9CuiP68vLw9/mZmZizCO8YnGoaIAjvMBB +; 71/qdUAP7MZwA1/c/+mk0A/nA4ANal0yjDMR3XCtU73SzEChb+6+vrFyaZiP6ysrIkLMn+k5OT/tPT +; 0/6AgID+WFhY01XTP9EhxTDEP/6RkZED/n19fTDHPyD+x8fHjoj+YmJiP80O0x3VLNI7/pSUlBI1LM +; A7xv6SkpL+2dnZ/oiIiDvO/oRFBp/D9qfxKKfx/cz+v2AA/oJBAP5wOADX/qRSACjD/opFAB3XCtZV +; 3FXGtIgaFyYX/tra2v53d3csxi/+vr6+mYggk4jUDtM/0UDEMMcY/sbGxoOI/lpaWjDEP/6FhYX+2N +; jY/oyMjA4/zQ7Tf9Us1biI/sjIyBz+YGBgO8SqiBeiiP5qamo7zwQe9zf+sVkA/sxnAKK2/cn+p1QA +; /nA4ANijpSjDMR3YCtdV3CzGpYj+n5+f/vLy8ibAlYj+oKCgCizDE/7S0tL+kJCQOyxV1VXTP9AhxD +; DIO/6ioqL+ysrK/m9vbzDCCv62trYPKz/ODtMd1SzXpIj+p6enGjI7wv6AgID+19fX/pmZmRk70AQe +; +f6ZTQD+yGQApcP9xp1b/o5HAP5wOADZ/qRSAP7DYgDD/o9IAB3Y/ltbW9dV3FXI/nl5ef7a2tr+/v +; 7+JkALCSzAN/6vr68PGCzBVdVV0z/QIcMwywz+0NDQ/peXlz8w/nd3d/7U1NT+nZ2dCj/Pf9Md1CzY +; f8D+fX19Jf6ZmZkZf/6urq7+zs7O/nR0dDvSBB76orb+tVsANf3E/q1XAP5wOADao6X+w2IAwzEd2Q +; rYVdxVyKmI/rGxsf75+fkmwI2IKYqIOBX+Xl5eLMJV1Q7TP9BAwjDNJP6zs7OsiD4DHz/QDtMd1SzX +; O8IR/re3t6yIJf6qqqokO9IKBB78/qNSABw1/cH+lEoA/nA4ANv+pFIAKMP+j0gAHdn+W1tb2VXbLM +; k7/oiIiP7m5uYXfyqViAAsxVXVDtM/zyHCMM62iP7d3d2tiP6Ojo4/0A7THdUs1zvE/pqamv7g4OBV +; /omJif5aWlrRCsEEHv2n8f66XQA1/BL+fD4Ami7bo6X+w2IAw51rHdr+W1tb2VXcLMqwiP7MzMwmwR +; X+fHx8LMRV1Q7TP88hwTDOB/7Y2Nj+hoaGhIj+wcHBj4j+XV1dP80O03/VLNc7ww3+xMTElYj+Z2dn +; p4gxBf5jY2M7zgrC/oRFBh79wKK2/rFZAP7MZwA1+f6gUAD+cDgA3f6kUgD+w2IAw/6PSAAd2v5bW1 +; vaVdssyv6Kior+1dXVMv7w8PAmwJeI/qenp/5dXV0swlXVDtM/zyHAf82piP69vb2iiP5mZmY/wCz+ +; m5ub/s7Ozv51dXU/zA7SHdUs1zvD/oeHh/7Y2Nj+kpKS/ltbWzvAGf6fn5/+z8/P/nh4eDvMCsMEn8 +; P9wv6ZTQD+w2IANfYS/oJBAP5wOADdo6Uow51rHdv+W1tb21XbLMck/ra2tqqIKyy6iP7U1NQXfxf+ +; 0dHR/nBwcCzCVdQO0z/PQMAwzP59fX3+1tbW/paWlh1Aw76I/s3NzQYdP8kO0x3ULNc7wqWI/rS0tL +; WIBTvEPTj+oKCg/l1dXTvJCsQEHv3Dorb+sVkA/tFpAPT+p1QA/nA4AN/+pFIA/sNiAMP+j0gAHdv+ +; W1tb21XbLMb+e3t7/tDQ0P6ampoZLMGniP6pqan+9/f3JsCQiP6UlJQswVXVVdI/z0Awyw7+q6urB/ +; 5wcHAwwD/EBv6srKwx/mlpaT/HDtMd1CzXf8I9/tPT0/6jo6P+Xl5eO8Yk/rCwsCIYO8cKxQQe/cX+ +; mU0A/sxnADXwnVsj/nA4AN+jpSjDnWsd3P5bW1vcVdsswxn+pKSkOP50dHQsxRP+4uLiFyaciP7AwM +; D+Z2dnLFXVDtM/ziEwyicW/qampv5dXV0wwD/H/oODg/7U1NT+jIyMP8YO0h3VLNY7wv6ioqIhOTvK +; /oeHhxL+j4+PO8UKxgSfw/3Gorb+ul0A/tFpAO7+v2AA/nw+AJou4P6kUgD+w2IAw/6PSAAd3P5bW1 +; vcVdsswraI/sfHx4KIJCzHrYj+vLy8/vz8/CbAhogiLFXUDtM/zjDJP/6VlZX+19fX/n5+fv5VVVXC +; P8gv/ru7u5yIJD/Df9Md1CzXO8CyiP7IyMiLiP5kZGQ7zCv+vr6+mIgvO8IKxwSfw/3I/qdUAP7MZw +; A16/6aTQD+cDgA4aOlKMOdax3d/ltbW91V2izAO/6RkZEhIizKCgP+7OzssogmmIj+rq6u/l5eXpqI +; 01XTP84wyK6IMZOI/mJiYjDCP8od/pWVlf7S0tL+enp6P8EO0x3ULNc7wBb+2dnZCzvPCv6YmJgD/n +; 9/fzvACsj+hEUGHv3Jp/EoNegSAP5wOADi/qRSACjDIx3dCt1V26uI/r29vZ2IPpKIzbWIBxd/F/7X +; 19cQg4jSVdM/zlXHE/7X19cW/ldXVzDCP802Lf6np6c7Pw7SHdQs1zsCKqeI/mxsbDvSAf7Kyso+FQ +; rIBB79yqK2/rFZABw15f6nVAD+cDgA46OlKMOdax3e/ltbW95V2f6AgIAD/pOTkwoszwb+oaGh/vT0 +; 9CbAlIj+nJycO0DRVdI/zjDFLP6zs7M8/mtrazDEP86miP6mpqY8Ng7SHdQs1jv+fX19/tbW1v6cnJ +; z+XFxcO9QG/qqqqjz+cnJyCscEHv3M/plNAP7IZAClw+IU/o5HAP5wOADk/p9QACjD/pRKAB3eCt47 +; 16SI/qysrC02LNP+e3t7/t3d3RcmOf7Hx8f+ampqjojQDtJVzjDEvogD/p+fnwowxD/RCAP+lJSUDt +; Ad1CzWGf6qqqolEDvWCsD+gICAA/6Xl5cZCsUEn8PcorYe7Df+tVsA/tFpAOD+uV0A/nY7AJ1b5KOl +; /sNiAMOdax3f/ltbW99V1bqI/szMzP6kpKT+X19fOyzUqoj+tLS0/vr6+ibAi4j+iYmJ/lhYWM9V0j +; /OMMI//p2dnf7V1dU9/lVVVcY/0Q4C/rW1taiIIA7NHdQs1reIB/6tra0zO9YKwquI/rq6uqKI/mlp +; aQrEBB7YN/6nVAD+ul0ANZkf/p5QAB7t/qNSAP7MZwA13f6aTQD+cDgA5iIow/6ZTQAd3wrfO9MK/p +; mZmQP+fX19O8FV1Tv+i4uL/ujo6Bcmmoj+tra2/mFhYZeIzVXTP80wwbKI/snJyYiIFTDGP9EOwv6O +; jo7+09PT/oKCgg7MHdQs1f6VlZX+2dnZ/oSEhDvYCsT+kJCQ/tTU1A8Kw/6ERQYe1afx/qxWACg1wx +; z+p1QANx7sKzQ12v6/YAD+fD4Ami7mo6Uow51r/no9AB3f/ltbW99V0rCI/sLCwpGIIDvCVdZVK/7G +; xsb+/f39JsD+3d3d/nl5eR3MVdM/zVXA/oqKiv7Y2Nj+iIiIPzDGP9IOw7WI/sTExIqI/l1dXQ7Jf9 +; Qs1C8TOf5oaGg72ArGNg8nAgrBBB7VNzQ1xv6+YAD+kEkAHuw3/rFZABw11/6nVAD+cDgA6CL+w2IA +; wwEd4ArgO9D+h4eH/tPT0/6NjY0KO8Ms11UZ/pubm/7x8fEmwJaIEf5bW1sdylXTP80ZGxM+MMg/0Q +; 7FCv6fn5/+zc3N/nR0dA7HHdQs1P6Dg4P+2NjY/pWVlQo72ArIo4j+o6Oj/s7Ozv53d3cKwASfw9f+ +; p1QAHDXFHA4e7f6ZTQD+yGQANdSdW/6IRAD+cDgA6KOlKMOda/56PQAd4P5bW1vgVc4VMhMJO8Ys1l +; XBvYj+1tbWFyYIB/5ubm4dylXSVc3+1dXV/piYmCyciMg/0g7HLv7Pz8/+nJyc/lhYWA7FHdQs0jf+ +; srKyuYj+cXFxO9kKyyoW/p6env5dXV0EHtin8f7DYgA1xg/+jEYAHuw3BTXS/q1XAP5wOADq/plNAC +; jD/qRSAB3h/lxcXArfVc0MFv6dnZ3+Xl5eO8cs1lXCJBj++Pj4JsCPiP6RkZEsHcgO0j/NvIgwyj/R +; Dskk/rCwsBP+aWlpDsMd1CzSEP7R0dH+paWl/l9fXzvZCs2piP60tLQTBB7Zorb+sVkA/sxnADXFHP +; 6eUAAe7f6ZTQAcNc/+lEoA/nA4AOqjpf7DYgDDnWv+ej0AHeH+XV1dQOBVygr+oaGhFi47ySzWVcT+ +; hISE/uPj4xcmnIj+vb29/mVlZR3HDtI/zTDKP9EOzP6Hh4f+1NTU/omJiQ7CHdMs0v6enp7+1tbW/n +; 5+fiw72QrQC/6NTw+V1NsB/shkADXGBf6MRgAe7Df+ul0ANcz+v2AA/nw+AJou6/6ZTQD+w2IAw/6k +; UgAd4v6/v786Ct9VybSI/sbGxhgzO8os1lXFroj+vr6+/vz8/KOIwAL+gICAHcYO0lXNMMo/0Q7NDR +; eXiBUOHdQs0LGI/sfHx4+IICzAO9kK0f6ERgae1Nw3/rVbAP7RaQDGKP6VSwAe7f6nVAAcNcn+oFAA +; /nA4AOyjpSjEFB3i/o6Ojv7V1dU8GQrdVccK/o+PjyH+hoaGO8ws1lXGCv6UlJT+7e3tJsCYiP6srK +; z+XV1dHcRV0j/LrYgQ/lVVVck/0Q7PLP6YmJj+0NDQDP5YWFjTLND+jIyM/tnZ2f6NjY07VcA72QrS +; BB7e/qNSABw1xRz+p1QAHu0r/sNiADXGEgD+cDgA7QEow/6kUgAd4xmxiP7FxcWMiCAK3VXFEf66ur +; qiiP5paWk7zizVVcg2Jf7+/v4mF/7U1NT+c3NzHcNV0j/K/n19ff7Ozs7+jo6OMMk/0Q7Ru4ga/qWl +; pQod0SzOFf64uLgAGCzBO9kK0wQe3ys0Ncb+vmAA/pBJAB7sNwU1xP6nVAD+cDgA7qOl/rldACjD/n +; o9AB3jGcB//qCgoCX+enp6CtxVxP5+fn4D/peXlxk7zyzWVcimiP6kpKT+9fX1JsCSiP6ZmZn+WVlZ +; HcFV0j/IGf6np6cT/mxsbD8wyD/RDtMG/qmpqb+IGB3PLM7+e3t7If6fn58ZLMI72ArUBB7gorYF/s +; xnADXFNP6KRgAe7QH+yGQANcCdW/6ORwD+cDgA7/6ZTQD+w2IAw/6kUgAd5BnBCr2I/s7OziT+X19f +; CtpVwqSI/qqqqhojO9Es1lXKCP7f39+/iCadiP7ExMQNHcAO0j/HNv7Hx8ckOz/AMMg/0Q7SHcH+gI +; CA/tPT0/6RkZEdzSzNCv6mpqYD/nd3dyzDO9kK1ASfw+IB/shkAKXDw/6nVAD+cDgA/oRCAB7uorb+ +; ul0AWv52OwAd76OlMSjD/no9AB3kGcIKpoj+sbGxs4g2CtlVwSMaL/5gYGA70izWVcuriB/++/v7Js +; CJiP6GhoYdVdI/xR3+lpaW/szMzD3+VlZWwjDHP9EO0h3DrYj+ubm5ooj+ZGRkHcsszCcahIgzLMM7 +; 2QrVBB7jNwU1wJ1b/ohEAP5wOADACx7wHehGHcb+j0gAKMP+pFIAHeUZwlXBD/7U1NT+kJCQGQrXVQ +; r+lpaW/tPT0/6AgIA71CzWVcws/o2Njf7q6uoXJpqI/rOzs/5fX18O0VXEsIgqlYgkP8Mwxz/RDtId +; xCz+kZGREjUdySzM/pOTk/7Z2dkP/llZWcU72ArWBB7l/plNAP6uVwD+cDgAwgse8B3onUuith3F/r +; ldACjD/no9AB3lGcIKwq6I/sDAwJeI/mdnZwrWrYg1Hy871SzWVc46/snJyRcmF/7a2tr+d3d3DtA/ +; w/6EhIT+0NDQ/oeHhw4/xDDGP9EO0h3HJ/7GxsaGiP5dXV0dxyzKEf6/v7/AHCzFf9kK1gSfw+b+dj +; sAnVvCCx7wHemZH6GnHcP+j0gA/sNiAMP+qVUAHeb+XFxcw1XD/piYmP7T09P+gICACtT+hYWFEv6Q +; kJAKO9Ys1lXPpYj+nZ2d/vLy8q2IwJWI/qCgoDsOzj/BBv6tra2wiA0/xjDGP9EO0h3IGf6jo6Ma/n +; Nzcx3FLMo1/tfX1/6Xl5cKLMY72ArXBJ/D5j6dW8ILHvAd6ptNnUun8R3B/rldAKXTwxQd5hnDCsQy +; Gv6np6ckCtCliP6xsbG0iCc72CzWVdEu/tnZ2RcmQBo6jIjNP8C+iBr+mZmZ/llZWT/HMMU/0Q7SHc +; sb/tHR0f6amposHcMsyBn+rq6uv4gyLMd/2QrXBB7m/nY7AJ1bwgse8B3rEhClwx3+gEAAKMP+qVUA +; Hef+XFxcwwrFBv6rq6s8MgrOPQf+oaGhBgo72SzVVdKpiDYMJsCNiP6NjY0dDss7/p2dnTwyP8kwxT +; /RDtIdzKuI/rKysq6IDR3BLMgy/tDQ0P6oqKgVLMg72ArY/oRFBh7mPp1bwgse8B3tnloS/qRSAP7D +; YgDD/oBAAB3nGcMKx/5/f3/+09PT/peXl/5dXV0Kyhn+np6eJRsKwVXZLNVV1P6Ghob+5eXlF3+biP +; 66uroClIjJs4j+wMDAiogoP8owxT/RDtEdziz+ioqKIf6Hh4cdwCzH/pqamv7X19f+f39/LMl/2ArZ +; /oRFBp/D5j6dW8L+hEIAHvAd76Ol/qlVAP7DYgDBGR3o/lxcXMQKxy8bo4j+ampqCsixiP7ExMSMiB +; EKwlXZLNVV01XAsIj+wcHB/v39/aKIwIGI/nx8fA7H/oyMjP7Pz8/+gYGBDj/LMMQ/0Q7SHdCziP7B +; wcGSiP5gYGAsxa+IMZSILyzKO9gK2f6ERQYe5v52OwCdW8ILHvAd8f6USgD+vl8AKP6AQAAd6BnEVc +; n+kJCQ/tXV1Q8Kxikh/oqKigrEO9ks1VXTDsE7/paWlv7v7+8mwJeI/qioqAoOwzP+tLS0Pf5lZWWR +; iM1VxD/RDtId0Qr+nJycFv54eHgsw/6Hh4f+2NjYJTssyjvYCtoEn8PmPp1bwv6EQgAey6XDHuId8j +; X+pFIAHen+XFxcxArKs4j+x8fHh4gCCsIC/re3t6iIOgrFVdks1VXTDsO6iAMXfxcD/nBwcA7B/nt7 +; e/7Nzc3+kpKSHQ4/zTDDP9EO0h3TLLyIOP6ioqIKLAb+tbW1Hv5ubm4szDvYCtoEn8PmPp1bwgseyv +; 6sVgD+0WkA/r5gAP6MRgAe4B394RnECsuiiP6jo6MHPQrA/n19fTT+mpqa/l5eXgrGVdks1VXTDsQ3 +; /qenpx8mwJGI/pWVlSj+paWlMf5vb28OwT/NVcM/0Q7SHdMswCQYuYj+i4uL/tPT0/6hoaEoLMw72A +; rbBB7m/nY7AJ1bwgseyKXD/sNiADXBnlr+nlAAHt8d/eEZxArNv4j+0NDQJKiIOP51dXUKyTvYLNVV +; 0w7G/n9/f/7h4eEXf52Iioj+sbGxGQ7CP80wwz/QDtId0yzCO/7BwcH+9fX1/qurqyzOO9gK2/6ERQ +; Ye5j6dW8ILHsf+o1IANcX+sVkA/oxGAB7dHf3hGcQKzr6I/u3t7ZeI/nV1dQrKO9gs1VXTDsesiP66 +; urr+/f39JsCJiCIOwj/NMMI/0Q7SHdMswQk8iYj+c3Nz/ry8vBv+ZGRkLMs72ArcBB7mPp1bwgsexw +; U1xig3Htwd/eEZxArMGf6Tk5P+1dXVIqaIMP6NjY0ZCsg72CzVVdMOx6+I/ru7uy3+7OzsFyaZiP6v +; r6/+XV1dDsA/zVXCP9EO0R3ULMD+j4+P/tnZ2Tz+WVlZwAr+lZWV/tLS0v5+fn4syjvYCtwEHuY+nV +; vCCx7I/plNAP7MZwA1xQUe3B394RnECsusiP6+vr6biP5oaGgKwLCI/sHBwZOILwrHO9gs1VXTDsb+ +; goKCJRodtIj+zMzM/v7+/iYX/tjY2P50dHQOP81Vwj/QDtJ/0yypiDkEKyzDuIgtgYgoLMc72Ard/o +; RFBh7m/nY7AJ1bwgseyTf+ul0ANcSeWv6VSwAe2x394RnECsoT/tPT0/6Tk5MZCsP+m5ub/tHR0f59 +; fX3+W1tbxTvYLNZV0w7Ep4j+q6urtYj+a2trDsEZ/qCgoP7z8/MmwDL+nJycHT/MMME/0Q7SHdMI/t +; bW1v6ampoKLMU3/qampv7KysoULMZ/2ArdBB7mPp1bwv6EQgAey/6nVAAcorbD/rpdAB7bHemith3z +; /lxcXMQKyKWI/q6uri3+cHBwCsYBKSAGCsM72CzVVdRVw7yI/srKyv6dnZ07DsT+eXl5/tvb2xcmnY +; gt/mlpaY2IyzDBP9EO0R3So4j+q6ur/s/PzwEsyf5+fn4D/peXlyzFO9cK3RkEHuY+nVvC/oRCAB7M +; K/7DYgA1wx8e2h3pp/H+qFQA/nk9AB3xGcQKx7uI/s3Nzf6jo6MVCsgV/q2trS0UCsI72CzVVdNVwi +; z+m5ub/srKyhAOx6qII/76+vomwIyIPD/KMMA/0Q7SHdEFB/6rq6sVLMusiP62trapiA0swjvYf90Z +; BB7mPp1bwv6EQgAezaK2/rFZADXCm003Htkd6f6fUAD+u14AnloeHfAZxArFGf6bm5sDCP5bW1vME/ +; 7T09P+lJSUCsE72CzVVdMOwRwmGBUOyR3+iIiI/ufn57eIf5qILhU/yDDAP9EO0h3Q/paWlv7Y2NgT +; LM47/o6Ojv7U1NT+hYWFLME72ArcGcAEHub+djsAnVvCCx7P/plNAP7IZAA1wf6sVgAe2R3oEf68Xg +; A8wf6aTQA2He4ZxArEsIj+w8PDkIj+ZmZmCs6siP68vLxADQo72CzVVdMOwP6JiYkl/oSEhB0OzD7+ +; xMTE/v39/SbA/t7e3v55eXn+VlZWxzDAP9EO0R3PIP7CwsKZiD4s0bSIMYyIBjvYCt0ZwP6ERQYe5v +; 52OwCdW8ILHtCitjQ1wJ5a/pBJAB7YHej+oFAA/rxeAMFawDcd7hnECsP+ioqKIf6NjY0ZCtH+k5OT +; If6Dg4MKO9cs1VXTM/6ysrIb/mdnZw7OVTv+mZmZ/vDw8CbAloj+pKSkOz/FMMA/0A7SHc7+hISE/t +; jY2BL+WVlZVSzSpIj+oKCg/s7Ozv53d3c71wrdGcAEHub+djsAnVvC/oRCAB7S/qdUABw1NB7YHec2 +; /r1fAMBWwZ5aHe8ZxArBM/61tbUTGArUtYj+yMjIg4gkO9Ys1VXSDP7Nzc3+lpaWLEDPVcG8iP7V1d +; UXfwgWGD/EVT/RDtIdzCj+srKyuIj+b29vHcEs1L+IFv6fn58KO9UK3BnBBB7mPp1bwv6EQgAe0yv+ +; w2IANf6ZTQAe1x3n/qFQAAfBVsD+kUkAHe8ZxArA/nt7e/7Q0ND+nZ2d/l9fXwrWo4j+pqamKf51dX +; U71SzVVdAZ/qKiog8UDtBVwzf+qqqq/vj4+CbAj4j+kZGRDj/CMD/RDtEdzBD+0dHREf5dXV0dwSzW +; qoj+sLCwtIgJO9MK3RnBBB7mPp1bwgse1KK2/rFZAKTUNx7WHeaitv6+XwDBB8CdazYd1f6USgD+y2 +; YAIx3VGcQo/qOjo/7Ozs4uhIjZOyr+0dHR/pubmxk70yzUVdAY/sPDw4SIKA7RP8X+gYGB/uLi4hcm +; nIj+vb29/mNjYz/BMD/QDtIdy/6fn5/+1dXV/nx8fB3DLNU7wf6Hh4cw/o2NjTvSf9wZwv6ERQafw+ +; b+djsAnVvCCx7WAaOlHtYd5v6jUgD+wGEAUlrAWv6gUAAd1f6tVwA1wf65XQA+HdMZw7OI/sfHx4aI +; ApiI2jvAAv62trapiP5ra2s70SzVVc/+kZGR/s7Ozv5+fn4dDtI/xq6I/ry8vP78/PyjiMCFiP6AgI +; A/0w7SHcmyiB6NiAIdxCzVO8Ir/r6+vpmIAjvQCtwZwgSfw+Y+nVvCCx7wHeWjpf7BYQDBRlbA/no9 +; AJs90/6CQQD+xWMApuLDnVv+jkcAHdIZwjQh/oaGhv5bW1vbVcP+i4uLMP6JiYkKO88s1VXNIC4fIB +; 3ADtI/xx00/u3t7SbAmIj+q6urCj/RDtIdyP6MjIz+2dnZGv5ZWVkdxSzVO8MZ/pmZmQP+fHx8O84K +; 3RnCBB7mPp1bwgse8B3lOP7BYQDCVv6hUQD+cDgA0yU1xhQAHdEZwKqI/ry8vMArCtw7xLGIIo6I/m +; RkZDvOVdVVzP6AgIAW/o6OjiwdwQ7SP8m3iP7Ozs7+/v7+JhcwP9EO0R3HqIj+ubm5IjodxizVO8a5 +; iP7Ly8v+p6enKDvMCt0ZwgQe5v52OwCdW8ILHvAd5KOl/sFhAMT+f0AA/nA4ANEAEv7RaQDGMT4d0h +; n+gICA/tPT0/6Wlpb+Xl5eCt1VxQr+np6e/tDQ0P56eno7zSzVVcqmiP6pqakTGB3DDtFVyxn+oqKi +; /vT09CbAP9AO0h3G/nt7ezD+nZ2dCh3HLNU7xyQc/snJyf5wcHA7ywrcGcMEHuY+nVvCCx7wHeT+nk +; 8A/sFhAMP+rVcA/nA4ANH+mk0A/tFpAMcEHdT+rKysvoj+cnJyGcAK3TvHvIj+zc3N/qGhoTc7yyzU +; Vcoj/sjIyP6goKAZHcQO0T/NG/7e3t4X/pqamv5aWlo/zg7SHcQ7/qenpyUQHcks1TvJ/oGBgRL+lJ +; SUO8kK3RnBf/6urq7+nnBBHub+djsAnVvC/oRCAB7wHeOjpf7BYQDEMv5wOADPPi81xv6/YAD+fD4A +; HdT+hFcp/qampv5iYmIZwQrdVcimiP6vr6+2iP5vb287ySzVVcg7/piYmP7MzMz+eHh4gIjFDtI/zq +; uI/rS0tP79/f3+xsbG/mhoaD/NDtIdw7aIKYGIJB3KLNU7yq2I/ri4uKWIPjvHCt0ZwCP+1NTUGP5m +; Yl7+dE4o/oRFBh7k/nY7AJ1bwgse8B3j/phMAP7BYQDD/rJZAP5wOADQAv7LZgA1xf6nVAAd1f6Tc1 +; T+gICAGcMK3DvL/oWFhSH+kJCQCjvHLNVVxw3+vb29k4gzHcZV0j/PDiYXioj+iYmJDj/LDtEdw/6T +; k5P+2NjY/oSEhP5YWFjLLNU7zAr+kpKSIf6Dg4M7xgrdGf6ZmZk3MRnB/mdWRf6CRwse4z6dW8ILHv +; Ad4qOl/sFhAMT+iUUA/nA4ANH+gkEA/rldAP7RaQDCFP6IRAAd1Ktc/paIev5nZ2cZxArcVcw+/r6+ +; vpmI/mZmZjvGVdVVxv6Hh4f+0dHRHh3IDtE/0f7p6ekmwJuI/rOzsyQ/yQ7SHcER/sDAwAj+aGhoHc +; ws1TvOtogPiIj+X19fO8QK3D4tF/5paWkZw57U/nROKP6HRADi/nY7AJ1bwgse8B3i/phMAP7BYQDD +; mz3+cDgA0z7+p1QAFKO1/q1XAB3V/pJvTCUoGcQK3TvO/paWlv7T09P+gICAO8Us1FXFJP6vr68IHB +; 3JVdFV0Q0P/vz8/KOIVf7d3d3+eXl5P8gO0h3ANf7X19f+lZWVOx3NLNU7z6SI/qOjozgQO8MK2/6G +; hob+3t7e/pSUlP5dXV0Zxv5nVkUEHuA+nVvCCx7wHeGjpf63XACl08P+jkcA/nA4ANX+iEQApuId1f +; 6ZhG/+b29vGcYK3VXPt4j+ysrK/qioqP5gYGA7wizVVcS/iP7MzMz+mZmZOx3JVdI/0g7+mpqa/vLy +; 8q2IwJWI/qCgoBk/xg7SGf6vr6+8iAUdz3/Vf9H+e3t7NP6cnJwKO8AK2qOI/re3txYUi4jJntT+ek +; sdHt8+nVvC/oRCAB7wHeEC/sFhAMOda/5wOADt/oNVKP6RioP+YGBgGccK3DvRKA3+ysrK/nJycjvB +; LNVVwgr+oKCgLf5zc3Mdyw7SVdBVP8Ey/tbW1hcmFzj+bGxsiojFDtEj/tDQ0P6mpqY3HdAs1TvSEf +; 6zs7OwiDo7Ctm7iP7Z2dn+paWlJBnM/m9RNAQe3T6dW8L+hEIAHvAd4KOl/rdcAKXTwwL+cDgA7P6X +; e1/+enp6GcgK3VXT/n19ff7S0tL+l5eXCjtV1VXBCf7CwsKIiAYdzFXRP9EwP8IZGP739/eoiBeOiP +; 6QkJAdP8MO0P6ampr+1tbWCB3RLNV/1Ar+i4uL/tXV1f6KiooK2BX+29vbCP5bW1vAGc3+ZFdL/n9I +; ER7cPp1bwv6EQgAe8B3gAv7BYQDDnWv+cDgA6/6GWSv+mI2D/mVlZRnJCt071KqI/rm5uaSIHCzUVc +; As/o6Ojhb+gYGBHc0O0lXRVT/EBP7k5OQmwDn+u7u7/mNjYz/BDs+wiP7FxcWRiP5kZGQd0izVO9ay +; iP7BwcGUiAIK1LCI/s7OzomI/mZmZgrBGc6e1P50Tige2/52OwCdW8ILHvAd4P63XACl08P+jkcA/n +; A4AOr+l3hYPBnLCtw71/6Ojo7+1dXV/oWFhSzTVREQPf5nZ2cdzg7SP9FVP8UR/r+/v/77+/ukiBeE +; iDU/wA7O/oiIiP7Y2NgH/lhYWNQs1TvXKP6cnJw0GwrS/oyMjP7e3t7+jY2N/lxcXArCGdD+Z1ZF/o +; JHCx7Z/nY7AJ1bwf6USgD+p1QA/oxGAB7vHd8C/sFhAMOda/5wOADpq1z+mYh2/mtraxnLCt1V2An+ +; xcXFJ/5iYmIs0f59fX0W/pKSkixVz1XRVdEwwD/GDgP+7+/vJsCYiP6pqan+Xl5eDsw3HwAJDsAd0y +; zVO9gKu4j+zc3N/qSkpCgKziT+vr6+q4j+bW1tCsQZ0Z7U/npLHR7YPp1bwP6tVwD+0WkAwP6+YAD+ +; kEkAHu4d3/63XACl08P+k0oA/nA4AOj+hlwy/pKPizcZzArcO9gswAr+oKCg/s7Ozv53d3cszjf+pq +; amvoj+b29viYjQDtI/0TDAP8i4iP7Q0ND+/v7+fxf+1NTU/nJycg7K/nh4eP7T09P+n5+fCg7BHdMs +; 1TvYCsAz/qysrLuINgrMCP7b29v+np6e/l9fX5yIxRnT/m9RNP6ERQafw9Y+puL+xWMANcKeWv6nVA +; Ae7f5wOADeAv7BYQDDnWv+cDgA6P6agmn+dHR0Gc0K3TvYVcIuFjcZLMsF/sfHxxEom4jRVdJV0TDA +; P8k7/qWlpf729vYmwJGI/piYmDsOxn/+o6OjEi4Owx3TLNU72ArC/oSEhP7V1dX+kZGRCskZ/qqqqv +; 7X19c9CscZ1P5kV0v+f0gR/odEANX+nU8ANcYP/oxGAB7r/nA4AN7+t1wApdPD/phMAP5wOADm/oVX +; Kv6Rh3z+Y2NjGc4K3TvYLMOniP6ysrKwiP5sbGwsyDv+lZWVOP57e3v+WFhY01XRP9FVwT/LG/7f39 +; /+////wEAT/mdnZw7EOv7KysqGiCQOwx3ULNU72ArDroj+u7u7wP5mZmYKxjYS/q+vrwIKxxnWntT+ +; dE4oBB7TN/6xWQD+zGcANcUc/p5QAB7q/nA4AN3+jkcA/sFhAMOda/56PQCbPeX+lHVV/oODgxnPCt +; 072CzG/oeHh/7U1NQpOyzFPioy/mRkZJSI0w7SVdEwwT/MJP64uLj++vr6JlWIiB4dDsH+j4+P/tnZ +; 2f6GhoYOxR3Tf9Y72ArEGf6VlZUS/oCAgArEMP7e3t4PCsoZ1/5nVkX+gkcL/odEANT+mU0A/shkAK +; XDxgX+jEYAHuj+cDgA3f63XACl08P+o1IA/nA4AOSrXP6Zi33+aGhoGdAK3VXYLMcN/sDAwJSIESzD +; Mf7R0dEaLMBV0w7SP9FVwT/NDhr+7Ozss4jAmoj+sLCwJMD+vb29oogNDsYd0yzVO9gKx7eI/sjIyI +; SI/l9fXwrAEf7FxcWciP5qamoKyxnYLv53TSIe1Df+tVsA/tFpAMaZH/6VSwAe5/5wOADc/olFAP7B +; YQDEFJs94/6TcE3+k5OT/l1dXRnQCt072CzK/pmZmTT+fX19/llZWcAk/q2trbOIOizAVdQO0T/RMM +; I/zQ7As4g8/v39/aKIF/7c3NzA/peXlyxAxx3TLNU72ArIpYj+pqam/szMzP50dHSviP7d3d3+l5eX +; /l1dXQrMf9r+bFM5/oRFBh7UHxyitsWZHx7n/nA4ANz+t1wApdPDOP5wOADj/puGcf5wcHAZ0grdVd +; gsyyP+y8vLIP56enoa/p2dnQoswVXTVdI/0TDCP80OwSz+np6e/vT09KuIwAE3GQ7HHdMs1TvYCsr+ +; goKC/u/v75uI/nNzcwrOGdv+ZFdL/npLHR7UpcP+ul0ANcSZHx7n/nA4ANv+iUUA/sFhAMQUmz3h/o +; pcL/6lnpckGdIK3TvYLM0V/tXV1b6I/o2NjSzDVdMO0j/QVcM/zQ7CBf7Y2NiviBcmFxo6DsZ/0yzV +; O9gKybmI/tfX1xg9qYj+ampqCs0Z3C7+dE4o/oRFBh7Torb+rFYAHDXCmR8e5/5wOADb/rJZAKjww/ +; 6jUgD+cDgA4f6YfGD+mJiY/tTU1P6Wlpb+XV1dGdEK3TvYLMw6/sDAwBgPIf6UlJQ7LMBV01XSVdEw +; wz/NDsH+l5eX/tfX1/5/f38G/rGxsf74+PiniBeNiP6Ojo4sQMQd0yzVO9kKx/6dnZ3+3Nzc/n9/fw +; p/B/7V1dX+iIiICswZ3v5nVkX+f0gRHtT+mU0A/sNiAKfxwSge5/5wOADa/oRCAP7BYQDE/no9AJs9 +; 3/6HWiz+mpCF/mZmZhkN/rm5uaWIKxnPCt1V2SzL/ouLi/7Q0NAxLMCsiCpV/mdnZ5GI1A7SP9FVwz +; /NDiD+wsLCDCAOwTH+5+fnuIjAnIj+uLi4/mNjYw7DHdMs1TvZCsWuiP7MzMwMLwrCs4j+w8PDj4j+ +; YmJiCsoZ3y7+dE4oHtQ3/rFZADXAKB7n/nA4ANoIqPDD/qhUAP5wOADf/ph5Wf6MjIwZw/6Tk5P+09 +; PTDxnNCt472CzKAv6zs7OoiA0sw/6QkJD+1NTU/oODg/5YWFjTDtE/0TDEP80x/tjY2CX+WFhYDsOw +; iBP+/Pz8JlWCiP5+fn4Owh3TLNU72QrE/omJif7e3t4lGQrEo4j+n5+f/s/Pz/55eXkKyRnh/mdWRf +; 6CRwse1AH+yGQAKB7n/nA4ANmo8P7BYQDEMv5wOADdrEv+lYBr/m1tbRnFtYj+xsbGiYgzGcsK3VXZ +; LMn+e3t7/s7Ozv6VlZU7LMUY/sfHx4SI/mBgYJiI0A7SP9FVxD/LCiO2iBgOxh3+lpaW/vHx8SbAl4 +; j+pqamNw7AHdMs1TvZCsIV/ru7u7CIJwrIvYgW/qGhof5dXV0KxxniLv56Sx3+h0QA1KK2/qxWAB7n +; /nA4ANkR/sFhAMMI/nA4AN3+gVQm/o6HgP5fX18ZxxX+pKSk/s3NzT0ZyQreO9gsyCgRD/5xcXEsx1 +; U7/qOjo/7MzMz+dHR0Hc8O0lXQMMU/yr+I/tHR0f6ioqIKDskUEv7+/v5/FzT+cHBwDh3TLNU72QrB +; v4j+2traJAYKyqmINg8YCscZ4wMEHv3+cDgA2KXT/sFhAMT+iUUA/nA4ANz+nIRr/nd3dxnL/nx8fP +; 7Q0ND+nZ2d/l1dXRnGVd5V2SzHtoj+xcXFgoj+Xl5eLMhVwQwl/pqamgodzA7SP9EwxT/IDv6fn5/+ +; 1NTUDD8Oyxn+qampHybAj4j+lZWVCh3SLNV/2QrAL/7Y2NgbCs7+h4eH/tXV1f6Pj48Kxhnk/mRXS/ +; 5/SBH+h0QA/P5wOADY/qNSAP7BYQDDmz3+cDgA2/6GWCv+lIl//mRkZBnNqoj+s7OzExgZxQreO9hV +; xjv+k5OTB/5+fn4syVXDFf60tLSsiP5qamodyw7SP9FVxT/Hs4j+x8fHI/5hYWE/wA7NF/7h4eG+iM +; BANS8d0SzWO9gYNCMRCtAr/r29vZyIIArEGeWe1P50TigEn8P6/nA4ANcU/sFhAMT+jkcA/nA4ANr+ +; lnZX/oWFhRnR/oyMjP7U1NQpGcMK3jvYLMatiAyciCAsylXFPCE8LB3IDtI/0TDGP8Yp/tjY2DwOP8 +; EOzqyI/ry8vP76+vqliFWGiAD+WFhY0CzWO9f+kZGR/t7e3jwK0yj+mJiY/tPT0/5+fn4Kwxnn/mdW +; Rf6CRwse+f5wOADXOP7BYQDDnWv+cDgA2atc/paEcxwZ07CI/sHBwRAgGcAK3lXZLMX+goKCJf6Ojo +; 47LMpVxxz+wsLCj4j+YmJiHccO0lXRVcY/xKmIDBMcP8MOzx0H/u3t7SbAmoj+rq6u/mFhYR3OLNY7 +; 1SQTooj+ampqCta5iBr+qamp/l5eXgrCGecu/ndNIh74/nA4ANam0v7CYQBaw/6ORwD+cDgA2P6EWj +; D+j4uINxnVN/6cnJwl/nx8fArfO9gsxKeI/qqqqriI/m5ubizMVcgs/pubmyX+eXl5HcUO0j/RMMc/ +; w/56enr+1dXV/pqamiw/xA7RtYj+zMzM/v39/SZV/tjY2P52dnaCiM0s1n/U/oCAgP7c3Nz+mZmZKE +; DYJP6pqan+ysrK/nJycgrBf+n+bFM5/oRFBh72/nA4ANb+rVcA/s1nAJs9RsGda/5wOADY/ph/ZyMZ +; 2bqI/s3Nzf6lpaU3Ctw72VXDAQv+oKCg/l1dXSzMVcsBKf6ioqIZHcMO0lXRVcc/wR3+qKioJf5zc3 +; M/xg7SOzP+9PT0JsCSiP6cnJz+XFxcHcss1jvSCv6vr68h/nR0dDvBCtn+gYGBIf6Xl5cKwBnq/mRX +; S/56Sx0e9f5wOADVo7X+0WkAwFacTJ1LwAL+cDgA1v6EVin+lo+I/mJiYhnbM/6srKy7iP5xcXEK2j +; vZLMIK/pqamik9LM5VzCj+ra2tuIj+bm5uHcFV0j/RMMg/wLeIKf6rq6s3P8cO0h3APf7c3NwXJgj+ +; yMjI/mpqah3KLNZ/0RT+1dXVOiQ7wgrarYj+uLi4p4j+aWlpCn/qLv50Tij+hEUGn8Pz/nA4ANX+rV +; cANcKeav69XwAd1v6SclP+f39/GdwKwf6EhIT+1NTU/pKSkhkK2DvZLMEr/r6+vpKIMyzOVc/+gYGB +; Ev6QkJAdwA7SP9FVyD/+k5OT/tjY2P6CgoI/yQ7Sf8GoiP60tLT++fn5pohVi4j+jIyMLB3ILNY70P +; 6ZmZn+3d3dEzvECtsZNCH+hYWFGez+Z1ZF/oJHCx7y/nA4ANQ+NcOdW/6CQQAd1Ktc/pWHef5nZ2cZ +; 3ArDroj+u7u7wA0K1lXZVcE8/tHR0f6Hh4c7LM9V0K2I/ry8vJuIIA7RP9Ewyf7AwMAqID/KDtIdwx +; 7+6enptojAm4gQ/mNjYx3ILNU7zqyI/snJyZSIPjvFCt21iA+KiCQZ657U/nROKP6HRADx/nA4ANT+ +; rVcA/tFpAMIvHdX+km9MFv5dXV0Z3ArFGf6VlZX+0tLSEwrUO9kswDP+sbGxCP5qamos0FXTEgP+f3 +; 9/DtA/0VXJEh0/yw7SHcSxiP7FxcX+/Pz8o4gXgIj+fHx8Hccs1TvN/oaGhjf+kpKSGTvHCt0G/qKi +; ov7Ozs7+d3d3Gez+Z1ZF/oVGBh7v/nA4ANOjtTXC/pRKAB3V/piDbicZ3QrIt4gtCf5gYGAK0VXaLP +; 55eXk4/pmZmQpA0FXUVSctgIj+Xl5eDs0/0TDKP80O0h3FLP6ampr+8vLyJsCViP6jo6M3HcUs1n/K +; Nz0H/m9vbzvJCt/+e3t7/tDQ0P6fn58oGeko/peXl/7t6eX+rH5QHu7+cDgA0y81wP6/YAD+fD4AHd +; T+glUn/pCJghUZ3QrKFf6mpqYa/nZ2dgrQO9koMy3+dHR0LNJV01XBO/6lpaUL/nFxcQ7MP9Ewyj/N +; DtIdxzL+1dXVFyYX/s/PzzYdxCzWO8kf/tnZ2QL+X19fO8oK4KqI/rKysrGIGBnnqoj+xsbG/v39/S +; YX/ruegP6ERgYe7P5wOADSPv7RaQDA/qBQAB3V/pZ6Xv55eXkZ3grN/n5+fjT+mpqaGQrNVdknIoeI +; FZmI0lXUDsP+enp6NP6Xl5csQMk/0VXKu4g/zQ7SHcgo/q2trf739/cmwI6IAzsdwizWO8j+oaGh/t +; ra2jn+WlpazArgGcD+ioqK/tXV1SkZ5f6FhYX+6urqtIgmCD0V/mRXS/5/SBEe6/5wOADS/qdUABL+ +; gkEAHdT+hlkr/peMgiAZ3grPLxCriP5ra2sKyzvZ/pGRkRb+gYGBLNRV01XFqoj+tra2p4j+Z2dnDs +; g/0VXJ/pmZmf7S0tI/zQ7SHcoE/uTk5CbAnYgILx3Bf9Y7xrCIFhD+ZGRkO84K4BnAsIg1HyAZ4Qb+ +; tLS0OSbAjYj+iIiIGcGe1P50Tige6v5wOADRo7X+p1QAHdX+l3dYLRnfCtL+jo6O/tTU1C0KyTvYrI +; j+t7e3Pf5nZ2cs1FXUVcf+i4uLIf6FhYUOxj/RMMgR/sXFxYmINz/NDtMdyiD+v7+//vv7+6SIF4WI +; Ex3ALNc7xP6NjY3+3t7eGgo7zwrgGcGiiP6bm5sD/nx8fBnfu4j+3d3dFyYX/svLy/5oaGgZxP5nVk +; X+gkcLHuj+cDgA0aO1HdSrXP6Zh3Y6Gd9V1LKIIiP+Y2NjCsZV2P6AgIAWNDss1FXUDskcIif+YGBg +; DsM/0jDH/oaGhv7V1dX+hYWFP1U/zQ7THcss/pKSkv7v7+8mwJmI/qurqxUdLNY7whX+v7+/qIj+a2 +; trO9EK4RnCuoj+zc3N/qampgYZ23/+np6e/vf396iIwC7+m5ubKBnGntT+d00i/odEAOf+cDgA5/6F +; WzH+kY6K/l5eXhnfVdY3/p+fn/7Ozs4MCsU71qWI/qioqLyIBSzWVdMOyx3+np6eBx8Owj/RMMYK/r +; a2tqmIIDDBP80O0x3Nt4gW/v39/aKIF/7V1dX+dHR0LNY7wf59fX3+29vb/pycnCg703/hf8IC/qys +; rLyIFBnZOhb+/f39Jhf+2traARnK/mxTOf6ERQYe5f5wOADm/pqBaQEZ4ArZvYgHMygKwlXWuIgt/q +; Ojoygs1lXUDs2+iDg3Ow4/0lXF/nZ2dv7S0tL+lpaWHTDCf84O0h3OGS/+9fX1JsCRiP6ampoZLNR/ +; wP6rq6v+1tbWHzvVCuEZxP6EhIT+1dXV/pOTkxnX/ouLi/7u7u4mwJ2I/q+vr/5eXl4ZzP5kV0v+fE +; oXHuT+cDgA5P6FVyr+mJGK/mNjYxngCtsCNraI/m5ubgrAO9UK/peXlzj+e3t7OyzWVdRVzyg2tIj+ +; a2tri4jRVcQ/Mwf+b29vMMQ/zg7SHdAb/t7e3ibACAAcLNM2/tPT0/6tra3+YmJiO9cK4RnEHP66ur +; qiiP5oaGgZ0zM5/vz8/CYXioj+g4ODGc8u/nROKAQe4v5wOADj/pN0VBMZ4Qre/oeHh/7U1NT+j4+P +; CjvUHP68vLyWiBE7LNZV1Q7RMRIpDj/OMMQc/snJyf6np6cZMMU/zg7SHdEz/ri4uP76+vomF4mI/o +; mJiTss0P6WlpY3MTvZCuIZxCj+lJSUEiIZ0f59fX3+4+PjFyZA/sPDwxEZ0v5nVkUr/odEAOH+cDgA +; 4atc/piKfP5oaGgZ4Arhr4j+vb29nIj+ZmZmlIjSD/7R0dELO8As11XUDtJVIP6+vr6WiDM/zTDD/o +; 6Ojv7V1dX+fn5+/lVVVcc/zg7SHdIsGv7r6+smwBsj/mNjYyzNAgCaiA072wrhGca2iC0Y/mBgYBnN +; KP6np6cbpYjAlIj+k5OTGdUu/nROKB7g/nA4AOD+k3BN/pGRkSgZ4AriOwr+mJiY/tHR0f5/f387zz +; P+rq6usYj+bGxsO8As11XUDtM/wf6VlZU0/nx8fD/LVcIG/ry8vD0zMMg/zg7THdOziC3+/f39oohV +; /tzc3P56enosy/6Dg4P+3d3d/pWVlQoswDvaCuJ/xhX+paWl/szMzP51dXUZy7SI/tTU1BcmFyE2Gd +; j+Z1ZFBB7e/nA4AN/+moVwBRnhCuJVwrmI/srKyv6pqan+Xl5eO8y9iCn+nJyc/lxcXDvBVdZV1Q7S +; P8M2/snJyf6kpKQZP8lVwf58fHwhFg4wyT/ODtMd1Dv+np6e/vPz8ybAMv6hoaH+Xl5eLMcZ/rOzs7 +; 2IBSzCO9sK4hnH/n5+fgP+m5ubGcn+lJSU/vPz8ybAm4j+pqam/l1dXRna/mFZUP56Sx0e3f5wOADd +; /oNVKP6Si4QVGeBV4zvEFf6oqKg8IzvJKP6fn58LHzvCLNZV1Q7SVcU7Db+I/m5ubj/HMMAd/qurqz +; z+a2trMMpVP84O0x3VLB/+2NjY/v7+/iYXKf5tbW0sxbqI/tfX1y/+X19fLMM72wrjGceqiP61tbWt +; iDoZxaiI/sPDwwgmF4WIORndntT+b1E0BJ/D2/5wOADc/pd7XyoZ4QrjO8c1/tLS0v6WlpYKO8YJ/s +; HBwYuI/mFhYTvCLNdV1FXTVcc5NBIdP8QwwCf+zs7O/qCgoAowyyE/zw7SHdUswAb+sLCw/vj4+CYX +; joj+kJCQCizC/p6env7c3Nz+fX19LMY72wrjf8j+jY2N/tXV1f6KiooZw/6Dg4P+6OjoFyZA/ru7uy +; QZ4P5kV0v+f0gRHtr+cDgA2v6HWSz+mY+ELxnhCuNVyS/+t7e3poj+aWlpO8Q4/tDQ0DE7wyzXVdQO +; 0z/JJP64uLiiiP5lZWU/w1X+lpaW/tTU1P54eHgwzFXAoojPDtMd1CzCMf7m5uYmwJyIGyAsr4j+zc +; 3Ni4ggLMc72wrkGciyiBMB/mNjYxmiiAX+/Pz8JsCPiAsZ457U/nROKB7Z/nA4ANn+mHlZGhnhCuQ7 +; zP6QkJD+09PT/oWFhTvBIAEbDTvDLNdV1Q7SP8z+jY2N/tTU1P6BgYE/wQL+w8PDjoj+YGBgMM1VwD +; /PDtMd1CzDDf7BwcE5JheDiP6ioqL+3t7eODssyTvbCuQZyAb+n5+f/tDQ0Bb+2traFyYX/s/PzysZ +; 5v5nVkX+gkcLHtf+cDgA16xL/pR/av5sbGwZ4QrkVc60iP7FxcUU/mFhYb2IFv6VlZUKO8Ms11XVDt +; M/zbSIAIaI/l5eXv6CgoL+1dXV/oiIiD9VzSHBP88O0x3VLMT+lpaW/vDw8CbAQBAVLMo72wrlGckQ +; CCbAmIj+nZ2d/l1dXRnontT+eksdHtb+cDgA1v6GWCv+j4iB/l9fXxngCuU70Df+pqam/uHh4Y+IIz +; vEVddV1Q7TVc8d/qWlpf7k5OQ0DTDOIcI/zw7THdUsxf6SkpL++fn5pYgmF/7T09MjLMk73ArlGcYc +; /svLywgmFwwLGez+b1E0/oRFBh7U/nA4ANX+nIRrGwA2Gd5V5jvRtoj+xsbGATT+n5+fCjvCLNhV1A +; 7TP9C+iDT+qqqqJf6ampodMM0hwj/QDtMd1CzE/qenp/7Y2Nj+fHx8/qmpqf729vYmwJCI/peXlxks +; yH/cCuUZxC3+7OzsJsAI/rOzswv+1tbW/pCQkBns/mRXS/5/SBEe0/5wOADT/oVYKv6TiH4CGS3+1N +; TU/pGRkRncCuU70Qr+lZWVB/59fX07Av6wsLCyiP5sbGw7wCzYVdVV01XPBhYjg4go/rKysq6IDTDL +; VcM/0A7THdUswQn+0dHRBf5iYmIswP5+fn4kJsBAEw0sxzvcCucZwBU9/vz8/CbAi4j+hYWFGcCviP +; 69vb0b/mdnZxnrLv50TigEHtH+cDgA0v6VdlYxGcI6/r+/vz0NGdgK5jvRDRsQ/mVlZTvC/omJif7U +; 1NT+i4uLOyzXVdUO0z/ODf7IyMiCiDcwwgAS/oiIiD8wyFXEP9AO0x3VLMD+kpKS/t7e3v6GhoYsw6 +; uI/ru7u/76+vqliFWHiP6Hh4c7LMY73ArnvogkFyZA/sXFxf5lZWUZwij+l5eXEv6AgIAZ7P5nVkX+ +; gkcL/odEAND+cDgA0Ktc/pWDcv5paWkZxCj+mJiYEv6BgYEZ1QrnVdEx/tDQ0DgKO8SwiP6/v7+XiA +; Is1VXVVdM/zhr+1tbWBP5WVlYwxCAmFP5gYGAwx1XEP9AO1B3UFRPADSzFO/6Ojo7+7e3tsojAmogF +; MyzFO90K5Bn+oqKiDCbAloj+lpaWGca3iP7KysqBiBUZ6y7+d00iHs/+cDgAz/6MZj/+jYqGKBnHuY +; gL/qioqP5hYWEZ0QroO9Ak/qysrLWI/m1tbTvGLAr+mpqaJf57e3ss01XVDtNVzaiI/rq6un8RPzDG +; P/6YmJgl/nh4eDDFIcU/0Q7Tf9M1/tzc3P6Xl5cKLMgYKQgmF/7Z2dk9gYjFf90K4hj+0tLSFyYX/t +; fX1/5xcXGLiMgkDf7Ly8syGez+bFM5/oRFBh7N/nA4AM7+l35mFBnKFf6qqqoLARnOCuk70BALBig7 +; xizCARr+paWlKCzQVdVV1D/M/np6ehL+kpKSHT8wybuIGv6hoaEsMMIhxj/RDtMd0Tv+sLCw/tLS0v +; 5ycnIdLMoKJP709PQmwJOI/p6env5eXl4sxDvcCuH+kJCQ/vHx8SbAnIj+qampKAoZyjUS/piYmBns +; /mRXS/56Sx0ezP5wOADM/oNWKP6UjYb+YWFhGc01A/6YmJgoGcoK6TvQGf6cnJwaPTvHLMQVK72I/n +; BwcCzOVdYO0z/LHQ0aGD/BMMo7KwD+a2trMMFVxj/RDtQdzwX+1dXV/qmpqf5fX18dwCzMPf7b29sX +; JhcL/mxsbCzDO91/3qeI/r+/vwiiiBeHiP5+fn4Kw3/JrIj+t7e3qYj+ampqGeue1C/+hEUGn8PK/n +; A4AMv+mX1hFxnQPv65ubmkiCsZxwrqO9A6/r+/v5CIMzvHLMf+goKCEv6SkpI7LMtV1g7TP8sJKf6j +; o6MZP8FVzRf+0tLS/o+PjzAhxz/SDtMdzv6ampr+3Nzc/n9/fx3CLM0k/rOzs/75+fmmiFWMiAf+Wl +; paLMI73Qrc/oCAgP7m5uYXJkD+vr6+JArGf8n+kJCQ/tXV1Q8Z7P5nVkX+gkcLHsn+cDgAybYf/pyR +; hz4Z0yX+1NTU/oeHhxnECutV0P6Kioo0HjvILMmtiP65ubmiiC8syVXWDtM/y/6Tk5MwGz/CVc8z/r +; q6up2IM5KIxj/SDtMdzK2I/snJyRsgHcQszh7+6OjoJsCciC4RLME73grZGf6srKw5JsCRiP6NjY0K +; yRnJs4gAjIgzGeue1P50Tij+h0QAyP5wOADI/pFuSwcZ1rSI/sbGxoiIERnACus70BH+srKyqoj+am +; pqO8gsyzsS/tLS0hMsx1XWDtM/yjP+wcHBk4gkP8Mw0RYD/n19fSHFP9IO1B3K/oWFhf7d3d0lOx3F +; LM+wiP7FxcX+/Pz8JlWBiP5+fn4swTveCte3iP7X19f+/v7+Jhc0/mxsbArMGcijiP6hoaH+zs7OPR +; ns/mdWRf6ERQYexv5wOADH/peCbf5tbW0Z2DcCB/53d3cK61XQ/nt7e/7Nzc3+mJiYGTvILM42D4aI +; NyzEVdZV1FXJNf7W1tb+i4uLDlXDMNEhwCsPgYgKIcM/0w7THcgZ/ri4uCkYHcgszzv+mZmZ/vLy8i +; bAloj+paWlBpqIwDveCtX+l5eXECbAmYj+oKCgGQrPGci+iP7Q0NAV/l1dXRnrntT+eksdHsX+cDgA +; xf6CVCf+j4iBBhnZCsAb/s/Pz/6fn583Cuc70DcC/sfHx/5zc3M7ySzQKP6kpKT+y8vL/nR0dCzCVd +; ZV1D/IO/6wsLAAHD/EVdEhwg4C/srKyjYhwjA/0g7UHca+iP7Z2dkkKB3Jf9EB/tXV1RcmF/7Q0ND+ +; cHBwLDvfCtKriB7+/f39JheCiP53d3cK1BnGqYgUs4gYGez+b1E0BJ/Dw/5wOADE/p2FbP54eHiEiN +; gKxKiI/rOzs6+IGArlO9A2MYWI/l9fXzvILNQqNP6ampr+WlpaLFXWDtQ/yLyIJf6cnJw7P8RV0SHF +; LiX+lpaW/lZWViHAMD/SDtQdxf6ioqL+2dnZ/nl5eR3Mf9E3Cf739/cmwI+I/pSUlP5cXFw73wrQ/o +; WFhf7q6uomwAj+tra2BgrXGcb+iYmJ/tXV1f6Ojo4Z7P5kV0v+f0gRHsL+cDgAwv6GWCv+lYqA/mRk +; ZBnWCsk8/tTU1P6NjY0K4jvR/pKSkhY1O8lV1gIyrYj+aWlpj4jVDtQ/yP6cnJz+0tLS/nV1dT/FVd +; JVxij+tLS0qYggITA/0w7UHcIcFjL+YmJiHc4s0v6BgYH+4+PjvIjAOf6/v7/+Z2dnO98KzTf+tbW1 +; OaOIwI2I/oaGhgrbf8WwiP6/v7+YiCAZ657U/nROKP6ERQYewP5wOADB/pZ3V/6Hh4cZ1QrNCf7Bwc +; EQ/mZmZgrfO9A+/ri4uFU+O8ks2f6Li4v+09PTLf5YWFjTDtQ/xy/+xsbGiIgGP8Yw0SHJHhL+hISE +; MMA/0g7UHcE4/t7e3i0sHc8s0y/+vr6+/vv7+6SIVYaIADvfCsu7iDcXJhf+ycnJLwrfGcMo/pqamg +; P+fX19Gez+Z1ZF/oJHC/6HRAD+cDgAq1z+l4Z0/mpqahnTCtEZ/pubmzQI/ltbW9xV0f6BgYEl/pCQ +; kAo7ySzZVcAr/sHBwZGI/mFhYR3QDtQ/xy3+1tbWMf5WVlbHMNEhyy8EMMA/0w7UBv6/v7+miBwd0i +; zTO/6SkpL+7u7usYjAmYj+ra2tMzveCsgZ/p6enj0mwJeI/pmZmQrkGcK5iCn+p6enBhnrntT+hFow +; f/6QjIn+Xl5eGdAK17uI/svLy/6mpqYGCtg70aaI/qmpqbqINjvJVdodwgr+nJyc/s7Ozv54eHgdzg +; 7UP8Yo/re3t6iI/mVlZT/HMNEhzTA/MD/TDtP+fHx8/tvb2/6ampr+W1tbHdMs1TYH/v39/aKIF/7W +; 1tYfhIjfCsUrFggmF/7Z2dn+cnJyCul/M/6qqqo8/nFxcRnpBv6rq6sa/nNzcxnOCtwVGLqIFArWO9 +; Ey/sjIyP6ioqI3O8lV2h3FvIj+zc3NMwodyw7VVcU9Ev6VlZUsP8dV0SHPLv5iYmIwP9IO0R06/tXV +; 1TId1izVGf6lpaX+9fX1JsCSiP6bm5s3O95/w/6Li4v+7+/vJsCdiBgZCu3+goKC/tTU1P6VlZUZ5w +; H+y8vLLzMZywri/oODg/7S0tIwGQrSO9EZ/pmZmSkbO8pV2h3HFf6srKwA/m1tbR3JDtU/xA7+pKSk +; B/5vb28/yFXSVc/+mZmZ/tTU1P59fX0/0w7PGBI6Bg4d13/WG/7d3d0mwEAP/mtrazveCsAk/ry8vP +; 78/PwmF4mI/oGBgQrwroj+ubm5pIj+aGhoChnif/6Xl5cS/oCAgBnICugN/ru7u1UNCs870rCICJSI +; /mRkZDvKLNodyjES/o6Ojh3HVdU/xDoaLyg/yDDSVdAAAAAAAAAAAQ== +; thumbnail_QOI end +; + +; +; thumbnail begin 640x480 63884 +; iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAC68ElEQVR4Ae39V5QdZ5bnh+aLRg+6pm +; ete6WHWXddaR4k3atZXSNpNEbd1dO+ml1d0+yq6jI0RZCEBwgQIOG9twQS3nvvvbeEB+EJgPCGMISl +; N1WVM10h/jYqwJOJzDgRccJ8O86Otf6LIJB5TsQ5X3z7F9vW/PjHP/aS0k9/+lNv9OjR3scff+zV1d +; V5a9as8V566aXEXr+cfvKTn1SV+LyTVqdOnbzz5897/+W//BfvyJEjXosWLVJ5n6z0j//4j5mre/fu +; 3u3bt+UzPHTokHyGeZyHVv3sZz/LXH369PE+/PBDL+j45ptvvGXLlnm/+tWvcjlHTXrzzTe9s2fPev +; /0T//knThxwmvbtq2q868GBd2DL7zwgjdr1izvyy+/9L7++mtvwoQJ3s9//nMV+4dLwhawr/zn//yf +; vXfffdd7/fXXK37NJO1jTVLw9corr3hbt24Vo3fv3j1v8ODBAikGfu5DX0O9/PLL3vLly8XgPXz40B +; s+fLhsGHkAnAbg88UGuXjxYnn4YeOcMmVK2Y22muWaQXzttde8nTt3yvfX1PG73/1OwIYHJdfO3wX9 +; 8pe/9CZPnux99tlnsn/MnTvXe/HFF1VdQ7Wr9B7t0qWLd+nSJVn7Bw8elHtEy/6Sp7AFCxcu9H7zm9 +; 94n3/+uQB0w882CTkBgL169RLKZXPct2+f17x5cwM/heDXUAMGDBBPFk8vmzZtEjDM4zxchj5f7dq1 +; 886dOycejwsXLogHxIXzckGajB8aM2aMRDGCIPDTTz/1pk6dKsCj6drSVMuWLcXLgRPg6tWr4v3QdP +; 6mxtWsWTOJ5vFghENg4MCB8vea9qAshbf7/fffF1vAfzt06JD6e+YCgGx+eDx8yuXJj5Mx8NMNfqXi +; iW/Xrl1y81+/fl029UoWXJGgz9f48eO9r776Su6DVatWyX3h0vllKU2GLUjt27eX0CWbeFMHoMMDL+ +; Cj6drS0JAhQ7yPPvpIPpMNGzZYmLzA3zEPQGvXrhXPrqa9KQvV1tZ6X3zxhffrX//aW7JkifeLX/wi +; 83PIBADfeOMNoVsOPB9QroFfMaCvobj5gRy8IizsefPmCeRkfR5Z30jl9Oqrr3r79+8XSLhz547kkb +; l2jmlKk/GKI9b4/PnzJQcqyBuIURw2bJiqa0tKpP7gHfrtb3/rPXjwQD4HjJ6mazCFl+/lZc/Dy/vW +; W2/V+3dN+1fStsD3ft+8edPr2bNn7ueUCgDywhMnTpQcDzweS5culYRRA7/igV9D4RU5deqULPLTp0 +; 9L2DPt98z7JmpKffv2FYNHeHzPnj2yAbh6rklIi4GKql/9/B/K/ky3bt3E2AF7TR0AEF4RwmWuXWNa +; 8vPDgAGKncwTWh0qzfOEAWbOnBkI/Zr2uTjiwZ+6B6Jk27dvl4ci184xEQAkHLh3717ZCG/duiUXbu +; BXbOhrKGB/wYIFEvL85JNPJF+KhNck38O1m6dUbH54PAA/NsCxY8cWbpPTYIQq1Rsv/LW3vcUfiPhz +; uZ9nU9+4caOAXpA38PLlywKMrl53EiqtEEXkQvJ3mq7BVLnIc/ajgMePHw/9AKBpLyxnC1avXi3gR3 +; SMDiiuX18sAARUSPwk1MHTHpTLk66BX/WAX0Ph4iYnEBDavXu3rIeiQp8vNrwrV67IPeB7QDWcd5Bc +; NS5pavSv/rV3pk3NUx1t/V/J34X5XUKceH6DDnKA5syZU8jq1zZt2kh7KL/YqWEI0FRdojCQXDdSg3 +; AIjBw5UhwCUV5D035ZagvwfhMNO3bsmNwXms4fhQJANjES26Fc3+PDLxr4VSf4lQroozoYrwg5cIRF +; /YVVFOjzNX36dGlrwUaHB1RjTyzXjEfWIty74LX/vh78lWp98/+31+IXPyz7Oq1bt5aWGGz+TR0A0n +; vvvScPCRo+m3JivePhoAoUW0CbKHq8aroGU3pi7ycqiBccB1ElRUCu76N4vMkLRjzoae6PGAiAnTt3 +; FsrlSz158qRQroFfdUNfYxA3atQo8YoAgjTK5aGhCNCHaOKMIceg37hx42kVtAa5YBhcEWFePH1NwV +; +pN7D3S39U9vXY9HkowNsXFBIGmN55553IXhGXROrPli1bBPzu3r3r9e/fX/X1mNIRTY6BPx6MaB/W +; o0ePRF7Xlf0UW3D48GGxBUSCunbt6sy5xVWjAMjNPWPGjKddwGnmSZKngZ+BX1PCK0JoiJuf0BCNcj +; WDHxo6dKh4vTF8GEDCHa6ea16bvgbNePV/LAt+DcXvhCkQIQT6wQcfBBaI+OsHkNLweZWKHq9UNXJf +; U+yk8RpM2Ql2IC/68ePHsu5phpx0r8w89lda4HBNODnWr18v3m9X9v5K9AwAksh59OhR2dDI8YJyDf +; wM/MKIhwQqwugJiWeESjGNPfHwYGKwMXrc9ExCce0cs97YNcov9IgKf77CFohgDEiTIT0gyBsISFE4 +; p+Gz45ow3jgAaHqNUbem16awIvWBwhDWPaNF00yFSNsWkOZErvv9+/dlwplrtqAS1ZRC2YgRI7xHjx +; 7JxfrNPA38DPyiiEX19ttvexcvXhSAIldK0yxcHniYaoObnwehVq1aOXFeWWzaRVLDQo+48gtEwngD +; CY2SCxt0AFQ0z8eb7OpnR49Xipy4B86cOSP/7+q5mtwV8ET0kI4RRBNpkpxFj8ik9lzaHOEE02jHwk +; oAENCDcrlQv5knQGPgZ9AXFvoaijVFiTx9onhyIpzqcrIsGwfFHbj4MdKkQCS5mcQ5H1N0AWorXv8X +; icBfnAIRxmASKuUhOqhABLDq2LGjU58dxpmZpaQ9+MVONsfXVKnIm6aPJgfNkumZmtV7x917GXaA7S +; KSVeSZ7jV8OYQmcNXSzBPKNfAz8IsLfg2FyxyvCAaR3AkX8+gobsIgY5gpeqL4KetzyGpDLLIo3ghT +; 6FGJN3Dwy/+27HnwoDNu3DgJnQaFhAGtSZMmORFaBVxp54QTgGIn2jxZoYcpKdExgv2fvEAcAnjLsz +; 6HsLaAYQfYAkLXRZ/pXsOTHpQ7bdo0+ZAM/Az8KoW+huKhggbiQCDVUzTKdeUGoK0R69+f5EBD2yze +; N+vNr+gKau+StHivMCFhxmP6xqSpwy+uyHOKBj1eeUjjXMh9pel1XudiKq54oCCfGgDkniBvNk8Pc8 +; M9mWp9f7rJihUrMrMFearGr9g08DPwSxL6GorwEt4OvB7006N/Up43GEYOw4vRo8E5T6Rpv2deG12R +; RZHGvlb/TWbwV1ogEqZdDGt80aJFZecJA2B4y7P87LgHVq5cKeFe2tXQxNfm+JrSFh0jDhw48HRyDv +; yR5/ngndy1a9fT9jXVNNO9BgI38DPwSwv8Goq8J8YHcbOdOHHCa9u2beaLvnfv3gJ9nMP+/fultUUa +; 75PnplYNmvDK/5I5+MUtEKEnGgnlQQeeB7wiWXjgaF/Dw79f7IRRzvK7M1W3SHsg6kjHCB5A+HMeDx +; +0OaK3pT/Rqugz3RuqxsDPwC9t6GsoWkwwPgiviD9DkZs/7fflPfB4cLOz8ZDwziaQ1OtnvXlVqypt +; 75K0KBAJ0y4G47J161bJgwryBgJmVCCm8dnhkaSBNeuf6kzaNjUWhiNcF1dZrgWTbpFvTa4dBw8i5K +; Jm8b4AKNNsSP3x2xw19nNp2yS1AKgJ5gz68oe+xoSrnZYreOJ27Nghrvi03ot8LAo88HicPXtWWlsk +; 8bpZbFam75RUe5e0vIHlzh9AItRKu62gA0ADzgC2SmCsVHj5KPTzi52AzKReO29pWLumxkXHCHLu8I +; DTd5UuJGl+p9gCmrdzHzDhrH379qF+Ly3bpAoANQGdgZ+b4Fcqwq/btm0TrwizJAnPJn2z0ZAabyMb +; DJ7HSryNaW1KpjJGoswcX5e8gU2FhEuBhdQHf3JOUwcGitFTVCZWAkesd6CTFl/cZ7Rnwui6BHB5Sc +; v6L7r4LgYMGCC5sHjBN2/enEqvTPLQ8XyTh06bo7hh5yTtkwoA1AR0Bn5uQ19jUEU1Ll4RII1GuYSl +; Kn1dZlNiZDGkeBrJ94h7fqb8lFehRyXewD4v/3EoMJs1a5Y0yQ0KCVM1ybxtfj4q4PCARVN/wI+8Vw +; pN4ryOyWAxCxEC9gsyaE9Hx4ikXpdmzrzutWvXpDdhUuecll10AgA1AZ2Bny7wayjGBR07dkxuUvJC +; KBiJ+1qDBg0SoMTwMaAcj0eU309qczBVpjhzfF3RzG/P/ZVf/LgsWBCKZXJO0DxhcpVo1g/QhQUWev +; lh7HgA2rdvnxhBF8GqSNJ0b7kqHlDGjx//dA47TZkr6ZVJmyOq3HktPIvYgrTOPStbmToAagI6Az+9 +; 0NdQ5DzRIobefPRkolAjSsiW39+4caNAJAUmeE7C3JhpbQimeHKt0COutrf8A6/DCz8oCw4YJfpQ4g +; EP8gYCdHiyg16LIqv58+dLqMsvdkoyl9AUX5ruwdz3gDfekD6aHHSOIG0iyu8TRaL5NEV/ACA9CLM8 +; fxfsaSwA1AR1Bn7FAL+GwkVP02i/VQvh3HK/wwxiphjg8WAQOblT/2jQp06uFnrElRSIvPK/hvIGEq +; IlVBt0AHbkL5Ej1fD3MZoktnMPnDt3TiYZuA5FJoPDpsTDDGudHG4eZmjWHOazwhYwfg77QR4tM93z +; vA7X7GujAKgJ6gz8igl+paIqeN26deIVuXfvnhhHbv7GQI6h4/yc32S6qZsuz03AFKy05vi6ovXN/1 +; uv5S//riwIMBWEmanlCkQAPYCP3yFEVltbK15vP48W4+k69JjKS9M9nJZIZ6CPJl5wGvg31SuTz2v2 +; 7NnSW5DcWloeufQZumZjawz8dMu1BZWGaAuAVwRX/po1a8Sw+f/Gk53v8eCJj3wqgz596vLin6Y6x9 +; clb+CQX/3bskbfn5xDGkRQSJi2GRi5nTt3yv1BsRPtlazQo/jSdH8nIfJfyYNlnWMPWOel/06bIyI/ +; 2AJ6adLs3OXrccG21mgCOwO/6gK/UgF6jA/CK+L3MKOBNGEBEuSpdCytHHb5xjfVl+ZCj7ha8Nr/EC +; okTAiX/KegAhH+DZEqQbNpDfBiSk+a7v2o4vrI6yanD1uwbNkyyW/l7ygawftNm6M8ZwzHUV52tRAA +; qAncDPziC6/G1KlTxStCRRdPevQ2y3qGqqky+YaK4ghN7V2SFgUiYdrFYMwwdKQ3BEEgc0ypeHQdUE +; zZS9P+EEYUhJDbx7qn20PpTHdN19FQWdtU1QCoCdwM+pIRuSBAn+/1OH36tM0xdVRBBomiCE2wlpai +; FIj4k3OCDiCRqQrkSLkKIyY3pGkvaUx9+/aVCJCfE1tJY2cXlYU9VQmAmuDNwC8ZsWEtXbpUPH9+ci +; +GDoPHE6DfKFfTDV4khTU6gA7FEJogLQuFKRAh1EV7l6DiEN8bSAUwOVCuwofJTWnYa9jnmehE6g/R +; IMCPPpocjDqkY4SG6wirNO2qKgDUBG8GfsmJWY00huYpjxmONIj2bw7GBxH6IjF469atTVaHmZJTXO +; NC8YMmKMtavjewsc+OHFhy/IA/2iNR9U7Fb9CBcZw2bZr1ADRVJJf2HmwBs9y5D86cOSNzffl79n1y +; /4BCHAJ+xwiXzj0JVSUAaoI3g77kxIKniS19z7ix8fg11hGeCQd+FSR9AP1GuVpuapeVlBGh6EETjO +; UpvIGlIWGq4GmDxPqmsa0/xxdj6Fc9NnXwbxRPkSbhIlyY9CmvvQhbQPSHFi94ABvaAs7N76OJF5x7 +; hY4Ree+haagqAFATwBn4JQd9iJJ/DBcGDA9fw5L/hiIsMG7cOGmLwQZBWEBbJVjeSsNYVHuhR1zhDR +; zc/C+kDyYPP8wDBgQxeqWfL949RmXxkBQUEgYgmYTgEkiYiqG09yVsge/95gGfPPCgn6eP5t69e8V2 +; 0DuQptB5761pqZAAqAngDPySgz5fVHJR5o/HY/fu3dIQOuwN4U9CYLOgfQb/79pN64rSNgwTm/0vqq +; DLRd1Z3tE7tHebGLWgz7pbt24SGi43TxiviLWKMaWpJPeofv36ycMPud/btm0LneLDgxJ9ND/99FNZ +; 9zSHrmSesAYVAgA1QZyBX7LghzcDjwfgx407ZsyYWDeCPz6IcAGvw4SEot/85ZSlAcDrR4sTTaDlss +; LOE8Y40g8TgxfkDQQUe/TokTsomKpDcfYrojc0/Qf8iOpQ5BfntcgXxxHAwVzhvEfCZSGVAKgJ4gz8 +; koM+X507d5ZJHrjtuVHbtWtX8Y1AqODatWviDSQkULTqsCDltdlbe5d0FFQg0lCEimmVFHR88cUXEj +; q2cXGmrFVu78IWXL58Wfbt9957T3r+VbIXMjOb0Yh0jMAhEBcmtUkFAGqCOIO+ZKHP14wZMyRvD9Hm +; Ismbk3AX44Pwity9e1eqhot48+e9qVO0UOQ5vq4o7Dxhij4OHjxYdp7wsWPHpJgk7/Vjql6V7mHYAk +; Dt66+/lrBtUq29eG2/jyZe8B07dlRNxwgnAVATyBn4JQ99iLwmDJCfrEseUxo3ADf/yJEjn+aSUE3M +; U2GWN2Ea1+SKurz4Z1Uxx9cV8VmHmSCC8aQNDN6+oJAwbTNIk7D5waa8RGj26NGjYgvw/nXt2jWVfZ +; MoEO3CSDO6c+eOdIxwbW9PS04AoCaQM/BLB/wQFYm44wGyzZs3ZwJkbdq0kfFBeEU0DAsvlasb98wq +; nOPrivjsw0wQYZ3TGDqoQMRPsqelkqtrzVRMYQvoaekXKWEL/H9LYy/lQYf8cnILAUHCw+Sfu7bnp6 +; nMAVATyBn4pQN9iJubJzAgDM8D+UpZLnxu9JkzZ8oIIYpE/Ea5WZ5DGLm+aVuhhxsKWyDCfbdy5UpJ +; swjyBhIio/LS9fVn0i/6WW7ZskUgjOiM37y5KSW9x5JnTo4h654BA9XYMaKGD8HAz8AvbfBD3bt392 +; 7duiXwd+TIkVwrsugNhReQc8ErmHd1mOubdams0MMtRZknTIsl+moGHeRfMXbRbzhtMiUtbMHNmzdl +; /6Xfa7k2Rw2V1L5LtfGcOXPEGUAvzfHjx1fVSNEa3KDkfwA0Bn8GfklDnw83ixYtkjATNxlzfJO8ie +; PKHx+EV4SqSUIRWd78rm3K5WRzfN1W2AIRwrz018TzEuQNpH1Gp06dVKxNkw6xvy5cuND7zW9+I7mp +; kydPrjj3NIm9mPxzcg85aDpN8+ms7ECeqsEjQ+ydUVpctIGfgV9S4Ico4ceQkNzLwG5K/F27CQg9UC +; GMQdy4caN4PtJ4n7w330pkc3x1CG8g31UYQ8zknE8++SQQAsnTnTJlis0TNlUsoo2nT58WW0BO6ptv +; vpnK+8Tdn+kYQR9aHBU4BAYOHFjR62lQDbkh27dvF1csMEj+h4GfgV8l0OcLzzJPeTxg0NTT5UTbFi +; 1aSK9AIJDegX6j3EpeM6uNNW3ZHF99oiVPmJBwhw4dpO9m0DxhbMO7774raRIa1qvJPY0dO1byrvH8 +; LV++PLMHijh7tj97m3uCCFGRR4pKEQjGfsSIEVKJQ/4HXxAXbeCnC9hcAT+epDAY3EB41niocPUGKJ +; U/PgivCGFhGuVGvfmz2NSyks3x1S2+uzDtYljjhOXY+4O8gdzLQ4cOVbF2TW6IqOKePXueOpjoxZfX +; uUTZx3nYIRTsT85xMXKVhOpVAXPR/hxV30VbjfCnCdZcgT5f3OA8PbGG9u3bp3L6BuODzpw5I9cQZi +; pJXhtamrI5vsUQIeGJzf6V98ovg72BhIT9yTlBBx4cvPk2T9hUTtiCjz76SEKqu3btEhh04bzC2gG8 +; lFOnTn3quaRJddEKRJ5pA8MHNGvWLPGAkP/BBwAgGPgVU0ktJLxnuMsJoXLDkF/k4oIPK398EF4RPO +; P0jfLnCbuwiaUla+9STG1o8d96HV/8gRiwIAF2tOYoN0+YfF4S58u9XlRpuldMjQuPMi2HAD+iKeyd +; rp5rGFuA9w+HGAeDC6hYdtl2VQSAPtzRJoPZrBh0XKHkSBn4FUdJLiI8xVRQEfKl4KNI/ZR4ivXbFb +; j0FJuGrL1LsYU38J1vv+MwIMbknIcPHwZ6A3nQY1wX84STBkGDR53CFvBwwH5JNFHTmMEgO0Bh4LJl +; y8QTCNSSMlfud9QCoC9coHTm5sskrEf+hyawM/BLD/p84SFmdiM3Bh6zIrnI/Y3BHx+EV4QeauQ0Fs +; ngUCxghR7VI7yBrV74UVmgooKffp3l5gnzM6RJuACBBov5iWpx2nxhD5jpTsRE0/mXqil74PfRxAuO +; TdA+UrTsJBDAgTYZlEUTFgYIoWFNoGfgl/zCoZeYP7sRDxn5Q64t7jhqakPAaBDKwCsC7NIot3RkkV +; ZZoUd1Cm9g35f/uCwoYcRJCaJRblBIGPvwzjvvyM+7Dn8GicmKkCjN9LEF5JDS5FnT+QepKdtH2zwe +; jJicw/VqsW+RALAUmvCCHDx4UC7aH9asBfgM/JLVkCFDJC+O9ADmh6bVNy8rRdkQ/PFB3AeMD9LcKN +; fm+JpmvvY/es1++eOyMNSlSxeZnFNunjD5gxhIDYBngFi5cA4x0pPoyKZNmwo9PabUZvD9k+eOHWTd +; +x5PLTYvEACbAig+BDp34+alv9vcuXPlgzDwKz70IXJ9uMmBHxb+qFGjnFvQYVXJRsDnwPgg7gG/m7 +; 2mRrlW6GEqFWshTIEIxp0KYDzgQd7A69evS+6s6wBncBhfRD9omo8TwJ+ipOn8K5VvR8h3J9eRdX/2 +; 7NmyHSOcB8AwQEWbDLwfgADeEHJFDPyKC34Ij++NGzfEzU8lVJs2bZxayGGU9Cbgjw/iPsA7HnWeZR +; 6yQg9TY/ILRMJ4AwcNGiTtPYIOnASMfwQaNQCbgWF4YQsI9bLvHTp0yGvdurWq809S2BUcAngA6RiB +; Q4BUIb5rDTaxJm5bF9ydVMXg/sQFzEUb+BUP/FjkNETGxU9yL/lA/sLXoLQ3AH98EF6R+/fvS6GUi8 +; nPFHowHUITlJiyV9gCER52aPZebp4wo79wGLgOZnnLtf2iMXGe2AJqAcgJZaa7lnPPQn4fTdY9U6Wa +; NWvmvH2sqRS0cPXTIR5AIB8Mg2jgpx/8EF4+f0wU3dDJA3Jp8eYFfY2J8UF4RTCIAKFLuTBdXvwz8f +; BoAhFTfnoyT/jflYUWHnQmTpzoffbZZ4EQSLoIP0eahOsg5ppc2UOI8p04cUJsAbmgb731ljPn5pJo +; E+aHxumc4voUrJokwOuVV17xdu/eLS5hKkKBQoM/ndDni4o++nwB9lR+uz4P0YWb3x8fxM0PMBMizv +; ucrNDDFFcLX/8fQoWE6f3G5JygAhHuCWwEnkMN4OWyst5DRo8eLZBPlINm/4Q8897XXBd9NMmNhIlW +; rFjhrP2sSQrAfGhgegj5H0uWLJEnPgM/XeAHzNPw2O/9SL6PK4vVRehrKH98EPcBOSF+o9ysz8Pau5 +; iSEAUiYdrFsMZpjUSaSJA3kB5q7CkaQEuT0thDCGH67U7u3LkjPfCy3sc0iwgaOZKs+0uXLsmDkms2 +; tCZpIMNV7M9RZSpEhw4dDPwUgB/q1auXhPP9ogbaObiyUF2Gvsbkjw/is6Rohvsiq/e2Ob6mJBWlQK +; R3794SBQo6yCFbtWqVAIYGuNKoSvcQbAGwjueWXE9sQVb7V5GEQ2DmzJlSHMLDEc4Bvh9X7GlNGnDG +; BdIixh+bQpsM3szAz03w4/tavny5FPSwUCdNmiSL15VFqgn8SoU3lUIpbvzHjx9LKCXNAhEKPay9iy +; kthS0QoWfsjh07ZD8J8gbSSYKRo67DVFEUFljYs0j9Iew7fvz4RICy2sU6J3eSg8k5rjhXUgFAH9TI +; gaJ1iP8UwTxhAz93wA/Rx4iFSXLv+fPnpWLPhYWpFfoaE6GTW7duiTdw+/btUiiV9HsM+dW/VQUTJp +; 0KO0+YBx1SgnjwCTqAjBkzZkiOlOsAVTQ13EOI1mED2Keo3ub/s9ojq0F4vPF8A9fcF3SM4O/ztLGp +; AaAvckM2b94si4oqSTqHG/jlD34ITx/5mnhq8QC60Mlc0w0dRTzx+V4Rxgf5jXKTeG2b42vKWngDw4 +; SE27dvL71iecBs6uDfSDkhZ8p1aCqqsAW0diE8z0x3TY3ttYkcWFKt8IJTMeyPFM3D3qYOgIg3gnbp +; F8gCo5s8F13t8JcXZBGiITmVjZc8D/J28jqXooNfqQBsxgfx9Ad0s9FWMk/YCj1MeSrsPGFggpQgHj +; aDQsIUnTFdqEjzhF0XUbkDBw6Ig4ZoHbl//r+5sGcWVVTD79mzR2wwnzvNtf1/y9LuZgKAvlhsR48e +; lcV28eJFiYsb+GWrAQMGeA8fPpSwPNW+eTar1HTDJinC7vTU4j6gUITqsKivYYUeJlcUdp6wPzknqF +; 0M4TG8IjykugxORdDAgQOlVQlRCb+Hb9DPu7SHFkF+H006RrDuGS9a6nnNwgZnCoCIC5s2bZq0yKDP +; nD9ZwsAvXZFjs2HDBgE/CnPIz8lqkRn0PSs8f4wPIuziJ1uHCbvYHN/6Ovh6jXe8la5zLqLCzhPmgZ +; N9qNw84atXr8pkBRfBSbvYe2hWD/gRjcDrGvU1XN5btYlcSzqncJB7SSpE6b8XCgB90SYDLyBAQlUM +; F60J5jRAny+6tjOiBncznidakmR9Dlpuxqzljw/CG7hv3z7xkjf1szbHt77mPl/jdf2jGm/SD2u8/a +; /pOvciKmyBCCIliNGJQQcPRzwkASx5Q1NRRNSNJvXsN0TjsAVJvK6GvdZlscaZnU3HCBwCdIxo+LkW +; CgARHg+qYoBAXNE8iWiCO9fBD+Fh5Wmb3EvmOKa1kAz64ssfH0QYgORgv1Gu/+82x7e+8PoN/LMa78 +; 1//wQAh/1ljTf6r2u8db/UdR1FVdh2Ma1bt5bJOcBIUIEID614SbIEpaKJcCO2AMAgF5M/p5VrqWnv +; dU1+H0284H6KVsOfKQQAloISeWlUCGMAt2zZIrkImmDPRfBjLJk/uxEPU/fu3TN7by03m0ti4+QBCK +; 8IoRlGLjFP2Ob41teqnz8BP18+APqa9qMa70hzXddUREWZJ0xzXNKBgkLChCopoLJ5wtFFdI1m9NgC +; JlJQcJDVe2vag10R+a9wEI4xHAJAYWM/pxIAmwInoI9egTwNXr9+XaqRNACfi/DHHEISSwGJTZs2iX +; s57ffUcnO5Ln98kPTimvyiKqOftib+bX34awwAfW/gphd1XVtRFXaeMGkqFESVmydMKyXSJLICGO3C +; FpDzjXOFvD8eKvM8H017cZ7isyJPn84prHtG65LH39jPqgDAMADFxdTW1so0CtzUCxculCc+A79wAv +; RoNAw8UOlLnk3a76nlhtJ043d+5YfesTf/X6oMfZra06zG6/0nz8JfUwDoa8GPrUDEBVEg0vWlPy8L +; B+xfK1askHSVIG8gTdWJGuUJMq6LKURU9pbOdHftHDXty3mpXbt2kqvJumdgQ1BzbmcBMCpQ0SbDn6 +; N66tQp+X8Dv2D16NFDevrxmR0+fFj6DKX5fhpuHi0q3RTfsUKPepr+o8bBLwwAIgpEdjfTdc1FVJR5 +; wv369ZO9LOggl42RZYCOa2CTtygso9k8toAcS2yB6+esab/OWnj+Zs+eLUVROMZoHRM0UtQZAKwErr +; hAPIC4rj/++GNvwoQJcsIGfvXFzUNDYcK9LBDyaaIuAoO+bNXYBohhJHlek1FPU3j9/EKPSgCwtEDE +; vIH5K2yBCJNzSIIn/BVUIHL27FnpKOEy3GQlomXYAmwmOZVM99DYVFvTXp6l/D6aHEzOIVcw6OdzA8 +; AkQYunGVz+AM7u3btlYzDweyLGLLEBshHiHu7UqVMq7+P6jaFBQRseyfKajHjaWvLT8uAXBQCtQMQt +; RZknTEoQOWxBIWHynektW83zhImSvf/++2IL/Obyms6/MWna37MStRJr164VHiLNi9B+0M9nDoBpgB +; eJq+S28TR4584dyf+oZvBDVMTh8eNpj1Y6PP0l/R6u3wyuK8wmR5K8JuOdpo61rPHG/CA8/EUFQCsQ +; cUth5wmT93Ty5MnAecJ+uJPWMq4CTVqiiTx58+ROMtO9iCCsad/PQuT30zmFewIgfOmllwJ/PnUATB +; vCuIgRI0ZIOJgpIitXrpSk4TTfMwuQiyqeANjo+OIpESdfJsnXd33hu6woGxpTE2yO73fa9vITmIsC +; f2jW3z/J84sCgWj28xYSdkFh5wkDNaQEsfcHeQMxisOHD3cWZJIUIUC/cwZRsj59+qg6/7jSZBPSFK +; 3eGB4ACzA5hybf5X4nFQBMG/5KxROeP0f1gw8+kPyPagA/BOzRMBtP6N69e6WBcFKv7fJCd1lxNrCJ +; zf6VKiOdtsoVejQm8gP3/L64A5AjbIx3LyoEavqciqyw84T9yTlB7WJofE/LE/ZHFwEmCWELqO7FFu +; zcubPQ1xokTbYiDRH5mzJlikwPYd3PnDkzsEDEVyIAmCX4lYov3u9qzoXPmDFD/q6o4MeXvGbNGrnZ +; uV7Cv6VfooFftoq7WeH1szm+3ymovUuQAMbGXo9q39rnwgMgnkMtn1U1KOw8YaIgmzdvlvSXIG8gTY +; 9pgO8atFQiQn00iScHjNzIMWPGqCz0SFqa7EcaIv+fegCO48ePi3cwzO/FAsC8wK8hrHXp0kVcn4AR +; VTFcdJHAD/HFMrsRNy8Doyn8MPDLXpVuUNbepb6iFHqU5vsxCSTodfEG0v8vLARq+KyqSVEKREgJIi +; ISdJAbN3fu3ELMEybaBdQS/cLIYws0nX9W0mRXkhS1EkuXLpVcUB4OuD/KfR6RAdAF+CsVT0S4+4FA +; xmhx0UWBv+nTp4uXky+UQdF8mQZ92SmJzYiwlhV6fCcKPYb/ZbyQLzOAw74PhR5hQsJWFeymwraLad +; u2rfQ9LTdP+L333pNKWZdAJYqocib/ETHT3UbilZcmW5OkSA8gJxQvOM3AAcOmfrZGK/iVigsZPHiw +; ACBx8I0bN8oQZa3gx6gjNiw2rhs3bkiTZwO/bJTkBmSFHvXVcI5vWOEtjPN++197kucXBIDbX3b7M6 +; tmHW39z7y+v/q+ePuCBAyR+4S3LygkTNuMsWPHqgqZEtU6cuSIAC7RrqKFtLOSJhuUhGiX508FAwZh +; iMZ+rkY7/JWKqqgDBw7Uu1k0gR8aNmyYuG/J8di6davQu4FfukpjwyGpXZOxTVuNzfGNUugRV+UKRK +; wljPua+dr/5DV74SdlQZCUIPqhBhWI+PsqBtI1SGkobMHjx48l1xGnhk09qVya7FISdo0+mnROYd37 +; o3VLf6amCOBXKi6aDuj0yEPz589PpUde0iKUvWXLFoFXbnqGeBv4pXtzpCEr9KivpAs94qqpApG43k +; VTttre8p97HV/6m7IQyAMzRRLl5gnfvHlTQmX+77kEKVwDRS6kNZHjCAi6dH5FkSZ7VYnIFSVnlHV/ +; /vx5+X//32qKAn4NRSd02sSUJsy6AHqNiREvbEh+rgqtbgz8klfaG4oVetTX3OfjFXpseCGd8/G9gQ +; 2ng+T9OZnCiZDwO83+t1DeQCYk0Cc16CCfjtFppAsFvVaWUELUirQf7BaFjdXY2DprabJhcYWDidxR +; ZgnjGMMzyNquKSL8+cLzR1UM7k+8akm2T0lCnMuCBQvExc9mRB5LnPPTsgjzUtobCIUeK5r/C1XGNE +; 2FnePbUBSHUCSS9vnhDfRDwrXP5fc5meKJApHWL/6nshBI/hz9UsvNE6a7At0Wyr1emmDI6xGtIoed +; XEaKPlzzTBZdmmxaXJELSHocB02ka4oIfg3hiA7pjJADBHfs2CH5H3nDH9Vrp0+flg2I0n66eBv4Ja +; esNg2mGNC6QpMBTVNx2rtUUugRV3gD/QIRqwTWJ7yBQ1/596FAbeLEiTIvOCgkTN715MmTZeJIVBCs +; FArbtWvnnTp1SmwBUau33norVxAyFRsGaRq+YcMG4aGaIsFfECzh5t+9e/fTqpikR6hFEc07ecrD80 +; cLGzYdA7/KlfUmYe1dvlOcOb5x2rskKTyBBoC6xT0YJiRMSpD/wN3UgW3AY0jYtRIIjAKGVCX7UxyY +; 6V6EfoVFkib7F9VW0jKvRhPgxQG/UnHRo0ePlqdBQq4MzuaGywr8qOLas2ePbEKM8Rk4cKCBX0KLOU +; tZe5f6IuQbZ47v9Jzz7wA/awWjXxSIhGkXQx7UkiVLpLdqkDeQaNGQIUMSh8BS4YXxHRK8X//+/Z0A +; HlPj0mQPo0g9AMYBMVzu5H1w8zE+hafDtOGPMDSDynlPWtVECUNrWlBZKa+NwOb41lecOb7A4jZHoK +; v2OasELoKiFIj07t1bCi2CDqqIqSYG1JKGPz8lyZ/pji2IGz42ZStNNrLQAFgpkFEgwoggbnQ8glOn +; TpUvOGnw48ZeuXKl3OyEfclH4YM38IuuPG98phJYe5fvFLfQg36ALl1H7XNPRse5dE6m+ApbIELPWK +; YkkAcV5A2kr2DXrl0TAT88kCtWrJDUH2wORYnlfscF6DE9K012s1AAmCSc8QHQguX69euyEezfv99r +; 2bJlYq/foUMH7+LFixLyPXfunPy/gV905X2zD/nVv1NlBNNW3EKPcnN88xCFIJN+6N55meLL9waGgT +; JSgh49ehToDfz888+9WbNmSbpQXPjz25JhC8hF7NixY6zXcQF+TN9Jkx1VD4BJwl+puLE3bdok4VnC +; tEOHDq34NakoI8+Q5N5ly5bJzWvgF16u3OBW6PGdNBZ6lNO6X1ormKIKb2CYkDA9Yv3Rm00d/Bszh0 +; kfigpt2AL6r5F7yEz3SiuNDQjdkybbqg4A0wK/UvEl0nGdeZGEhanOpXI46uuQz8FGwYbx4YcfSr6J +; gV94uXJDW6FHfcWd40szaJevi1FwVglcXIWdJwyUzZkzR0AtKCTMvHm8hqQQlXtNokk0c8axQJSpV6 +; 9eiYGfAaGb0mRrnQfALMCvoVq0aPF0+Db9+cj/CPu7dJ8nnEC+386dO6Xq18BPF/ghm+NbX3nN8c1C +; +1+zSuBqUNh5wqQEse8HzRMmh4+IkV+80ZgGDx4szgR+ltnDr776aurwZzDojrTYXWcBMA/488XNQy +; d2QrgUblAsEjRPmKdHhnUDjTQU5QmRD9fATw/0IZvjW1+uzPFNU34rGDyBWs7ZFE9h5wkT+Vm/fr2k +; 7wR5A69duyYRntLfZY4vTXZxAuAMYKZ71uBnQOiOXLfBzgFgnuDXUJ07d5YiDsDu6NGjkv/R8GeY4I +; F7n5DviRMnZMKHgZ8u8EM2x7e+XJvjm6Zqn7NWMNWiKO1iyAWnX2vQwVzVhQsXSrSHaBFjtrAXRJGw +; Ba7An8FgvnLVFjsFgC7BX6l3jw7tPNHh0meKhw9z5IzwlEhyL15CvmgDPz3Qh2yOb325Psc3DVEJPN +; vxXEVTsgrbLoapIHSHAOqCCkSuXLkiueNEjagYDpMjaEBYfXLNLjsBgC6CX0OAGzBggFQIk9Oxa9cu +; 7+TJk3Lj88TXVJ6ga192HnL5Zuz60p/bHN8SaZnjm7ToA2itYKpPYecJA3NTpkyRdjBBB/aAnq9JVv +; kaDBZTrtjnXAHQdfBrKLrC86Tn54AQFm5qlJwrX3Becv0GtEKP74TnLm6hh6vtXaKIVjDkAWo6Z1Ny +; CjtPmJQgJkcFFYgQKcJBQAWwBtgzGMxPLtjp3ABQC/T5Irl3x44dEgp4/PixFIfQMoDZknR4N/BzH/ +; qQtXepr2oo9CgnKoCtFUx1K+w8YR74165dG9gzEEC8ffu2zHrXAHkGg/kqT5udOQBqgj5fPXv2lNmN +; wN+hQ4fk6e6NN96Qp0H+jrnCdHp3AcLykJYbzQo96kv7HN+kZK1gTChMgQi9/G7duiX7PgUgQQe54Y +; x+y6MFjMGgPuVhuzMFQE3Qh/hSli5dKmPiuNnJA+FD8/+dXI8FCxZIIQjtXyZNmpTbF5mHtNxYFHqQ +; 9K3JGKWposzxTVLWCsbkq7ECEfZ6bAE54J999pns9a1atZJ+r+XmCZ8/f166RWiBO4PBfJWlDc8EAD +; VBny/GA3Hj4upnIHinTp3q/XspBOEhZOoHG8HevXvFQ+gKpFUz+CGb41tfRZrjm6Rqn7NWMKbvVDpP +; mLm9RHuwBfwXW+DDDwUiY8eO9T7++ONACAQap0+fLulCWsDOQDA/ZWXLUwdALcBXqgkTJkh+H549qr +; oaNoFu7IOkD9T27dslNHD37l3pBO8KsFUj+CGb4/udijjHN0nRBmZagfIaTcloa9t/4X324La0eGGm +; e1MA16FDB+kDG5QbiG04cOCA0/0BDQbdkloA1AJ7paLKlxuUm5icv759+5YFv1Kx8EeMGCFFIuR/rF +; 69WsDQBXirJvCzQo/6ImeP3L2o8Of6HN8kZa1gTE3pbOf/pzejZ/m+fsAhKUH0AgzyBtJc2qUJIQaD +; 7ksVAGoBvlL169fPe/DggZTx79mzRxJ3w4JfQ9E89Pjx4/LER/iY/I+8Ia7o0OdrYrN/pcq4pK04hR +; 5a5vgmKWsFYyqnsPOEe/ToIf1hy80TZtzc66+/rgLiDATzl/MAqAn4fBHeXbdunYDfp59+KvkcfDBx +; wK9UvO7MmTPFE0gDUf7M3+UFdEUHP5vjW1/W3iWadjezVjCm8go7TxgHwqZNmwT0gryB9JUlh1wDwB +; kMuiEnAVAT9PmisSdPaoR8T506JYUfcb1+TalLly5ykwOYhw8flvyPPAGvaOCHrL1LfcUp9CBEXPRC +; jyBZKxhTWJUWiJQTKUFEloIOesrOmzdPes26Dm4Ggu7ICQDUBHylcDdjxgxJ7EUM8+YDTRL8SkXzUN +; /LyGYwatSo3ECvSOBnc3zri0IPZvJaoUc8AYArf6brnE35Kew8YR76Dx48WHae8LFjx6S/rAZwMxh0 +; Q5XYfgqXKgJATdDnixYt3GjccDdu3JB8jbTAryFoURl8//59qS7evHmzFJ0Y+MWTzfGtL7x3cdq7WO +; uT71T73JNiEE3nbMpXeAPDTBAh/QenA96+oJDwo0ePvNraWvl5DdBmIOiGotr/yZMnC4fEAkBNwFeq +; 4cOHS8Nm+vVt2bLl6RzfLMGrefPmUmnM0+C1a9eks3yW769pUTclm+NbX3Hn+FZboUc5WSsYU1yFLR +; AhJeiDDz4ILBDBPtFSrEWLFiqAzUDQDYWx/zidjhw58nStRQZATcDnC9DbunWrQBdPWIBgltDVUDzd +; 0Ume6SJo0aJF0kIgzffUtJCbkhV61JcVeiQrwr+1z+k6Z5M7ClsgQmuwVatWSfpRkDeQ4QL9+/dXAW +; sGg+6oKQZgNnXDhuU1RQY/1L1796ezG48ePSotWtIErShifjDTRji3kydPSkw+6ffQtHCDZIUe9UWP +; vjiFHhte0HWdWYpRcFYJbKpEYeYJ+8Ig02826KCnICPomjVrpgLUDATdUEOHExXpjXmda4oKf1w4xR +; 2405nqQf5FVmAXRXj+/HnD0Pn48eOf+QKrGfxsjm99xZ3jS3EIRSKarjVr+a1gdlto3FShwhaIkJNO +; 31kKBIO8ge+//750rdAAaQaC7uitt94ST3JTR00RvX5UXXHDUOhx6dIl+RDyhLwwsNanTx/v9u3bAo +; K7du2S/I9qBj9kc3zrK+4cXyv0CCc8f9YKxpSU8AYOfeXfl4UZPDSMH6UPbRAE8u9Tpkwp9DxhA8Hk +; NHv2bOGJoKOmaPBHBRWVVjTgXLNmjffiiy86A3rlRPNQ4I+QMDA4YMCAqgQ/ZHN8v5PN8c1Otc8ZMJ +; uSFXtZmJAwKUH0oy03T/jdd9+VVCYNgGYgmL1atWrlnT592it3UIxaUxTwI0eCG4Ob56OPPooETy6J +; xTx69GipVib/Y+XKldIgtFrAz+b41pfN8c1WVAFbKxhT0qJAJEy7GLx7ixcvLjtP+O7du96wYcOcB7 +; OspcnWpSFmTAe1GuIgJQ6PM59XTRHgr2/fvjJgm6ej/fv3S6sVDbAXpHbt2gnFc00UipD/UXT4szm+ +; 9RVnji9VwdbeJb5oBTPph7rO2aRDUQpEaA92/fr1QENOHzeiXLT2cB3MspYmu5eEcBIRPSx3XLhwoV +; 6xaY1m+OOLppyeBFrm7UK1WgAvjAhfz507V9oFfPbZZ9706dMlX0TTwgwja+9SX3Hbu9APUNN1uqh1 +; v6zxRv+1rnM26RIFImHaxbz++uvSvqzcPOGLFy963bp1cx7K8pAmOxhXzJIm6hl0wEhLliwRD7P/ew +; KAmoCvVB07dpQCD0K+FHyk0ULFBfFF0cqGptF8icTt27Rpo2qBBsnau9RX3EKPap7jm6SsFYwpC4Wd +; J8wDP6ND6V8bdOAAIemfnrcawCxrabKJYcV1AXVBOaMcpAv07t270ddQCYBUQn3zzTfiAucD4IPQBH +; VxIBAXL718CAkT7qaZtZaF2pho72KFHt/J5vi6of2vWSWwKTvhDQwTEm7fvr30sQ0y9vwbUx74WdeB +; LC9pspFB4jtmokzQgXeYsDDFpU29jioApDUKC5yFTm+brMeo5QF+peJpkMTfhw8fSlh4w4YNkv/h+m +; JtKCv0qC+b4+uOjrd6AoB4AjWdt0mvws4TJiUILx/To4KM/oMHD7x33nmnqucJFxkESXULmiLDQcoY +; xaTlrlUNAA4ZMsR7/PixhEGZk0jVryaYqwT8Gooy78OHD4s38MqVK16PHj2cXawNZXN868vm+Lqn2u +; cMrk3ZK+w8YfL9yPsrN0+YeffVPk+4SCCIJ+/gwYNeuYPiUXohh3lN5wGQpEU/9MmkDPIhNMFc0vDn +; i6fBadOmSUk3T4Tz58+vl+DpmqzQo75sjq+7ohJ4trXRMeWgsPOEcYCsXbtW0qCCvIFUEtMlQwOM5S +; lX7aYv2tqVywOlWIiiUdgg7Os6DYBdunTxbty4ISHfY8eOSfGDJphLA/waiiknlHYDyHxGb7zxhjOL +; 1pcVetSXzfF1W/QBtFYwprwUpV0MkbFyFaA4CRYtWuS98sorKmAsL7lmNxFh/HXr1gV6ezngJDzDUV +; /fSQAEiObNmydPNxR7kPfAyWoCurTBr1RUftEwmvA4TwlMQ8lrwZaKQo8Vzf+Fqs03TdkcXx2iFQx5 +; gJrO2VQ8hZ0nzFSQffv2Bc4TxolCaJBpIxpgLE+5YDtRp06dyvaCBAw3btwoRaJx3sM5AMTL54/DIb +; +ta9euqoAuS/ArFQsXNzFPg7iCyZMk/yPLBVuqri/9+bdPsv+Vqg03TdkcXz2iAthawZhcUJR5wpMn +; T5bk/yBYII1q4sSJEiZ0HcTyVl62E82YMSOw/yMHzh5/Gkzc93EKAKlcop8RCaxUuJLTpgnq8oK/Ut +; E8dO/evRISplK6X79+qS/WhrJCj++E5y5uoYe1d8lH1grG5JpomfXqCz8tCy1MjKIvblDIEE/h7t27 +; pZjQdQhzQVnazpYtW3onT570yh0UgSbh4HECAMlNoF8N0HL//n1v8ODBqoDOBfArFU+DhIF5GiT/Y9 +; myZbFdxFFk7V3qywo99AoAJBSs6ZxNxdaOlv/c6/fKn5Rt70JKEHs+6VNB3sDbt2+LrdUAYXkrbduJ +; hg4dGujB5eA7nTp1amITwXIHQHr50aka+KPEGarVBHUugV9DMR3l7Nmz8tnyVEhOQVrvZYUe9bXt5S +; eFG1ELPbaZ18kJ1T5n4XeTe3rv25DwmFf/d+/VF38qEBAEg1T/EgUKOugnxzhVmyecHwgS6SRlq9zB +; 5LOkbXhuAMib85RCuJc2JuQv8HeawM5l+CtdXAsWLJCCmk8++STRpwdEoQfJypo20awEzIX1ANocX7 +; dEG5hp5ok1OSr23DYv/f1TCGwKBps3b+7t2LFD7GyQN5CpEnTd0AJiRYFAKnfv3LkTCH44cCjyTKPN +; Wy4ASKsSFhyFHufPn5e5vpqgTgP4lYpFy8DomzdvykZAxRiVY5W+7pBf/TtVm2YeCpMDaHN83RPev9 +; rndJ2zqbrkewMbQmBDGOTPY8aMkQKQoIPw48yZMwU0tMCYZhBcuHBhYOU2B2Nf08zjzxwAqUAiLw2P +; 1IoVK2RxagI7TeDXUDQP3bp1qzxRUC1MzkHsxWtzfCOJHn4NQ8JW6OGu/FYwVglscl14A0tDwk2BII +; 4XesWWmydMKla7du1UwZgmEGRKx7lz57xyB8WcaY96zQwAqU49dOiQLDCST/v06aMK7LTDny82hJEj +; R8pYPfI/6CYfNCy6oazQI76AvTE/eAJ/c23ShNPa3cwA0KRHeAP9ApEg4d1jahROmKYOQsJ4npi6Zf +; OEk4XAsWPHel9//XUg+H3xxRfyc3Hg0kkApD/dw4cPn5afAxyawK4I4NdQ9FvkaRBvIMmlYbqIT2z2 +; r1Rtii6KkLA1dXZfgJ+1gjFp06zX/qdAb6Av5sfTZzeoXQx96GgyjPNGE4y5CIJE3959912v3EHRZv +; v27TPjgFQBkGaT69evF/D79NNPJQ9BE9gVEfxKxfdDzgdPJDx1zJkzp9E5gjbH11SNqn3OKoFN+kS7 +; mDdffq4sBOKIAfDKzRO+evWqdOvQBGIuQSDV2A8ePAgEP3LzKdaMMsfXaQBkRu21a9ck5EtjQ6hWE9 +; wVHf5KRfXX5cuXBdSPHDlS7wnE2ruYqlW1zz2ZC6zpnE0mVK5ApFTDhw+X/rtBB506ABT6yWqCsbxF +; i51yc3xp1YNHNg/bnzgAAkezZs2S/DJEvgFvpAnuqgX8SsWNTT4gEEi4ftyowTbH11TVohXMpB/qOm +; eTqVRNtYtpKAoTDhw4IClBTR04c06cOCFdOzRBWB5i5jIh9qADMKQok0EYedn9RAGQ0TLHjx+XhcIQ +; 4+7du6uCu2qFP18sXDrDXz+wzDvX2UK+puoWlcCj/1rXOZtMDYU3cFizf18WAgk/Tps2TdKBgqCFGb +; Tjx4+3ecJNiF67QWF1DnryjhgxQn4+T5ufGAByMeT5EcvetGmTjKPRBHfVDH6lsjm+JtMTbXrRKoFN +; xdHC1/9lqAKRt99+W/rzlpsnTINpZtdqgrM0xRQzCivLHfwMzjIX7H3FAAjobdu2TVzHhA7pLacJ7g +; z+nsjau5hM9bX/NasENhVLFIh0/9VflIVAwpJMnyCNK8gbeOvWLenyoQnU0hCRM7x6QQdeQYouk5zE +; lSsAkrjIAgD+KB6AajXBnYHfE1mhh8n0rI63egKAeAI1nbfJFKTG5gk3JeCOvr1BxzfffCNjXWl1og +; nakhB9Fbds2VK20IN8QDyr/u+5YvtjASCAtHjxYgn30lCSvAFeTBPgGfjZHF+TqZxqn7NWMFkI2NZ0 +; vkVQ2AIRHDv07w0aWwYA0cOO7h+aAK4S0T0DB1jQQT3EmjVrJFLa8PddYIDIAEiLEL5oLuzixYte58 +; 6dVQGewd8T2Rxfk6m8qASebVNbUtOeZk9GIk742ychd03nXgRFaRczbty4wDAnEEgdAA6hos8Tnjt3 +; btk5vvT+GzRoUODr5M0BkQCQBUA/IDqEr169WqqANAGegd8TsfCm/P0/s6duk6mM6ANorWDSEZ5Vfy +; 720L94UnFN5bXtS9mJqUQTv4XvMACIaAFz6tSpwHnCpITt27dPpk1pgLko4pref/99r9yxf/9+r3nz +; 5qFfNy8WCAWAdAznC+VLv3v3rtevXz9VgGfw9x34+Rr8V/9MNlxLcDeZmhZAQh6gpnN2XUCHPxO7FA +; D5nNG0H1nldRbC+9r7T558/mEBEBHOXLRoUeBMW7yBH330kTSZdhnoomj06NHiAAs6+PcJEybEapGT +; BxOUBUDGmNAlHHcns+yYC6gJ8Az+ftHoYgMA/Q2XJ3F76jaZnhUPSNYKJjlt+/bz7PpH9eGvIQAiHk +; 6t+CY9Tf9R/c8/CgD6Yjwc/X6DCiCofF23bp3qecJURO/Zs8crd3zwwQdehw4dKnqvrNmgSQDkCyZ5 +; EfD77LPPpPGjJrgz8Gsa/hoCICLMZTk4JlN9WSuY5NQQOoIA0BcheHs4TU5+zmXDzz8OACLAjipY0s +; KCvIGXLl2SriGuQF1Y9e7d27t3714g+MFIFMU2VujhOgg2CoD+GBNCvmfOnPHeeOMNVYBn8PeLsgus +; IQD6T92Wg2My1Ze1gqlMpaHGqADoP5wagFeu0pzLpADQ16hRo6QPcNDBhBGKJzTME+aali9fHpjryH +; Hnzh2BxDTOIQtOeAYAqeChrw8NIKFaTkQT4Bn4hVtcjQGg5eCYTM+q9jlrBRNXQdARFgDt4bQyNZZz +; mTQAonbt2kk/4HLzhN977z1xKmUJdFFEGJcOJ0EHXs2dO3dKfUSa55I2LzwFQMaYHD16VL6gmzdvej +; 179lQFeNUOf1EXVhAAWg6OyfSdaAPDQ5Gmc85bQMfwvwwHf2EA0B5O46mpnMs0ABBR/DBr1qzAYgng +; CW/h2LFjnZsnPGnSpMDpJxy0uqEghOvN4pxSB0DGt3388cfS2JmxbrhoNQFeNYNfHPgLA4CWg2MyPR +; H3QO1zus45T636eXjwiwqApd5ATZ9JHgrKuUwLAH117drVu3DhQmCBCLyxdetWmSecFUw1JXIZDx06 +; 5JU7aIGTR3ub1ABw8+bN4rIFAEeOHKkK8MzrF39BhQVAy8ExVbv8VjD2IFRe9JSLCn9+I+iw+1GpN9 +; C+k2cVJucybQBEVM9SSFpunjARR1rL5QWBAwcO9B4/fhwIflQzz5kzJ/cG14kDoB+Th2o1AV41w18S +; CykKAFoOjqmatbuZtYIppzjQgab/PrTOvkK+IPtMlH1p5c90fU5pK2zOZRYA6Gvw4MHSPzjoYKQsNQ +; fME84KBIG5DRs2lJ3jS6ubbt265QJ8aUNgDfF6/qAJ8KoV/JKCvzgAaDk4pmoV691awTStuc9HBw7y +; 0ja88OxrAdu1z4V7ILXv4ztFzbnMEgBR69atpY9wuXnCdB3p1KnT099LC6KYWXzjxo1A8ON8AEQXq5 +; YTA0BNgGdev+QUFwAtB8dUjWLNWyVwfTXVU66cABWApanXxRtI3qWFfsMpTs5l1gDoF4hQZEERRRB0 +; MW948uTJ4qFLAwJxepF/GHQ8evRIaiOy8kbmBYHqAdBVYHMZ/ioFQNuITdWm2ueeQImmc05TcUONUS +; CaLgQNQ8L24FlfcXIu8wJAX3j48PQFhV6pS2D6Bp7DpLyBrVq18k6fPu2VOw4fPixdUfKEu6wgUDUA +; ugpsLoNfkgBooRhTtchawTxRmJ5yjQlP4cHXo78fk1j47G1SUX3Fzbl0AQARYdWlS5dKz+EgbyCNln +; 1PXCUgyEzizz//PBD8mG08ZcoU51rTpAmBagHQVWhzHfySBkBfNk/YVGTheap9Ttc5Jy2gI0xPuYaa +; XiE4275SX3FyLl0DQF99+vSRKuCggyri1atXe6+99lpkCAQ0adhc7mBUHZ7JvGEuawhUB4Aug5sW+E +; sDAJE9pZuKKsKRrPFqLYCK0lPOF7C4zaIDiSluzqXLAIiaN2/ubd++PTAvD28gfQWpxg3rDWT2cLnq +; Y4pSGPmW5BxfTSCoCgBdBjct4JcmAFqBiKmo8lvB8F9N512p4kIHuWmartN1xc251ACAvt55552y/f +; gI41LEAbA1BYH8HS1lgkbScdy7d0/6D7oAbnlBoBoAdB3gNMFfmgBYWiBi7WJMRZHfCqaaxiPGhQ6q +; UjVdp8uKm3OpEQARM4LpS0x/4qYO/o1Cjfbt2z/jDeTvzp8/Hwh+eBP37t0rIWVXoC0vCHQeAF2HN2 +; 3glxUAWoGIqWiqfa46WsHE7SkXt9DD1LjCzvEtEgAi2r/MnTtXmkMHQdz9+/efzuRF48ePDywq8T2I +; zCAuFz6uFgh0GgBdBzit8JcVAPqyecKmIgivdtFbwcTtKUdhgqbrdF1xci6LAoC+unfv7l2+fDnUPO +; EDBw545Y73339fPIQuwJkrEOgsAGqAOI3glwcAWoGIqQgC/ljHms45iuLO8d1TZXmRaSrNQg9tAIhe +; ffVVb/369TKLN+7x29/+1ps/f37uc3xdhEAnAVADyGmGvzwA0A8J2zxhk1axdlnDms45jCqd42tKRm +; kXemgEQF/Dhg2TkG/U48MPP5RqYJdsr0sQ6BQAaoA47eCXJwD6sgIRk0YVsRVMknN8TfFU6RzfagBA +; 1KZNG2///v1lq3s5CBtv2bLFa9asmfyuizbYBQh0BgC1wFwR4C9vAPS9gdVUUWnSL1IYWLtFKGxKa4 +; 6vKZqSmONbLQCIZs6cGQoAv/jiC2/ixIlP5wkbBDYOgU4AoBaYKwL4uQKAvhjzZCFhkwaxTlmz2h9c +; spjjayqvpOb4VgMAtmzZ0jtx4oQX5aDJM1NAmAFsENg4BOYKgFpgrmjw5xIAotrnrF2MSYdqn9MLQn +; ju4hZ6WHuX5JT0HN+iAyCzgD/99FMvzkEo+Pbt297AgQPrvaYGG50FBOYGgJqArkjg5yIA+iHhlT8z +; b6DJbeGxnqaw+MEKPdxQGnN8iwqAzPHdtm1bYBsYjmvXrgnkBR30B1yxYoVUFRsEfgeBuQCgJqgrIv +; y5CIC+MK7WLsbkqjS2grE5vvkrr/YuWgGQmb/loI5cwNWrV3uvvPKKhIh37dpVdp7wuXPnvLffftsg +; MC8A1AR1RYU/lwHQ9wbaPGGTi2JdskY1nKvN8XVDebZ30QiACxYskPy9oIOWMIMGDXrmd2tra71PPv +; kkEAIJJ0+fPj1wnnC1KFMA1AR1RQU/DQBY6g20djEml7S72ZO16fq6tDm++StuzmW1AmC7du28s2fP +; euUOWsG0aNGiydfp0KGDd/LkycB5wngPmR7Stm3bqobATABQE9RVA/xpAUDfG2gFIiZX5HorGKBjzA +; +iA4AVeiQrFws9XAbAMWPGBM7+5fjyyy9l3u+LL75Y9vVo/4In8euvv/aCvIH37t3zRo4cWbUQmDoA +; aoK6agA/bQDoC4+GFYiYXBDr0cUUBXL2yN2Lavxtjm+yynqOr2YApChj7969Xrnj/PnzXseOHSO/fs +; +ePb2rV68GFpIwKo5xc82bN686CEwVADWBXTXBn0YARDZP2OSCap9zrxVMHOiwOb7JyvVCD9cAsG/f +; vmXHu9XV1XmLFi2ql68XVa+99pq3adMmAb0gb+CVK1cEGKsJAlMDQE1gV23wpxUAkc0TNuUtl1rBWH +; sXN6Sh0MMVACSEu3LlyrLtXagC7t27d2LvO2LECO/BgweB78kEkXnz5kkLmmoAwVQAUBPYVRv4aQdA +; X1YgYspLGPva59w4j6iGnhCxFXokp7g5l9UKgG+++aZ36dKlQAgDDHfs2CGeu6Tfn0KTQ4cOBY6To3 +; jk2LFjUkxSdAismTJlilykwV/1wF8RAND3Bto8YVPWYs2x/vJ6AAE6mMkb1chboUeyiptzWa0ACGv8 +; 5je/CYQ/WrSMHj06VKFHJR5IZgrj7QuC0EePHklbGX5ek22PBICQMERMWbWBX3XA3y8LAoC+aM5rIW +; FTVvJbweSRj4r3Lo6Btzm+yUpjoUdeAEhxxdGjR71yB7N+S9uypK2uXbt6H3zwQWAouq6uTqaR0Gha +; i22PopqNGzcKlRMbZ+Yef2nwp+tLrHYARBSIWLsYUxbC88eay3q9xZ3ja4UeyUlbe5e8AXDw4MHexx +; 9/HAh+8Mfs2bOldUtW8OeLKSJME/n1r3/tBXkDP/zwQ69///6qbHwoAATgSI6kGofu2xs2bJAESAv5 +; GgBqkxWImLJS7XPZedWs0MMNuTrH10UApGqXytswc3wZ+5Y1+DXUwIEDvTt37gSeKz0FlyxZ4jVr1k +; yVrS8LgKh169bewYMHxeXpfylAkcFf8SRPZgUEQF9WIGJKW6wxUg/Sfp840EFe2oYX0j+3ahE5l1rb +; u+QBgF26dBGPWdBBoQW99/DA5Q1/vlq1aiU9CYPG0AG077//vte5c2dVNr8sACI+hBkzZnifffaZuE +; TnzJkjCZAGf8WCv6IDYKk3UJOhMekRrWBIO0jr9eP2lKM4BGBJ89qrSXFzLrUpKYiCGXAiBR0PHz6U +; dLM0Cz0q0YQJE6QYJQgCmTdMUQtha032PxAAfb311luSHEmBCDP1SMwsIvxp+qKSAr9qAcBSb6CFhE +; 1Ji4cLHjLSeO24PeWs0CNZaZnjm4SAsUqArE2bNt6ZM2e8cgcFpxRUuAh+paJdzenTp8vOE3733Xfl +; 2jWxQFkARLhmV61a5X3zzTdCu5Rm8wuaAM/gr3H4qyYA9L2BViBiSlJptIKxOb5uqKiFHmEAMA4IMk +; s3qKUKB/lzkydPzqXQI67IYyTnr9w84bt373rDhg1TxQRlAdCXnxwJ7e7cuVNm92kCvWqGv6DFXU0A +; 6MvmCZuSEi1gWFNJPVjYHF83VLT2LnEBMAwEUgyxa9cur9xx4cIFr1OnTs6CXjkxjeTGjRuB10gl85 +; o1a6R5tSZGCDUJBJft7t27Jbbvj2fhlzVBn8GfASCyecKmJMSDBOspiUbkcaADD5W1d0lOmuf4pgWA +; QSDYq1cv76OPPgqEIoopli1b9nSsmmbRy5B+gHUB+Y14Ay9evCgFtFo4oSwAloLTxIkTpacPQ5Vxje +; LONfDTBX7VDoDICkRMSaj2uRpv5c/i/37cUCO5aXldcxGlfY5v2gBYCoH8eenSpYG5cRzAYb9+/ZwH +; u6giFY4JIUHH559/Ln0NCSG7zgs1Udu8dOzY0Tt16pSEhM+fPy/JkgZ/uuCv2gHQl7WLMVUiKoHjFl +; 7EhQ6b45ucijLHNwsARMzGpTg06MALtmfPHu/11193Hubiqn379jLZJAiC+bcjR47Iz7rMDDVR4M8X +; ZLto0SLvq6++kuRPPIN8MAZ/OuDPALC+N9DmCZviiD6A8yP2ArQ5vm6oSHN8swBAbHzQtAwO2seNHT +; vW2fYuSYprpOUNDBQEw0xYe+edd+R3XOSGmqjwVypyAa9fvy7ewP3790uc3ODPbfAzAGxcNk/YFFWk +; EUQJx9ocXzdUrYUecQAQTx4DIsodtIDB2+UqsKUl8v0uXbpUdp7w5s2bhY9c44eauPDniwWyZcsWqY +; JhnByz/3hhgz934c8AsHHZPGFTFLFWav8m3M/aHN/8VY3tXSoBQDqAPH78OBD8qAeYP3++RAVdhbS0 +; RTX0unXrhIGCvIE4y/r06eMUQ9RUAn+lIjmSDt9U/lAOzYIw+HMP/AwAg2XzhE1hRTX50L8I/hmb4+ +; uGrNAjPABiuwGacnN8aY3So0ePqgj5hhHTTe7duxf4mREyJn2OPssusERNEvDnq127dpL4CARevnxZ +; ZgIa+LkHfwaA5WUFIqYwAgCbWic2xzd/xc25rFYAZAoYnqqgAzDctGmT9ASO2zy6qGIqyL59+yQtrq +; mDAhEKaSmgzZsnapKCP18sBEqgKYWmg/bMmTPl7wz+3IE/A8BwsnYxpnIiBNywr6TN8XVD1TLHNykA +; xFYT0g06CAmPGDFCWsDFaR5dLWJOMEUxQRBNSz2Ka/jc8mKKmrSArGvXrtIUERI+duyYkHFa7+UCnG +; kBPwPA6LJ5wqamNOvv6+eN2hxfN1RNc3yT0MmTJ71yB61PWrduHbpnYLWrc+fO3tmzZwND6URLGbLR +; qlWrXNgiNQBEuIjXrl0r84T9Jwc+GIO/fOHPADC6bJ6wqTHN/4cnbYTw3MUt9LD2LsnJCj3iKejAfk +; +fPl1yA8P0CzQQ/E5MQVm+fLl8hkHeQCasDRo0KHO+SBUAfZEcSWdwvIGMU6FqxuAvP/gzAIwvmyds +; KtXaX9R4M35khR4uKE7OpSkYAMnlJy8wCvgZBD6rvn37eh9++GEgaNNnceXKleI4y4oxMgFAhOuY5M +; i6ujr5IHr27CknYPCXLfjxFMfT3Ki/+7+rAi+XZPOETb7w/kWFPwo9tpk3OTFV+xzfNAAQZ82qVauk +; WjUu/BkE1leLFi28nTt3lp0nzIQ1CmizYI3MABDxIUydOtX75JNPpGfOggULYs0TdgnWNMFf27ZtvQ +; MHDsjNPeb5P1AFXa7JCkRMaPcr0QDQ5vgmK2vvkjwA0s+XHoCVgp9BYONiWgoFIEEHBSQzZswQPioM +; APrq1KmT9/777wuIkCTJjMFqB7+04Y/8S3oUkXS6YcMGb+gP/mtVwOWqrF1MdQtPcFgAtDm+ySluzq +; WpaQDE+0SUDk9VkvBnIPisYJ7jx4+XnSfMFBYcN4UCQERy5LJly6QxIi1jamtr5YMxr1+yYlLL+vXr +; pbyfJzu/hN9yAJOTFYhUr4D/cgBohR7Jygo9ktcXX3zhjRs3rsn2LgaByYvPmikq5eYJ47gZOXJksQ +; DQV79+/bybN2+KN3Dv3r0CLAZ/yYgu7VeuXJEniUOHDkkrHv/fDACTlxWIVKeCYITCBE3X4rpsjm86 +; wiOVNvgZBDZtp69evRrYLgYHDpE7+KhQAIhwOW/fvl0ukmph8g84OYO/eCJxd+7cud6XX34pT3bkXT +; ac1WgAmI6sQKT6xDSQhgbV5vgmKyv0SFdZwp9B4LOi8nfjxo2BjbgBRBw6vXr1KhYA+qBHGPjRo0eS +; p0Y5NNDiIri5DH88ydF4G6/fhQsXpJqosZ8zAExPNk+4ujT2b+obU2vvkqys0CN95QGABoH1BRcMHz +; 7ce/DggRd04NghdEwaXWEA0Ncbb7whAENImEkib7/9tpMA5xr4cTP5AM1TBM0n6bfY1M8bAKYvKxCp +; DvnFCLR3sUKP5EShx5gf6AIprcoLAA0C6wMgouiD4o9y84QpIsHhUygARH5yJKTLPGH61qVdDq0Z/l +; q2bCkNtvGc3rlz52kJf9DvGABmI7yB9IrTZHhN0TT1756EJ22Ob3La8MIToNYEUaUa8uc1XhdF558n +; ABoEPguB/BnuIYUrKCSMw4findLfVQ2ApSfnFzFAwkeOHJFm0i6CXZ7w179/f2mszWe0Z88egcEwv2 +; cAmK0W/NhCwkWVVfgmK+3tXeY8X+Mt/LGuc84bAA0Cn4VARASUxtDl5gnv2LFDbL9qAGzsBKl6ofqF +; MSnQLjFyFouLkJcl/BHeXbJkicwYpLE2TwENCz0MAN0SBSLWLsZkalza27v0/9MnE14oAuv5fV3n7g +; IAGgQ+C4CIok6mssBAQd5AHEE4hFQCYLkTLW1kvGnTJvlQXAO9rOCvc+fO0kCbPIDTp097HTt2jPwa +; BoD5yApETKZnpX2OL/m+gB9a9o+6zt0lADQIbBwC0YABA7zbt297QQcpc/RXxkGkBgDDwpSfHAkEXr +; 9+XULETX1YRYQ/8iCnTJniffrpp+L5Y5QeIBzntQwA85UViJhMT8Lnmtu74OkjX9GHP4QnUNM1uAaA +; BoFNQyBh3t27dwsDBXkDcRDhKHIeAKNCFYuDGXnMysMlSr8719rFpLEYaOL87rvvSq4f8Nu7d++KXs +; 8AMH/ZPGFTNYuKaU2Q1FC0/ykFP63ePxcB0CDwhUC+GD9+vDiCgiCQf6cHcFABba4AWAlk+cmRABFh +; UNrHFBX+hg0bJg2yudYtW7Z4r732WsWvaQDojvAGWkjYVC0qwhxfijwawp9W75+rAGgQGBzdJPXr1K +; lTgfOEYQbmO+NAcgoAk4At4tw0jCbuTSHEO++8I4umKPBHAcyaNWu83/zmN97Dhw+90aNHC80n8doG +; gG7J5gmbqkFFKvRoKELBmq5FAwAaBAZDIDywaNEiYaAgb+Ddu3fFkeQEACYNXvS9IzkS2iU+nvS8vD +; zgr1u3bt6lS5eE7mmBQ/5jkq9vAOimbJ6wqajSPseX9i6NgZ9275/rAFjtEBiGPxgPR2pY0IEjae3a +; tRJBzA0A0wKwVq1aebt27fLq6uqkGXK/fv1Cf3guwR/jXWbPni0NIL/66ivJd+Tvkl5UBoDuyuYJm4 +; ok7XN8AbuGhR5F8v5pAECDwPIcguNr69atZecJ41jq3r179gCYBYhNmDDB+/jjjwUEKYdOu0AkyS+5 +; ffv24u3D68eXhBcwrQVlAOi2rF2MqQjSPseXXMUg8PM18q90XZdGADQIDMcjo0aNkpSxoAMH05w5c7 +; IDwDQhrKHefPNN7+TJkxIS/uCDD0KVQ+cJfyxs8hf50gDX1atXB87xNQCsHlm7GJNGaZ/jyxg3KnrD +; wJ92758mAKxmCIzCJu3atRNnUrl5wpkAYJqw15Tw/JEcyTxhRP+8pOYJJ/mltmjRQip7AT8qfYcMGZ +; LJIjcA1CObJ2zSJIoktM/x3f1KOPgrgvdPGwAaBIYTn9OsWbOEf5o6UgfALKGvMdEv79q1a0LCNJGO +; My8vLfjr06ePd+PGDSFxSrXJY8xqIRkA6pPNEza5Lu2FHnj+woIfAnY1XZ8BYPUAoK+uXbt6Fy9ebH +; SecKoAmDXsNaXmzZt7mzdvliqY+/fve0OHDo31YSb1JRLe9Uu3aWhN3mKUOb4GgNUrmydsclHa27v4 +; aqq/X1OiGbSm6ysKABoERhMTw/yWcpkAYF6wFyT66D148EDGqKxfvz7SPOGkvjzyE2lcjdfv3LlzXq +; dOnXJZRAaAemUFIiaXpL3Qw1e1ev+0AqBBYHQNHjxYUs1SBcC8AC+MSI48fPiwQOCVK1eeVtqmDX/k +; H06cOFEaVkPhixcvjj3H1wDQhKxAxJSnKPQY/pe6QCdI5fr8FdX7pxkAqxUCK2EgUs0YKwsDJQ6Aec +; JdWAFj9Nr7/PPPvW+++Ub+3FS7mCS+LD5wGlSTh3jz5k2vb9++uS9cA8BiyOYJm/KQ9jm+DRXV+0eR +; iKbrKzIAGgRGF78/adKk6gRAX35yJGB24sQJ8Q4mDX+4XGlMzXts375d8hFdWDwGgMWSzRM2ZSXtc3 +; wbUzV7/wwAdSoJBkoUAF2Cu7B69dVXZTwKnkAaSNNEMYkFxbgV5hT/+te/9h4/fuyNGTMmsTm+BoCm +; xmTzhE1pqiiFHg1V7d6/IgCgQWDOAOgq4IUV/fcYmIynbseOHQJwcb+ULl26SANqCj2OHz8uEz5cWz +; gGgMWVzRM2Ja25z+sCmiiqdu9fUQCwGiHQCQB0FeqiqnXr1pIcWVdX5926dUv69EVZVOQRMruX3EJa +; vDBqJY05vgaApnKyecKmJKR9jm85mfevWABoEJgxALoKc3HEh8kCYmqIX61Lv74wENe2bVtpNI3Xj+ +; riHj16OL1oDACLLysQMVWiorR3CRK5s9Xu/TMA1K3cANBVkIsLf6VifvCZM2ckJEy/Pvr3Nfbhs+BG +; jhwpDab9/oLkFbq+aAwAq0fWLsYURdrn+IaVef+KCYAGgRkAoMswVyn8+aJP39KlS72vvvpKwrpM7C +; gt5KCid+PGjRIyBgCHDRumYvFxjgaA1SUrEDGFkfY5vlEU1ftXxOrnogJgtUFgpgDoMswlBX+l6tev +; n/TvwxvIzN4WLVp4PXv29K5evSohX0K/5A+6vkh8+DMArF5ZgYipKWmf4xtF5v0zACyaDABTgD9fLV +; u2lD5+v/3tbyU/EK/gF198IfmCWc/xrRT+DACrW1YgYipV0Qs9GpN5/4oPgOYFTAEAXQa6tODPV8eO +; Hb3bt2/LDL3f/e533t69e3Md5xYH/AwATcjmCZtQNRR6NBTeP0Ld5v0rPgAaBCYIgC4DXZrwxyIaN2 +; 6cNHTGA0jj6GPHjknRx6VLl2SiiKsLoqmbwgDQhKxApDpVtDm+URTV+zetCkLjGkDOADBHAHQZ6NKE +; Pz/0C+zh/RswYIAsKsK+8+bNkzAwPf9mzpzpXCg46KYwADT5whu46UVdAGOKr6LN8Y0i8/5VHwAaBF +; YIgK5DXVrw179/f4E+ij927dolxR8Nf4Z+f5cvX5afOXr0qNemTRsnFkG5G8IA0NRQC35sIeGiq+i5 +; bOVk3r+aqgTAaoLARAHQdahLAwDp40f7F38+cG1tbaB37/XXX5f+f8z9ffTokfQFzHPub5ibwQDQ1J +; goELF2McVTUef4RlEc71/P7+u6xrhyHd4MANOBwKoBwLAf3FtvvSWNn2nvcurUKa9Dhw6hf3f48OHe +; vXv3xBu4ZcuWXBpCh70ZDABNTYmQ8MqfmTewKCryHN8oYopHFO/fnCr63FyHN4PAHADQdahLEv7w2E +; 2dOtX77LPPxPM3f/78WBW+jIQ7cOCA5AzeuHHD69WrV2YLL8qNYABoKic/XKYJdkzfqRrbuzQl8/4F +; y2VoMwDMAQBdh7ok4Y+8PRo847m7du2aQFslHz4wOX36dIFJwsLAZJh5wlnBnwGgKaxsnrBOVWN7ly +; BF9f4t/LGu66tULkObQWB6EFhT7fDH+LaPPvpI4G/Tpk2Jhm3ffvttCSfz2u+//36kcHKa8GcAaIoq +; axejQ7R3qfZCj4aK6v1D1eT9MwA0AKw6+KNwg35+9PV78OBBaoUbAOWKFSukVcynn37qjR07NtH3iX +; sTGACaosrmCbstK/RoXOb9Ky+Xgc0gMD0ILCQAlvtQunfvLg2cKfQ4fPiw5O2l/UUMHDjQu3XrlngD +; mSDSvHnzil+zkhvAANAUVzZP2D1V0xzfqDLvX3m5CmoGgBkCoAa4qwT+yMObM2eO9+WXX4rI00s7N6 +; 9UrVq1kn6CdXV13p07d542lY7zWpXeAAaApkpk84TdkBV6BMu8f+HkIqQZBGYIgBrgrhL4e+ONN6RR +; M16/ixcv5ja+jYU3YcIEGSsHCBIejlptnMTiNwA0VSqbJ5yvrNCjvMz7F04uAloWyoMBXIFAahRqip +; 73x5c8ZswY7+HDhwJcq1at8po1a5b7l/Lmm296J06ckJDwhQsX5MsI83tJLXwDQFNSsgKRbEWhx5gf +; 6AKMPGTev/ByEc6yUN4ckBcAzpo1S+oSCgOAjV04o9toyAz43b171xs0aJBTXzrh50WLFkk4+quvvv +; KmTZsWOHEkyYVvAGhKUjZPOBvh0er6R7rgIi+Z9y+8XISzrOQKD2QBgLS8e++996RP8fXr170aLYAX +; Ff769u0rjZjxsL377rtey5Ytnf2Cevfu7V29elXO9dChQ17r1q1ThT8DQFNasnnC6ckKPcLLvH/R5C +; KYZSVXuSBpCKTTCalndD7BMUYkVD0ANrxQLmrx4sVP266MHz8+0KvmivBWbt682fvNb34j4WrGyvnt +; YtJY9AaAprRk84STlbV3iS7z/kWTa1CWpVxng0oFE23dulUioY8ePfJGjBjxlJ9UA2DDCyWv7syZM1 +; LoQeNl/l/Ll+Rr9OjR3v3798VFu3HjRukjaABo0iYrEElGVugRXVG9f8v+Udf1pSHXoCxraWKEKOrZ +; s6d38+ZN4QmKYAkBlzJUIQAQT9nEiRPF48foNfLq4szxdUXt27eX/oR8aYym69GjhwGgSaWsQCSeKP +; QY/pe6IMIVmfcvulyEsiyliQ/CiGuCg+AhagxmzJghf9+QodQCoH+h5Mvt2bNH8ucg3T59+hTiCyVs +; PXv2bO/zzz+XL3Hu3LlSNGIAaNImmyccTat+rgseXJJ5/+LJRSjLWpr4IEiMnCUSigOJgRd+h5HGOE +; olAPoXOmTIEKnuBf62bdsm4920fElBKl2U9CukTQzXeOrUKelnaABo0ii8gRYSDpbN8a1MUb1//f9U +; 1/WlJZdALC9pYoSmNG7cOO+zzz6TQo81a9bUG3RRCADkQl577TXp54dnjKRG8uZeSmGObx5qbGECtn +; yZ33zzjffJJ59477zzjngIDQCjiwIFTedbNNk84cZlhR6VK6r3b7V5Wp/KBQBzQZpYoVSMlmXEbN3v +; W94xZayxn1MNgFxAly5dxCNGoQf9bNq1a6fmS4oDf6XC48kIObyBjJTjS3/xxRdjqVoAEK/Typ/VeL +; ubPTG0eKBIrNd0DUWUzRP+TnOf1wULrsq8f/EV144UTZp4wVf//v0F+gj57tu3TzqKNPWzagEQj9fM +; mTO9L774Qlq8kB+X5RzftBV2gVLF45P+7du3vX79+on30wDwO81+vnwFKkBY+5yu6yqaqn2esM3xTU +; 5RvX8bXtB1fWkrb/ByRZqYASZavXq1tI4j7MuI2XK/oxIA27ZtKw2S8fpdvnzZ6969u9NfTFrw5wvg +; mzJlioSDifUvXbpUqp6rFQABCTxKvpcvrABEYFHTtRZN1VogYu1dklVU79/Iv9J1fWkrb/BySRqYoX +; Pnzt7FixfF6xel5Z0qAOSER40a5T148EAudO3atdIXz9UvJSsA9MUioNqHkPD58+fl/6sFAGufewIO +; SbQY4XUAEU3XXzRVS7sYm+ObvIA58/5VJhfAyxW5zguMjKW1CzUQS5YsiVz/oAIAyW/btGmThDrv3b +; vnDR06VMWXkxX8+aLLNx5AZgkTHp80aVKoApEieACTHDuGYbACkXxV9HnCNsc3HZn3r3K5Al+uyEVW +; oOWd3x/Yb3kX53WcB8BevXpJA2RCvvv37290Nm4RlOSCJRfQn3184MABr1WrVoUHQAS0RQ39NiW/QM +; S8gfmqiPOEbY5vOorq/QMWNV1fVnIFvFyRa6wwbNgwGRFb961DbPv27RVFQp0FQIo6FixYIN4sGiD7 +; 3iwXvoCklcaibdmypfRDJC+QcXJUDTdVIFKkHMCkx45ZgUj+ShLs85S1d0lX5v1LRi7BlwtyhRPI7S +; cSik3/+OOPpQVcpa/pJADSvfrkyZPi9fPz2fL84NNU2ot37Nix8rTg500SJi4yAPpKMo8MmMQTpen6 +; iybt84St0CNdmfcvOcXpJFF05c0JFLsSCcWOHzt2LNGWd84AIB/0+PHjhW4pZ/YrWvP4wLNQVosXoG +; b4M4uHyulu3boVHgBLoSEpI05OmoWE85W2AhGb45uNKOaIAoC0itF0fVnKANAdAOS958+fL4MfaHk3 +; Z86cxM/HCQAkZLlz506BlFu3bkkeW9Yfdh5fblYipD5v3jwpDmExzZo16+k84aI3gk5y7BjGw9rF5C +; st7WJsjm82Mu9fsgIADQLzB8D27dtLJBQmunLlijhu0nif3AFw4MCB0siYogUgkKpf/j6rDzoP5bWQ +; e/ToIUOh+axxJbPIqmESSO1zyY0dswIRN+TyPGGb45udzPuXrAwA84dAUrf83r4bNmxINRKaGwCSj7 +; Z8+XLpYUPYl4tm4Rn8pSsAe/369eIJfPz4sTfkr/9rVYY/rgC2JMeOUZhg7WLyVZJgn4Ss0CNbRfX+ +; 7X5F1/XlIeyyQWA+APj666/LaNe6ujop3hw8eHAm75s5AL711ltS4EGhx4kTJ7w33nij3r9ncdF5yK +; UFPXz4cOmrOOb5P1Bl9CtVkmPHbJ5w/koa7OPK5vhmL/P+JS8iQ9hjA8BsAZBefkRCCfky7Yz2bVlx +; SWYAyKKiezXz6vBAkZdGLprBXz6immj0j/4fqgx+UtCQZB4Z3kALCecrQsJJgX1Ur5/N8c1e/f/UvH +; 9pyI8MjRkz5mmeuCk9CISJVqxYIYWv5OhPnjw5tffKFQDbtGkjzZzJP7t69arXs2fPZ34my4vOWq4u +; 6mrIAQyChiTbxViBSL7KukDE2rvkJ/P+pSMmbd25c0fsNI2GSRly1XZlraSZgLm9RELx+uXZ8i51AP +; TDjVwoSY10r27s5/K4+Czk8qKuZgD0oSHJPDKbJ5y/0m4XQ3sXK/TIT+b9S094pHDW7Nmz5+moMbpy +; WEg4WQDE04fHD88fHsA8B12kBoAkNa5bt06qWR48eOCNGDFCPkiDPwNA15RkHhlGxwpE8lXSYF8a8r +; VCj3y1+ufm/UtLfgEI4V8mcFGgSaHm4sWLGx0kUE1KggdoeXfw4EGBa3L+XGh5lwoA0nKExsMUepDU +; yBzfpn427w8gLWlY1AaA3ymNAhHzBuarJMHe5vjmL/P+pSsfAH0Rljx9+rSEhM+dOyf/r8GupaVKeI +; CqXqp76+rqpNqX8LorrJIYAOLKnDt3rvfll1+Ki5OiD/4u6Hdc+RCSloYFbQBYXzZPuHiqFOyt0MMd +; LfvHaABoofpoagiA6LXXXhMP4FdffeV9+umn3sSJE6u2QCQOB/BZ0XqNSCifHy3vXGOVRACQ8nHKyP +; H6XbhwwXv77bfL/o5rH0RS0rKgDQAbl80TLpbigr0Verijnt8371/aagwAfRGuvHHjhngD9+3bJ61K +; tNi5vACwa9euMsmDz4zJHjCSi7xSEQDyAlDto0ePhHJp8Ez3aoM/A0Dt0MAM4CQgEPFaQ/9C12dQNI +; UFewo9xvxAl/Euusz7l76CABB7QQ7bli1bxM5/9NFH3pAhQ6qqQCQKB8yePVtm+NJah5m+cQDSeQBs +; 0aKFt23bNolrUz7OaDdeMCw4FlGaFrQBYHnhvUsiJIxBGvTnuq69iCoH9syL7fpHugx30WXev2xUDg +; ARf6ZPIIWd2P3Vq1dLZw9Ndi9NCKS/LpFQCj2uXbsmLe9cZ5ZYANi3b1/vww8/FPcmZePAYBSvYRGl +; aSEbAIYXeWRxq0pLK0cNAN1RY2BvhR5uauGPowHgNPseYykMAPoinHn06FGx/8yY79atmxq7lxYAjh +; 49Wiqn8ZBu3rxZKqc1cEskAOSilixZIq5NhhaPGzfu6RzfagZATYvYADC64uSRNQQKA0C35IO9tXdx +; V13+KLr3D4+hpmt0RUEA2BgEkuo1Z84c7/PPP5fCz5kzZ8rfabF/SQEgHlA/Evrw4UPpfayFWyIBYK +; dOnbz3339fCj3OnDnjdezYMbLnUNMHU2T4MwCMpzB5ZE1VjhoAuifAXouBdlnt/u0TJf265v3LTlEB +; 0Bdt3/AC4g08cuSIhEE12L8kALB3794SCSXky7XTSFsLt4QGQC6c7tWUMdMYcuHChc/M8a1W+DMArE +; 5oaGrsWFDlqAGgm9JgnDWow7+r8dr8H0/+m8TrmfcvW5UDwCAIpK/d2rVrhQ/wghEOpQWcBjsYBwD5 +; LIiEcr14P6dPn94oIKoHQJo4v/vuu0L3169fF+Lll6LCn3n/DACLJrwNfkg4TOWoAaCb0mCcNanV// +; 4EBCt9nTnPRwNAvIV5XG9RVAkA+iL8effuXfGIkQfHRDANtjAKABL5JBLKNV68eFFa3mliltAASJk3 +; XybwR/k3TSHjgJ/BnwFgUYU3cPFPwlWOGgC6KQ3GWZte+1+fKK430Lx/2SsJAERt27aVXoEAEk6jPn +; 36OG8Lw2rChAneZ599JnN816xZI5FQTcwSCgABPcq7uUjcuaNGjZKLjwt/BoAGgEVWr5AFBAaAbsp1 +; w6xVLf63Gu/n/ypebiDedfP+ZaukABABRlOnTn2aNrZgwQLV84TpgQjU1tXViVNswIABqlglNADSvR +; q3JoUelHlD85WAn8GfAWDRZQCoW64bZs0iHPz8//zEGxjl9+jHGAUAzftXucIAYBQIRIRHz549K1FE +; 5gq/+eabztvFhgL2aHwN/JEOR8s7TawSCgBJ2Jw1a5bM8GXuHyXd5eb4Viv8GQCaSmUAqFuuG2btIh +; /wH///3wLGH4YLCY/9G/P+5aE0ABARUVy2bJlMxvBbx2mYJ8w5UthCXz/CvoR/+XtNnBIKACnbPnz4 +; sHj9KOfGC1gp+Jn3zwCwWmQAqFuuG+YiCPADAAHBciFh8/7lo7QA0BeetNLhEYRVXbWNeC5hIfIY/Z +; Z3/r9pYpVQAFg61oU4vcFf8eHPADA5GQDqluuGuUgiJPyT/9+T/MDG/n3In5v3Ly+FBcBKILBVq1be +; 9u3b642PreT10tCMGTMkCkru4uLFi585P028EgoA/cHO/E9S8GcAaABYLTIA1C3XDXPRhDfwV9+r8Z +; r962f/bdk/mvcvL2UBgIjfJwz86NEjCa8uX77ciXnC1DtQ94DX7+bNm9LyrrGf08QroQAQV2yS4Gfw +; ZwBYTTIA1C3XDXNRhTcQEPT/P2rrF/P+JausANAXYdXjx49LSPjChQtely5dcrOFI0aMeAqkjHUjbz +; Ho5zVxS1kATBr+DAANAKtJBoC65bphLrLwBgKC/Dlq6xfz/iWrrAEQkXI2f/58KT5FtI7JskAEzyM9 +; juu+DUk/fvz4acu7ctLELZkDoKaLr1b4MwBMTgaAuuW6Ya4W4QEEAmnqbN6/7JUHAPrq1auXd+XKFf +; EGHjp0SGbqpm3/evbsKY2qec/33nvPa9++fejf1cQuBoAVKu2FaACoWwaAuuW6Ya42UQiy4QXz/mWt +; KACYBgTSX2/Dhg0ygOL+/fsSlk1jnjDnvnDhQu+bb76R1jS0v4t6PZr4JVMA1HThBoAGgEnIAFC3XD +; fM1Si8gU3NAqZQRNO1aFHeAOiLMOy9e/ekGAMgLJePF0UdOnTwTp06Ja99+fJlr1u3brFeRxO/GABW +; oKQXtwFg8WQAqFuuG+ZqFt7Ahn0B+/+prmvQIlcAEBGOPXDggIRnr169KuHaSl+ztrZWRtNR6LF+/X +; rvlVdeif1amhgmMwDUdNEGgAaASckAULdcN8zVLryBfnsY8/6lJ5cAEFEMQk8+pnAQrp07d24saGve +; vLk0nq6rqxPP4uDBgys+N00MYwAYU0kvaAPAYspVABz91zXegh9n+54a5bphNj3RxL8171+acg0Afd +; Ee5vz58+INPHHihPfGG2+E/t1+/fpJw2lCvgcPHpRG1EmckyaOyQQANV2wAaABYJJyEQCppjzeqsY7 +; 06bG292sxqt9Lrv31ibXDbPJlIVcBUD0+uuveytXrhRPIO1axowZE1gggveQn6eghPYykyZNSvR8NH +; GMAaDBnwFginINANf98gn4lQoYnP18dp+JJrlumE2mLBQVALOGQLFZ34Zvb9++Ld7AHTt2SOVww5/p +; 3Lmz98EHH4jX79y5c/L/SZ+HJpZJHQA1XawBoAFg0nIFACf9sMY70vxZ+CsVcEhoOOvPyGW5bphNpi +; ykAQBR69atvV27dklO34cffihhXv9caCT95ZdfyhxfRsyl0UbGANC8fwaApqdyAQBX/iwY/EpFMj2w +; mMdn5aJcN8wmUxbSAoAIsJs4caKEgwnzEu49fPiweP1u3brl9e3bN9X318QzqQKgpgs1ADQATEN5Ai +; DePIAuLPyVhoSX/NS8gch1w2wyZSFNAOirU6dO3s2bNz2O3/3ud96RI0ek6jft99XEMwaABn8GgCkq +; LwCkwjcq+DWUFYi4b5hNpiykDQCZI7xx40YJBRP2ZaoHLWPwDKY9T1gT06QGgJou0gDQADAt5QGAeO +; 7K5ftF8QZWc7sY1w2zyZSFNAFg9+7dpUG03xqGCR+Efa9duyZ/t3//fskVTOv9NTGNAaDBnwFgisrL +; AwgEbnoxGQhEtI7J+7PMQ64bZpMpC2kBwHnz5om3j5Yw/Ln0PFq2bOlt3rxZpn189NFH3tChQ1M5T0 +; 1cYwBoAGgAmKLyLgLBe+f3/Ks0HJzXZ5inXDfMJlMWch0AGQ+Ht49CDzx9TY2H47zeeecd78GDBxIe +; XrNmjffqq68mei6auCYVANR0gWGU5UI2ACyWXKgCpqp3+8uVQ2A1hoJdN8wmUxZyGQBp/PzJJ5+IZ2 +; /Tpk2S/1fud5gYQmUwIeHLly9L2Dip83nhhRcKIwNAA0BTBXKlDyAhYfr8VeINJK+w2iqDXTfMJlMW +; chEAmQBCw+e6bz15ePSGDRsW6feZHTx79mzv888/97766itv1qxZseYJGwAa/BkAmhqVa5NAyOWL0x +; rGFz0Fs/rsXJDrhtlkykKuAWCfPn2kpx8hXzx5bdq0if1aeP8uXLgg3sD33nvPa9euXUXnpolvDAAN +; AA0AU5SLs4B9b2DcquBqahTtumE2mbKQKwBIk+dly5bJNA/au0ybNi2R98KbSD4gr/vo0SNv9OjRsS +; eFaOKbRAFQ04UZ/DUtbigDwGTkIgD6whsYJyRMPmHW55qXXDfMmjXoz3SdbzXLBQCksTPze/H64bF7 +; ++23E38PKoPv3Lkj77FlyxYBw6ivoYlxDAANAA0AU5TLAIjwBsYpEKmWtjCuG2bN6vH9Gm/pT5/8V9 +; N5V6PyBsBJkyZJrh6j3VatWpVIrl5TIpy8d+9egcAbN25IuDnK72tiHANAg79n4M8AMDm5DoC+GP0W +; xRtYLQUhrhtmzZrzfI2341c13uaX7LN2XXkBIL37Dhw4IDCGZ27AgAGZ2EHCv1OmTJHqYsLCCxcuDF +; VdXLUAqOmiDAANALOSFgBE5PZFKRCphoIQ1w2zVnX5P5/AX6mm/+jJ32u6jmpRHgA4aNAg7969e1Ll +; i0euRYsWmdvDzp07e2fOnJECEf775ptvlv0dTZxTFgAprTYArC74MwBMTpoAEEVpF8PPFN0L6Lph1i +; rf+9dQq35e4/X9j7qupRqUJQAS3l23bp309fv000+98ePHJx5SjiIaRS9ZskQmjOAR5HyC5glr4pyy +; AEjMnS+DZEgL/xoAmqJJGwD6IscvzDxhYNGl805arhtmjcLLt/NXT9QYBBISHveceQNdUlYA2KVLF2 +; nMTMj39OnTXseOHZ2xj/379/du3rwp3kA8koSnXyw6APrdsi9duiT9csz7V3z4MwBMTloBEIWdJ1zk +; ghDXDbNG4f3zATAIBK1AxB1lAYA0YqYhMzl3ixYtit2GJU21atXK27Ztm3gnyUkcOHDgM9eqiXXKAi +; BfAr126LlDFc6cOXPkizEANAA0lZdmAPRVbp5wkecEu26YtanU+xcGAgkJa7q+oipNAKTxMg2Y/arb +; 3r17O28ra2trvYcPHwoIrlixot48YU2sUxYAfcjDNXvx4kXxBh49elRm6Rn8GQCaglUEAETl5glTRe +; zy+ceV64ZZm2b8pxpv1yvhIRAvtKbrK6rSAsBRo0Z5jx8/lkKPrVu3eq+99przttJXhw4dvGPHjgkT +; wUYwkibWiQSAiOTMlStXypcF/TKEuWjEq2XxpQl/BoDJqSgAiIIKRIpaEOK6YdYkvH/An6+mIHBnSS +; 6gpusrspIGQDxmAB8sweQNQFCDrWwomGju3LneF1988XQyCQUimpgnNAD6Hj/i3sS/cX/yJVKeremi +; DP4MALNSkQDQV1MFIkUsCHHdMGvS5B/WB8ByIEhbGE3XV2QlCYC9evWSUC8h3yRm77qgnj17SvEK3k +; B/NrEm9gkNgL6APiphuGAqY/r27avqwgwAm4Y/A8DkVEQARI3NEy7inGDXDbMW4f3b/UrjANgUBFoB +; iDtKAgD5O4o7KPKg2GPmzJmhQ8Ua1Lx5c2/9+vUyreTBgwfeiBEj5Po0MVBoAEQUg4wbN0564/CFLl +; 68WLpla7pAA0ADwDRVVAD01XCecNHmBLtumLVoyg+fAGAQBJaCIJXCmq6v6KoUAGnnQlsXvH54yrp2 +; 7arGRkYRHAH4ffTRR3KtGzdulLxGTRwUGgD9H6I7dmm3bIY2a7rIaoU/A8D0VXQARA3nCc9+Xtf5B8 +; l1w6xBeP82vvAdAJYDQQCwCM2gu/5Rjbfh2+ve9u290ftP4r+OC6oE/mic/Nlnn0nKGD2F05zjm7d8 +; nmjbtq23f/9+gcCrV69KiFgTDyEYryZs42e+VObl4d7FI8jwZk3uT02LLCkF3cAGgMmoGgDQlz9PuE +; hzgl03zBo04W+fhb8gCASaNF1fYxr+7do51vK7hyL+POYHuq6hVHEA0E8Tq6urE48Yo9002cc4KmUK +; v4Ue00y++eYbb968ecJJWpgoEgD66tOnz9MEzz179nitW7dWcbGaFllSMgBMX9UEgMifJ1yUOcGuG2 +; bX1eVbL9iWl2q8d1+t8fY0CweC7ygGJcSDUFPtkuhriGdQ0/WgqADI1Iy7d+8KBxw4cEAaKGuyjXHU +; FFu8/fbb3rlz5yRCevLkSWmhp4GJYgEgYmwc1cF8+VQLDxkyxOkL1bTIkpQBYPqqNgBEeP8AwNrndJ +; 13Y3LdMLsuvF7An6+9ZSBw68u6rq9UA/+sxjv4evnJOYCwNm9gWPDDw7V69WophGBwhB8J1GQX4yqI +; MWh7s3z5cvEEfvzxx97YsWOdj5DGBkDExY0ePVr6BRIWXrVqlbPJkJoWWVIqdyMbACajagRAX0UIA7 +; tumF2X7/2rB4F4A5uAwPHP6bo+X7SsKQd+jTVP1+INDAN/eLouXLggjp+zZ89KLYAmm1ipwrAGLfRu +; 3bol3sCdO3dK5bCLTBQIgFFehB4/TA7hgj/44AOp/nHtQjUtsqRkAJiNqhkAiyDXDbPLauj9a6iGIW +; GKJSgY0XSNANy2l6PDX6k3EM+h69dZzl5Mnz5dGiHj7Fm2bJmTc3zTVljeIBwO/NXV1QkMEi6P8vtZ +; wV8iAIjojO0PeqYaiD+zQFy4UE0LLEkZAGYjA0Ddct0wu6zGvH/PeANLIHCestYvE/82PviVigIR15 +; teN2UnaHh85MgR8fp9+OGH0g+4WkK+DRWFO/iMJkyYIGPwCJcvXbrUqRZ6iQKgr+7du3uXLl0Sb+Ch +; Q4e89u3b536hmhZYUgrjzjcATEYGgLrlslF2WeW8f415A3sqavxMMUcS8OcXhrh+vY3ZiOHDh0uKV9 +; 23nqwdO3ZI7n+1wl9UAPRFf8QTJ04IE50/f9576623cmeiQACs9IVJhlyzZo0smvv370ueYNwPzwAw +; ngwAs5MBoG65bphdVRjvX6kW/FjHdYUt9AgrvIgarrvUNmDDN2/eLDacgoYxY8Y80wKm2lQJg1A4s2 +; DBAgmh+4UzeUdIUwNAH7qoDL537564Pzdt2pTbPGFNiywpGQBmJwNA3XLdMLuoqN4/pMH7N/f55MAP +; j6em5tC+XejRo4d37do18VgdP35cWpo0tB2abGFSSoJFevfuLU2j+Wz91jl5MFHqAOiLC6RbNhfMou +; IDyPIiNS2wpBQG/gwAk5MBoG65bphdVFTv39Kfun09eP0AtqTgb67CMXfYhPnz50sbk6+//tqbO3eu +; eKkasx2a7GFSSopJqApmfBxTU3CQDR06NNHXjwJ/qQMgYhHh8sT1iQuUaSJZdcvWtMCSkgFgtjIA1C +; 3XDbNrGvFX0b1//f7U3euJ095Fe8VvY6KBsT/WDC9gkO3QZA+TUtJcMmrUKEmRq/s2zL527VoJu2fB +; RIEAmOabdu7cWZIg/W7ZzBdO+0I1LbCkFAb+yOkY9Xf/N1WG2lUZAOqW64bZNUX1/q3/pZvXQXuXJA +; s9ljju5SwnPFJ4pgARg7/04K9UtNCjWBYmunz5shTRps1EuQEgogx6yZIlkhdIeTRl0iyqtN5P0yJL +; SkE3L+5nqrlYcMe2L/MG/Y1BYKUyANQt1w2zS4rj/Rv85+5dB1660jm+lbZ40TwDuMdf/oG3c8U0CU +; WGcR5osoVJKU0mIkI6c+ZMaZ9H6H327NnSVi/N98wNAH0o69evn/QUqvvW/UnDxDSSITUtsKQUdOMy +; w5mmlP7sRvo7/eqFn3k9f/j/UWWwXZMBoG65bqBdEt487d6/4X+ZHPzRIFrj3F9f41p939u9fYvYgr +; CpQ5rsYVLKgosYoMEgDZwz7733nngH03ifhil/mQOgr1JvFGAyaNCgRF9f0wJLSo3dsDxN0LkdFz95 +; mJMnT37mZzr+w7+xnMCYMgDULdeNtCvCkxfV+4fH0MVrSaLow/XGzuW0aeZgb+rUqaHBzwAwfTFKl5 +; G6TFt59OiR98477yQeIW0SALO6yFJxcQxNJhxM9RHDlJOaJ6xpgSWlhjcrsxrPnTvn/dM//ZP8lyaU +; TYaHf/5Dr9/f/HNVxtsFGQDqluuG2hVF9f6RK+jy9eC5I28vTqGHpvYuDTXype95O9Ytlrm+Bn/llQ +; cX0ULvzp07Eq3bunWrNOBO6rWdAkBf9Bryu2UDKizOSl9T0yJLSqU3K5XX/uxGwJrK63I3+CvfhoTf +; /k//s3kDI8gAULdcN9guiCreqN4/LXlxUULC2gs9lo5o461cuVKiQlHhzwAwW7Vu3drbs2ePQODNmz +; clhSuJ83ESABGAQu8hPIGffvqpDJ+upFu2pkWWlLhJyackxw+YJs+SfMuoN3qrn/2VN/AH/40qQ56X +; DAB1y3Wj7QQ4/DQa/O38la7rwxu44YXgQo/hitcKhR571i3w+vfvHwv8DADzke/I+eSTT8SRs2jRoo +; pa6DU29tcZAPTVs2dP78qVK0K+NJGOkwypaYElJRYLeZR+b6Fdu3bJ9JW4NzvewG4/+peqjHkeMgDU +; LdeNd94qsvevocjra+gN1DDHN0jz+70knqRKbEG1wl/eAOiLVK7Tp0+LU+fs2bOxW+ipAEBEL6INGz +; YIBH700UfeyJEjI/2+pgWWhHgqoJlk3e9nN5JXWcnNXqo2P/2PFhIOkAGgbrluwPNW0b1/DVVaIKJl +; jm9T2r6o1hs3blwidkCTPUxKLjERLfQWL17sffXVVxIhHT9+fOQIaZMA6NKF+mLRjRgxQjxa9A1cv3 +; 69VA4bANZXly5dpIkkhR7MbuzYsWNi8Ofr1V8+b+1impABoG65bsTzFPN7o3r/JiiHJkRIWHOhx4iX +; /tDbvWVtorZAk01MSi5yUd++fb0bN26IN/Ddd9/1WrZsWUwA9EWPotJu2YSIDQCfhHxnzZolzSMRcx +; ybmt2YhAgJd3r+D80b2EAGgLrlujHPUwt+XF3evyJo7cTuMm41aVugyTYmJVeZiHD+li1bpLXb3bt3 +; JfWr3Pk2Bn8qABBRtUTPoi+++EI0b968wGRITYssjtq2bSvNIvH6MbsRKE4L/BqKAhFrF/OdDAB1y3 +; WDnpfieP+m/p2uayySRr78PW//ttVer169Et/zNdnGpOQ6E3GOjHR98OCBpH5R3U2YuJAA6Iv2MBcu +; XHgyyuzYMa9Dhw5VB3/kQ5LnB/1v2rRJ+iby91kBoO8NpF2MJkOflgwAdct1w56X4nj/uiieiqFZy0 +; e2E48QtiCN/V6TfUxKWpioffv23tGjR4WJLl68KBNFCguACMqlrx0A9PDhQ6+2trbeF6ZpkUURhTE0 +; haQwBuofPnx4vX/PEgBLvYHV3i7GAFC3XDfueQiQM++f+6K9y+Z5o8UpkNYer8U+Ji1NTESEdM6cOR +; IdpfcvLfRK5wk3BX8qAdCHvIEDB0q37Lpv3Z/bt29/mgypaZGFFW59mkFC+YcPH5YQcN7wV+oNrOYC +; EQNA3XLdyOehON4/QsaarlG7Rrz4h96WtcukTVqa+7sWG5mkNLFQqbp37+5dunTpKSdQP1EWADVdYE +; MBffQ48pse0+hS00IrJ25Amj9SBQ3dQ/aN3ZR5AqCvap0nbACoW64b+qwVx/sHMGq6Ru3aPHuIN2PG +; jFSL/gwAdYqxcbSEgxmIFOId5poKCYCIm4BeR3TLphp26dKlEi7VtOAaEyX8Z86ckUKPDz74QPIfXw +; wARRdUjfOEDQB1y3Vjn7Wiev+Qef+yEXN8d29YJnleWeznGuxkGtLEP01p2LBhUiFMyhi1AjBRIQHQ +; F92x/W7Z77//vte5c2dVi65UNHn8/PPPheJXrVolFc9BP+8KAKJqmydsAKhbrhv9LGXeP3e1bERb8e +; yEmeluABhfmpinnAgB0ysQCLx27ZqkkvH3hQRAxM2xYMECmZuHR3DKlCniIdSy+Ojvs3fvXoFY8hsH +; DBgQ6vdcAkBf1VIgEgYAaSw74z/puq5qkQbjn5XM++eeKPTYvnSK9HrLeg931U6mKU28E0Z8j7TQY3 +; oIXES/YApECgmA/pfYp08f7/r160K+AFXDogkXRf4iY+/8c27VqlXo33UF+hqqGuYJlwNAxkn5c0U3 +; vVjjjf5rXddXdGmAgCxk3j/3VNvyj73tmzeILch673bRRmYhTbxTTqWePiKizBHGuXTq1ClpoVdYAE +; QkQ9IbCaAiFk5M3MUFB42vXr1aqpn9GX9Rb0BXgK8ptf/JfyhsSDgIABkkXzpYHu1/rcab/byuayyy +; NIBAFpr99+b9c0kbpg/0Jk6cmNue7ZqdzEKaWCcqACJa6FEjQa0EvYTHjh1bXABEhH9Hjx4t/QJxf6 +; 5Zs0bmCbuy4N566y1p3giVk79IHmOc13EB8sqJApEitotpDAAZKH/w9Wfhz9fxVjXekp+aN9AFaQGC +; NBXH+7f0p7quUYuY47t323qvU6dOue3VrtjHrKWJdeIAoC+ijXRNgTsKDYC+6JVU2i2bfjl5L7Zp06 +; Z5X331lffNN994ixcvrihX0QXAC6MiFog0BEDArinwa6jdzb4N8zyn63qLJi1gkKbieP/6/amua9Sg +; pcPbeEuWLJGoUJ77dN62MQ9p4pxK4M8XLfTon1wVAIgoEJk5c6ZAFxW2dM4uV12bhqjMoUkj7V3IU+ +; zdu3fFr+kC3EVRkQpEfADE67enWXj4a+gN1HTNRZImQEhDcbx/63+p6xpdF4UeB3es9fr27Zv73py1 +; PXRFmjgnCQD0B4BUDQD66tat29Nu2UeOHPHeeOONzBbZ0KFDvUePHkm+H2PdyFNM4nVdgLqoKso8YQ +; Bw+o+ig19j3kALCWcvTaCQhhjhFhUAR/yVrmt0WfP6viieGGyBC/tyVrbQNWninCTgr9BVwOVEU0S/ +; 8IJu2WPGjEl18ZN8STNGClIAQPISk3x9FzaOuMIbqDkkPPZvnnjxKgVA3xtoBSLZShMsJC28f1teig +; Z//Lyma3RZWxeM8d555x1n9uK07J/r0sQ4BoAJAKAPTUOGDJHWK7/97W+9zZs3S1w86cVFviGhXjyO +; 5CG2b98+8fdwZQOJK+3zhPHcbX85GQhE635p3sCspAkYklYc79+YH+i6RhdFocf2DSsl+uTSPpy0Xd +; IiTYxjAJgQAPqix9K+ffsE0AA18jCSWFTcUPPmzZPqY/IOZ82aldpN5tImUom0zxMmly8pbyDtYib9 +; UNf1a5QmcEhScbx/O3+l6xpd1JoJ3ST/PIs5vgaABn8GgCFE1RU9l+jD9+WXX3qLFi2qaJ4wT3YnT5 +; 6UQg/yDck7THMRu7aRVCLt84SBNuAtqZCwFYikK03wkKTw5Jn3LzuNfPl73t7NKyQi5OK+m6Z9clma +; +MYAMCUA9EW37HPnzj3tlk0vpqivQT4hIMkc33Xr1kn+X9qL2MUNpRJpLxAhfEsYNylvoLWLSU+aIC +; JJmfcvOy0b2dbbsGGDOBVc3XPTtlEuShPbpAF/BoCNCGCjFxMAR7fsSZMmherRRxXXzp07BR7JKxw8 +; eHBmC9nVTaVSaW8XM+1HNd6R5lYg4rI0gURSMu9fNqK9y47lUyXX3OV9Nis75Zo0sY0BYEYA6ANVv3 +; 79vJs3b0rl7u7du73WrVs3+fPkDd6+fVt+dv/+/YE/m4Zc3lwqlfZ5wngDmQFsBSJuShNQJCXz/qWv +; ca2+7+3atln6vrq8v2Zpp1yTJrYxAMwQAH0xNo4eTXj1ALyGXj08g8uXL5cqYppL4y3M46ZyeYNJSt +; rnCS/4cXIhYSBQ07W7LE1QkYTieP8m/K2ua8xbG2cO8qZMmaJiX83aVrkiTVyTFvwZAIYQkEde3+PH +; j2Vs28qVKyXcS37g+fPnpdDj/fffl/zBvBazho0mCVEg0vtv/ztVgFEqCkSSaBdjAJicNIFFEjLvX3 +; oa+dL3vP07N8mMdw37aV72ygVp4hoDwBwB0BeVvcePHxdv4LVr16RamBYvy5YtkyriPBezhs0mKWmf +; J5xEgYgBYHLSBBiVKo73j16Bmq4xLy0d0cZbsWKFjBfVspfmabPylCamSRP+DAAjiibOhII5fve738 +; k4t7zhr9oA0Fc1F4gYACYnTZBRqeJ4/3p+X9c1Zi0KPS4f3+0dOnTIa9GihZr9M2+blac0MY0BoCMA +; OGjQIBkbR6HHwYMHvatXr4o3kD+nMd0jirRsOkmrKO1iDADzkybYqERxvH/krWq6xqw1v99L3rvvvu +; udOHFCbAE9X3v06KFi78zTXuUpTTyTNvwZAIYQLn36+dXV1UkeIPmA/P1rr73mrV+/XoDw3r173qhR +; o3Jb1Bo2nDSlfZ4w3sAoIWEDwOSkCTgqUVTvHzLvX9PatnCsV1tb++RB9FsbwXQPcsS/+OILmQDlci +; g4LzvlgjTxjAFgzgDYtWtX78qVK/J0d+zYMckDbAhew4cP9+7fvy+VwBs3bpQwQNaL2tWNJktV0zxh +; A8DkpAk64mrEX5n3L7HP8qU/9HZtXuN17NjxmT2IKR++vWDue2M/k7eytk0uSRPLZAF/BoABmj17tj +; zRff31197cuXMDbx56PREK5sZnA+jdu3fmi9u1jSYvCKyGecIGgMlJE3zE1fpfmvcvCa2d2N1bsGBB +; 4Bxfpn34kaGHDx96Y8eOdWqPzNouuSRNLGMAmBMAtmvXTrx9tHcB5sjpCPN7FIPQ+4kQAJo/f34mY+ +; AMAJ9V0ecJGwAmJ00AEkfm/atczPHdv3W116tXr1D7D4A4bNiwp5Ghbdu2ea1atcp9X8zKFrkoTRyT +; FfwZADYQeXyffPKJ3LT+7Maor/H22297H3zwgXgDaRvz5ptvZrLA895cXFORC0QMAJOTJhCJI/P+VS +; bm+G7evFlyvqPuQUyE8iNDN27ckOlSee6JWdghV6WJYwwAYyru4uDm5imNG5WnNp7eKllsgCMTQpgn +; /OjRI2/8+PGp34B5biwuq4jtYgwAk5MmGImqfn8aHf6W/lTXNaYl2rtsmjvKGzFiREX7D5GhyZMnS1 +; Toq6++8pYuXSr2Iet9ME3b47o0MUyW8GcA+K1w63/44YdPW7qQz5fEouOmGzBggPQNrKur83bs2CFh +; gLQWedYbiiYVrUDEADA5aYKSqIrj/QMaNV1jGhrx4h96W9Yu89q2bZvYHsR0ED8ydPr06UynhaRlcz +; RIE78YAFaoqMC0ePFi8dLxdDZ16tRUbpaWLVt6u3fvlhv/1q1b3sCBA1NZ6FltJppVlAIRA8DkpAlM +; oiiO9w9g1HSNaWjTrMHe9OnTAws94oqccCZH4RD4+OOPJWc8jfcxACwuACYJf1ULgJTnM7+XQg/m+f +; I0luYi5CYfN26c3PRUFbMJEHZO+n3S3kiKoCIUiJCkr+mcXZYmOIkiQrlRAZCCEU3XmKSY47trw1Kv +; S5cuqe4/2IL+/ft7d+7cERDcs2dPop5Gg7/iwp8BYAIAOGHCBO/zzz8Xz9/KlSszHeUGeJ46dUq8gW +; fPnpWCkSRfP82Nq0jSPk/YlJw0QUpYUcRh3r/wYo7vmjVrxEOX1R5Ev1g/MgQMDh06NPH3yMquuSpN +; 7JIH/FUVAHLDMbaHG468PJ7C8liUbDK0iPn1r3/tffrpp960adMSg9CsNq+iSHuBiKlyaQKVsMJDHB +; UAGRWn6RqTEIUe25ZMlrScPPYf9n36BNJ5gp6zQOjrr7+e2OvnYd9ckSZuyQMAiUCuWrWqOgAQ2GNc +; G805eerKY1pHQ9Es+tq1a3JO+/btkzBApa+ZxyamXXgDu/3oX6qCFlNy0gQsYdTlj6LDH2PiNF1jEp +; r21t95WzetkxztvPegDh06SGEIzokLFy543bp1q/g187RtLkgTt2QNf0w4I/WN9VYoAGwIgTxhrV69 +; WnIteMoiD8+lRcrT3qZNmwQCP/roIxkrV8nr5bmJaVf7n/wHCwlXoTRBSxiZ96+8NkwfKKlAWRRghB +; Wzg5khTGSIFCUmUWG/4rxW3nYtb2nilSwBkPU+Y8YMiTxS+Er0sbAASGHHxYsXhXJPnjwp+XcuLla+ +; FBpQP3jwQPIS161b5zVv3jz267myoWkUBSKa28WYoksTuJRTHO/fzl/pusZKxBzfPVvXSXN+V/egnj +; 17Po0MHT58WObPR/l9F2xantLEKlnCHxPODh06JOsKLzP1B4XLAfQBkDJ+qm3Jq1i4cKGKG4Mv6MiR +; IwKsly5dCj2CzgAwWVmBSHVJE8CUk3n/mhZzfGn7FderlqXIz/IjQzgGRo8eHfp3XbFneUkTq2QBgP +; w+DiZS4JhwtmLFCvE2F7IIhCbOQBTtXXiKosmzpsXLF4OL9ssvv5QwwNy5c+XvoryGixuaRlmBSHVI +; E8QEybx/jWvA8/9f7+D2tV6fPn1U7T9EhphC8vDhQ4kMMY6uXL6ia/Ysa2lilSzgj0jixo0bZf3cvX +; tXip0avmZhAJD8OcauQbncLHHm+LoikoD98PV7770nScJhf9f1jU2TtM8TNpWXJpgJ0uy/jw6AE/5W +; 1zVG1by+L8qIzyQra7MWTg1CwdiCq1evSvFgYz/noh3LUppYJQsAZJ2wXlg3e/fulcLXXxaxDQxtVQ +; A+3OU8LY0cOVLVwm1KACxl2gAt10W7gLA3upbNTYvwBlpIuJjSBDRNybx/z2rL/HcihU5dFlEg0pqY +; JUx0iLSm0p6FrtqwLKWJWdKEP9YKbeYo8qDYg8JX1khTP68aALt37+5dv35dKJfQL3l0/L2mhVsO5A +; YPHizuW0Bw69atoeYJa9rc1GzCyucJm4oLgOT+7YsIgFP/Ttc1hhWFHtvWr/Dat2+van8JI6aU+JGh +; EydOeJ06dZK/d9mGZSFNzJIm/FHoevz4cXGGnTlzRv6/3O+oBEC+dCiX2DZPROTNlS4ETYs3jIA+v4 +; n1jRs3vH79+gX+vKZNTZu0zxM2FQsAu37r/TvTpsY72arGO/h6eO8f00I0XWcYbZk9VNqnuNTeJWnh +; +WOCVV1dnff48WNpZ8Pfu27D0pImbkkLAPkcxo8fLylwtBHCQ0yxU5jfVQeAPNkxTo1CD8qZeSpqDB +; CLJr5QbnbcuoQClixZEpjnqGlT0ybt84RNxQHAJT99AoDodOsa70jz8t5A8gU1XWM5jXz5e96eTcsT +; aaCsQQAuCf1+ZGjnzp1e69atVdixpKWFW9KCPwqDduzYIQ8EOIfI/YvyGqoAkDy4zz77TDx/jM0h3t +; 3Yz2lawFGF2585wngDcfN27tzZMwDMXtYuphjSBDoN5Xv/GurEt97A/a9Vh/dv2Yi23vr16zOd4+uC +; 2OMx/n5k6NatW96gQYPU2LEkpIVb0gJAHgI+/PBDCflu2bJF2gdFDRurAECquHbt2iULvbScuSlpWs +; RxxGZHTyvcvUw4mTx5sjwVGgBmL2sXo1uaYKehSr1/DYU38HDzZwGQfEFN19iUmOO7Y9lUyZHWtF8k +; odJ9nn2fRH8cI/S+pc+b5g4YYeU6s6QJf3y/y5Ytk++bsC/FTnwmUeFPBQD27dvXu3PnjlAuTzvkw5 +; X7HU0LOa7YCMgFvHnzpnw2e/bskZYBLxoEZi6bJ6xXmqCnVE15/xrqeMv6IeEieP9qW/6xt3PbJgl7 +; atonklBT9oDpJkSGSI06d+6cpEa5bsMqkcvMkib8MeHMjwDSIq5t27axwM95AOTJhqcZchx4uiH/Lc +; oXr2kxVyKaPdLrCggElIcOHWoAmJNsnrA+aQKfUs19PhwAolPfegMPvV4M79/GGYOeRjw07Q1JKcgW +; kBJFAQApUuSKUxzZMDJUBLnIK2kDIN89s3uJ+FEDMGvWLPluK4E/ZwGQPLcPPvhAnmbIc+PpJupraF +; rQlYqFMGbMmKdVQKtXr5awuUFg9rJ5wrqkCX58hfX+lWr3K09+T9N1lmrkS9/z9u3YKDnPmvaDJBXG +; FvBzFAL4kaEDBw5IezSX7VcUucQpWcEfkb19+/bJ93n58mUpdqoU/JwFQJ7uaO3CHF8qXYGbOK+jaV +; EnJSqkjx07Ju5hKqSrpSrONVmBiB5pgiBfUbx/iFxBTdfXUEuHt/GWL1+uYo5vWopqC3AAEBnCFjAH +; lrFyLtuusHKFU5JWY3DG3zPhjLqHuro6KXwl/z8p+HMKAMntO3jw4NNed345c1xpWtRJijDAnDlzJE +; GUecK4ivk7TZtdUWQFIu5LEwghvHj0+wsDfsda1njDFV6jLwo9dq+dL7nOmu77pBXXFuA8GTVqlPQL +; JDK0YcMGSRly2X4FKW9GSUuNgRkAv27dOvneAPghQ4Y0+bPqAZBKrgcPHgjl+rMbk3hdTYs7afXo0U +; PcxQD1oUOHvDfeeEPVplcU2Txht6UJiNDEvw0Hf6t+ruu6Goo5vvS3A1g03e9JKwlbQAjYjwxhE7AN +; rtqtppQXm6StxqCs1Hbv379fnGNJg99TAPRdjXkIzxQ9nOp+39WccuYkX1/TAk9D9AVau3at5A7cv3 +; /fe+edd1RtfkWSzRN2U5qgKKz3D0jUdF0NtW3h2Kezz6tZSdoCwudEg0itYk7svHnz5O9ctFsNlTWX +; ZKlSGOP7mDt3rkTuKHydOHGirIO04C9XAOzatat39erVp+XM5K8l/R4aFnfaYgENGzZM3MhUh23atE +; kaiGraCIsimSf8d1Yg4pI0gVE579+eZjVe7z/RdU2lYo7vjo2rvA4dOqi6r9NQWvaAvPBLly6JUwC7 +; y2ftms1qqCy5JEuVghgROr4PeOj8+fNS7JQm+OUKgOSo8SRCOTN/TutLdn1hZykqiagIY4Fdu3bN69 +; Onj6oNsQiihxOf/Xurxnpjnv8DVaBUVGkCpCDvH4Uhmq6lodZM6CZeqWpt71KqtG0BhQR+ZIjOEbW1 +; tU7ZqlJlxSRZywcwrhFvNylw5PtR+EpkNAv4yxwA/VwE2rvwFMLTSNrv6erCzkMsLKqscTFTaU3PKL +; qKa9octYqeXPS09I/7V095s1r9a1WwVERpAaSmvH94/Qb+mZ7raCjm+O7bssrr2bOnqvs5LWVlC3gv +; CguIDLEvbd++XXLNXLJXabNBnoK7WrRo4W3dulU+f0a6UexUCoeFAkDy+2hiyMWS99fUHN+k5dKCzl +; v+JvP2229Ln0W8gSdPnpS+i1o2SG1iWsGJEye8xo5ff/mpt2Pym6qAqWjSAkqNef+0t3dZNrKtt3Hj +; RnsI/b3ysAlAH4UG2AJ6B/bv398JW5UFG+QlmAvYo9sJn/uOHTuk8DVL8KsHgGlCIDc3Txe4m0vLmb +; OSC4vZJfmbjT9PkLxACnCYtGLhl2RF7iUd+YMO3P7jBrxp7WJykgZQauj9o73LmB/oOPfGRHuXXSum +; S48zTfdzmsrTJrDvU3BAcQhpWUuXLpUwcZ7nlCUjZCk+18WLF8vnjN1lgAPXmwf8pQ6AvXr18m7dui +; WUS/4Z3pA8PvQ8F7JrKt10uPEHDBgg31FdXZ20XSBXUMum6aq4yWln9Lvf/S4Q/q5cuSLFUPyOFIjY +; BJHMpQGYSr1/217WPdFjXKvve3t2bJUZppru6TTlim2g8IACBOw1E7jIWc7jPPJghCzE53v69Gn5fI +; kKUfiaF/ilCoAsapIZCfeSb0beWZ5fbB6L2GU13ICoCt61a5csTGBw0KBBajZP19S9e3eZyRx0kANL +; EnZjoa+O//BvrF1MhnIdmEq9f9N/5P75BmnTzMEyz9QiDd/JNdtAapZvu0nZmjJlSqbnmRcjpCk+Px +; jo448/luJXWr3Q8iVv+EsFADt27OidPXtWjNy5c+eEevP+ArJavFrU2EbEgqQazF+kK1askLwEDZuo +; K1qwYIGkOgQdVHuRBhH0OswT7vc3/1wVSGmV69CE9099ocdL3/N2rlsiucca7uOs5LJ96Nu3r3f79m +; 2JDL377rsSGUr7ffPmhDRE1HPPnj1iF2h7R5NnF8AvFQAkj4w8AvKamN0Yd45vGkp78WpTU5sSAE9h +; CN5AAL5Lly4qNtM8RQ8nPqtyB2kQJF2HeU2bJ5yNXAYn8vzUz/Ed0cZbtWqVjaNsIA02gikspAVhC5 +; hHO3To0NTeyxVOSFJ8Xj5EM4aPiI9L8FcPACuBQMqZ9+3b9zSEyNODa19GFjeMJgVtTuSwzZ8/XzyB +; FDFMnz7dNvAmhNeUhN6gg5Y7JFnHGWZv84TTlcvwpLmpM4Uee9cvlBxjl+/fPKTJTuDEYYKUHxkidY +; XIUJLv4RorVCo+n9WrV4sjjIgPxU5cp2vwlwgAcoNT3Vv3+yICnhpc/FKyvnE0qNxG1bt3b3Fb476m +; VQBJqxo22CzEmD1CI+WOCxcuVNxmB29gtx/9S1VgpUWaoEqL5vd7ydu7d69NHGpEmuxDqYhy+JGhix +; cvSg/fpF7bRV6IKz4Xv8XaoUOHJATsIvhVDIB4M9asWSPgx9MB3axd/3LyuHFcV7kNi6cZenX5bXxG +; jhypYqNNU/RwYrZy0MF9QTI13tSk3rf9T/6DhYQNAJ3W9kW13vjx463QoxFpsguNCZvvT/GiuHP27N +; nyPVfymq4zQxQeYtYyM3xJg6N4hs/GZfiLDYAk816+fFkKPShnZp6g61+QAWDjCrNxsZABP9zZ9A2k +; kTdhf9c33KTF50A+U7n2LlQBkwaRxjlQIGLtYgwAXRNzfHdvWeu9+eabud2fLkuTTSgnChn8yNCRI0 +; fEOxjndTQwQxgRGTt8+LB4/Yj4wEeug1+jABgGAhlp9fXXX4vIE9P0RbpyA7mmsJsYo/z8hc4DAH0e +; Xd94kxJhXK456AAM/TSINM/FCkQMAF3S2ondZaykef0alyZbEFZENihsAAJxDJAnGOX3tTBDOZ7guo +; kG0TaHwlc+F/6tcABIGfjRo0fF6wf9M7tRw5dkEFhekeDjlVekKAQ3N2KAe5JhThc1depU8XwGHRTL +; 0NU9SyNIgYi1izEAzEvM8T2wbY3kCme15jVJkw2IazeYduRHhrZs2SKRoXK/p4kXmhLXuWnTJrluIj +; 7UQvjXpgX+QgMgVSyMLYFyyQfzKVejXL6h8r6Zo4gJFiQD4w08duyYtI9xeTOOI5LYeegpd5AcnVeB +; jO8N1ARdLkkTcLmk5SPbySB7iqHyWPeuS9PeX6kodKDgAVtw7do1r0+fPk3+rCZWaEpcH9fJ9dLjDx +; j0/00L+DUJgKUQCOht3rz5qZsXENTyJRkERlfUTY7Nf+XKlfJg8PDhQ2/cuHGFCQPRrJnipqCDpz+/ +; q3ve54s30ELCBoBpi/Yum+eN9kaNGpX7mndVmvb8pMS+T6SElli0vWLeLdGi0p/RxAiNCR6i2T/Xx5 +; QU395phb9AACTR88aNG0/LmQkBa/qyDACjK85mxw3A6DgahdbV1Xnbt28P3ezYRbFp4dovV+jBEyBj +; 35w6d5snbACYoka8+Ife1nXLrR1UgDTt92mIAggKIeAGIiPkTvP3mvigMVHc5LfBYZ4vEa+GP6MF+g +; IBkAuBcvFuQLrMbtTyJRkAVq64Gx/QR+8vbpCbN2+qbABLWJtzDzrIgfW7urt6HTZP2AAwaW2ePcSb +; OXOmFXoESNM+n6Z4iKYgou73LeJogs/fa2IEX3yvTDgjBY72N7AR11cE+HsGAHmyO3XqlBg5mhlC81 +; q+KIPA5BR3A+TGoAcY7nFCAUuXLlWTI0R/KzasoOPRo0eSBqHBCNo8YQPAJEShx+6Ny+ThyPU1n5c0 +; 7e1Z2pD+/ftLgQT76q5duyRXUBMj4NSgqwPnf/36dSl2auzntMBeIADSyJkmhnj+6HVGXpOWLyqONN +; 1Med3AcYXb/8yZM+INfP/997233nrL2c2b1jaca7mD9jdsYK5eR2OydjEGgJVo2Yi2Mvqr6FX+lUjT +; np6HaIlFoQS2gLm4pAtp4APO88MPP5T6B+ogcGQUDf4EAJn0sHv3bvmCoHWonX/Q8CVVKk03UtaqdG +; MkREpvMOYh4hEkQdiFYolSjR49WlrZBB30u3Tx3KPI5gkbAEYRhR7bl00RI6hpnWctTft5HvLtLJ8V +; DiZaZbGfUjiIfXCRCQC9FStWyHlS2OgXOxUR/gQASd6Hcv1y5tJ/dPELSlKabqY8lMQmyUQMiolYY8 +; zObdu2be4bN5sPDz3lDtrcuOy9jCKbJ2wAGEa1Lf/Y27Flozpvd9bStI/nocbsLRPDiAjhbDp//rxz +; KWZdunTxzp07J+fHhBNsVVM/qwXwygIgVE45c2MX5dKXk5Y03VR5KInNkjAAPcOAQLzMNA/Na+Mmj+ +; Ojjz4KBD/O0+/qntd5piWbJ2wA2JQ2zBgoCftW6NG0NO3deSnI3hJJYYIYkSFSzpgsxueaJwOw3hlu +; AAtR+Mo5lUuB0wJ4QfrFL37h1VDO3NQP5PmlZCVNN1ZeSmLj5IZibA6FFNz8a9asSX1kWkNRlEKBU9 +; ABHJIGkeV5ZS2bJ2wAWCrm+O7dtl5ydzWt46ylac/OS2FtLpPE/MjQgQMHJBc7D/vP+/L+nMelS5ek +; 2Knc72iAuzDwJwBY7gfz+FKylqYbLC8ltYlSac50DdzshFjpN5n2xs1DDr2pgg76/tHGhjSItM/HBV +; mBiAEgWjq8jTwYac5xzUKa9uq8FNXukopDgQW24N69e96IESMytfkjR46UB/66ujpv9erVoSacaYC7 +; sPBnAGgAGElJbabcaLNmzZJWMZ9//rm0YEkr3Dpp0iTxOAYdnENtbW1Vhr6qvUBEE6wlKQo9dq2ZJz +; m6mtZr1tK0P+epuLaXzxjw8yND9FilMDVNe0/kaf369dLxBAAcPHhwqGvQAHcGgDGl6WbLU0lurkzS +; uHz58tOk2zfeeCOx1+YmP3jwoFfuICmZ5OQ0jYjrquZ5wpqgLSnN6/uit2PHjsxTMLRJ076cp5Kwv0 +; wa8yNDV65ckRBxGna+V69e8vq8z759+2Tee5jf0wB2UeEvFACaF9BUqqQ3WcruyQes+9YNz7zpMWPG +; VOyJo30FT5RBB/OL/a7uWRkU11WN84Q1gVsS2rpgjNxjmtZl1tK0H+etJG0w+z4FGLRgoT0XxSJJ9S +; Nmn583b568LsUnTPconeNbjfBnAGgQGEtJb7jciEOHDhV3PGBGXkicecJsFrj2y83xZdwbT5h5GBfX +; VW3zhDXBWyWi0GP7hpWJetmLKE37cN5Kyw7TjoWCDLx0x44dkwhNJa/H7/M6vB5tXih2Cvu7rkNdJf +; BnAGgQGFtpbL6EAfbv3y83KqN3ouQn0VOK3wk6AMMtW7aoGU+Xp6plnrAmiIurNRO6eXPnzrX2LmWk +; af/NW2nbYTx2FGZQnUs0x29VF0V8p+R209CZ/MLFixc3OsfXANAg0AAwhtLYhLlBKdzATU9PpkWLFk +; m1WNDvMKQez2HQwTBvqr7MCIZXNcwT1gRyUcUc372bV2RSaa9ZmvbcvJW1LSadx48Mbd++PXTOHj+3 +; bds2SS0i4oMzIer5uw51lcKfAaBBYMVKa1NmAofflf3UqVON9ijDY3jy5Emv3PHee+/Jz7pkdLSo6A +; UimoAuipaNbOtt3Lix7MNTtUvTXpu38rLHtOaiYANbAMzRpzXo5wcMGCA/h/cQaIxTVew61CUBf5EA +; 0LyApqaU1uaM8VqyZImU63/88ccypcDvVzZ8+HDxEgYduP39ru6uGR5tKmq7GE1QF0a0d9m5fJrk1G +; paX1lL0/7qgvK2yXxn48ePl5ZdtA9btmzZM337sBf0tOTfifgweIDfi/perkNdUvBnAGgQmJjS2qgJ +; 2fLE9+GHH4o7nxm+u3btKlvoQXsZkoldMjraVcR5wprgrpzGtfq+t2v7ZvN2l5GmfdUFuWSX33zzza +; eRIVp4de7cWf6eiJE/Z5iCDwYOxHl916EuSfiLDIDmBTQFKc1Nm5yO48ePe+UONgC/q7trhqcoKtI8 +; YU2AF6RNMwd7U6ZMsRzXAGnaS12Ri7aZiA654USGmN/LnPlPPvlE2scwVCBu6xiXgS4N+DMANAhMRW +; ls3tzwwF3Qcf/+fUkads3wFFEUiPT+2/9OFewVEQBHvfyvvSvnTkhTdU3rJ0tp2jtdkuu2edSoUQKB +; RIOAP9Ie4r6ey0DnFAAaBJrCKKnNm75l58+fL+v587u6u2iAiqoizBPWBHsNtXREG0mN+Kd/+ifv7N +; mzjRZKVbs07ZkuyXW7PGzYMO/OnTtS6OGHhKkWjgOBLsNcmvBnAGgAmKoq3bzp/URCb9DhP/0xW9im +; euQjzQUimoDPF4Uee9YtkNxYelquWLFC7gES5EmUt6InA79K5LI9pqKXyVEU+BHxAQRJe8AbSOHHN9 +; 98461du1buizCv5zLMpQ1/BoAGgakrzubNTY5Hr9yBZ3D06NHe1atX5QmQ2b8k/7psmIoqrQUimsAP +; ze/3khRC0Rqj9PMfOHCgeAP9GadxJukUQZr2Rhflsh0mzeHixYtP9/rWrVvX+/d27dpJnjj/zs9169 +; bNM/hLAQANAk1RFGUDx6vBTOCgo66u7mlXd34HYNywYYOEA+7duydPg64bqqKqzU//o6qQsCb4276o +; VrziTX32GMSdO3dK01zCYYMHD66qohBNe6Jrctn24tGePXu2tP3Cyz158uQm5/jy9/wsnkB+lqKQxn +; 7WZZjLCv4MACNI083sospt3tykVO+Wa+9y69Ytr0+fPo3+/ogRIyQsgAEECC0nMB+9+svn1cwT1gB+ +; zPHdtXmN17Fjx1CfP4PuCYfxQFQNFfGa9kEX5bLdJQf8yJEj4tX74IMPpN1LmN/D+3flyhW5B/h9Xs +; fgL0EANAg0xVFjGzi9nOjbVy7Xz+/qHmQM2rZt6x06dEg2DDaA3r17O228iipCwp2e/0PnvYGuw9/a +; id29BQsWRPbk0S+tNBzWtWtX59dMVGna91yVy7Z2zJgx8kBPpW9jjZ/LiQjRunXrBAKZB0xj6CIBYK +; XwZwAYc2GaKlPpJj5t2jS5wYMOejxx84Y1gtz4U6dO9b744gvR/PnzbSRWTqJAxOV5wq6CH3N8929d +; 7fXq1Sv2Z4/BXLhwoczUpkiEqThFKJTStNe5LFdtLPmtmzdvFrtw+/ZtSQuKe778HpXBPkhu2bLFa9 +; 68udNgpwYADQJNcUV4lhm95Y4TJ05Icm8cQ8EkkAsXLogXBG8IXhGXDVtR5fI8YRfhb/nIdmIAqWRM +; 4vPHC+4XShEOw0vu6lopJ017nMty1bb27dvXu379uqxVJj4Ba0m8LvbmwIED8rq8PmlEroJdVvCXCA +; BWIwRqutFdFE9kePWCDp7W/K7ulRgMv00Gr/fo0SNpk2HTEvKRi+1iXAI/2rtsnjdaclmT/uwxgJs2 +; bXp6H4wcOVLVfaBpf3NZrtpU31tN2y/mvtfW1sr3nsRr+5zC61FAgkec96GQEPviKuSlDX8CgHhXDA +; CjS9NN74q42XDBlyv0uHbtmiTxJmU8MHS0yaBxaF1dnbdjxw6pmHTR0BVdeANdKhBxBf5GvPiH3pa1 +; y2J7u8OKCnnCYXhCkvQypiVN+5vrctWWEpk5deqUrEn+26FDh8ReuzFeIeecghL//Xh/F0EvbfgTAO +; RpkE2hqQ/LINAgMAmRhE4Fb9DBRAOSdtPK18MLQg81bnx6pgGFrhm8alHHf/g3ThSIuAB/m2cNkfy8 +; rDxy9MqkUIrk+Bs3blSUZ5iWNO1tGuSi/eR7njhx4tMGzuRqk6Oa1OsH8Qr32tKlS8UhgMeR8yj3O0 +; WDPwFADC9tMxioTIWlAWA0adoE8tLcuXPF2AQdVGkRGk7bsOCFpJeaPzyc6rJylcWmdMQ84bwLRPIE +; v5Evfc/bvWGZ5Kpm/dlzHzA9h95qTFXwjW/ea0LTvqZBrtpNIjDk+AFgRHx4CEny9cMyCzmHFJpwHj +; gHaKDuGvilBX9PAdA/SI7kizAIjCZNG0KWIpz1/vvve+UOurpz45X+btqGBrf/6dOnxRvIHNW33347 +; d+NXjcp7nnBe8MccX0Za5Q1dpFr44bAzZ86E7jWYtLTsaZrkqr2kQTnRIJwCGzduDD22LayicgvvD4 +; xyDwCDgwYNqgr4ewYAOfCKkByJi9QAMJw0bQpZiRFtJNsGHSTiTpo0SdZaY6+RttEh1EyPNTwgn376 +; qbSksTmq+SivApGswY9Cj21LJzuVfoAHnIbR7P14xseOHZvZfaBlP9MmF+0koLVy5UoJ9xLxoRCJNZ +; Dke8SFLD4zWo35kaFVq1ZJYUqR4a9RAPR+n4sVNzky60XlijRtDmkKqMKVXu6gOW2nTp1CvWbaRoiW +; AHi/eSJljmraifimxpXHPOEs4a+25R972zatd3ZCzZAhQ8QDgidkz549z8wbTlKu72Oa5aJ9JAec2e +; 2srcOHD3tt2rRJ/D2SAC7yY/3IEJ5xIkNFhT8BwKCKTGiYsUJRPtwsF5Vr0rRJpCFAilm8QQeQRfIt +; oa8or5228cMLQlUk58cc1eHDh6f+nqbG1f4n/yGzkHBW8Ldh+kDZS13/7DHMPMDV1dVJ1fyAAQMSLU +; 5xfQ/TLBdtIp5kCpyIsBARmj59uvxd0u+TJHixTslbJzJEjuzMmTPl8y0a/AkAkqMVBIEYRJ4GeWo1 +; CCwvTRtGUuKGWb58uXiOg467d+9K0m2l75WW2JgIXROeoF/a2rVrU/WCmJoWBSJZtItJG/yY47t323 +; rxdmv57AE+UjOojgQEubcrnSfs2p5VNLloC4mkkN8NQxDxodgpjfdJA8B43e7duz+NDHEdNFAvEvwJ +; AHJj45Eh7h108DRI3opBYHlp2jgqFUnj3NxBBw8YeBXo6p7U+6ZpANm4/AHkly5d8nr06JHq+5kaVx +; YFImnCH3N8lyxZojavlH5pfn82PxwW5fdd2J+KLlftH63liAbRYYS8v6hzfMMqbRjjvGmgzj1A/0zy +; FtN8vyzhTwDQf2Pm7dEbLcgbiEuU5EjCdwaA5W+CogsvAWsi6MCFzlDvtAxCWsaPG5/QBWEL5gkTEq +; jUC2KKpzQLRNIAv4H/8N97B7evlZQITZ9zY2LNA7EUbHEvTJkypSzQ5rUfVZtctHs85G/YsEEiKER8 +; qKhN41zThLCG4vyHDRv2NDJE5XIlLfNcgb96AIgI827fvl3c/kHeHJI5cecaBAZL02YSRdzkJPKWO0 +; imJak27fNJ0wDSJgMvIE+AR48elS71eRjialda84SThr95fV/0tm3bVrjekqUzWgmHNTZJJ8s9qNrl +; or0rnTn97rvvCk+k8T5pA19TolUZdo/r4zp79uypGv6eAUAf2miUy4SQIG8gXhE8JOW+kKwWn4vStK +; GEFU90dG4POnD7z5s3TzwFWZ5bWsaP9gW0yaj79sHowYMH0ibD5gnnI7yBSYaEk4S/LfPfkRxSTZ9n +; pM/+WwPIwAC8INwHeEWaauFkSkcu2jkigjQShwko9vDnrafxXlnBXlPiPqBdmO8Rp41YnJZ5LsBfow +; DoC0/HsWPHAhP7IWFGCgUlR2a1CF2Vps0lSCxyXPvl5vgyWoqcuTzPNQ3jx/XTJoMKYQCXmcYYRNeM +; dDUoyXnCSYAfhR7b1q8Qb7emzzGuSOkgHEZyPHsCrZ/yvN+rRS7aN3LAjx8/LixAQSmt49J6r6xhL0 +; hvvfWWd+HCBblurh9e0gZ/gQCI8ODgyYHsgw6eBkeMGGEQ+EIxIZDkb8Au6AAMSZZ1yRikYfyAPnoF +; cuPzmZA765qBrhYlMU+4Yq/f7KHe7Nmzq8IjXHpvYfAolAICCYf5hVKu3PtFkos2je8aTx+RQvLAFy +; 1alOgc3xcchj9f3PMUuNR9GxkiKkbkVBP8lQVAX8S6r1y5Euj9wSviA4BBYHEgkHmhdQE5oRwsfh4A +; XDUASRtCNjqGhxPuIBTA5BzCxK4Z7GpQpfOE44LfyJe/5+3dtEJyRDV9XnHU1H2Fg4DiqM8//1ymO8 +; yZMyfztI+iy0VbxkPwjh07xC7wEEyxU5rnmjfoBYnzo1cmBS8wEJ8LrcM0wF9oAEQkNePu5yKDvEBN +; JUdmsTBdl6aNh4awtIAod+AFICFcy3UlaRhpk8EcYbyBFLzw/y4a8KKrknYxceBv2ci23vr16wtdFR +; 7lnqJfGq2guA9OnDiRSeFX0eWqDaMVHN1C+K7JB4UL0nw/V0CvnCiM3Lt3r3wufD5EhlyHv0gA6EMc +; fXDo7xPkDaSnIMmRbCQGgfWlYfPBm8dTfdDBEz/JsFqTwJMylHi88QASBqFx7uTJk22ecE6K0y4mCv +; gxx3fH8qkyzF7T5xJWldxPgMC6detkX+A+YK6qFYjEk4t2i31u2bJlYtsJ+/L9smbSej8XoC6q+O5q +; a2ul9RmfEw3UG7bMcwn+IgOgL4o+Dhw4EFggwr/xNNgwOTKLxeq6XN148Gjgwi5X6EFbFPICXb2OKE +; rCcGLo+vXr5928efPp5BzuEReNfNEVdZ5wWPgb1+r73s5tmxptf6JdSd5PVAZTKIUnhL0kyebvRZer +; 9oq93o90vPfee9IoP833cwnq4gjm8T8v/ktkyEX4iw2AiI2DNjDQbtDB0yBtM/wvNu3FqkWubT6E7c +; ljCDpY0CS9Rp3jq0WVGlJyP+ijyefE5JyhQ4c6a/SLrrDzhMPA38YZg8Szq+n6yynN+4iHHwql6urq +; vFu3bkkPwbTfU7tctFE82BLl+eSTTyTXmXzwNOb4lspVqIvDR0RBaZlErvjUqVPl+goDgL54OqAxdJ +; DXiI1g586dT5Mj0164muTC5kMIE2gJOgj7k+zqwvmmrUoMKxskbTIojPEn5+AFcRUEiqww84QDCz1e +; +p63b8fGwuR2Zn0PYfSAB4wge0xRHxwrkat2yYd4IhqXL1+WPM+039NVmIsrrgnHCpEhGIjPkwhCoQ +; AQkR+wYsUKyf8IOngaJFRmEFhfeW0+b7zxhsB70AHY09W9WkM5cY0tny19NP05qtVQLeqiyhWINAV/ +; S4e3kRwezfmcLtw/9EujPxz3wblz57xOnTo5cV4uyFV7RA440SCgZe3atWLf03w/VwEuCQFZpFZRMM +; M9QHoEkaFCAaAvpkTcvn070BsIJPoba9oLWZOy3nzo4USiatBB/0d6G7liTPJWVAPMjU9rDD5nUiUI +; oeAFcRkaiqqmCkQaK/TYvXa+PKhquj5fLt433AcUEBBGpLhswoQJVV0g4qoN4iEf4CNyQcQHUEn7fF +; 2EtiThzxfXSgGtHxmiYAqwLhQAInoE7dq1S1zHQV4lkiN5Okx7UWtSFpsP/er279/vlTt4WieZ1aWN +; 0xVFNcp+H01/cg7eQZchoqhqbJ7wmw3m+JKqoi1kr+W+IYWEcBj3AeEwbIWWc09CLtseGnlT3Ifdxj +; 7w3aT9ni5CWxrwVyraq1FIwz3A501kqFAAiFjsPOVBu0HeQJ4G/eRI0xOluQHRm4gRTkFH3bdu/4UL +; F1pD15AKa6T9NhlssPfv3y/0zFjXVTpP2Ie/bQvHSrGahvPXdH80lN9EmH6yeJn8ljqariGOXLU3RC +; SIUmCL0aRJk1Kb41sq14AtC/jzxXqYOXOmREOJsvH583eFAUBfzAk8efJk2XnCPHFAxmkvOi1KevNh +; g12zZk3Z9i7kaPbq1cu5zVOTggw3GyttMjB8/uScli1bOg8cRVSvbm95C7v+QOb47ty0WrzdLp+vpn +; sgjEgtoZ8cD0TsTYSJNZ1/WLlsZ4hEHD169GmeMsVOab+na7CWNfyVqkuXLk+9rnwPtNcpFAAinjDw +; KJH/EXT4OQdpL0AtSmoD4qYm/Bh0AIbbtm2T8LCLm6hWNWXMedihjyYbL5NzGKXkMnwUTcw35+kb8W +; cX5/hqWudxhYPAL5TCEHbt2lXV+QfJdduCt/vBgwdSob106VIB8LTf1yVQyxv+fLH38AAEBBKdo8F2 +; oQDQV+/evb1r164FeqFYjP6IpbQXoxZVsglNnz5dPtOggz6No0aNqhqjk6dKDTwPRvSWI+zy5Zdfyk +; MSScGugUiRhMeD5vRstuxF5D25cm6a1nGS4j6YP3++hMIoliI0pj39xGV7QsSBilQiEH5XjizO2SVQ +; cwX+fPH7FND6kSG+H/KQCwWAiIsi7FVunjBPg1n0HdKiqBsQeTY8WZc7+Bm8US5vpkUWhp8+moRf/D +; mqb775pjNQUiTRm5GedH7oPU/Y1rRGs1JpoRRJ8vSh03T+yHU7AuzduHFDPmO/2Cnt93QBzlwFv4bi +; +yAdju+H74nIUKEAELEocHPifg7yBhIynjt3roobKwuF3YQIo9N5POigDJ2WJDar0w35czbx1pIX5b +; fJyAtQiiQ2VYxdXV2d7DnkYGZ9DprWYp7iu9q4caPsT9wHtM3QsEe5bjv8eeXYVAoz/WKntN/XFUjT +; AH+++NzY//GI833xvXEPFAYAfbVv3947fPhw2XnCPA0Sukl7sWpQ0CZE2ATXcblCD3LOSD51fVOtNr +; Eh0yaDPpp1v5+cU8S5s1mKEWSEuQj5stekPZ9Z03pzWTQiJhyGJ2TLli3O5iZrsBk03j59+vTTCENW +; ttQFQNMGf6UiP5ZWbHxvfH9EhpJ+j5///Of5ASCCbGfPni15UEEHTy14DbNYuBrUcCOilxDgEHQA0z +; T5tHFMboscHfpocuMDL+SGNPyZtMFJu3gYwqOKJ4n8SuaZJv0eWtaTVlERSaEU8E44jO4ELn3urtsI +; PityjMnx9oud2PvTfl8X4CxtpQ1/vvgO8QCStsL3SIuepN4f+EP/FxBdm6TCN1vCAAAAAElFTkSuQm +; CC +; thumbnail end +; +; + +; external perimeters extrusion width = 0.45mm +; perimeters extrusion width = 0.45mm +; infill extrusion width = 0.45mm +; solid infill extrusion width = 0.45mm +; top infill extrusion width = 0.42mm +; first layer extrusion width = 0.50mm + +M73 P0 R21 +M73 Q0 S21 +M201 X2500 Y2500 Z200 E2500 ; sets maximum accelerations, mm/sec^2 +M203 X200 Y200 Z40 E100 ; sets maximum feedrates, mm / sec +M204 P2000 R1200 T2000 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2 +M205 X8.00 Y8.00 Z2.00 E10.00 ; sets the jerk limits, mm/sec +M205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec + +M486 S0 +M486 Axyz-cali-cube-by-r3d_v1.stl +M486 S-1 + +;TYPE:Custom +M17 ; enable steppers +M862.3 P "MK4" ; printer model check +M862.1 P0.4 A0 F0 ; nozzle check +M115 U6.1.2+7894 +M555 X115 Y91 W32 H24 + +G90 ; use absolute coordinates +M83 ; extruder relative mode + +M140 S85 ; set bed temp +M104 T0 S175 ; set extruder temp for bed leveling +M109 T0 R175 ; wait for temp + +M84 E ; turn off E motor + +G28 ; home all without mesh bed level + +G1 X42 Y-4 Z5 F4800 + +M302 S160 ; lower cold extrusion limit to 160C + + +G1 E-2 F2400 ; retraction + + +M84 E ; turn off E motor + +G29 P9 X10 Y-4 W32 H4 + + + +G0 Z40 F10000 + +M190 S85 ; wait for bed temp + +M107 + +; +; MBL +; +M84 E ; turn off E motor +G29 P1 ; invalidate mbl & probe print area +G29 P1 X0 Y0 W50 H20 C ; probe near purge place +G29 P3.2 ; interpolate mbl probes +G29 P3.13 ; extrapolate mbl outside probe area +G29 A ; activate mbl + +; prepare for purge +M104 S230 +G0 X0 Y-4 Z15 F4800 ; move away and ready for the purge +M109 S230 + +G92 E0 +M569 S0 E ; set spreadcycle mode for extruder + +; +; Extrude purge line +; +G92 E0 ; reset extruder position +G1 E2 F2400 ; deretraction after the initial one before nozzle cleaning +G0 E7 X15 Z0.2 F500 ; purge +G0 X25 E4 F500 ; purge +G0 X35 E4 F650 ; purge +G0 X45 E4 F800 ; purge +G0 X48 Z0.05 F8000 ; wipe, move close to the bed +G0 X51 Z0.2 F8000 ; wipe, move quickly away from the bed + +G92 E0 +M221 S100 ; set flow to 100% +G21 ; set units to millimeters +G90 ; use absolute coordinates +M83 ; use relative distances for extrusion +M900 K0.07 ; Filament gcode + + + +M142 S36 ; set heatbreak target temp +M107 +;LAYER_CHANGE +;Z:0.2 +;HEIGHT:0.2 +G1 E-.8 F2100 +G1 Z1.7 F720 +M486 S0 +G1 X121.962 Y101.06 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Perimeter +;WIDTH:0.616021 +G1 F1200 +G2 X121.586 Y101.857 I2.38 J1.61 E.04216 +G1 X121.448 Y101.853 E.00658 +G1 X121.332 Y101.56 E.01502 +G1 X121.312 Y100.759 E.03818 +G1 X121.431 Y100.463 E.0152 +G1 X121.626 Y100.432 E.00941 +G2 X121.923 Y101.014 I1.492 J-.396 E.03137 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.962 Y101.06 E-.01253 +G2 X121.586 Y101.857 I2.38 J1.61 E-.18386 +G1 X121.448 Y101.853 E-.02869 +G1 X121.332 Y101.56 E-.06549 +G1 X121.312 Y100.759 E-.16651 +G1 X121.431 Y100.463 E-.0663 +G1 X121.626 Y100.432 E-.04103 +G2 X121.76 Y100.769 I1.492 J-.395 E-.07559 +;WIPE_END +G1 X123.899 Y101.508 Z.24 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.425498 +G1 F1200 +G1 X123.634 Y101.401 E.00909 +;WIDTH:0.409674 +G1 X123.388 Y101.233 E.00908 +;WIDTH:0.40393 +G1 X122.981 Y101.051 E.01338 +G2 X123.667 Y100.805 I-1.462 J-5.154 E.02189 +G1 X123.659 Y100.964 E.00478 +;WIDTH:0.409674 +G1 X123.731 Y101.296 E.01036 +;WIDTH:0.425498 +G1 X123.862 Y101.461 E.0067 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.899 Y101.508 E-.01243 +G1 X123.634 Y101.401 E-.05939 +G1 X123.388 Y101.233 E-.06191 +G1 X122.981 Y101.051 E-.09265 +G2 X123.667 Y100.805 I-1.461 J-5.154 E-.15157 +G1 X123.659 Y100.964 E-.03308 +G1 X123.731 Y101.296 E-.0706 +G1 X123.862 Y101.461 E-.04378 +;WIPE_END +G1 E-.11459 F2100 +G1 X126.248 Y104.468 Z.267 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.499999 +G1 F1200 +G3 X125.332 Y104.608 I-.927 J-3.002 E.03535 +G3 X124.372 Y104.36 I.093 J-2.345 E.03797 +G1 X123.916 Y103.982 E.02251 +G1 X123.702 Y103.637 E.01543 +G1 X123.454 Y103.961 E.01551 +G3 X122.47 Y104.5 I-2.007 J-2.5 E.04286 +G1 X121.722 Y104.607 E.02872 +G3 X120.257 Y104.359 I-.104 J-3.828 E.05683 +G3 X119.333 Y103.722 I2.196 J-4.174 E.04276 +G1 X118.927 Y103.167 E.02613 +G3 X118.552 Y102.393 I2.826 J-1.848 E.03278 +G3 X118.389 Y101.245 I5.555 J-1.372 E.04414 +G1 X118.467 Y100.308 E.03574 +G1 X118.67 Y99.627 E.02701 +G1 X119.187 Y98.758 E.03843 +G1 X119.356 Y98.573 E.00952 +G1 X119.971 Y98.108 E.0293 +G1 X120.607 Y97.827 E.02643 +G1 X121.525 Y97.684 E.03531 +G1 X122.227 Y97.736 E.02675 +G3 X123.636 Y98.401 I-.566 J3.024 E.05987 +G3 X124.32 Y99.552 I-1.745 J1.816 E.05151 +G3 X125.105 Y99.141 I2.033 J2.927 E.03376 +G3 X126.786 Y99.156 I.808 J3.78 E.06441 +G3 X127.448 Y99.494 I-.455 J1.707 E.02846 +G3 X127.454 Y98.284 I10.164 J-.558 E.04602 +G1 X127.639 Y97.967 E.01395 +G3 X128.174 Y97.775 I.528 J.632 E.02206 +G3 X129.574 Y98.031 I-.068 J4.332 E.05434 +G1 X130.157 Y97.856 E.02313 +G1 X130.974 Y97.785 E.03117 +G1 X131.337 Y97.86 E.01409 +G3 X131.682 Y98.282 I-.444 J.715 E.0211 +G3 X131.494 Y99.766 I-3.402 J.323 E.05732 +G3 X131.717 Y100.934 I-5.077 J1.575 E.04529 +G1 X131.717 Y103.842 E.11052 +G1 X131.656 Y104.124 E.01097 +G1 X131.451 Y104.384 E.01258 +G1 X131.13 Y104.521 E.01326 +G3 X129.576 Y104.27 I-.033 J-4.733 E.06011 +G1 X128.986 Y104.386 E.02285 +G3 X127.96 Y104.24 I-.199 J-2.279 E.03973 +G1 X127.314 Y104.444 E.02575 +G1 X126.568 Y104.525 E.02852 +G3 X126.304 Y104.49 I-.046 J-.672 E.01019 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.248 Y104.468 E-.0125 +G3 X125.332 Y104.608 I-.927 J-3.001 E-.19327 +G3 X124.372 Y104.36 I.094 J-2.344 E-.20761 +G1 X123.916 Y103.982 E-.12309 +G1 X123.702 Y103.637 E-.08437 +G1 X123.646 Y103.71 E-.01916 +;WIPE_END +G1 X123.811 Y102.041 Z.229 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:External perimeter +;WIDTH:0.453002 +G1 F1200 +G1 X123.82 Y102.068 E.00097 +;WIDTH:0.439922 +G1 X123.737 Y102.376 E.01053 +;WIDTH:0.473838 +G1 X123.654 Y102.684 E.01143 +;WIDTH:0.499999 +G3 X123.114 Y103.65 I-2.94 J-1.008 E.04229 +G3 X122.253 Y104.089 I-1.64 J-2.155 E.03693 +G3 X120.916 Y104.09 I-.671 J-4.196 E.05103 +G1 X120.47 Y103.955 E.01771 +G3 X119.622 Y103.368 I1.871 J-3.612 E.0393 +G3 X119.014 Y102.345 I4.503 J-3.369 E.04531 +G1 X118.888 Y101.771 E.02234 +G3 X118.922 Y100.351 I5.313 J-.582 E.05414 +G1 X119.105 Y99.768 E.02322 +G1 X119.575 Y99 E.03422 +G1 X120.246 Y98.473 E.03243 +M73 P1 R21 +M73 Q1 S21 +G1 X120.777 Y98.251 E.02187 +G1 X121.585 Y98.137 E.03101 +G3 X122.581 Y98.294 I-.091 J3.835 E.03843 +G3 X123.374 Y98.776 I-1.46 J3.296 E.03537 +G1 X123.694 Y99.188 E.01983 +G1 X123.896 Y99.729 E.02195 +G1 X123.9 Y100.074 E.01311 +;WIDTH:0.497015 +G1 X123.812 Y100.256 E.00763 +;WIDTH:0.499999 +G1 X123.355 Y100.5 E.01969 +;WIDTH:0.528931 +G1 X122.631 Y100.738 E.0308 +;WIDTH:0.553099 +G1 X122.453 Y100.786 E.00782 +G1 X122.362 Y100.719 E.00479 +;WIDTH:0.526549 +G1 X122.271 Y100.652 E.00454 +;WIDTH:0.499999 +G1 X122.051 Y100.139 E.02121 +G1 X121.892 Y99.98 E.00855 +G1 X121.569 Y99.897 E.01267 +G1 X121.221 Y99.973 E.01354 +G1 X121.024 Y100.128 E.00953 +G2 X120.802 Y100.676 I2.045 J1.149 E.02253 +G2 X120.825 Y101.717 I10.091 J.296 E.03959 +G1 X121.028 Y102.178 E.01914 +G1 X121.214 Y102.328 E.00908 +G1 X121.625 Y102.395 E.01583 +G1 X121.927 Y102.263 E.01253 +G2 X122.303 Y101.442 I-2.055 J-1.437 E.03451 +;WIDTH:0.526549 +G1 X122.388 Y101.368 E.00453 +;WIDTH:0.553099 +G1 X122.474 Y101.294 E.00481 +G1 X122.735 Y101.396 E.01189 +;WIDTH:0.526549 +G1 X122.996 Y101.498 E.01127 +;WIDTH:0.499999 +G2 X123.766 Y101.911 I2.337 J-3.435 E.03327 +G1 X123.791 Y101.984 E.00293 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.811 Y102.041 E-.01255 +G1 X123.82 Y102.068 E-.00591 +G1 X123.737 Y102.376 E-.06629 +G1 X123.654 Y102.684 E-.06629 +G3 X123.114 Y103.65 I-2.94 J-1.009 E-.23122 +G3 X122.253 Y104.089 I-1.641 J-2.154 E-.20192 +G3 X121.987 Y104.123 I-.67 J-4.196 E-.05582 +;WIPE_END +G1 X125.688 Y111.411 Z.343 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.570342 +G1 F1200 +G1 X125.688 Y107.761 E.16007 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.688 Y110.841 E-.64 +;WIPE_END +G1 X129.574 Y103.671 Z.342 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.594116 +G1 F1200 +G1 X129.574 Y103.646 E.00115 +;WIDTH:0.592014 +G1 X129.574 Y100.016 E.16574 +;WIDTH:0.636936 +G1 X129.596 Y99.905 E.00559 +;WIDTH:0.681858 +G2 X129.596 Y99.683 I-.325 J-.111 E.01201 +;WIDTH:0.636936 +G1 X129.574 Y99.572 E.00559 +;WIDTH:0.592014 +G1 X129.574 Y98.654 E.04191 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.574 Y101.734 E-.64 +;WIPE_END +G1 X131.077 Y110.085 Z.348 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.453162 +G1 F1200 +G1 X131.631 Y110.056 E.01892 +;WIDTH:0.418041 +G1 X131.908 Y110.037 E.00866 +;WIDTH:0.42004 +G1 X131.762 Y110.133 E.00548 +;WIDTH:0.453162 +G3 X131.516 Y110.465 I-.659 J-.231 E.01431 +G1 X131.313 Y110.491 E.00698 +G1 X131.293 Y110.435 E.00203 +G3 X131.017 Y110.089 I.297 J-.519 E.01546 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.077 Y110.085 E-.0125 +G1 X131.631 Y110.056 E-.11529 +G1 X131.908 Y110.037 E-.0577 +G1 X131.762 Y110.133 E-.03631 +G3 X131.516 Y110.465 I-.659 J-.232 E-.08717 +G1 X131.313 Y110.491 E-.04253 +G1 X131.293 Y110.435 E-.01236 +G3 X131.017 Y110.089 I.297 J-.52 E-.09421 +;WIPE_END +G1 E-.18193 F2100 +G1 X130.836 Y99.794 Z.38 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.535823 +G1 F1200 +G1 X130.532 Y99.794 E.01246 +;WIDTH:0.584501 +G1 X130.228 Y99.794 E.01369 +;WIDTH:0.63318 +G1 X129.923 Y99.794 E.01497 +;WIDTH:0.681858 +G1 X129.619 Y99.794 E.01615 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.836 Y99.794 E-.25291 +G1 X130.532 Y99.794 E-.06318 +G1 X130.228 Y99.794 E-.06318 +G1 X129.923 Y99.794 E-.06338 +G1 X129.619 Y99.794 E-.06318 +;WIPE_END +G1 E-.13417 F2100 +G1 X125.951 Y101.212 Z.269 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.408646 +G1 F1200 +G1 X125.782 Y101.254 E.0053 +;WIDTH:0.451168 +G1 X125.288 Y101.338 E.01701 +G1 X125.432 Y101.075 E.01018 +;WIDTH:0.407996 +G1 X125.568 Y100.939 E.00584 +;WIDTH:0.408646 +G1 X125.85 Y100.896 E.00867 +G2 X126.058 Y101.051 I.271 J-.148 E.00814 +G3 X126.04 Y101.19 I-.059 J.063 E.00497 +G1 X126.009 Y101.198 E.00097 +M204 P1000 +G1 X126.009 Y101.198 F12000 +G1 X125.225 Y101.347 +M204 P600 +;WIDTH:0.473629 +G1 F1200 +G1 X124.968 Y101.323 E.00924 +;WIDTH:0.499999 +G3 X124.178 Y101.191 I.329 J-4.377 E.03048 +G1 X124.157 Y101.176 E.00098 +G3 X124.085 Y100.761 I.387 J-.281 E.01658 +;WIDTH:0.499 +G1 X124.236 Y100.444 E.01332 +;WIDTH:0.499999 +G1 X124.543 Y99.963 E.02169 +G1 X124.975 Y99.687 E.01948 +G3 X125.765 Y99.509 I.887 J2.096 E.03094 +G3 X127.045 Y99.754 I.188 J2.486 E.05011 +;WIDTH:0.502231 +G1 X127.374 Y100.047 E.01683 +;WIDTH:0.504003 +G1 X127.559 Y100.429 E.01627 +G1 X127.623 Y100.984 E.02142 +;WIDTH:0.490194 +G1 X127.624 Y101.012 E.00104 +;WIDTH:0.493681 +G1 X127.624 Y102.611 E.05993 +G1 X127.677 Y103.189 E.02175 +;WIDTH:0.461656 +G1 X127.735 Y103.425 E.00846 +;WIDTH:0.423996 +G1 X127.793 Y103.66 E.00767 +;WIDTH:0.424224 +G1 X127.773 Y103.721 E.00204 +;WIDTH:0.462112 +G1 X127.754 Y103.782 E.00223 +;WIDTH:0.499999 +G1 X127.735 Y103.843 E.00243 +G3 X126.526 Y104.07 I-1.323 J-3.714 E.04694 +G3 X126.303 Y103.94 I.012 J-.277 E.01021 +G1 X125.865 Y104.107 E.01782 +G3 X124.859 Y104.081 I-.438 J-2.528 E.0385 +G3 X124.228 Y103.637 I.714 J-1.684 E.02955 +G3 X123.986 Y102.799 I1.847 J-.989 E.0334 +;WIDTH:0.496571 +G1 X124.026 Y102.616 E.00707 +;WIDTH:0.45412 +G1 X124.067 Y102.433 E.00641 +;WIDTH:0.411669 +G1 X124.108 Y102.251 E.00572 +;WIDTH:0.412812 +G1 X124.167 Y102.101 E.00496 +;WIDTH:0.456406 +G1 X124.227 Y101.951 E.00555 +;WIDTH:0.499999 +G1 X124.287 Y101.802 E.0061 +G1 X124.694 Y101.504 E.01917 +G1 X124.975 Y101.427 E.01107 +;WIDTH:0.473629 +G1 X125.228 Y101.357 E.0094 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.225 Y101.347 E-.00217 +G1 X124.968 Y101.323 E-.05364 +G3 X124.178 Y101.191 I.325 J-4.377 E-.16668 +G1 X124.157 Y101.176 E-.00536 +G3 X124.085 Y100.761 I.387 J-.281 E-.09064 +G1 X124.236 Y100.444 E-.07297 +G1 X124.543 Y99.963 E-.11858 +G1 X124.975 Y99.687 E-.10653 +G3 X125.08 Y99.646 I.887 J2.096 E-.02343 +;WIPE_END +G1 X129.45 Y103.786 Z.305 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.547058 +G1 F1200 +G1 X129.363 Y103.868 E.00501 +;WIDTH:0.499999 +G1 X128.835 Y103.946 E.02029 +G1 X128.273 Y103.875 E.02153 +G1 X128.19 Y103.845 E.00335 +G1 X128.094 Y103.163 E.02618 +;WIDTH:0.493681 +G1 X128.07 Y101.009 E.08074 +;WIDTH:0.504003 +G2 X127.944 Y99.862 I-12.017 J.743 E.04426 +;WIDTH:0.502231 +G1 X127.895 Y99.318 E.02086 +;WIDTH:0.499999 +G3 X127.911 Y98.36 I7.094 J-.359 E.03644 +G1 X128.036 Y98.245 E.00646 +G1 X128.868 Y98.296 E.03168 +G1 X129.361 Y98.434 E.01946 +;WIDTH:0.546007 +G1 X129.467 Y98.544 E.00639 +;WIDTH:0.592014 +G1 X129.574 Y98.654 E.00701 +G1 X129.68 Y98.544 E.00697 +;WIDTH:0.546007 +G1 X129.787 Y98.434 E.00642 +;WIDTH:0.499999 +G3 X131.006 Y98.241 I1.212 J3.709 E.0471 +G3 X131.242 Y98.407 I-.017 J.275 E.01154 +G1 X131.202 Y99.131 E.02756 +G1 X131.031 Y99.69 E.02222 +;WIDTH:0.498942 +G1 X130.836 Y99.794 E.00838 +G1 X131.031 Y99.898 E.00838 +;WIDTH:0.499999 +G3 X131.26 Y100.934 I-3.397 J1.291 E.04047 +G1 X131.26 Y103.842 E.11052 +G1 X131.188 Y104.008 E.00688 +G1 X131.015 Y104.07 E.00698 +G3 X129.786 Y103.866 I.063 J-4.176 E.04753 +;WIDTH:0.547058 +G1 X129.68 Y103.769 E.00602 +;WIDTH:0.594116 +G1 X129.574 Y103.671 E.00662 +G1 X129.494 Y103.745 E.00499 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.45 Y103.786 E-.0125 +G1 X129.363 Y103.868 E-.02484 +G1 X128.835 Y103.946 E-.11092 +G1 X128.273 Y103.875 E-.11772 +G1 X128.19 Y103.845 E-.01834 +G1 X128.094 Y103.163 E-.14313 +G1 X128.083 Y102.14 E-.21255 +;WIPE_END +G1 X131.908 Y110.037 Z.353 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.423086 +G1 F1200 +G1 X132.169 Y110.058 E.00828 +;WIDTH:0.463251 +G1 X132.431 Y110.078 E.00918 +;WIDTH:0.503417 +G1 X132.692 Y110.099 E.01003 +;WIDTH:0.543582 +G1 X132.954 Y110.12 E.01094 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.908 Y110.037 E-.21806 +G1 X132.169 Y110.058 E-.05441 +G1 X132.431 Y110.078 E-.05461 +G1 X132.692 Y110.099 E-.05441 +G1 X132.954 Y110.12 E-.05462 +;WIPE_END +G1 E-.20389 F2100 +G1 X119.651 Y108.788 Z.433 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Perimeter +;WIDTH:0.612184 +G1 F1200 +G1 X119.531 Y108.933 E.00891 +G1 X119.328 Y109.514 E.02913 +G1 X119.232 Y109.626 E.00698 +G1 X119.12 Y109.59 E.00557 +G1 X119.008 Y109.296 E.01489 +G1 X118.991 Y108.557 E.03499 +G1 X119.131 Y108.185 E.01881 +G1 X119.297 Y108.16 E.00795 +G2 X119.614 Y108.741 I2.036 J-.731 E.03145 +M204 P1000 +G1 X119.614 Y108.741 F12000 +G1 X120.708 Y108.781 +M204 P600 +;WIDTH:0.429457 +G1 F1200 +G2 X121.418 Y108.463 I-1.077 J-3.365 E.02505 +G2 X121.427 Y109.148 I3.206 J.303 E.02206 +G2 X120.763 Y108.803 I-2.119 J3.265 E.02409 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.708 Y108.781 E-.01231 +G2 X121.418 Y108.463 I-1.081 J-3.364 E-.162 +G2 X121.427 Y109.148 I3.206 J.3 E-.14263 +G2 X120.763 Y108.803 I-2.118 J3.265 E-.15574 +;WIPE_END +G1 E-.16732 F2100 +G1 X123.705 Y107.124 Z.259 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.499999 +G1 F1200 +G1 X123.77 Y107.227 E.00463 +G1 X123.949 Y106.946 E.01266 +G1 X124.201 Y106.881 E.00989 +G3 X125.229 Y106.993 I-.08 J5.461 E.03936 +G1 X125.124 Y106.355 E.02457 +G1 X125.152 Y106.022 E.0127 +G3 X125.557 Y105.603 I.761 J.33 E.02263 +G3 X127.135 Y105.549 I1.059 J7.861 E.06011 +G3 X127.79 Y106.232 I-.058 J.711 E.03932 +G1 X127.704 Y106.863 E.0242 +G3 X128.891 Y107.016 I.351 J1.957 E.0462 +G3 X129.574 Y107.672 I-1.198 J1.929 E.03626 +G1 X129.861 Y107.299 E.01789 +G3 X130.568 Y106.901 I1.649 J2.103 E.03096 +G3 X132.348 Y107.013 I.729 J2.629 E.06905 +G1 X133.089 Y107.534 E.03443 +G1 X133.28 Y107.757 E.01116 +G1 X133.62 Y108.387 E.02721 +G1 X133.679 Y108.638 E.0098 +;WIDTH:0.468009 +G1 X133.738 Y108.889 E.00911 +;WIDTH:0.436018 +G1 X133.748 Y109.639 E.02452 +;WIDTH:0.456324 +G1 X133.717 Y109.807 E.00587 +;WIDTH:0.487207 +G1 X133.685 Y109.975 E.00632 +;WIDTH:0.515537 +G1 X133.585 Y110.175 E.00879 +G1 X133.668 Y110.555 E.01529 +;WIDTH:0.492475 +G1 X133.625 Y110.811 E.0097 +;WIDTH:0.499999 +G1 X133.472 Y111.199 E.01585 +G3 X132.948 Y111.828 I-2.619 J-1.647 E.03121 +G3 X131.82 Y112.32 I-1.616 J-2.172 E.04718 +G3 X130.197 Y112.093 I-.433 J-2.83 E.06317 +G1 X129.728 Y111.729 E.02256 +G1 X129.557 Y111.467 E.01189 +G3 X128.745 Y112.179 I-1.478 J-.867 E.04176 +G1 X128.156 Y112.316 E.02298 +G1 X127.625 Y112.258 E.0203 +G3 X127.172 Y112 I.146 J-.782 E.02019 +G1 X126.792 Y112.096 E.0149 +G3 X125.747 Y112.043 I-.399 J-2.489 E.04006 +G1 X125.688 Y111.97 E.00357 +G1 X125.63 Y112.043 E.00354 +G1 X125.068 Y112.138 E.02166 +G1 X124.395 Y112.066 E.02572 +G1 X124.227 Y112.007 E.00677 +G1 X124.013 Y112.177 E.01039 +G3 X123.043 Y112.324 I-.987 J-3.235 E.03742 +G1 X122.357 Y112.13 E.02709 +G3 X121.768 Y111.673 I1.423 J-2.441 E.02842 +G1 X121.543 Y111.256 E.01801 +G1 X121.213 Y111.671 E.02015 +G3 X120.082 Y112.26 I-1.95 J-2.367 E.04882 +G1 X119.323 Y112.357 E.02908 +G1 X118.546 Y112.283 E.02966 +G1 X117.944 Y112.1 E.02391 +G3 X117.027 Y111.468 I2.068 J-3.987 E.04244 +G3 X116.283 Y110.272 I3.093 J-2.752 E.05379 +G1 X116.239 Y110.007 E.01021 +;WIDTH:0.450951 +G1 X116.196 Y109.743 E.00908 +;WIDTH:0.401903 +G1 X116.148 Y108.988 E.02258 +;WIDTH:0.382805 +G1 X116.188 Y108.2 E.0223 +;WIDTH:0.42187 +G1 X116.204 Y108.03 E.00538 +;WIDTH:0.460935 +G1 X116.219 Y107.861 E.0059 +;WIDTH:0.499999 +G1 X116.235 Y107.691 E.00649 +G3 X116.879 Y106.488 I4.526 J1.65 E.05204 +G3 X117.951 Y105.689 I3.087 J3.024 E.05102 +G3 X119.211 Y105.424 I1.507 J4.035 E.04912 +G3 X120.775 Y105.773 I.018 J3.601 E.06142 +G1 X121.317 Y106.16 E.02531 +G3 X121.862 Y107.062 I-1.765 J1.682 E.04037 +G1 X121.873 Y107.122 E.00232 +G3 X123.456 Y106.898 I1.378 J4.035 E.06112 +G1 X123.592 Y106.946 E.00548 +G1 X123.673 Y107.073 E.00572 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.705 Y107.124 E-.01251 +G1 X123.77 Y107.227 E-.02531 +G1 X123.949 Y106.946 E-.06924 +G1 X124.201 Y106.881 E-.05408 +G3 X125.229 Y106.993 I-.075 J5.462 E-.21522 +G1 X125.124 Y106.355 E-.13437 +G1 X125.152 Y106.022 E-.06945 +G3 X125.31 Y105.783 I.761 J.33 E-.05982 +;WIPE_END +G1 X127.039 Y111.514 Z.304 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:External perimeter +G1 F1200 +G1 X126.999 Y111.582 E.003 +G1 X126.449 Y111.686 E.02127 +G1 X125.916 Y111.614 E.02044 +;WIDTH:0.535171 +G1 X125.802 Y111.513 E.00623 +;WIDTH:0.570342 +G1 X125.688 Y111.411 E.00671 +G1 X125.575 Y111.513 E.00668 +;WIDTH:0.535171 +G1 X125.461 Y111.614 E.00623 +;WIDTH:0.499999 +G1 X124.963 Y111.689 E.01914 +G1 X124.465 Y111.614 E.01914 +G3 X124.276 Y111.4 I.07 J-.252 E.01148 +G1 X123.964 Y111.562 E.01336 +G1 X123.85 Y111.746 E.00823 +G3 X122.971 Y111.855 I-.731 J-2.293 E.03386 +G1 X122.435 Y111.66 E.02168 +G1 X122.088 Y111.333 E.01812 +G1 X121.904 Y110.903 E.01778 +G1 X121.91 Y110.427 E.01809 +;WIDTH:0.474155 +G2 X121.893 Y109.669 I-4.871 J-.267 E.02721 +;WIDTH:0.499999 +G1 X121.869 Y109.386 E.01079 +G3 X121.887 Y108.192 I3.287 J-.546 E.04563 +;WIDTH:0.492723 +G1 X121.949 Y107.609 E.02193 +;WIDTH:0.499999 +G3 X122.441 Y107.432 I.916 J1.769 E.01993 +G3 X123.396 Y107.358 I.832 J4.575 E.03647 +;WIDTH:0.515742 +G1 X123.534 Y107.564 E.00975 +G1 X123.538 Y109.512 E.07659 +;WIDTH:0.506762 +G1 X123.555 Y110.064 E.0213 +;WIDTH:0.480361 +G1 X123.622 Y110.271 E.00791 +G1 X123.854 Y110.235 E.00854 +G1 X123.958 Y110.107 E.006 +;WIDTH:0.506762 +G1 X124.002 Y109.512 E.02301 +;WIDTH:0.515742 +G1 X124.007 Y107.564 E.07659 +G1 X124.19 Y107.343 E.01128 +;WIDTH:0.499999 +G3 X125.488 Y107.541 I.036 J4.105 E.05012 +;WIDTH:0.534167 +G1 X125.588 Y107.651 E.00607 +;WIDTH:0.568334 +G1 X125.688 Y107.761 E.00649 +G1 X125.703 Y107.556 E.00898 +;WIDTH:0.534167 +G1 X125.717 Y107.351 E.00839 +;WIDTH:0.499999 +G2 X125.575 Y106.281 I-10.855 J.899 E.04104 +G3 X125.743 Y106.023 I.268 J-.01 E.01247 +G3 X127.114 Y106.006 I.8 J9.292 E.05216 +G3 X127.333 Y106.233 I-.047 J.264 E.01281 +G2 X127.173 Y107.665 I9.851 J1.823 E.05481 +G2 X127.397 Y107.436 I-.19 J-.409 E.01245 +G1 X127.904 Y107.283 E.02013 +G1 X128.377 Y107.315 E.01802 +G3 X129.086 Y107.758 I-.48 J1.558 E.03213 +G3 X129.547 Y108.56 I-4.337 J3.029 E.0352 +G3 X130.185 Y107.624 I5.982 J3.391 E.0431 +G1 X130.632 Y107.355 E.01983 +G1 X131.17 Y107.255 E.0208 +G1 X131.854 Y107.324 E.02613 +G1 X132.249 Y107.474 E.01606 +G1 X132.814 Y107.899 E.02687 +G1 X133.217 Y108.603 E.03083 +G3 X133.328 Y109.635 I-2.296 J.768 E.03976 +G1 X133.15 Y109.997 E.01533 +;WIDTH:0.519187 +G1 X133.109 Y110.056 E.00285 +;WIDTH:0.542875 +G1 X132.961 Y110.12 E.0067 +G1 X133.094 Y110.212 E.00672 +;WIDTH:0.519187 +G1 X133.222 Y110.555 E.0145 +;WIDTH:0.499999 +G1 X133.048 Y111.028 E.01915 +G1 X132.614 Y111.517 E.02485 +G3 X130.419 Y111.69 I-1.251 J-1.867 E.08745 +G1 X130.029 Y111.367 E.01925 +G1 X129.853 Y111.097 E.01225 +G3 X129.538 Y110.589 I1.153 J-1.068 E.02286 +G1 X129.258 Y111.079 E.02145 +G3 X128.624 Y111.738 I-1.326 J-.639 E.03534 +G1 X128.13 Y111.857 E.01931 +G1 X127.602 Y111.782 E.02027 +G2 X127.102 Y111.409 I-.915 J.706 E.02401 +G1 X127.07 Y111.463 E.00239 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.039 Y111.514 E-.0124 +G1 X126.999 Y111.582 E-.01639 +G1 X126.449 Y111.686 E-.11632 +G1 X125.916 Y111.614 E-.11177 +G1 X125.802 Y111.513 E-.03165 +G1 X125.688 Y111.411 E-.03179 +G1 X125.575 Y111.513 E-.03163 +G1 X125.461 Y111.614 E-.03165 +G1 X124.963 Y111.689 E-.10466 +G1 X124.465 Y111.614 E-.10466 +G3 X124.297 Y111.472 I.07 J-.252 E-.04708 +;WIPE_END +G1 X120.072 Y109.098 Z.285 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.55192 +G1 F1200 +G1 X120.157 Y109.027 E.00469 +G1 X120.43 Y109.128 E.01232 +;WIDTH:0.52596 +G1 X120.704 Y109.23 E.01174 +;WIDTH:0.499999 +G1 X121.444 Y109.65 E.03234 +G1 X121.508 Y109.731 E.00392 +;WIDTH:0.473299 +G1 X121.522 Y109.9 E.00607 +;WIDTH:0.474155 +G1 X121.433 Y110.267 E.01354 +;WIDTH:0.499999 +G3 X120.804 Y111.427 I-2.003 J-.335 E.05108 +G3 X120.024 Y111.806 I-1.765 J-2.638 E.03306 +G1 X119.265 Y111.903 E.02908 +G3 X118.162 Y111.698 I.151 J-3.873 E.04279 +G3 X117.31 Y111.11 I1.788 J-3.5 E.03946 +G3 X116.686 Y110.044 I3.302 J-2.65 E.04711 +G3 X116.531 Y108.955 I4.788 J-1.236 E.04189 +G3 X116.694 Y107.75 I5.083 J.075 E.04633 +G3 X117.263 Y106.737 I4.228 J1.705 E.04428 +G1 X117.715 Y106.356 E.02247 +G3 X118.519 Y105.976 I1.487 J2.104 E.03397 +G1 X119.27 Y105.877 E.02879 +G3 X120.442 Y106.112 I-.097 J3.53 E.04565 +G1 X120.972 Y106.475 E.02441 +G3 X121.297 Y106.879 I-1.022 J1.157 E.0198 +G3 X121.511 Y107.725 I-2.432 J1.064 E.03332 +M73 Q2 S21 +;WIDTH:0.457904 +G1 X121.512 Y107.824 E.00342 +M73 P2 R21 +;WIDTH:0.473449 +G1 X121.464 Y107.907 E.00343 +;WIDTH:0.499999 +G1 X121.416 Y107.991 E.00368 +G1 X120.775 Y108.317 E.02733 +;WIDTH:0.530705 +G1 X120.293 Y108.479 E.02062 +;WIDTH:0.55192 +G3 X120.051 Y108.452 I-.106 J-.147 E.01129 +;WIDTH:0.52596 +G1 X119.961 Y108.385 E.00451 +;WIDTH:0.499999 +G1 X119.778 Y107.913 E.01924 +G1 X119.556 Y107.713 E.01136 +G1 X119.252 Y107.635 E.01193 +G1 X118.891 Y107.719 E.01409 +G1 X118.715 Y107.863 E.00864 +G1 X118.509 Y108.306 E.01857 +G2 X118.498 Y109.402 I8.198 J.634 E.04169 +G1 X118.656 Y109.811 E.01666 +G2 X118.982 Y110.107 I.682 J-.425 E.01695 +G1 X119.299 Y110.137 E.0121 +G1 X119.613 Y110.003 E.01298 +G2 X119.985 Y109.173 I-2.052 J-1.416 E.03476 +;WIDTH:0.52596 +G1 X120.026 Y109.137 E.00219 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.072 Y109.098 E-.01253 +G1 X120.157 Y109.027 E-.02302 +G1 X120.43 Y109.128 E-.06049 +G1 X120.704 Y109.23 E-.06076 +G1 X121.444 Y109.65 E-.17682 +G1 X121.508 Y109.731 E-.02145 +G1 X121.522 Y109.9 E-.03524 +G1 X121.433 Y110.267 E-.07848 +G3 X121.138 Y111.03 I-2.002 J-.335 E-.17121 +;WIPE_END +G1 X134.093 Y95.907 Z.548 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Perimeter +;WIDTH:0.499999 +G1 F1200 +G1 X134.093 Y97.286 E.05241 +G1 X134.096 Y108.485 E.42563 +;WIDTH:0.493213 +G1 X134.114 Y108.77 E.01069 +;WIDTH:0.457681 +G1 X134.132 Y109.055 E.00985 +;WIDTH:0.425441 +G1 X134.13 Y109.642 E.01867 +;WIDTH:0.470489 +G1 X134.108 Y109.917 E.00981 +;WIDTH:0.515537 +G2 X134.1 Y110.711 I4.643 J.44 E.03124 +;WIDTH:0.499999 +G1 X134.093 Y114.093 E.12854 +G1 X115.907 Y114.093 E.69118 +G1 X115.907 Y110.187 E.14845 +G1 X115.888 Y110.013 E.00665 +;WIDTH:0.461512 +G1 X115.869 Y109.84 E.00606 +;WIDTH:0.423025 +G1 X115.849 Y109.666 E.00554 +;WIDTH:0.384537 +G1 X115.835 Y108.875 E.02247 +;WIDTH:0.382805 +G1 X115.848 Y108.186 E.01948 +;WIDTH:0.42187 +G1 X115.868 Y108.009 E.00561 +;WIDTH:0.460935 +G1 X115.888 Y107.832 E.00619 +;WIDTH:0.499999 +G1 X115.907 Y107.655 E.00677 +G1 X115.907 Y95.907 E.4465 +G1 X134.033 Y95.907 E.6889 +M204 P1000 +G1 X134.55 Y95.45 F12000 +M204 P600 +;TYPE:External perimeter +G1 F1200 +G1 X134.55 Y97.286 E.06978 +G1 X134.55 Y114.55 E.65614 +G1 X115.45 Y114.55 E.72592 +G1 X115.45 Y95.45 E.72592 +G1 X134.49 Y95.45 E.72364 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.55 Y95.45 E-.01247 +G1 X134.55 Y97.286 E-.38154 +G1 X134.55 Y98.47 E-.24599 +;WIPE_END +G1 X133.887 Y97.043 Z.227 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Solid infill +;WIDTH:0.502851 +G1 F1200 +G1 X133.163 Y96.318 E.03918 +G1 X132.512 Y96.318 E.0249 +G1 X133.682 Y97.488 E.06328 +G1 X133.682 Y98.138 E.02486 +G1 X131.862 Y96.318 E.09843 +G1 X131.212 Y96.318 E.02486 +G1 X133.682 Y98.788 E.13359 +G1 X133.682 Y99.439 E.0249 +G1 X130.561 Y96.318 E.1688 +G1 X129.911 Y96.318 E.02486 +G1 X130.966 Y97.374 E.05709 +G1 X131.041 Y97.371 E.00287 +G1 X131.528 Y97.49 E.01917 +G1 X131.92 Y97.833 E.01992 +G3 X132.115 Y98.523 I-1.031 J.664 E.02783 +G1 X133.682 Y100.089 E.08472 +G1 X133.682 Y100.74 E.0249 +G1 X132.069 Y99.128 E.08721 +G3 X131.953 Y99.661 I-1.777 J-.109 E.02094 +G1 X133.682 Y101.39 E.09351 +G1 X133.682 Y102.041 E.0249 +G1 X132.073 Y100.432 E.08702 +G3 X132.128 Y101.138 I-4.331 J.691 E.02711 +G1 X133.682 Y102.691 E.08402 +G1 X133.682 Y103.342 E.0249 +G1 X132.128 Y101.788 E.08405 +G1 X132.128 Y102.438 E.02486 +G1 X133.682 Y103.992 E.08405 +G1 X133.682 Y104.642 E.02486 +G1 X132.128 Y103.089 E.08402 +G1 X132.128 Y103.739 E.02486 +G1 X133.682 Y105.293 E.08405 +G1 X133.682 Y105.943 E.02486 +G1 X132.032 Y104.294 E.08921 +G1 X131.754 Y104.666 E.01776 +M73 Q2 S20 +G1 X133.682 Y106.594 E.10427 +G1 X133.682 Y107.244 E.02486 +G1 X131.332 Y104.894 E.1271 +G3 X130.695 Y104.908 I-.351 J-1.447 E.02456 +G1 X132.369 Y106.582 E.09054 +G2 X131.548 Y106.412 I-.932 J2.423 E.0322 +G1 X129.95 Y104.814 E.08643 +G1 X129.574 Y104.706 E.01496 +G1 X129.247 Y104.761 E.01268 +G1 X130.907 Y106.421 E.08978 +G1 X130.366 Y106.53 E.02111 +G1 X128.634 Y104.798 E.09367 +G3 X127.958 Y104.678 I.182 J-2.993 E.02631 +M73 P2 R20 +G1 X127.885 Y104.7 E.00292 +G1 X129.933 Y106.747 E.11074 +G2 X129.564 Y107.013 I.948 J1.7 E.01744 +G1 X127.384 Y104.849 E.11747 +G1 X126.798 Y104.914 E.02255 +G1 X127.017 Y105.133 E.01184 +G3 X128.193 Y106.309 I.072 J1.104 E.07196 +G1 X128.572 Y106.688 E.0205 +M204 P1000 +G1 E-.16 F2100 +M73 Q3 S20 +;WIPE_START +G1 F9600 +G1 X128.193 Y106.309 E-.11138 +G2 X127.017 Y105.133 I-1.104 J-.072 E-.39105 +M73 P3 R20 +G1 X126.798 Y104.914 E-.06436 +G1 X127.148 Y104.875 E-.07321 +;WIPE_END +G1 X126.555 Y105.321 Z.213 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +G1 F1200 +G1 X126.154 Y104.921 E.02166 +G3 X125.584 Y105 I-.597 J-2.193 E.02207 +G1 X125.733 Y105.149 E.00806 +G2 X125.24 Y105.307 I.093 J1.14 E.01997 +G1 X124.908 Y104.975 E.01796 +G3 X123.67 Y104.309 I.325 J-2.086 E.05481 +G1 X123.63 Y104.347 E.00211 +G1 X124.901 Y105.618 E.06874 +G2 X124.721 Y106.088 I.917 J.621 E.01942 +G1 X123.244 Y104.612 E.07986 +G1 X122.794 Y104.813 E.01885 +G1 X124.464 Y106.483 E.09032 +G2 X123.872 Y106.541 I-.182 J1.183 E.02299 +G1 X122.29 Y104.959 E.08556 +G1 X121.704 Y105.023 E.02254 +G1 X123.164 Y106.483 E.07896 +G1 X122.56 Y106.53 E.02317 +G1 X120.757 Y104.726 E.09754 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.56 Y106.53 E-.53003 +G1 X123.088 Y106.489 E-.10997 +;WIPE_END +G1 X129.055 Y96.113 Z.409 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +G1 F1200 +G1 X130.361 Y97.419 E.07063 +G2 X129.819 Y97.528 I.077 J1.795 E.02123 +G1 X128.61 Y96.318 E.06542 +G1 X127.959 Y96.318 E.0249 +G1 X129.1 Y97.459 E.06171 +G2 X128.369 Y97.379 I-.954 J5.339 E.02814 +G1 X127.309 Y96.318 E.05736 +G1 X126.659 Y96.318 E.02486 +G1 X127.763 Y97.422 E.05971 +G1 X127.355 Y97.666 E.01818 +G1 X126.008 Y96.318 E.07288 +G1 X125.358 Y96.318 E.02486 +G1 X127.102 Y98.062 E.09432 +G2 X127.025 Y98.637 I1.206 J.452 E.02238 +G1 X124.707 Y96.318 E.12539 +G1 X124.057 Y96.318 E.02486 +G1 X126.406 Y98.668 E.12707 +G1 X125.731 Y98.643 E.02583 +G1 X123.406 Y96.318 E.12575 +G1 X122.756 Y96.318 E.02486 +G1 X125.157 Y98.719 E.12986 +G2 X124.662 Y98.875 I.079 J1.118 E.02003 +G1 X122.105 Y96.318 E.13829 +G1 X121.455 Y96.318 E.02486 +G1 X122.516 Y97.38 E.05741 +G2 X121.763 Y97.277 I-1.137 J5.512 E.02909 +G1 X120.805 Y96.318 E.05184 +G1 X120.154 Y96.318 E.0249 +G1 X121.154 Y97.318 E.05408 +G1 X120.59 Y97.405 E.02182 +G1 X119.504 Y96.318 E.05876 +G1 X118.853 Y96.318 E.0249 +G1 X120.116 Y97.581 E.06831 +G2 X119.69 Y97.805 I.614 J1.683 E.01846 +G1 X118.203 Y96.318 E.08042 +G1 X117.552 Y96.318 E.0249 +G1 X119.319 Y98.085 E.09557 +G2 X118.97 Y98.386 I.985 J1.497 E.01767 +G1 X116.902 Y96.318 E.11185 +G1 X116.318 Y96.318 E.02233 +G1 X116.318 Y96.385 E.00256 +G1 X118.698 Y98.765 E.12872 +G1 X118.448 Y99.166 E.01807 +G1 X116.318 Y97.036 E.1152 +G1 X116.318 Y97.686 E.02486 +G1 X118.243 Y99.611 E.10411 +G1 X118.09 Y100.108 E.01989 +G1 X116.318 Y98.337 E.09581 +G1 X116.318 Y98.987 E.02486 +G1 X118.024 Y100.692 E.09224 +G1 X117.981 Y101.3 E.02331 +G1 X116.318 Y99.638 E.08992 +G1 X116.318 Y100.288 E.02486 +G1 X118.32 Y102.29 E.10828 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.318 Y100.288 E-.58837 +G1 X116.318 Y100.039 E-.05163 +;WIPE_END +G1 X116.113 Y100.733 Z.213 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +G1 F1200 +G1 X118.349 Y102.969 E.12093 +G2 X118.908 Y103.876 I4.782 J-2.325 E.04081 +G2 X120.13 Y104.75 I3.1 J-3.04 E.05775 +G1 X120.645 Y105.265 E.02785 +G2 X119.781 Y105.052 I-1.106 J2.628 E.03417 +G1 X116.318 Y101.589 E.18729 +G1 X116.318 Y102.239 E.02486 +G1 X119.105 Y105.026 E.15073 +G1 X118.531 Y105.102 E.02214 +G1 X116.318 Y102.89 E.11966 +G1 X116.318 Y103.54 E.02486 +G1 X118.012 Y105.234 E.09162 +G2 X117.559 Y105.432 I.499 J1.757 E.01896 +G1 X116.318 Y104.191 E.06712 +G1 X116.318 Y104.841 E.02486 +G1 X117.163 Y105.686 E.0457 +G1 X116.803 Y105.977 E.0177 +G1 X116.318 Y105.492 E.02623 +G1 X116.318 Y106.142 E.02486 +G1 X116.646 Y106.47 E.01774 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.318 Y106.142 E-.0964 +G1 X116.318 Y105.492 E-.13508 +G1 X116.803 Y105.977 E-.14254 +G1 X117.163 Y105.686 E-.0962 +G1 X116.586 Y105.108 E-.16978 +;WIPE_END +G1 X133.333 Y111.717 Z.514 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.508794 +G1 F1200 +G1 X133.69 Y112.074 E.01956 +G1 X133.687 Y112.73 E.02541 +G1 X133.146 Y112.189 E.02964 +G3 X132.739 Y112.441 I-1.043 J-1.233 E.01861 +G1 X133.684 Y113.386 E.05177 +G1 X133.682 Y113.682 E.01147 +G1 X133.321 Y113.682 E.01398 +G1 X132.267 Y112.628 E.05774 +G3 X131.712 Y112.732 I-.597 J-1.646 E.02197 +G1 X132.662 Y113.682 E.05204 +G1 X132.003 Y113.682 E.02553 +G1 X131.066 Y112.745 E.05133 +G3 X130.259 Y112.578 I.242 J-3.208 E.03201 +G1 X130.223 Y112.56 E.00156 +G1 X131.344 Y113.682 E.06144 +G1 X130.685 Y113.682 E.02553 +G1 X129.324 Y112.32 E.07459 +G3 X128.902 Y112.557 I-.886 J-1.083 E.01884 +G1 X130.027 Y113.682 E.06163 +G1 X129.368 Y113.682 E.02553 +G1 X128.386 Y112.7 E.0538 +G3 X127.71 Y112.682 I-.283 J-2.091 E.02631 +G1 X128.709 Y113.682 E.05476 +G1 X128.05 Y113.682 E.02553 +G1 X126.861 Y112.493 E.06514 +G3 X126.243 Y112.533 I-.405 J-1.434 E.02417 +G1 X127.391 Y113.682 E.06292 +G1 X126.732 Y113.682 E.02553 +G1 X125.524 Y112.474 E.06618 +G1 X124.949 Y112.557 E.0225 +G1 X126.073 Y113.682 E.0616 +G1 X125.415 Y113.682 E.02549 +G1 X124.246 Y112.513 E.06404 +G3 X123.742 Y112.668 I-.681 J-1.319 E.02054 +G1 X124.756 Y113.682 E.05555 +G1 X124.097 Y113.682 E.02553 +G1 X123.146 Y112.73 E.05213 +G3 X122.303 Y112.546 I.021 J-2.116 E.03366 +G1 X123.438 Y113.682 E.06221 +G1 X122.779 Y113.682 E.02553 +G1 X121.262 Y112.164 E.08313 +G1 X120.84 Y112.401 E.01875 +G1 X122.12 Y113.682 E.07015 +G1 X121.462 Y113.682 E.02549 +G1 X120.376 Y112.596 E.05949 +G3 X119.825 Y112.704 I-.604 J-1.631 E.02185 +G1 X120.803 Y113.682 E.05358 +G1 X120.144 Y113.682 E.02553 +G1 X119.237 Y112.775 E.04969 +G1 X118.495 Y112.692 E.02892 +G1 X119.485 Y113.682 E.05424 +G1 X118.826 Y113.682 E.02553 +G1 X117.413 Y112.269 E.07741 +G3 X116.546 Y111.55 I1.827 J-3.086 E.04381 +G1 X116.318 Y111.196 E.01631 +G1 X116.318 Y111.833 E.02468 +G1 X118.167 Y113.682 E.10129 +G1 X117.508 Y113.682 E.02553 +G1 X116.318 Y112.492 E.06519 +G1 X116.318 Y113.15 E.02549 +G1 X117.055 Y113.887 E.04038 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.318 Y113.15 E-.2166 +G1 X116.318 Y112.492 E-.13674 +G1 X117.294 Y113.467 E-.28666 +;WIPE_END +G1 X130.832 Y109.045 Z.449 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:External perimeter +;WIDTH:0.435226 +G1 F1200 +G3 X131.159 Y108.665 I.607 J.192 E.01681 +;WIDTH:0.439557 +G3 X131.597 Y108.769 I.093 J.577 E.01524 +G1 X131.745 Y109.008 E.00927 +G1 X131.751 Y109.046 E.00127 +G1 X130.886 Y109.048 E.02853 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.832 Y109.045 E-.01124 +G3 X131.159 Y108.665 I.608 J.192 E-.10707 +G3 X131.597 Y108.769 I.094 J.577 E-.09603 +G1 X131.745 Y109.008 E-.05842 +G1 X131.751 Y109.046 E-.00799 +G1 X130.886 Y109.048 E-.17976 +;WIPE_END +G1 E-.17949 F2100 +G1 X127.713 Y109.759 Z.257 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;TYPE:Perimeter +;WIDTH:0.663948 +G1 F1200 +M73 Q4 S20 +G1 X127.708 Y109.507 E.01302 +M204 P1000 +G1 X127.708 Y109.507 F12000 +G1 X127.312 Y110.189 +M204 P600 +;TYPE:External perimeter +;WIDTH:0.499999 +G1 F1200 +M73 P4 R20 +G3 X127.308 Y110.184 I.495 J-.351 E.00024 +G1 X127.189 Y109.797 E.01539 +G1 X127.195 Y109.229 E.02159 +G1 X127.343 Y108.85 E.01546 +G3 X127.669 Y108.7 I.333 J.294 E.01404 +G3 X128.079 Y108.852 I.06 J.467 E.01728 +G1 X128.229 Y109.267 E.01677 +G3 X128.229 Y109.879 I-3.346 J.304 E.02329 +G1 X128.081 Y110.276 E.0161 +G3 X127.752 Y110.443 I-.373 J-.327 E.01437 +G3 X127.349 Y110.236 I.055 J-.605 E.01765 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.312 Y110.189 E-.01243 +G3 X127.308 Y110.184 I.472 J-.382 E-.00133 +G1 X127.189 Y109.797 E-.08414 +G1 X127.195 Y109.229 E-.11804 +G1 X127.343 Y108.85 E-.08455 +G3 X127.669 Y108.7 I.333 J.294 E-.07676 +G3 X128.079 Y108.852 I.06 J.467 E-.09451 +G1 X128.229 Y109.267 E-.0917 +G3 X128.242 Y109.635 I-3.347 J.306 E-.07654 +;WIPE_END +G1 X126.198 Y102.228 Z.334 F12000 +G1 Z.2 F720 +G1 E.8 F1500 +M204 P600 +;WIDTH:0.48942 +G1 F1200 +G1 X126.193 Y102.389 E.00598 +G1 X126.093 Y102.624 E.00948 +G1 X125.792 Y102.778 E.01255 +;WIDTH:0.488168 +G1 X125.566 Y102.762 E.00839 +G1 X125.437 Y102.606 E.00749 +G1 X125.459 Y102.456 E.00561 +G1 X125.7 Y102.342 E.00987 +;WIDTH:0.48942 +G1 X126.148 Y102.232 E.01713 +M204 P1000 +;LAYER_CHANGE +;Z:0.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.4 + + +G1 X126.148 Y102.232 Z.2 F12000 +G1 X125.693 Y101.255 Z.4 F4366.903 +;AFTER_LAYER_CHANGE +;0.4 +M104 S240 ; set temperature +M140 S90 ; set bed temperature +G1 X125.693 Y101.255 F12000 +G1 Z.4 F720 +M204 P800 +;WIDTH:0.428385 +G1 F2100 +G1 X125.682 Y101.258 E.00037 +;WIDTH:0.45401 +G1 F2091.271 +G1 X125.277 Y101.345 E.01416 +G1 F2062.106 +G1 X125.387 Y101.165 E.00721 +;WIDTH:0.425937 +G1 F2083.336 +G1 X125.497 Y100.985 E.00672 +;WIDTH:0.402759 +G1 F2100 +G1 X125.649 Y100.909 E.00508 +G1 X126.061 Y100.914 E.01233 +G1 X126.178 Y100.958 E.00374 +G1 X126.238 Y101.132 E.00551 +G1 X126.086 Y101.17 E.00469 +;WIDTH:0.428385 +G1 X125.751 Y101.243 E.01099 +M204 P1000 +G1 E-.16 +;WIPE_START +G1 F9600 +G1 X125.693 Y101.255 E-.01231 +G1 X125.682 Y101.258 E-.00237 +G1 X125.277 Y101.345 E-.08608 +G1 X125.387 Y101.165 E-.04384 +G1 X125.497 Y100.985 E-.04384 +G1 X125.649 Y100.909 E-.03532 +G1 X126.061 Y100.914 E-.08563 +G1 X126.178 Y100.958 E-.02598 +G1 X126.238 Y101.132 E-.03825 +G1 X126.086 Y101.17 E-.03256 +G1 X125.751 Y101.243 E-.07125 +;WIPE_END +G1 E-.16257 F2100 +G1 X120.227 Y109.357 Z.571 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.449999 +G1 F2093.072 +G1 X120.294 Y109.318 E.00262 +G1 F2088.592 +G1 X120.949 Y109.507 E.02308 +G1 F2080.642 +G1 X121.39 Y109.647 E.01566 +G1 F2075.676 +G1 X121.526 Y109.874 E.00896 +G1 F2068.427 +G1 X121.338 Y110.359 E.01761 +G1 X121.181 Y110.688 E.01234 +G1 F2088.607 +G1 X120.725 Y111.212 E.02351 +G1 F2095.435 +G1 X120.029 Y111.571 E.02651 +G1 F2093.327 +G1 X119.265 Y111.678 E.02611 +G1 X118.645 Y111.609 E.02112 +G1 X118.25 Y111.491 E.01395 +G1 F2089.971 +G1 X117.926 Y111.311 E.01255 +G1 F2084.66 +G1 X117.474 Y110.954 E.0195 +G1 F2077.587 +G1 X117.081 Y110.38 E.02355 +G1 F2072.021 +G1 X116.9 Y109.975 E.01502 +G1 F2069.193 +G1 X116.811 Y109.584 E.01357 +G1 F2067.629 +G1 X116.756 Y108.952 E.02147 +G1 X116.808 Y108.262 E.02342 +G1 F2069.247 +G1 X116.908 Y107.821 E.01531 +G1 F2072.06 +G1 X117.077 Y107.438 E.01417 +G1 F2077.17 +G1 X117.433 Y106.886 E.02223 +G1 F2084.501 +G1 X117.946 Y106.47 E.02236 +G1 F2090.528 +G1 X118.322 Y106.271 E.0144 +G1 F2094.424 +G1 X118.687 Y106.17 E.01282 +G1 F2097.766 +G1 X119.218 Y106.107 E.0181 +G1 F2064.426 +G1 X119.785 Y106.168 E.0193 +G1 X120.082 Y106.209 E.01015 +G1 F2095.871 +G1 X120.398 Y106.339 E.01157 +G1 F2091.5 +G1 X120.908 Y106.679 E.02075 +G1 F2084.865 +G1 X121.224 Y107.064 E.01686 +G1 F2079.878 +G1 X121.364 Y107.325 E.01003 +G1 F2076.154 +G1 X121.514 Y107.827 E.01773 +G1 X121.363 Y107.991 E.00755 +G1 F2085.602 +G1 X120.329 Y108.238 E.03598 +G1 F2092.727 +G1 X120.186 Y108.219 E.00488 +G1 F2093.883 +G1 X120.09 Y108.106 E.00502 +G1 F2094.684 +G1 X120.006 Y107.865 E.00864 +G1 F2095.926 +G1 X119.783 Y107.582 E.0122 +G1 F2097.542 +G1 X119.538 Y107.451 E.0094 +G1 F2098.311 +G2 X118.851 Y107.489 I-.276 J1.25 E.02358 +G1 F2096.018 +G1 X118.536 Y107.724 E.0133 +G1 F2093.563 +G1 X118.291 Y108.25 E.01964 +G1 F2092.015 +G2 X118.276 Y109.44 I6.001 J.671 E.04035 +G1 F2093.099 +G1 X118.458 Y109.916 E.01725 +G1 F2094.332 +G1 X118.53 Y110.057 E.00536 +G1 F2096.079 +G1 X118.872 Y110.308 E.01436 +G1 F2098.983 +G1 X119.226 Y110.37 E.01216 +G1 X119.592 Y110.289 E.01269 +G1 F2097.458 +G1 X119.819 Y110.099 E.01002 +G1 F2095.986 +G1 X119.983 Y109.875 E.0094 +G1 F2094.514 +G1 X120.15 Y109.403 E.01695 +G1 F2093.072 +G1 X120.175 Y109.388 E.00099 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.227 Y109.357 E-.01258 +G1 X120.294 Y109.318 E-.01611 +G1 X120.949 Y109.507 E-.14167 +G1 X121.39 Y109.647 E-.09615 +G1 X121.526 Y109.874 E-.05499 +G1 X121.338 Y110.359 E-.1081 +G1 X121.181 Y110.688 E-.07576 +G1 X120.755 Y111.176 E-.13464 +;WIPE_END +G1 X125.688 Y111.415 Z.486 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.570342 +G1 F2076.055 +G1 X125.688 Y107.756 E.16047 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.688 Y110.835 E-.64 +;WIPE_END +G1 X129.574 Y98.649 Z.623 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.592014 +G1 F2091.049 +G1 X129.574 Y99.572 E.04214 +;WIDTH:0.636937 +G1 F2074.61 +G1 X129.596 Y99.683 E.00559 +;WIDTH:0.68186 +G1 F2060.956 +G1 X129.619 Y99.794 E.00602 +G1 X129.596 Y99.905 E.00602 +;WIDTH:0.636937 +G1 F2074.61 +G1 X129.574 Y100.016 E.00559 +;WIDTH:0.592014 +G1 F2099.997 +G1 X129.574 Y103.651 E.16596 +;WIDTH:0.594112 +G1 F2095.381 +G1 X129.574 Y103.675 E.0011 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.574 Y100.595 E-.64 +;WIPE_END +G1 X130.842 Y99.794 Z.426 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.535823 +G1 F2067.294 +G1 X130.536 Y99.794 E.01254 +;WIDTH:0.584502 +G1 F2040.036 +G1 X130.23 Y99.794 E.01378 +;WIDTH:0.633181 +G1 F2016.969 +G1 X129.924 Y99.794 E.01502 +;WIDTH:0.68186 +G1 F1997.196 +G1 X129.619 Y99.794 E.0162 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.842 Y99.794 E-.25415 +G1 X130.536 Y99.794 E-.06359 +G1 X130.23 Y99.794 E-.06359 +G1 X129.924 Y99.794 E-.06359 +G1 X129.619 Y99.794 E-.06338 +;WIPE_END +G1 E-.1317 F2100 +G1 X123.796 Y102.032 Z.509 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.449999 +G1 F2039.599 +G1 X123.795 Y102.065 E.00112 +;WIDTH:0.410733 +G1 F2065.951 +G1 X123.794 Y102.162 E.00297 +;WIDTH:0.41018 +G1 F2066.39 +G1 X123.711 Y102.418 E.00822 +;WIDTH:0.448893 +G1 F2042.565 +G1 X123.629 Y102.674 E.00907 +;WIDTH:0.449999 +G1 F2069.042 +G1 X123.515 Y102.925 E.00933 +G1 F2096.679 +G1 X123.039 Y103.471 E.02452 +G1 F2098.527 +G1 X122.648 Y103.705 E.01542 +G1 F2090.726 +G3 X121.576 Y103.938 I-1.177 J-2.829 E.03733 +G1 F2097.481 +G1 X120.982 Y103.872 E.02023 +G1 F2096.719 +G1 X120.39 Y103.662 E.02126 +G1 F2081.287 +G1 X119.745 Y103.167 E.02752 +G1 F2099.59 +G1 X119.394 Y102.639 E.02146 +G1 F2098.813 +G3 X119.11 Y101.737 I2.435 J-1.262 E.03217 +G1 X119.07 Y101.209 E.01792 +G1 X119.144 Y100.392 E.02777 +G1 X119.31 Y99.86 E.01886 +G1 X119.745 Y99.149 E.02821 +G1 F2097.499 +G1 X120.362 Y98.666 E.02652 +G1 F2094.983 +G1 X120.835 Y98.468 E.01736 +G1 F2093.582 +G1 X121.589 Y98.363 E.02577 +G1 X122.247 Y98.432 E.02239 +G1 F2094.32 +G1 X122.618 Y98.554 E.01322 +G1 F2096.407 +G1 X123.218 Y98.936 E.02408 +G1 X123.653 Y99.538 E.02514 +;WIDTH:0.498622 +G1 F2056.741 +G1 X123.763 Y99.83 E.01182 +;WIDTH:0.547244 +G1 F2028.599 +G1 X123.872 Y100.122 E.01307 +G1 F2099.268 +G1 X123.827 Y100.197 E.00367 +;WIDTH:0.532161 +G1 F2032.717 +G1 X123.519 Y100.279 E.01297 +;WIDTH:0.49108 +G1 F2057.204 +G1 X123.21 Y100.362 E.01192 +;WIDTH:0.449999 +G1 F2100 +G1 X122.644 Y100.497 E.0197 +G1 X122.5 Y100.478 E.00492 +G3 X122.284 Y100.055 I.831 J-.692 E.01621 +G2 X121.852 Y99.711 I-.771 J.524 E.01898 +G1 F2099.997 +G1 X121.443 Y99.687 E.01387 +G1 F2100 +G1 X121.168 Y99.752 E.00956 +G1 X120.847 Y99.983 E.01339 +G1 X120.65 Y100.373 E.01479 +G1 X120.564 Y100.791 E.01445 +G1 X120.59 Y101.7 E.03078 +G2 X120.844 Y102.317 I4.43 J-1.468 E.0226 +G1 X121.156 Y102.547 E.01312 +G2 X121.756 Y102.605 I.407 J-1.096 E.02064 +G1 X122.083 Y102.429 E.01257 +G1 X122.285 Y102.118 E.01255 +G1 X122.463 Y101.662 E.01657 +G1 X122.607 Y101.577 E.00566 +G1 F2099.999 +G1 X123.704 Y101.907 E.03878 +G1 F2100 +G1 X123.796 Y101.968 E.00374 +G1 F2039.599 +G1 X123.796 Y101.972 E.00014 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.796 Y102.032 E-.01247 +G1 X123.795 Y102.065 E-.00686 +G1 X123.794 Y102.162 E-.02016 +G1 X123.711 Y102.418 E-.05593 +G1 X123.629 Y102.674 E-.05586 +G1 X123.515 Y102.925 E-.05729 +G1 X123.039 Y103.471 E-.15053 +G1 X122.648 Y103.705 E-.09469 +G3 X121.782 Y103.924 I-1.177 J-2.829 E-.18621 +;WIPE_END +G1 X131.197 Y110.079 Z.596 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.464256 +G1 F2096.789 +G1 X131.327 Y110.08 E.00455 +G1 F2066.926 +G1 X131.618 Y110.059 E.01022 +;WIDTH:0.423588 +G1 F2062.064 +G1 X131.908 Y110.037 E.00921 +;WIDTH:0.42372 +G1 F2061.964 +G1 X131.764 Y110.126 E.00536 +;WIDTH:0.464256 +G1 F2068.053 +G1 X131.656 Y110.319 E.00775 +G1 F2099.772 +G1 X131.522 Y110.461 E.00684 +G1 X131.24 Y110.493 E.00994 +;WIDTH:0.460114 +G1 X131.009 Y110.369 E.00909 +G1 X130.837 Y110.078 E.01173 +;WIDTH:0.464256 +G1 F2096.789 +G1 X131.137 Y110.079 E.01051 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.197 Y110.079 E-.01247 +G1 X131.327 Y110.08 E-.02702 +G1 X131.618 Y110.059 E-.06063 +G1 X131.908 Y110.037 E-.06044 +G1 X131.764 Y110.126 E-.03518 +G1 X131.656 Y110.319 E-.04596 +G1 X131.522 Y110.461 E-.04057 +G1 X131.24 Y110.493 E-.05898 +G1 X131.009 Y110.369 E-.05448 +G1 X130.837 Y110.078 E-.07025 +G1 X131.137 Y110.079 E-.06234 +;WIPE_END +G1 E-.11168 F2100 +G1 X125.243 Y101.355 Z.584 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.450918 +G1 F2091.217 +G1 X124.667 Y101.269 E.01976 +;WIDTH:0.449999 +G1 F2099.999 +G1 X124.187 Y101.17 E.01659 +G1 F2100 +G1 X124.087 Y101.008 E.00644 +;WIDTH:0.49108 +G1 F2065.635 +G1 X124.172 Y100.724 E.01105 +;WIDTH:0.532161 +G1 F2040.497 +G1 X124.258 Y100.441 E.01203 +;WIDTH:0.547244 +G1 F2088.245 +G1 X124.299 Y100.364 E.00366 +G1 F2040.173 +G1 X124.508 Y100.185 E.01154 +;WIDTH:0.498622 +G1 F2064.976 +G1 X124.717 Y100.006 E.01043 +;WIDTH:0.449999 +G1 F2097.05 +G1 X125.246 Y99.792 E.01932 +G1 X125.821 Y99.733 E.01957 +G1 F2099.395 +G3 X126.883 Y99.89 I.08 J3.119 E.03652 +G1 F2098.404 +G1 X127.242 Y100.117 E.01438 +G1 X127.478 Y100.433 E.01335 +;WIDTH:0.475379 +G1 F2071.152 +G1 X127.551 Y100.697 E.00985 +;WIDTH:0.500759 +G1 F2054.368 +G1 X127.625 Y100.961 E.01044 +G1 F2095.491 +G1 X127.628 Y101.012 E.00194 +;WIDTH:0.496223 +G1 F2098.74 +G1 X127.626 Y102.611 E.06027 +G1 F2083.514 +G1 X127.677 Y103.189 E.02187 +;WIDTH:0.462817 +G1 F2072.448 +G1 X127.736 Y103.427 E.00856 +;WIDTH:0.423845 +G1 F2088.091 +G1 X127.795 Y103.666 E.0078 +;WIDTH:0.417436 +G1 F2065.61 +G1 X127.732 Y103.758 E.00347 +;WIDTH:0.449999 +G1 F2042.048 +G1 X127.668 Y103.85 E.00379 +G1 F2098.726 +G1 X126.558 Y103.839 E.03757 +G1 F2099.124 +G1 X126.443 Y103.713 E.00577 +G1 F2087.868 +G1 X126.412 Y103.615 E.00348 +G1 X126.07 Y103.808 E.01329 +G1 F2097.705 +G1 X125.639 Y103.919 E.01506 +G1 X125.048 Y103.9 E.02001 +G1 X124.721 Y103.793 E.01165 +G1 F2099.539 +G1 X124.361 Y103.547 E.01476 +G1 F2094.668 +G1 X124.103 Y103.171 E.01544 +G1 X124.006 Y102.753 E.01452 +;WIDTH:0.448893 +G1 F2020.985 +G1 X124.054 Y102.514 E.00823 +;WIDTH:0.41016 +G1 F2043.267 +G1 X124.103 Y102.276 E.00742 +;WIDTH:0.410713 +G1 F2042.859 +G1 X124.147 Y102.12 E.00496 +;WIDTH:0.449999 +G1 F2044.977 +G1 X124.191 Y101.964 E.00549 +G1 F2098.87 +G3 X124.691 Y101.531 I1.165 J.84 E.02259 +;WIDTH:0.450918 +G1 F2091.217 +G1 X125.186 Y101.374 E.01762 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.243 Y101.355 E-.01249 +G1 X124.667 Y101.269 E-.12103 +G1 X124.187 Y101.17 E-.10185 +G1 X124.087 Y101.008 E-.03956 +G1 X124.172 Y100.724 E-.06161 +G1 X124.258 Y100.441 E-.06147 +G1 X124.299 Y100.364 E-.01813 +G1 X124.508 Y100.185 E-.05719 +G1 X124.717 Y100.006 E-.05719 +G1 X125.205 Y99.809 E-.10948 +;WIPE_END +G1 X131.908 Y110.037 Z.613 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.38292 +G1 F2096.072 +G1 X131.965 Y110.037 E.00161 +;WIDTH:0.423801 +G1 F2061.725 +G1 X132.217 Y110.058 E.00801 +;WIDTH:0.464681 +G1 F2065.9 +G1 X132.468 Y110.079 E.00883 +;WIDTH:0.505562 +G1 F2069.401 +G1 X132.72 Y110.1 E.00973 +;WIDTH:0.546442 +G1 F2072.378 +G1 X132.971 Y110.121 E.01055 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.908 Y110.037 E-.22159 +G1 X131.965 Y110.037 E-.01185 +G1 X132.217 Y110.058 E-.05255 +G1 X132.468 Y110.079 E-.05234 +G1 X132.72 Y110.1 E-.05255 +G1 X132.971 Y110.121 E-.05234 +;WIPE_END +G1 E-.19678 F2100 +G1 X120.048 Y108.994 Z.626 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.442183 +G1 F2093.668 +G1 X120.004 Y108.999 E.00147 +;WIDTH:0.449999 +G1 F2095.792 +G1 X119.817 Y109.168 E.00853 +G1 F2097.071 +G1 X119.639 Y109.618 E.01638 +G1 F2098.065 +G1 X119.475 Y109.879 E.01043 +G1 X119.3 Y109.937 E.00624 +G1 X119.008 Y109.918 E.0099 +G1 F2097.647 +G1 X118.855 Y109.798 E.00658 +G1 F2096.733 +G1 X118.726 Y109.527 E.01016 +G1 X118.675 Y109.168 E.01227 +G1 F2095.87 +G1 X118.673 Y108.448 E.02437 +G1 X118.861 Y107.989 E.01679 +G1 F2097.473 +G1 X118.945 Y107.927 E.00353 +G1 F2098.26 +G1 X119.141 Y107.847 E.00717 +G1 F2099.243 +G1 X119.409 Y107.844 E.00907 +G1 F2098.398 +G1 X119.572 Y107.973 E.00704 +G1 F2097.516 +G1 X119.741 Y108.323 E.01316 +G1 F2096.118 +G1 X119.995 Y108.574 E.01209 +;WIDTH:0.442183 +G1 F2095.087 +G1 X120.12 Y108.594 E.0042 +;WIDTH:0.410588 +G1 F2096.01 +G1 X120.245 Y108.614 E.00387 +;WIDTH:0.415447 +G1 F2093.925 +G1 X120.509 Y108.596 E.0082 +;WIDTH:0.457055 +G1 F2088.545 +G1 X120.659 Y108.581 E.00519 +;WIDTH:0.498663 +G1 F2083.55 +G1 X120.81 Y108.566 E.00575 +;WIDTH:0.540271 +G1 F2078.62 +G1 X120.96 Y108.552 E.00623 +;WIDTH:0.581878 +G1 F2073.882 +G1 X121.111 Y108.537 E.0068 +G1 F2070.216 +G1 X121.442 Y108.458 E.01525 +G1 F2067.525 +G1 X121.445 Y109.168 E.03182 +G1 F2070.329 +G1 X121.092 Y109.056 E.0166 +G1 F2074.27 +G1 X120.925 Y109.029 E.00758 +;WIDTH:0.534668 +G1 F2079.592 +G1 X120.759 Y109.002 E.00688 +;WIDTH:0.487457 +G1 F2085.138 +G1 X120.592 Y108.975 E.00625 +;WIDTH:0.440247 +G1 F2090.694 +G1 X120.425 Y108.948 E.00559 +;WIDTH:0.442183 +G1 F2093.668 +G1 X120.107 Y108.987 E.01064 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.048 Y108.994 E-.01235 +G1 X120.004 Y108.999 E-.0092 +G1 X119.817 Y109.168 E-.05238 +G1 X119.639 Y109.618 E-.10057 +G1 X119.475 Y109.879 E-.06406 +G1 X119.3 Y109.937 E-.03831 +G1 X119.008 Y109.918 E-.06081 +G1 X118.855 Y109.798 E-.04041 +G1 X118.726 Y109.527 E-.06237 +G1 X118.675 Y109.168 E-.07535 +G1 X118.674 Y108.571 E-.12419 +;WIPE_END +G1 X123.77 Y107.757 Z.49 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;WIDTH:0.555552 +G1 F2040.722 +G1 X123.77 Y109.504 E.07447 +G1 X123.764 Y109.636 E.00563 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.77 Y107.757 E-.39048 +G1 X123.77 Y108.958 E-.24952 +;WIPE_END +G1 X123.531 Y107.332 Z.429 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;WIDTH:0.449999 +G1 F3494.313 +G1 X123.583 Y107.371 E.0022 +;WIDTH:0.484172 +G1 F3462.723 +G1 X123.645 Y107.5 E.00525 +;WIDTH:0.518344 +G1 F3443.996 +G1 X123.708 Y107.628 E.00564 +;WIDTH:0.552516 +G1 F3441.779 +G1 X123.77 Y107.757 E.00606 +G1 F3460.12 +G1 X123.833 Y107.628 E.00608 +;WIDTH:0.518344 +G1 F3497.717 +G1 X123.895 Y107.5 E.00562 +;WIDTH:0.484172 +G1 F3545.037 +G1 X123.958 Y107.371 E.00527 +;WIDTH:0.449999 +G1 F3629.722 +G1 X124.2 Y107.191 E.01021 +G1 F3709.572 +G1 X124.432 Y107.145 E.00801 +G1 F3851.776 +G1 X125.341 Y107.145 E.03077 +G1 F3978.278 +G1 X125.341 Y106.389 E.02559 +G1 F4033.285 +G1 X125.409 Y106.108 E.00979 +G1 F4069.521 +G1 X125.569 Y105.913 E.00854 +G1 F4063.188 +G1 X125.829 Y105.791 E.00972 +G1 F4023.012 +G1 X126.637 Y105.779 E.02735 +G1 F3984.94 +G1 X126.945 Y105.779 E.01043 +G1 F3966.41 +G1 X127.23 Y105.85 E.00994 +G1 F3936.717 +G1 X127.431 Y106.021 E.00893 +G1 F3897.126 +G1 X127.53 Y106.214 E.00734 +G1 F3810.295 +G1 X127.555 Y106.84 E.02121 +G1 F3702.603 +G1 X127.555 Y107.134 E.00995 +G1 F3660.211 +G1 X128.068 Y107.073 E.01749 +G1 F3700.699 +G1 X128.602 Y107.164 E.01834 +G1 F3794.86 +G1 X129.075 Y107.379 E.01759 +G1 F3884.556 +G1 X129.482 Y107.709 E.01774 +G1 F3936.249 +G1 X129.566 Y107.81 E.00445 +G1 F3977.241 +G1 X129.985 Y107.447 E.01876 +G1 F4039.887 +G1 X130.375 Y107.227 E.01516 +G1 F4110.027 +G1 X131.078 Y107.08 E.02431 +G1 F4165.537 +G1 X131.649 Y107.102 E.01934 +G1 F4199.979 +G1 X132.236 Y107.263 E.0206 +G1 X132.559 Y107.453 E.01268 +G1 F4200 +G3 X133.059 Y107.922 I-1.304 J1.892 E.02329 +G1 X133.368 Y108.495 E.02204 +G1 F4195.959 +G3 X133.581 Y109.847 I-3.875 J1.303 E.04655 +G1 F4175.709 +G1 X133.548 Y110.067 E.00753 +G1 F4165.612 +G1 X133.502 Y110.166 E.0037 +G1 F4149.067 +G1 X133.53 Y110.475 E.0105 +G1 F4105.609 +G1 X133.236 Y111.116 E.02387 +G1 F4065.74 +G1 X132.763 Y111.63 E.02364 +G1 F4077.32 +G1 X132.248 Y111.919 E.01999 +G1 F4053.497 +G1 X131.429 Y112.076 E.02823 +G1 X130.769 Y112.027 E.0224 +G1 F4070.455 +G1 X130.245 Y111.864 E.01858 +G1 F4099.408 +G1 X129.878 Y111.63 E.01473 +G1 F4129.776 +G1 X129.547 Y111.329 E.01514 +G1 X129.174 Y111.681 E.01736 +G1 F4091.29 +G1 X128.731 Y111.93 E.0172 +G1 F4061.502 +G1 X128.08 Y112.075 E.02258 +G1 F4052.703 +G1 X127.583 Y112.038 E.01687 +G1 F4072.926 +G1 X127.16 Y111.873 E.01537 +G1 X127.015 Y111.986 E.00622 +G1 F4063.504 +G1 X125.951 Y112.002 E.03602 +G1 X125.779 Y111.977 E.00588 +G1 F4074.045 +G1 X125.688 Y111.867 E.00483 +G1 X125.598 Y111.977 E.00481 +G1 F4063.504 +G1 X124.5 Y112.002 E.03718 +G1 F4040.826 +G1 X124.375 Y111.989 E.00425 +G1 F4020.768 +G1 X124.17 Y111.896 E.00762 +G1 F4007.025 +G3 X123.162 Y112.072 I-.94 J-2.404 E.03487 +G1 F4006.083 +G1 X122.673 Y111.974 E.01688 +G1 F4000.556 +G1 X122.263 Y111.768 E.01553 +G1 F3988.55 +G1 X121.91 Y111.455 E.01597 +G1 F3965.618 +G1 X121.651 Y110.996 E.01784 +G1 F3943.941 +G1 X121.6 Y110.783 E.00741 +G1 F4011.518 +G1 X121.099 Y111.402 E.02696 +G1 F4099.523 +G1 X120.84 Y111.626 E.01159 +G1 F4094.705 +G1 X120.208 Y111.936 E.02383 +G1 F4067.437 +G1 X120.016 Y111.994 E.00679 +G1 F4055.742 +G1 X119.317 Y112.082 E.02385 +G1 F4054.155 +G1 X118.595 Y112.013 E.02455 +G1 F4073.202 +G1 X118.055 Y111.848 E.01911 +G1 F4035.922 +G1 X117.673 Y111.631 E.01487 +G1 F3954.175 +G1 X117.222 Y111.273 E.01949 +G1 F3843.335 +G1 X116.742 Y110.605 E.02784 +G1 F3743.897 +G1 X116.504 Y110.068 E.01988 +;WIDTH:0.495928 +G1 F3664.612 +G1 X116.456 Y109.957 E.00456 +;WIDTH:0.541856 +G1 F3620.4 +G1 X116.409 Y109.847 E.00496 +;WIDTH:0.587785 +G1 F3581.733 +G1 X116.361 Y109.737 E.00544 +;WIDTH:0.633713 +G1 F3547.681 +G1 X116.314 Y109.627 E.00588 +G1 F3536.605 +G1 X116.273 Y108.993 E.03121 +;WIDTH:0.632055 +G1 F3537.019 +G1 X116.311 Y108.223 E.03777 +;WIDTH:0.642604 +G1 F3538.556 +G1 X116.321 Y108.124 E.00496 +G1 F3546.331 +G1 X116.374 Y108.008 E.00636 +;WIDTH:0.594453 +G1 F3582.453 +G1 X116.428 Y107.892 E.00587 +;WIDTH:0.546302 +G1 F3623.628 +G1 X116.481 Y107.776 E.00534 +;WIDTH:0.498151 +G1 F3670.905 +G1 X116.534 Y107.659 E.00486 +;WIDTH:0.449999 +G1 F3747.351 +G1 X116.735 Y107.217 E.01644 +G1 F3825.232 +G1 X117.091 Y106.665 E.02223 +G1 F3888.022 +G1 X117.227 Y106.516 E.00683 +G1 F3957.139 +G1 X117.695 Y106.149 E.02013 +G1 F4048.453 +G1 X118.178 Y105.89 E.01855 +G1 F4117.906 +G1 X118.639 Y105.766 E.01616 +G1 F4168.109 +G1 X119.17 Y105.703 E.0181 +G1 F4197.625 +G1 X119.832 Y105.764 E.0225 +G1 F4179.651 +G1 X120.232 Y105.831 E.01373 +G1 F4148.719 +G1 X120.624 Y106.001 E.01446 +G1 F4087.791 +G1 X121.134 Y106.34 E.02073 +G1 F3989.884 +G1 X121.522 Y106.781 E.01988 +G1 F3881.939 +G1 X121.779 Y107.278 E.01894 +G1 F3818.691 +G1 X121.912 Y107.18 E.00559 +G1 F3734.854 +G1 X122.509 Y107.145 E.02024 +G1 F3593.964 +G1 X123.108 Y107.145 E.02028 +G1 F3527.92 +G1 X123.34 Y107.191 E.00801 +G1 F3494.313 +G1 X123.482 Y107.297 E.006 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.531 Y107.332 E-.01251 +G1 X123.583 Y107.371 E-.01351 +G1 X123.645 Y107.5 E-.02974 +G1 X123.708 Y107.628 E-.02965 +G1 X123.77 Y107.757 E-.02974 +G1 X123.833 Y107.628 E-.02983 +G1 X123.895 Y107.5 E-.02956 +G1 X123.958 Y107.371 E-.02983 +G1 X124.2 Y107.191 E-.06268 +G1 X124.432 Y107.145 E-.04915 +G1 X125.341 Y107.145 E-.1889 +G1 X125.341 Y106.496 E-.1349 +;WIPE_END +G1 X126.992 Y111.506 Z.492 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +G1 F2094.606 +G1 X126.923 Y111.59 E.00368 +G1 F2093.653 +G1 X125.894 Y111.587 E.03483 +;WIDTH:0.490114 +G1 F2058.783 +G1 X125.825 Y111.529 E.00335 +;WIDTH:0.530228 +G1 F2034.666 +G1 X125.757 Y111.472 E.0036 +;WIDTH:0.570342 +G1 F2041.214 +G1 X125.688 Y111.415 E.00392 +G1 F2031.807 +G1 X125.611 Y111.474 E.00425 +;WIDTH:0.530228 +G1 F2022.49 +G1 X125.533 Y111.533 E.00396 +;WIDTH:0.490114 +G1 F2045.611 +G1 X125.455 Y111.593 E.00366 +;WIDTH:0.449999 +G1 F2086.888 +G1 X124.5 Y111.595 E.03233 +G1 F2082.035 +G1 X124.376 Y111.553 E.00443 +G1 F2079.771 +G1 X124.296 Y111.392 E.00609 +G1 F2077.916 +G1 X124.296 Y111.36 E.00108 +G1 X124.075 Y111.499 E.00884 +G1 F2078.339 +G3 X123.119 Y111.661 I-.822 J-1.951 E.03311 +G1 X122.83 Y111.599 E.01 +G1 F2077.826 +G1 X122.511 Y111.445 E.01199 +G1 F2076.924 +G1 X122.25 Y111.231 E.01142 +G1 F2075.021 +G1 X122.04 Y110.873 E.01405 +G1 F2071.55 +G1 X121.923 Y110.358 E.01788 +G1 F2067.756 +G1 X121.925 Y109.838 E.0176 +G1 F2066.324 +G1 X121.918 Y109.258 E.01963 +G1 X121.914 Y108.424 E.02823 +G1 X121.914 Y107.859 E.01912 +;WIDTH:0.445651 +G1 X121.92 Y107.705 E.00516 +;WIDTH:0.449999 +G1 F2065.129 +G1 X122.047 Y107.564 E.00642 +G1 F2053.708 +G1 X122.919 Y107.552 E.02952 +G1 F2038.902 +G1 X123.215 Y107.583 E.01007 +G1 F2032.637 +G1 X123.312 Y107.756 E.00671 +G1 F2029.561 +G1 X123.31 Y109.653 E.06421 +G1 F2039.304 +G1 X123.36 Y110.297 E.02186 +G1 F2050.361 +G1 X123.462 Y110.44 E.00595 +G1 F2053.85 +G1 X123.68 Y110.5 E.00765 +G1 F2056.242 +G1 X123.937 Y110.432 E.009 +G1 F2057.168 +G2 X124.173 Y110.095 I-.209 J-.398 E.01447 +G1 F2055.374 +G1 X124.23 Y109.527 E.01932 +G1 X124.229 Y107.756 E.05995 +G1 F2056.63 +G1 X124.325 Y107.583 E.0067 +G1 F2061.159 +G1 X124.584 Y107.552 E.00883 +G1 F2072.833 +G1 X125.426 Y107.552 E.0285 +;WIDTH:0.489445 +G1 F2021.369 +G1 X125.513 Y107.62 E.0041 +;WIDTH:0.528891 +G1 F2000.384 +G1 X125.601 Y107.688 E.00449 +;WIDTH:0.568336 +G1 F2013.554 +G1 X125.688 Y107.756 E.00482 +G1 F2075.013 +G1 X125.708 Y107.581 E.0077 +;WIDTH:0.528891 +G1 F2073.15 +G1 X125.728 Y107.406 E.00712 +;WIDTH:0.489445 +G1 F2070.986 +G1 X125.748 Y107.231 E.00654 +;WIDTH:0.449999 +G1 F2082.029 +G1 X125.748 Y106.389 E.0285 +G1 F2084.859 +G3 X125.951 Y106.186 I.235 J.032 E.01043 +G1 F2080.14 +G1 X126.945 Y106.186 E.03365 +G1 F2075.896 +G1 X127.107 Y106.266 E.00612 +G1 F2073.33 +G1 X127.148 Y106.465 E.00688 +G1 F2060.762 +G1 X127.148 Y107.717 E.04238 +G1 F2052.258 +G1 X127.406 Y107.58 E.00989 +G1 F2049 +G1 X127.925 Y107.475 E.01792 +G1 F2055.739 +G1 X128.534 Y107.565 E.02084 +G1 F2065.803 +G1 X128.903 Y107.755 E.01405 +G1 F2073.168 +G1 X129.256 Y108.075 E.01613 +G1 F2079.878 +G1 X129.509 Y108.517 E.01724 +G1 F2084.128 +G1 X129.554 Y108.617 E.00371 +G1 X129.872 Y108.077 E.02121 +G1 F2086.504 +G1 X130.258 Y107.751 E.0171 +G1 F2089.781 +G1 X130.546 Y107.601 E.01099 +G1 F2093.775 +G1 X131.158 Y107.479 E.02112 +G1 F2097.383 +G1 X131.652 Y107.513 E.01676 +G1 F2098.996 +G1 X132.039 Y107.62 E.01359 +G1 F2099.354 +G3 X132.701 Y108.115 I-1.235 J2.34 E.02809 +G1 F2098.212 +G1 X133.01 Y108.688 E.02204 +G3 X133.174 Y109.862 I-3.542 J1.094 E.0403 +G1 X133.145 Y109.974 E.00392 +;WIDTH:0.498221 +G1 F2069.427 +G1 X133.058 Y110.047 E.0043 +;WIDTH:0.546442 +G1 F2040.356 +G1 X132.971 Y110.121 E.00478 +G1 F2028.487 +G1 X133.05 Y110.229 E.0056 +;WIDTH:0.498221 +G1 F2056.409 +G1 X133.129 Y110.336 E.00504 +;WIDTH:0.449999 +G1 F2096.668 +G1 X133.11 Y110.46 E.00425 +G1 F2095.175 +G1 X132.872 Y110.933 E.01792 +G1 F2093.7 +G1 X132.476 Y111.341 E.01925 +G1 F2094.505 +G1 X132.013 Y111.572 E.01751 +G1 F2093.34 +G1 X131.366 Y111.674 E.02217 +G1 X130.812 Y111.622 E.01883 +G1 X130.435 Y111.504 E.01337 +G1 F2095.225 +G1 X130.152 Y111.329 E.01126 +G1 F2097.285 +G1 X129.748 Y110.943 E.01891 +G1 F2085.124 +G1 X129.538 Y110.517 E.01608 +G1 X129.234 Y111.073 E.02145 +G1 F2096.052 +G1 X128.927 Y111.355 E.01411 +G1 F2094.716 +G1 X128.47 Y111.596 E.01749 +G1 F2093.217 +G1 X127.953 Y111.675 E.0177 +G1 X127.575 Y111.616 E.01295 +G1 F2094.487 +G1 X127.09 Y111.387 E.01815 +G1 X127.03 Y111.46 E.0032 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.992 Y111.506 E-.0124 +G1 X126.923 Y111.59 E-.02259 +G1 X125.894 Y111.587 E-.21384 +G1 X125.825 Y111.529 E-.01873 +G1 X125.757 Y111.472 E-.01844 +G1 X125.688 Y111.415 E-.0186 +G1 X125.611 Y111.474 E-.02016 +G1 X125.533 Y111.533 E-.02032 +G1 X125.455 Y111.593 E-.02045 +G1 X124.5 Y111.595 E-.19846 +G1 X124.376 Y111.553 E-.02721 +G1 X124.296 Y111.392 E-.03736 +G1 X124.296 Y111.36 E-.00665 +G1 X124.277 Y111.372 E-.00479 +;WIPE_END +G1 X123.833 Y101.467 Z.573 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.551904 +G1 F2100 +G1 X123.299 Y101.298 E.0237 +G1 X123.159 Y101.275 E.006 +;WIDTH:0.512214 +G1 X123.019 Y101.253 E.00553 +;WIDTH:0.472524 +G1 X122.879 Y101.23 E.00507 +;WIDTH:0.432834 +G1 X122.74 Y101.208 E.00456 +;WIDTH:0.442123 +G1 X122.318 Y101.259 E.01411 +;WIDTH:0.449999 +G1 X122.131 Y101.427 E.00851 +G1 X121.856 Y102.059 E.02333 +G1 X121.629 Y102.21 E.00923 +G1 X121.386 Y102.195 E.00824 +G1 X121.169 Y102.058 E.00869 +G1 X120.995 Y101.636 E.01545 +G1 X120.973 Y100.865 E.02611 +G1 X121.035 Y100.506 E.01233 +G1 X121.233 Y100.193 E.01254 +G1 X121.399 Y100.105 E.00636 +G1 X121.723 Y100.104 E.01097 +G1 X121.887 Y100.219 E.00678 +G2 X122.309 Y100.833 I1.231 J-.394 E.02558 +;WIDTH:0.442123 +G1 X122.732 Y100.866 E.01408 +;WIDTH:0.432834 +G1 X122.878 Y100.851 E.00476 +;WIDTH:0.472524 +G1 X123.024 Y100.836 E.00524 +;WIDTH:0.512214 +G1 X123.17 Y100.822 E.00572 +;WIDTH:0.551904 +G1 X123.316 Y100.807 E.00621 +G1 X123.668 Y100.723 E.01532 +G1 X123.63 Y101.038 E.01343 +G1 X123.807 Y101.413 E.01755 +G1 E-.16 +;WIPE_START +G1 F9600 +G1 X123.833 Y101.467 E-.01245 +G1 X123.299 Y101.298 E-.1164 +G1 X123.159 Y101.275 E-.02948 +G1 X123.019 Y101.253 E-.02945 +G1 X122.879 Y101.23 E-.02948 +G1 X122.74 Y101.208 E-.02925 +G1 X122.318 Y101.259 E-.08833 +G1 X122.131 Y101.427 E-.05224 +G1 X121.856 Y102.059 E-.14323 +G1 X121.629 Y102.21 E-.05666 +G1 X121.386 Y102.195 E-.05059 +G1 X121.376 Y102.189 E-.00244 +;WIPE_END +G1 X126.293 Y104.155 Z.492 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;WIDTH:0.449999 +G1 F2098.023 +G1 X125.809 Y104.304 E.01714 +G1 X125.034 Y104.307 E.02623 +G1 X124.568 Y104.173 E.01641 +G1 X124.107 Y103.871 E.01865 +G1 F2099.523 +G1 X123.809 Y103.483 E.01656 +G1 X123.724 Y103.305 E.00668 +G1 X123.346 Y103.739 E.01948 +G1 F2098.618 +G1 X122.879 Y104.045 E.0189 +G1 F2097.792 +G1 X122.386 Y104.24 E.01795 +G1 X121.623 Y104.343 E.02606 +G1 F2099.139 +G1 X120.933 Y104.276 E.02347 +G1 F2100 +G1 X120.248 Y104.043 E.02449 +G1 F2099.905 +G3 X119.406 Y103.392 I2.976 J-4.714 E.03608 +G1 X119.055 Y102.865 E.02143 +G1 F2097.666 +G1 X118.815 Y102.314 E.02034 +G1 F2096.53 +G1 X118.709 Y101.807 E.01753 +G1 F2095.966 +G1 X118.664 Y101.239 E.01929 +G1 F2096.079 +G1 X118.738 Y100.354 E.03006 +G1 F2097.004 +G1 X118.922 Y99.736 E.02183 +G1 F2098.809 +G1 X119.4 Y98.934 E.0316 +G1 F2097.144 +G1 X119.55 Y98.769 E.00755 +G1 F2094.88 +G1 X120.116 Y98.341 E.02402 +G1 F2091.478 +G1 X120.683 Y98.09 E.02099 +G1 F2089.182 +G1 X121.536 Y97.959 E.02921 +G1 F2088.779 +G1 X122.293 Y98.028 E.02573 +G1 F2090.351 +G1 X122.837 Y98.211 E.01943 +G1 F2093.451 +G1 X123.437 Y98.593 E.02408 +G1 F2095.89 +G1 X123.611 Y98.761 E.00819 +G1 F2098.211 +G1 X123.988 Y99.306 E.02243 +;WIDTH:0.453941 +G1 F2099.868 +G1 X124.21 Y99.844 E.01989 +G1 F2100 +G1 X124.212 Y99.922 E.00267 +G1 X124.538 Y99.64 E.01473 +;WIDTH:0.449999 +G1 F2099.531 +G1 X125.094 Y99.415 E.0203 +G1 X125.785 Y99.327 E.02358 +G3 X126.989 Y99.497 I.096 J3.672 E.04135 +G1 X127.411 Y99.73 E.01632 +G1 X127.695 Y100.024 E.01384 +G1 X127.695 Y98.649 E.04654 +G1 F2094.428 +M73 Q5 S20 +G1 X127.768 Y98.36 E.01009 +G1 F2092.221 +G1 X127.886 Y98.206 E.00657 +G1 F2090.443 +G1 X128.2 Y98.048 E.0119 +G1 F2089.524 +G1 X129.299 Y98.038 E.0372 +G1 X129.477 Y98.065 E.00609 +M73 P5 R20 +G1 F2090.484 +G1 X129.574 Y98.195 E.00549 +G1 X129.67 Y98.065 E.00547 +G1 F2089.823 +G1 X130.842 Y98.038 E.03968 +G1 X131.134 Y98.113 E.0102 +G1 F2091.32 +G1 X131.336 Y98.291 E.00911 +G1 F2093.165 +G1 X131.421 Y98.456 E.00628 +G1 F2094.857 +G1 X131.452 Y98.649 E.00662 +G1 F2097.961 +G1 X131.452 Y99.572 E.03124 +G1 F2099.212 +G1 X131.439 Y99.887 E.01067 +G1 F2100 +G1 X131.452 Y100.016 E.00439 +G1 X131.452 Y103.651 E.12304 +G1 X131.399 Y103.901 E.00865 +G1 X131.198 Y104.147 E.01075 +G1 X130.92 Y104.257 E.01012 +G1 F2099.692 +G1 X130.177 Y104.262 E.02515 +G1 F2098.766 +G1 X129.7 Y104.244 E.01616 +G1 X129.574 Y104.198 E.00454 +G1 F2097.868 +G1 X129.299 Y104.262 E.00956 +G1 F2096.062 +G1 X128.306 Y104.262 E.03361 +G1 X128.058 Y104.21 E.00858 +G1 X127.965 Y104.079 E.00544 +G1 X127.872 Y104.21 E.00544 +G1 X127.624 Y104.262 E.00858 +G1 X126.637 Y104.262 E.03341 +G1 F2097.263 +G1 X126.401 Y104.214 E.00815 +G1 X126.346 Y104.184 E.00212 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.293 Y104.155 E-.01256 +G1 X125.809 Y104.304 E-.10524 +G1 X125.034 Y104.307 E-.16106 +G1 X124.568 Y104.173 E-.10076 +G1 X124.107 Y103.871 E-.11453 +G1 X123.809 Y103.483 E-.10167 +G1 X123.724 Y103.305 E-.04099 +G1 X123.714 Y103.317 E-.00319 +;WIPE_END +G1 X129.426 Y103.779 Z.5 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.546075 +G1 F2013.561 +G1 X129.404 Y103.794 E.00111 +;WIDTH:0.498037 +G1 F2039.947 +G1 X129.319 Y103.854 E.00394 +;WIDTH:0.449999 +G1 F2098.158 +G1 X128.306 Y103.855 E.03429 +G1 X128.223 Y103.837 E.00287 +G1 F2039.206 +G1 X128.179 Y103.744 E.00348 +;WIDTH:0.416786 +G1 F2063.049 +G1 X128.135 Y103.651 E.0032 +;WIDTH:0.421123 +G1 F2069.946 +G1 X128.117 Y103.3 E.01105 +;WIDTH:0.458673 +G1 F2070.527 +G1 X128.098 Y102.949 E.01215 +;WIDTH:0.496223 +G1 F2072.757 +G1 X128.079 Y102.598 E.01325 +G1 F2099.082 +G1 X128.08 Y101.012 E.05978 +;WIDTH:0.494958 +G1 F2067.299 +G1 X128.102 Y100.362 E.02445 +;WIDTH:0.449999 +G1 F2099.984 +G1 X128.102 Y98.956 E.04759 +G1 F2096.475 +G1 X128.126 Y98.553 E.01367 +G1 F2094.392 +G1 X128.271 Y98.449 E.00604 +G1 F2093.917 +G1 X129.358 Y98.454 E.03679 +;WIDTH:0.497338 +G1 F2050.966 +G1 X129.43 Y98.519 E.00367 +;WIDTH:0.544676 +G1 F2023.94 +G1 X129.502 Y98.584 E.00405 +;WIDTH:0.592014 +G1 F2032.261 +G1 X129.574 Y98.649 E.00443 +G1 F2022.447 +G1 X129.655 Y98.582 E.0048 +;WIDTH:0.544676 +G1 F2010.935 +G1 X129.736 Y98.515 E.00439 +;WIDTH:0.497338 +G1 F2036.723 +G1 X129.818 Y98.448 E.004 +;WIDTH:0.449999 +G1 F2094.271 +G1 X130.842 Y98.446 E.03466 +G1 X131.007 Y98.53 E.00627 +G1 F2095.174 +G1 X131.045 Y98.649 E.00423 +G1 F2098.586 +G1 X131.045 Y99.572 E.03124 +G1 F2099.85 +G1 X131.029 Y99.652 E.00276 +;WIDTH:0.487144 +G1 F2082.912 +G1 X130.842 Y99.794 E.00867 +G1 F2079.39 +G1 X131.041 Y99.973 E.00989 +;WIDTH:0.449999 +G1 F2099.572 +G1 X131.045 Y103.651 E.1245 +G3 X130.842 Y103.855 I-.235 J-.031 E.01047 +G1 X129.848 Y103.855 E.03365 +G1 X129.757 Y103.833 E.00317 +;WIDTH:0.498037 +G1 F2072.647 +G1 X129.696 Y103.781 E.00303 +;WIDTH:0.546075 +G1 F2043.385 +G1 X129.635 Y103.728 E.00338 +;WIDTH:0.594112 +G1 F2048.535 +G1 X129.574 Y103.675 E.0037 +G1 F2025.863 +G1 X129.489 Y103.735 E.00477 +;WIDTH:0.546075 +G1 F2013.561 +G1 X129.475 Y103.745 E.00072 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.426 Y103.779 E-.01239 +G1 X129.404 Y103.794 E-.00553 +G1 X129.319 Y103.854 E-.02162 +G1 X128.306 Y103.855 E-.21051 +G1 X128.223 Y103.837 E-.01765 +G1 X128.179 Y103.744 E-.02138 +G1 X128.135 Y103.651 E-.02138 +G1 X128.117 Y103.3 E-.07304 +G1 X128.098 Y102.949 E-.07305 +G1 X128.079 Y102.598 E-.07305 +G1 X128.079 Y102.067 E-.1104 +;WIPE_END +G1 X134.368 Y95.632 Z.557 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 F3422.326 +G1 X115.632 Y114.368 E.63419 +G1 F3406.106 +G1 X115.632 Y110.169 E.14213 +;WIDTH:0.495928 +G1 F3395.487 +G1 X115.655 Y110.039 E.00497 +;WIDTH:0.541856 +G1 F3390.355 +G1 X115.678 Y109.91 E.00544 +;WIDTH:0.587785 +G1 F3386.012 +G1 X115.701 Y109.781 E.00594 +;WIDTH:0.633713 +G1 F3382.29 +G1 X115.724 Y109.652 E.00644 +G1 F3383.577 +G1 X115.71 Y108.858 E.03901 +;WIDTH:0.642604 +G1 F3382.286 +G1 X115.728 Y108.059 E.03985 +G1 F3381.573 +G1 X115.705 Y107.963 E.00492 +;WIDTH:0.595846 +G1 F3385.257 +G1 X115.682 Y107.866 E.00458 +;WIDTH:0.549088 +G1 F3389.557 +G1 X115.658 Y107.77 E.00416 +;WIDTH:0.50233 +G1 F3394.644 +G1 X115.635 Y107.673 E.00381 +;WIDTH:0.455572 +G1 F3404.707 +G1 X115.632 Y107.473 E.00686 +;WIDTH:0.449999 +G1 F3406.106 +G1 X115.632 Y95.632 E.4008 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2192.981 +G1 X134.775 Y95.9 E.02285 +G1 F2399.997 +G1 X134.775 Y114.1 E.61605 +G1 F2178.214 +G1 X134.775 Y114.775 E.02285 +G1 X134.1 Y114.775 E.02285 +G1 F2202.093 +G1 X115.9 Y114.775 E.61605 +G1 F2180.794 +G1 X115.225 Y114.775 E.02285 +G1 F2177.391 +G1 X115.225 Y114.1 E.02285 +G1 F2197.965 +G1 X115.225 Y95.9 E.61605 +G1 F2229.567 +G1 X115.225 Y95.225 E.02285 +G1 F2197.965 +G1 X134.1 Y95.225 E.6389 +G1 F2181.066 +G1 X134.715 Y95.225 E.02082 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y95.9 E-.14027 +G1 X134.775 Y98.245 E-.48726 +;WIPE_END +G1 X121.572 Y100.489 Z.634 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.514337 +G1 F7636.551 +G1 X121.775 Y100.879 E.01723 +G1 X121.96 Y101.054 E.00998 +G1 X121.779 Y101.245 E.01031 +G1 X121.535 Y101.807 E.02402 +G1 X121.513 Y101.81 E.00087 +G1 X121.392 Y101.56 E.01089 +G1 X121.371 Y100.838 E.02831 +G1 X121.446 Y100.615 E.00922 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.371 Y100.838 E-.04889 +G1 X121.392 Y101.56 E-.1501 +G1 X121.513 Y101.81 E-.05772 +G1 X121.535 Y101.807 E-.00461 +G1 X121.779 Y101.245 E-.12732 +G1 X121.96 Y101.054 E-.05468 +G1 X121.775 Y100.879 E-.05292 +G1 X121.572 Y100.489 E-.09137 +;WIPE_END +G1 E-.05239 F2100 +G1 X116.109 Y96.031 Z.523 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.548766 +G1 F7116.79 +G2 X116.093 Y96.093 I-.058 J.018 E.01318 +;WIDTH:0.449999 +G1 F8843.491 +G1 X116.743 Y95.998 E.02224 +G1 X115.932 Y96.815 E.03897 +;WIDTH:0.41646 +G1 F9637.536 +G1 X115.866 Y96.888 E.00306 +;WIDTH:0.38292 +G1 F10588.235 +G1 X115.795 Y96.869 E.00208 +G2 X115.855 Y96.929 I.04 J.02 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y97.318 E.01403 +G1 X117.318 Y95.998 E.06319 +G1 X117.894 Y95.998 E.0195 +G1 X115.998 Y97.894 E.09076 +G1 X115.998 Y98.47 E.0195 +G1 X118.47 Y95.998 E.11833 +G1 X119.046 Y95.998 E.0195 +G1 X115.998 Y99.046 E.14591 +G1 X115.998 Y99.621 E.01946 +G1 X119.621 Y95.998 E.17343 +G1 X120.197 Y95.998 E.0195 +G1 X115.998 Y100.197 E.201 +G1 X115.998 Y100.773 E.0195 +G1 X120.773 Y95.998 E.22858 +G1 X121.348 Y95.998 E.01946 +G1 X115.998 Y101.348 E.2561 +G1 X115.959 Y101.487 E.00489 +;WIDTH:0.41646 +G1 F9637.536 +G1 X115.92 Y101.625 E.00445 +;WIDTH:0.38292 +G1 F10588.235 +G1 X115.849 Y101.606 E.00208 +G2 X115.909 Y101.666 I.04 J.02 E.00315 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X115.849 Y101.606 I-.02 J-.04 E-.02315 +G1 X115.92 Y101.625 E-.01527 +G1 X115.959 Y101.487 E-.0298 +G1 X115.998 Y101.348 E-.03 +G1 X117.842 Y99.505 E-.54178 +;WIPE_END +G1 X120.232 Y104.901 Z.503 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.545046 +G1 F7169.522 +G1 X120.075 Y104.811 E.00756 +;WIDTH:0.50168 +G1 F7847.24 +G1 X119.919 Y104.722 E.00685 +;WIDTH:0.458315 +G1 F8666.461 +G1 X119.762 Y104.632 E.00625 +;WIDTH:0.414949 +G1 F9676.665 +G1 X119.659 Y104.572 E.00369 +G1 X119.279 Y104.952 E.01662 +;WIDTH:0.452787 +G1 F8783.348 +G1 X119.546 Y104.965 E.00911 +;WIDTH:0.490624 +G1 F8041.027 +G1 X119.813 Y104.978 E.00995 +;WIDTH:0.528462 +G1 F7414.402 +G1 X120.08 Y104.991 E.01079 +;WIDTH:0.566299 +G1 F6878.38 +G1 X120.347 Y105.004 E.01163 +;WIDTH:0.59395 +G1 F6533.22 +G1 X120.409 Y105.001 E.00284 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.347 Y105.004 E-.0129 +G1 X120.08 Y104.991 E-.05555 +G1 X119.813 Y104.978 E-.05555 +G1 X119.546 Y104.965 E-.05555 +G1 X119.279 Y104.952 E-.05555 +G1 X119.659 Y104.572 E-.11168 +G1 X119.762 Y104.632 E-.02477 +G1 X119.919 Y104.722 E-.03761 +G1 X120.075 Y104.811 E-.03732 +G1 X120.232 Y104.901 E-.03761 +;WIPE_END +G1 E-.15591 F2100 +G1 X123.453 Y105.061 Z.456 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G3 X121.935 Y105.486 I-1.704 J-3.167 E.05379 +G1 X122.379 Y105.969 E.02221 +G1 X123.295 Y105.984 E.03101 +G1 X123.767 Y106.102 E.01647 +G3 X124.212 Y105.985 I.794 J2.105 E.0156 +G3 X124.481 Y105.386 I2.336 J.688 E.02229 +G3 X123.634 Y104.969 I.728 J-2.548 E.03213 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X124.481 Y105.386 I1.575 J-2.131 E-.19725 +G2 X124.212 Y105.985 I2.067 J1.288 E-.13687 +G2 X123.767 Y106.102 I.346 J2.222 E-.09579 +G1 X123.295 Y105.984 E-.10111 +G1 X122.771 Y105.975 E-.10898 +;WIPE_END +G1 X125.479 Y105.071 Z.45 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G3 X124.24 Y104.873 I-.221 J-2.591 E.04289 +G1 X123.632 Y104.5 E.02414 +G3 X122.443 Y105.018 I-2.541 J-4.208 E.04403 +G3 X121.373 Y105.108 I-1.427 J-10.52 E.03636 +;WIDTH:0.49584 +G1 F7948.432 +G1 X121.014 Y105.081 E.01356 +;WIDTH:0.54168 +G1 F7217.901 +G1 X120.655 Y105.053 E.01493 +;WIDTH:0.585366 +G1 F6636.605 +G1 X120.528 Y105.045 E.00574 +G1 X120.672 Y105.132 E.00759 +;WIDTH:0.540244 +G1 F7238.746 +G1 X120.817 Y105.22 E.00701 +;WIDTH:0.495122 +G1 F7961.055 +G1 X120.961 Y105.307 E.00633 +;WIDTH:0.449999 +G1 F8843.491 +G3 X121.675 Y105.795 I-3.835 J6.386 E.02929 +G1 X122.183 Y106.373 E.02605 +G1 X123.288 Y106.391 E.03741 +G1 X123.797 Y106.532 E.01788 +G3 X124.586 Y106.372 I.745 J1.647 E.02748 +G1 X124.619 Y106.017 E.01207 +G1 X124.856 Y105.548 E.01779 +G1 X125.207 Y105.234 E.01594 +G1 X125.445 Y105.15 E.00854 +;WIDTH:0.41686 +G1 F9627.226 +G1 X125.682 Y105.066 E.00782 +;WIDTH:0.419087 +G1 F9570.227 +G1 X125.882 Y105.039 E.00631 +;WIDTH:0.454454 +G1 F8747.772 +G1 X126.082 Y105.012 E.00691 +;WIDTH:0.48982 +G1 F8055.493 +G3 X126.61 Y105.003 I.295 J1.772 E.01969 +;WIDTH:0.454782 +G1 F8740.791 +G1 X126.94 Y105.02 E.01132 +;WIDTH:0.437552 +G1 F9122.422 +G1 X127.085 Y105.029 E.00477 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.94 Y105.02 E-.03019 +G1 X126.61 Y105.003 E-.06867 +G2 X126.082 Y105.012 I-.234 J1.781 E-.11014 +G1 X125.882 Y105.039 E-.04194 +G1 X125.682 Y105.066 E-.04194 +G1 X125.445 Y105.15 E-.05225 +G1 X125.207 Y105.234 E-.05245 +G1 X124.856 Y105.548 E-.09787 +G1 X124.619 Y106.017 E-.1092 +G1 X124.603 Y106.186 E-.03535 +;WIPE_END +G1 X129.42 Y105.01 Z.487 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G3 X127.912 Y104.981 I-.616 J-7.058 E.05115 +G3 X127.085 Y105.029 I-.57 J-2.649 E.02815 +G1 X127.59 Y105.166 E.01771 +G1 X128.048 Y105.555 E.02034 +G3 X128.355 Y106.337 I-1.997 J1.234 E.02859 +G1 X128.801 Y106.392 E.01521 +G1 X130.16 Y105.034 E.06503 +G1 X129.624 Y105.003 E.01817 +G1 X129.207 Y105.429 E.02018 +;WIDTH:0.423783 +G1 F9452.218 +G1 X128.665 Y105.971 E.02427 +G2 X128.433 Y105.429 I-2.089 J.576 E.01873 +G1 X129.003 Y105.429 E.01805 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.433 Y105.429 E-.11845 +G3 X128.665 Y105.971 I-1.858 J1.116 E-.1229 +G1 X129.207 Y105.429 E-.15929 +G1 X129.624 Y105.003 E-.12388 +G1 X130.16 Y105.034 E-.11157 +G1 X130.147 Y105.047 E-.00391 +;WIPE_END +G1 X123.679 Y105.517 Z.513 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.60766 +G1 F6374.615 +G2 X123.661 Y105.585 I-.065 J.019 E.0165 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X123.679 Y105.517 I-.047 J-.049 E-.073 +;WIPE_END +G1 E-.567 F2100 +G1 X129.4 Y111.959 Z.55 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G3 X128.097 Y112.438 I-1.454 J-1.944 E.04766 +G3 X127.215 Y112.291 I-.086 J-2.194 E.03048 +G1 X126.874 Y112.369 E.01184 +G3 X125.57 Y112.349 I-.385 J-18.212 E.04415 +G1 X125.42 Y112.369 E.00512 +G1 X124.341 Y112.352 E.03653 +G1 X124.151 Y112.284 E.00683 +G3 X123.12 Y112.434 I-.87 J-2.355 E.03552 +G3 X122.109 Y112.107 I.309 J-2.677 E.0362 +G1 X121.683 Y111.756 E.01868 +G1 X121.496 Y111.492 E.01095 +G1 X121.107 Y111.892 E.01889 +G3 X120.095 Y112.352 I-2.639 J-4.469 E.0377 +G1 X119.264 Y112.451 E.02833 +G3 X118.041 Y112.237 I.202 J-4.744 E.04215 +G3 X116.785 Y111.333 I1.43 J-3.31 E.05279 +G3 X116.169 Y110.208 I3.747 J-2.783 E.04355 +G1 X116.062 Y110.06 E.00618 +G1 X115.998 Y110.244 E.00659 +G1 X115.998 Y114.002 E.1272 +G1 X130.547 Y114.002 E.49247 +G1 X130.547 Y113.858 E.00487 +G1 X132.056 Y112.348 E.07226 +G1 X131.489 Y112.438 E.01943 +G3 X130.24 Y112.259 I-.074 J-3.938 E.04289 +G3 X129.558 Y111.831 I1.019 J-2.381 E.02737 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X130.24 Y112.259 I1.701 J-1.953 E-.16801 +G2 X131.489 Y112.438 I1.176 J-3.758 E-.26334 +G1 X132.056 Y112.348 E-.1193 +G1 X131.752 Y112.652 E-.08935 +;WIPE_END +G1 X130.979 Y114.002 Z.427 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X134.002 Y110.979 E.14471 +G1 X134.002 Y114.002 E.10232 +G1 X131.554 Y114.002 E.08286 +G1 X133.858 Y111.698 E.11029 +M204 P1000 +G1 X133.858 Y111.698 F12000 +G1 X133.541 Y112.665 +M204 P1500 +;WIDTH:0.556149 +G1 F7014.413 +G1 X133.541 Y113.541 E.03738 +G1 X132.665 Y113.541 E.03738 +G1 X133.397 Y112.809 E.04418 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.665 Y113.541 E-.21513 +G1 X133.541 Y113.541 E-.18204 +G1 X133.541 Y112.665 E-.18204 +;WIPE_END +G1 E-.06079 F2100 +G1 X120.25 Y113.17 Z.632 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.4855 +G1 F8134.123 +G1 X120.194 Y113.174 E.00207 +;WIDTH:0.477254 +G1 F8288.552 +G1 X119.839 Y113.191 E.01284 +;WIDTH:0.443664 +G1 F8983.291 +G1 X119.485 Y113.207 E.01181 +;WIDTH:0.410074 +G1 F9805.15 +G1 X119.13 Y113.224 E.01085 +;WIDTH:0.413851 +G1 F9705.308 +G1 X118.797 Y113.205 E.01029 +;WIDTH:0.451218 +G1 F8817.089 +G1 X118.464 Y113.187 E.01132 +;WIDTH:0.496656 +G1 F7934.129 +G2 X116.836 Y113.164 I-1.204 J27.971 E.06144 +G1 X116.836 Y112.494 E.02528 +G2 X117.861 Y113.06 I2.47 J-3.266 E.04433 +G1 X118.265 Y113.145 E.01558 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.861 Y113.06 E-.08579 +G3 X116.836 Y112.494 I1.447 J-3.831 E-.24416 +G1 X116.836 Y113.164 E-.13923 +G3 X117.658 Y113.164 I.423 J27.994 E-.17082 +;WIPE_END +G1 X121.27 Y112.751 Z.463 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.501827 +G1 F7844.727 +G3 X120.25 Y113.17 I-2.044 J-3.53 E.04221 +G1 X122.399 Y113.161 E.082 +G1 X122.681 Y113.186 E.0108 +;WIDTH:0.453165 +G1 F8775.255 +G1 X122.963 Y113.21 E.00965 +G1 X122.682 Y113.178 E.00965 +;WIDTH:0.501827 +G1 F7844.727 +G1 X122.4 Y113.146 E.01083 +G1 X121.741 Y112.862 E.02738 +G1 X121.452 Y112.659 E.01348 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.741 Y112.862 E-.07339 +G1 X122.4 Y113.146 E-.14912 +G1 X122.682 Y113.178 E-.05898 +G1 X122.963 Y113.21 E-.05877 +G1 X122.681 Y113.186 E-.05881 +M73 Q6 S20 +G1 X122.399 Y113.161 E-.05883 +M73 P6 R20 +G1 X121.523 Y113.165 E-.1821 +;WIPE_END +G1 X122.963 Y113.21 Z.425 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.404502 +G1 F9956.247 +G1 X123.671 Y113.213 E.02129 +;WIDTH:0.435193 +G1 F9177.282 +G1 X124 Y113.195 E.01075 +;WIDTH:0.471236 +G1 F8405.009 +G3 X125.407 Y113.185 I.828 J17.777 E.05012 +;WIDTH:0.454826 +G1 F8739.859 +G1 X125.954 Y113.185 E.01873 +;WIDTH:0.45471 +G1 F8742.32 +G1 X127.499 Y113.201 E.0529 +;WIDTH:0.422486 +G1 F9484.517 +G1 X128.046 Y113.219 E.01727 +;WIDTH:0.43145 +G1 F9265.694 +G1 X128.392 Y113.197 E.0112 +;WIDTH:0.476114 +G1 F8310.364 +G1 X128.737 Y113.174 E.01245 +;WIDTH:0.477182 +G1 F8289.926 +G1 X128.744 Y113.174 E.00025 +;WIDTH:0.449999 +G1 F8843.491 +G1 X129.377 Y112.889 E.0235 +;WIDTH:0.451806 +G1 F8804.41 +G1 X128.905 Y113.118 E.01784 +;WIDTH:0.477182 +G1 F8289.926 +G1 X128.744 Y113.174 E.00616 +G1 X129.059 Y113.188 E.01139 +;WIDTH:0.448163 +G1 F8883.57 +G1 X129.374 Y113.203 E.01063 +;WIDTH:0.419143 +G1 F9568.792 +G1 X130.072 Y113.203 E.02184 +G1 X130.196 Y113.079 E.00549 +G3 X129.56 Y112.8 I.75 J-2.575 E.02179 +;WIDTH:0.449999 +G1 F8843.491 +G1 X129.386 Y112.455 E.01308 +G3 X127.222 Y112.726 I-1.387 J-2.31 E.076 +G3 X125.954 Y112.776 I-.884 J-6.327 E.04302 +G2 X125.414 Y112.776 I-.27 J1.873 E.01834 +G1 X124.334 Y112.759 E.03656 +G1 X124.125 Y112.716 E.00722 +G3 X123.16 Y112.846 I-.844 J-2.629 E.03313 +G1 X122.486 Y112.722 E.0232 +G1 X121.93 Y112.473 E.02062 +G1 X121.489 Y112.124 E.01904 +G1 X120.557 Y112.626 E.03583 +G1 X120.144 Y112.756 E.01466 +G1 X119.312 Y112.856 E.02836 +G3 X117.973 Y112.644 I.156 J-5.335 E.04601 +G1 X117.407 Y112.376 E.0212 +G1 X116.751 Y111.887 E.0277 +G1 X116.406 Y111.497 E.01762 +G1 X116.406 Y113.594 E.07098 +G1 X130.259 Y113.594 E.46891 +G1 X131.01 Y112.818 E.03655 +G3 X129.565 Y112.358 I.121 J-2.88 E.05194 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X131.01 Y112.818 I1.566 J-2.42 E-.31889 +G1 X130.259 Y113.594 E-.22442 +G1 X129.794 Y113.594 E-.09669 +;WIPE_END +G1 X119.806 Y98.117 Z.722 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X121.924 Y95.998 E.10141 +G1 X122.5 Y95.998 E.0195 +G1 X120.813 Y97.685 E.08076 +G1 X120.69 Y97.765 E.00497 +;WIDTH:0.41646 +G1 F9637.536 +G1 X120.566 Y97.846 E.0046 +;WIDTH:0.38292 +G1 F10588.235 +G1 X120.495 Y97.827 E.00208 +G2 X120.555 Y97.887 I.04 J.02 E.00315 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X120.495 Y97.827 I-.02 J-.04 E-.02315 +G1 X120.566 Y97.846 E-.01527 +G1 X120.69 Y97.765 E-.03078 +G1 X120.813 Y97.685 E-.03049 +G1 X122.5 Y95.998 E-.49579 +G1 X122.286 Y95.998 E-.04452 +;WIPE_END +G1 X123.078 Y96.552 Z.417 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X122.389 Y97.261 E.03346 +G1 X123.123 Y97.496 E.02609 +G1 X123.949 Y98.004 E.03282 +G1 X125.547 Y96.406 E.0765 +G1 X123.219 Y96.406 E.0788 +G1 X122.889 Y96.185 E.01344 +G1 X121.457 Y97.617 E.06855 +G3 X122.332 Y97.663 I.28 J3.002 E.02976 +G1 X122.992 Y97.882 E.02354 +G1 X123.631 Y98.282 E.02552 +G1 X123.937 Y98.591 E.01472 +G1 X126.53 Y95.998 E.12413 +G1 X122.932 Y95.998 E.12179 +M204 P1000 +G1 X122.932 Y95.998 F12000 +G1 X123.432 Y96.858 +M204 P1500 +;WIDTH:0.541478 +G1 F7220.825 +G1 X124.454 Y96.858 E.04237 +G1 X123.886 Y97.425 E.03327 +G1 X123.234 Y97.056 E.03106 +G1 X123.288 Y97.002 E.00317 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.234 Y97.056 E-.01587 +G1 X123.886 Y97.425 E-.15569 +G1 X124.454 Y96.858 E-.16678 +G1 X123.432 Y96.858 E-.21238 +;WIPE_END +G1 E-.08928 F2100 +G1 X124.173 Y98.93 Z.438 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X127.105 Y95.998 E.14035 +;WIDTH:0.494 +G1 F7980.85 +G1 X127.157 Y95.968 E.00225 +;WIDTH:0.538001 +G1 F7271.544 +G1 X127.209 Y95.937 E.00249 +;WIDTH:0.582002 +G1 F6678.028 +G1 X127.261 Y95.907 E.00269 +;WIDTH:0.626002 +G1 F6174.088 +G1 X127.313 Y95.876 E.00294 +G3 X127.332 Y95.806 I-.048 J-.05 E.01759 +;WIDTH:0.449999 +G1 F8843.491 +G1 X127.681 Y95.998 E.01348 +G1 X124.391 Y99.289 E.15751 +G1 X124.987 Y99.209 E.02035 +;WIDTH:0.38292 +G1 F10588.235 +G3 X124.927 Y99.149 I-.02 J-.04 E.00315 +G1 X124.998 Y99.168 E.00208 +;WIDTH:0.41646 +G1 F9637.536 +G1 X125.12 Y99.091 E.00448 +;WIDTH:0.449999 +G1 F8843.491 +G1 X125.242 Y99.013 E.0049 +G1 X128.257 Y95.998 E.14433 +G1 X128.832 Y95.998 E.01946 +G1 X125.871 Y98.96 E.14177 +G1 X126.421 Y98.986 E.01864 +G1 X129.408 Y95.998 E.14301 +G1 X129.984 Y95.998 E.0195 +G1 X128.299 Y97.683 E.08066 +G1 X128.155 Y97.755 E.00545 +;WIDTH:0.41646 +G1 F9637.536 +G1 X128.012 Y97.828 E.00499 +;WIDTH:0.38292 +G1 F10588.235 +G1 X127.725 Y98.054 E.01033 +;WIDTH:0.449999 +G1 F8843.491 +G1 X127.365 Y98.58 E.02158 +;WIDTH:0.397289 +G1 F10158.903 +G1 X127.355 Y99.313 E.0216 +G2 X126.837 Y99.108 I-.807 J1.28 E.01651 +G1 X127.221 Y98.724 E.016 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.837 Y99.108 E-.11285 +G3 X127.355 Y99.313 I-.288 J1.486 E-.11643 +G1 X127.365 Y98.58 E-.15234 +G1 X127.725 Y98.054 E-.13246 +G1 X128.012 Y97.828 E-.07591 +G1 X128.155 Y97.755 E-.03337 +G1 X128.227 Y97.719 E-.01664 +;WIPE_END +G1 X128.755 Y97.881 Z.41 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G3 X128.695 Y97.821 I-.02 J-.04 E.00315 +G1 X128.766 Y97.84 E.00208 +;WIDTH:0.41646 +G1 F9637.536 +G1 X128.824 Y97.758 E.00312 +;WIDTH:0.449999 +G1 F8843.491 +G1 X128.881 Y97.677 E.00335 +G1 X130.559 Y95.998 E.08035 +G1 X134.002 Y95.998 E.11654 +G1 X134.002 Y101.911 E.20015 +G1 X133.858 Y101.911 E.00487 +G1 X131.697 Y104.088 E.10383 +G1 X131.819 Y103.65 E.01539 +G1 X131.819 Y100.019 E.1229 +G3 X131.819 Y99.568 I.935 J-.225 E.01541 +G1 X131.819 Y98.652 E.03101 +G2 X131.649 Y98.104 I-1.213 J.074 E.01961 +G1 X131.347 Y97.819 E.01406 +G1 X130.837 Y97.672 E.01797 +G1 X129.43 Y97.704 E.04764 +G1 X130.949 Y96.185 E.07271 +G1 X131.279 Y96.406 E.01344 +G1 X133.594 Y96.406 E.07836 +G1 X133.594 Y101.624 E.17662 +G1 X133.57 Y101.624 E.00081 +G1 X132.226 Y102.967 E.06431 +G1 X132.226 Y98.652 E.14606 +G1 X132.178 Y98.307 E.01179 +G1 X131.962 Y97.838 E.01748 +G2 X131.376 Y97.383 I-1.413 J1.217 E.02528 +G2 X130.444 Y97.265 I-.801 J2.582 E.03196 +G1 X131.139 Y96.554 E.03365 +G1 X131.472 Y96.813 E.01428 +G1 X133.187 Y96.813 E.05805 +G1 X133.187 Y97.535 E.02444 +;WIDTH:0.484098 +G1 F8159.972 +G1 X133.17 Y97.754 E.00806 +;WIDTH:0.518197 +G1 F7574.53 +G1 X133.153 Y97.974 E.00872 +;WIDTH:0.552296 +G1 F7067.471 +G2 X133.151 Y98.652 I4.154 J.356 E.02875 +;WIDTH:0.523679 +G1 F7488.159 +G1 X133.151 Y101.415 E.11045 +G1 X132.67 Y101.896 E.02719 +G1 X132.67 Y98.652 E.12968 +;WIDTH:0.552296 +G1 F7067.471 +G1 X132.63 Y98.234 E.01779 +G1 X132.517 Y98.022 E.01018 +;WIDTH:0.518197 +G1 F7574.53 +G1 X132.405 Y97.81 E.00948 +;WIDTH:0.484098 +G1 F8159.972 +G1 X132.292 Y97.599 E.00878 +;WIDTH:0.449999 +G1 F8843.491 +G2 X131.486 Y96.991 I-1.764 J1.501 E.03445 +G1 X131.339 Y96.954 E.00513 +M204 P1000 +G1 X131.339 Y96.954 F12000 +G1 X132.744 Y97.359 +M204 P1500 +;WIDTH:0.626002 +G1 F6174.088 +G3 X132.763 Y97.289 I-.048 J-.05 E.01759 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X132.744 Y97.359 I-.067 J.019 E-.0754 +;WIPE_END +G1 E-.5646 F2100 +G1 X119.518 Y98.375 Z.632 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.408308 +G1 F9852.54 +G1 X119.168 Y98.698 E.01447 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.052 Y98.842 E.00574 +;WIDTH:0.449999 +G1 F8843.491 +G1 X118.936 Y98.986 E.00626 +G1 X115.998 Y101.924 E.14064 +G1 X115.998 Y102.5 E.0195 +G1 X118.43 Y100.068 E.11642 +G1 X118.533 Y99.939 E.00559 +;WIDTH:0.41646 +G1 F9637.536 +G1 X118.636 Y99.81 E.00513 +;WIDTH:0.38292 +G1 F10588.235 +G3 X118.576 Y99.75 I-.02 J-.04 E.00315 +G1 X118.647 Y99.769 E.00208 +M204 P1000 +G1 X118.647 Y99.769 F12000 +G1 X118.34 Y100.734 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y103.075 E.11209 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.176 Y100.898 E-.64 +;WIPE_END +G1 X118.161 Y101.488 Z.41 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X116.142 Y103.507 E.09665 +G1 X115.998 Y103.507 E.00487 +G1 X115.998 Y105.954 E.08283 +G1 X118.801 Y103.152 E.13415 +G1 X118.568 Y102.655 E.01858 +G3 X118.35 Y101.878 I2.493 J-1.119 E.02742 +G1 X118.305 Y101.344 E.01814 +M204 P1000 +G1 X118.305 Y101.344 F12000 +G1 X118.005 Y102.22 +M204 P1500 +G1 F8843.491 +G2 X118.305 Y103.071 I3.202 J-.649 E.03064 +G1 X116.406 Y104.971 E.09093 +G1 X116.43 Y103.795 E.03981 +G1 X117.861 Y102.364 E.0685 +G1 X117.821 Y102.98 E.02089 +G1 X116.813 Y103.988 E.04825 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.821 Y102.98 E-.29624 +G1 X117.861 Y102.364 E-.12828 +G1 X117.128 Y103.097 E-.21548 +;WIPE_END +G1 X119.04 Y103.489 Z.434 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X115.998 Y106.53 E.14559 +G1 X115.998 Y107.105 E.01946 +G1 X119.301 Y103.803 E.15809 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.123 Y105.981 E-.64 +;WIPE_END +G1 X131.207 Y104.503 Z.647 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X130.941 Y104.622 E.00986 +G1 X129.851 Y104.628 E.0369 +G2 X129.297 Y104.628 I-.277 J.9 E.01903 +G1 X128.308 Y104.628 E.03348 +G1 X127.965 Y104.547 E.01193 +G1 X127.622 Y104.628 E.01193 +G1 X126.64 Y104.628 E.03324 +G1 X126.281 Y104.545 E.01247 +G1 X125.714 Y104.686 E.01978 +G1 X124.844 Y104.649 E.02948 +G3 X123.883 Y104.159 I.891 J-2.935 E.0367 +G1 X123.679 Y103.914 E.01079 +G3 X122.913 Y104.431 I-1.769 J-1.792 E.03146 +G1 X122.314 Y104.632 E.02139 +G1 X121.43 Y104.705 E.03002 +G1 X120.718 Y104.604 E.02434 +G1 X119.913 Y104.273 E.02946 +G1 X119.623 Y104.056 E.01226 +G1 X118.168 Y105.512 E.06967 +G1 X118.475 Y105.426 E.01079 +G1 X119.312 Y105.339 E.02848 +G1 X120.284 Y105.465 E.03318 +G3 X121.334 Y106.034 I-1.316 J3.683 E.04058 +G3 X121.958 Y106.803 I-3.118 J3.167 E.03359 +G3 X123.281 Y106.798 I.72 J16.34 E.04479 +G3 X123.77 Y107.052 I-.266 J1.111 E.01884 +G3 X124.214 Y106.805 I.73 J.79 E.01736 +G1 X124.981 Y106.779 E.02598 +G1 X124.999 Y106.189 E.01998 +G1 X125.167 Y105.811 E.014 +G1 X125.38 Y105.603 E.01008 +G1 X125.779 Y105.429 E.01473 +G1 X126.281 Y105.412 E.017 +G1 X126.94 Y105.412 E.02231 +G1 X127.441 Y105.552 E.01761 +G1 X127.742 Y105.829 E.01385 +G1 X127.887 Y106.139 E.01158 +G1 X127.922 Y106.716 E.01957 +G3 X128.738 Y106.823 I-.117 J4.067 E.0279 +G1 X128.881 Y106.888 E.00532 +G1 X131.072 Y104.697 E.10488 +G1 X131.233 Y104.558 E.0072 +;WIDTH:0.418994 +G1 F9572.596 +G1 X131.393 Y104.42 E.00661 +;WIDTH:0.427728 +G1 F9355.315 +G1 X131.697 Y104.088 E.0144 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.393 Y104.42 E-.09355 +G1 X131.233 Y104.558 E-.04391 +G1 X131.072 Y104.697 E-.0442 +G1 X129.512 Y106.257 E-.45834 +;WIPE_END +G1 X134.002 Y102.343 Z.504 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X129.271 Y107.074 E.22647 +G1 X129.751 Y107.169 E.01656 +G1 X134.002 Y102.919 E.20347 +G1 X134.002 Y103.495 E.0195 +G1 X130.707 Y106.79 E.15773 +G1 X130.583 Y106.861 E.00484 +;WIDTH:0.41646 +G1 F9637.536 +G1 X130.46 Y106.933 E.00443 +;WIDTH:0.38292 +G1 F10588.235 +G1 X130.389 Y106.914 E.00208 +G2 X130.449 Y106.974 I.04 J.02 E.00315 +M204 P1000 +G1 X130.449 Y106.974 F12000 +G1 X131.355 Y106.717 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X134.002 Y104.07 E.12671 +G1 X134.002 Y104.646 E.0195 +G1 X131.874 Y106.774 E.10187 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y104.646 E-.6254 +G1 X134.002 Y104.576 E-.0146 +;WIPE_END +G1 X133.858 Y105.366 Z.414 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X132.311 Y106.913 E.07405 +G3 X133.387 Y107.758 I-.984 J2.359 E.04688 +G1 X133.767 Y108.523 E.02891 +;WIDTH:0.48884 +G1 F8073.206 +G1 X133.864 Y108.876 E.01357 +;WIDTH:0.52768 +G1 F7426.355 +G1 X133.961 Y109.229 E.01476 +;WIDTH:0.529176 +G1 F7403.508 +G1 X133.982 Y108.893 E.01361 +;WIDTH:0.489588 +G1 F8059.687 +G1 X134.002 Y108.55 E.01276 +;WIDTH:0.449999 +G1 F8843.491 +G1 X134.002 Y105.222 E.11265 +M204 P1000 +G1 X134.002 Y105.222 F12000 +G1 X133.575 Y106.251 +M204 P1500 +;WIDTH:0.488786 +G1 F8074.175 +G1 X133.575 Y107.307 E.03915 +G2 X133.016 Y106.81 I-2.341 J2.068 E.0278 +G1 X133.431 Y106.395 E.02176 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.016 Y106.81 E-.12196 +G3 X133.575 Y107.307 I-1.781 J2.566 E-.15582 +G1 X133.575 Y106.251 E-.21945 +;WIPE_END +G1 E-.14277 F2100 +G1 X133.962 Y109.236 Z.453 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.529176 +G1 F7403.508 +G1 X133.974 Y109.86 E.02523 +;WIDTH:0.546642 +G1 F7146.799 +G1 X133.953 Y110.37 E.02138 +;WIDTH:0.556074 +G1 F7015.438 +G1 X133.949 Y110.44 E.00299 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.953 Y110.37 E-.01457 +G1 X133.974 Y109.86 E-.10607 +G1 X133.962 Y109.236 E-.1297 +;WIPE_END +G1 E-.38966 F2100 +G1 X116.912 Y106.551 Z.701 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G1 X116.488 Y107.077 E.0191 +;WIDTH:0.429185 +G1 F9320.026 +G1 X116.384 Y107.219 E.00565 +;WIDTH:0.47545 +G1 F8323.123 +G1 X116.281 Y107.361 E.00631 +;WIDTH:0.521715 +G1 F7518.876 +G1 X116.177 Y107.503 E.00701 +;WIDTH:0.56798 +G1 F6856.359 +G1 X116.074 Y107.645 E.00766 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.177 Y107.503 E-.03645 +G1 X116.281 Y107.361 E-.03658 +G1 X116.384 Y107.219 E-.03645 +G1 X116.488 Y107.077 E-.03658 +G1 X116.912 Y106.551 E-.1404 +;WIPE_END +G1 E-.35354 F2100 +G1 X119.271 Y108.235 Z.451 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.514386 +G1 F7635.757 +G2 X119.645 Y108.795 I1.19 J-.39 E.02673 +G1 X119.465 Y108.989 E.01037 +G1 X119.258 Y109.501 E.02165 +G1 X119.208 Y109.549 E.00272 +G1 X119.078 Y109.3 E.01101 +G1 X119.057 Y108.578 E.02832 +G1 X119.142 Y108.355 E.00936 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.057 Y108.578 E-.04959 +G1 X119.078 Y109.3 E-.1501 +G1 X119.208 Y109.549 E-.05837 +G1 X119.258 Y109.501 E-.0144 +G1 X119.465 Y108.989 E-.11477 +G1 X119.645 Y108.795 E-.055 +G3 X119.271 Y108.235 I.816 J-.95 E-.14169 +;WIPE_END +G1 E-.05608 F2100 +G1 X130.826 Y109.05 Z.602 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.425468 +G1 F2097.414 +G1 X130.947 Y108.794 E.00901 +G1 X131.146 Y108.673 E.00741 +;WIDTH:0.437065 +G1 F2098.309 +G1 X131.438 Y108.674 E.00957 +G1 F2099.141 +G1 X131.597 Y108.768 E.00605 +G1 X131.746 Y109.008 E.00926 +G1 X131.752 Y109.047 E.00129 +G1 X131.399 Y109.047 E.01157 +G1 F2090.448 +G1 X130.881 Y109.053 E.01698 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.826 Y109.05 E-.01145 +G1 X130.947 Y108.794 E-.05884 +G1 X131.146 Y108.673 E-.0484 +G1 X131.438 Y108.674 E-.06068 +G1 X131.597 Y108.768 E-.03838 +G1 X131.746 Y109.008 E-.05871 +G1 X131.752 Y109.047 E-.0082 +G1 X131.399 Y109.047 E-.07336 +G1 X130.881 Y109.053 E-.10765 +;WIPE_END +G1 E-.17433 F2100 +G1 X127.639 Y109.993 Z.459 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.389516 +G1 F2091.95 +G1 X127.541 Y109.796 E.00634 +;WIDTH:0.403435 +G1 F2086.643 +G1 X127.549 Y109.294 E.01505 +G1 F2081.7 +G1 X127.63 Y109.081 E.00683 +G1 F2080.123 +G1 X127.759 Y109.055 E.00394 +G1 F2082.116 +G1 X127.872 Y109.296 E.00798 +G1 F2087.606 +G1 X127.879 Y109.848 E.01655 +;WIDTH:0.389516 +G1 F2092.656 +G1 X127.775 Y110.073 E.00714 +G1 F2093.433 +G1 X127.668 Y110.044 E.00319 +G1 X127.668 Y110.044 F12000 +G1 X127.29 Y110.202 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2092.007 +G1 X127.287 Y110.198 E.00017 +G1 F2089.91 +G1 X127.196 Y109.947 E.00904 +G1 F2086.872 +G1 X127.151 Y109.622 E.01111 +G1 F2081.819 +G1 X127.179 Y109.132 E.01661 +G1 F2075.978 +G1 X127.323 Y108.834 E.0112 +G1 F2072.364 +G1 X127.493 Y108.701 E.00731 +G1 F2071.178 +G1 X127.729 Y108.655 E.00814 +G1 X127.93 Y108.7 E.00697 +G1 F2073.971 +G1 X128.099 Y108.836 E.00734 +G1 F2078.734 +G1 X128.254 Y109.265 E.01544 +G1 F2083.199 +G1 X128.27 Y109.461 E.00666 +G1 F2086.899 +G1 X128.254 Y109.881 E.01423 +G1 F2090.999 +G1 X128.102 Y110.291 E.0148 +G1 F2093.096 +G3 X127.752 Y110.469 I-.397 J-.347 E.01362 +G1 X127.486 Y110.398 E.00932 +G1 F2092.007 +G1 X127.333 Y110.244 E.00735 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.29 Y110.202 E-.01249 +G1 X127.287 Y110.198 E-.00104 +G1 X127.196 Y109.947 E-.05548 +G1 X127.151 Y109.622 E-.06818 +G1 X127.179 Y109.132 E-.10199 +G1 X127.323 Y108.834 E-.06878 +G1 X127.493 Y108.701 E-.04486 +G1 X127.729 Y108.655 E-.04997 +G1 X127.93 Y108.7 E-.0428 +G1 X128.099 Y108.836 E-.04508 +G1 X128.254 Y109.265 E-.09479 +G1 X128.27 Y109.461 E-.04087 +G1 X128.267 Y109.527 E-.01367 +;WIPE_END +G1 X126.198 Y102.228 Z.532 F12000 +G1 Z.4 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.489421 +G1 F2087.808 +G1 X126.193 Y102.389 E.00598 +G1 F2100 +G1 X126.093 Y102.624 E.00948 +G1 X125.792 Y102.778 E.01255 +;WIDTH:0.488168 +G1 X125.566 Y102.762 E.00839 +G1 X125.437 Y102.606 E.00749 +G1 X125.459 Y102.456 E.00561 +G1 X125.7 Y102.342 E.00987 +;WIDTH:0.489421 +G1 F2087.808 +G1 X126.148 Y102.232 E.01713 +M204 P1000 +;LAYER_CHANGE +;Z:0.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.198 Y102.228 E-.01042 +G1 X126.193 Y102.389 E-.03347 +G1 X126.093 Y102.624 E-.05307 +G1 X125.792 Y102.778 E-.07026 +M73 Q7 S20 +G1 X125.566 Y102.762 E-.04708 +G1 X125.437 Y102.606 E-.04207 +M73 P7 R20 +G1 X125.459 Y102.456 E-.03151 +G1 X125.7 Y102.342 E-.0554 +G1 X126.148 Y102.232 E-.09587 +;WIPE_END +G1 E-.20085 F2100 +G1 X126.148 Y102.232 Z.4 F12000 +G1 X134.368 Y95.632 Z.6 +;AFTER_LAYER_CHANGE +;0.6 +G1 X134.368 Y95.632 +G1 Z.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X121.602 Y105.391 Z.862 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +G1 F8843.491 +G2 X123.634 Y104.812 I-.109 J-4.233 E.07228 +G2 X124.742 Y105.309 I1.772 J-2.465 E.04138 +G1 X124.564 Y105.527 E.00953 +G1 X124.333 Y106.125 E.0217 +G2 X123.763 Y106.272 I.665 J3.766 E.01994 +G1 X123.277 Y106.121 E.01723 +G1 X122.314 Y106.106 E.0326 +G2 X121.757 Y105.523 I-2.18 J1.523 E.0274 +M204 P1000 +G1 X121.757 Y105.523 F12000 +G1 X122.586 Y105.691 +M204 P1500 +;WIDTH:0.474658 +G1 F8338.39 +G2 X123.621 Y105.302 I-.959 J-4.132 E.03981 +G1 X124.099 Y105.547 E.01928 +G1 X124.009 Y105.757 E.0082 +G1 X123.774 Y105.813 E.00867 +G2 X122.79 Y105.694 I-.836 J2.765 E.03576 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X123.774 Y105.813 I.15 J2.884 E-.207 +G1 X124.009 Y105.757 E-.0502 +G1 X124.099 Y105.547 E-.04748 +G1 X123.621 Y105.302 E-.11162 +G3 X122.617 Y105.683 I-1.995 J-3.742 E-.2237 +;WIPE_END +G1 X116.12 Y106.267 Z.714 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G3 X116.879 Y105.455 I2.501 J1.577 E.03785 +G3 X118.477 Y104.74 I2.217 J2.814 E.05986 +G1 X115.998 Y102.262 E.11864 +G1 X115.998 Y106.43 E.14108 +M204 P1000 +G1 X115.998 Y106.43 F12000 +G1 X116.406 Y105.319 +M204 P1500 +G1 F8843.491 +G1 X116.406 Y103.245 E.0702 +G1 X117.69 Y104.529 E.06146 +G2 X116.57 Y105.199 I1.576 J3.906 E.04435 +M204 P1000 +G1 X116.57 Y105.199 F12000 +G1 X116.862 Y104.324 +M204 P1500 +;WIDTH:0.584208 +G1 F6650.803 +G2 X116.928 Y104.342 I.019 J.062 E.0151 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X116.862 Y104.324 I-.048 J.044 E-.06973 +;WIPE_END +G1 E-.57027 F2100 +G1 X118.986 Y104.673 Z.638 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y101.686 E.14301 +G1 X115.998 Y95.998 E.19253 +G1 X130.508 Y95.998 E.49115 +G2 X130.3 Y96.374 I.351 J.44 E.01492 +G1 X130.353 Y96.467 E.00362 +G1 X130.898 Y97.012 E.02609 +G1 X128.309 Y96.997 E.08764 +G2 X127.327 Y97.338 I.246 J2.294 E.03549 +G2 X126.807 Y98.037 I1.059 J1.33 E.02982 +G1 X126.701 Y98.35 E.01119 +G2 X124.686 Y98.446 I-.788 J4.628 E.06882 +G2 X121.976 Y96.935 I-2.94 J2.085 E.10857 +G2 X119.835 Y97.304 I-.353 J4.352 E.07432 +G2 X118.212 Y98.842 I1.771 J3.493 E.07675 +G2 X117.669 Y100.472 I3.543 J2.087 E.05859 +G2 X117.857 Y102.741 I5.56 J.681 E.07761 +G1 X118.036 Y103.148 E.01505 +G1 X116.185 Y101.297 E.08861 +G1 X116.406 Y100.967 E.01344 +G1 X116.406 Y96.406 E.15438 +G1 X120.246 Y96.406 E.12998 +M73 Q7 S19 +;WIDTH:0.492474 +G1 F8007.95 +G1 X120.394 Y96.427 E.00559 +;WIDTH:0.534948 +G1 F7316.664 +G1 X120.542 Y96.448 E.00612 +;WIDTH:0.577422 +G1 F6735.245 +G1 X120.689 Y96.469 E.0066 +;WIDTH:0.619896 +G1 F6239.428 +G1 X120.837 Y96.49 E.00717 +G1 X120.812 Y96.501 E.00131 +;WIDTH:0.608337 +G1 F6366.982 +G1 X120.655 Y96.558 E.00785 +;WIDTH:0.568753 +G1 F6846.287 +G1 X120.497 Y96.614 E.00733 +;WIDTH:0.529168 +G1 F7403.629 +G1 X120.34 Y96.67 E.00674 +;WIDTH:0.489584 +G1 F8059.759 +G1 X120.183 Y96.726 E.00619 +;WIDTH:0.449999 +G1 F8843.491 +G2 X118.179 Y98.151 I1.439 J4.144 E.08436 +G2 X117.263 Y100.444 I3.784 J2.843 E.08456 +G2 X117.255 Y101.791 I6.614 J.711 E.04567 +G1 X116.554 Y101.106 E.03318 +G1 X116.822 Y100.769 E.01457 +;WIDTH:0.513898 +G1 F7643.68 +G1 X116.845 Y100.429 E.01335 +;WIDTH:0.558977 +G1 F6975.978 +G1 X116.867 Y100.089 E.01462 +;WIDTH:0.604056 +G1 F6415.557 +G1 X116.89 Y99.749 E.0159 +;WIDTH:0.449999 +G1 F8843.491 +G1 X116.831 Y99.294 E.01553 +;WIDTH:0.515188 +G1 F7622.799 +G1 X116.845 Y99.443 E.00588 +;WIDTH:0.542779 +G1 F7202.03 +G1 X116.859 Y99.592 E.00622 +;WIDTH:0.573418 +G1 F6786.083 +G1 X116.874 Y99.67 E.0035 +;WIDTH:0.604056 +G1 F6415.557 +G1 X116.89 Y99.749 E.00376 +G1 X116.956 Y99.599 E.00765 +;WIDTH:0.565542 +G1 F6888.347 +G1 X117.023 Y99.448 E.00718 +;WIDTH:0.527028 +G1 F7436.365 +G1 X117.09 Y99.297 E.00665 +;WIDTH:0.488514 +G1 F8079.117 +G1 X117.157 Y99.146 E.00612 +;WIDTH:0.449999 +G1 F8843.491 +G3 X118.22 Y97.478 I5.334 J2.227 E.06728 +G3 X119.074 Y96.813 I4.439 J4.819 E.03668 +G1 X116.813 Y96.813 E.07653 +G1 X116.813 Y99.091 E.07711 +M204 P1000 +G1 X116.813 Y99.091 F12000 +G1 X117.225 Y98.109 +M204 P1500 +;WIDTH:0.460578 +G1 F8619.492 +G1 X117.225 Y97.225 E.0307 +G1 X117.894 Y97.225 E.02323 +G2 X117.34 Y97.941 I3.34 J3.157 E.03149 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X117.894 Y97.225 I3.894 J2.441 E-.18844 +G1 X117.225 Y97.225 E-.13903 +G1 X117.225 Y98.109 E-.18371 +;WIPE_END +G1 E-.12882 F2100 +G1 X120.837 Y96.49 Z.669 F12000 +G1 Z.6 F720 +M73 P7 R19 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.619896 +G1 F6239.428 +G1 X121.134 Y96.475 E.01427 +;WIDTH:0.589127 +G1 F6590.908 +G1 X121.43 Y96.46 E.01346 +;WIDTH:0.572536 +G1 F6797.378 +G1 X121.993 Y96.467 E.0248 +;WIDTH:0.621635 +G1 F6220.678 +G1 X122.336 Y96.491 E.01655 +;WIDTH:0.670734 +G1 F5734.182 +G1 X122.68 Y96.516 E.01801 +G1 X122.771 Y96.562 E.00532 +;WIDTH:0.638782 +G1 F6041.668 +G1 X122.862 Y96.607 E.00503 +;WIDTH:0.60683 +G1 F6383.999 +G1 X122.974 Y96.663 E.00587 +;WIDTH:0.567623 +G1 F6861.035 +G1 X123.085 Y96.719 E.00542 +;WIDTH:0.528415 +G1 F7415.121 +G1 X123.197 Y96.775 E.00506 +;WIDTH:0.489207 +G1 F8066.563 +G1 X123.308 Y96.831 E.00461 +;WIDTH:0.449999 +G1 F8843.491 +G3 X124.851 Y97.988 I-1.676 J3.841 E.06587 +G3 X126.422 Y97.897 I1.09 J5.215 E.05346 +G1 X126.733 Y97.346 E.02142 +G3 X127.593 Y96.744 I1.8 J1.657 E.03581 +;WIDTH:0.49688 +G1 F7930.214 +G1 X127.737 Y96.685 E.00587 +;WIDTH:0.543761 +G1 F7187.91 +G1 X127.881 Y96.627 E.00647 +;WIDTH:0.590642 +G1 F6572.678 +G1 X128.025 Y96.569 E.00707 +;WIDTH:0.637523 +G1 F6054.46 +G1 X128.169 Y96.51 E.00769 +;WIDTH:0.645418 +G1 F5975.124 +G1 X128.184 Y96.503 E.00083 +G1 X128.069 Y96.479 E.00589 +;WIDTH:0.596564 +G1 F6502.383 +G1 X127.954 Y96.454 E.00542 +;WIDTH:0.547709 +G1 F7131.7 +G1 X127.84 Y96.43 E.00489 +;WIDTH:0.498854 +G1 F7895.884 +G1 X127.725 Y96.406 E.00445 +;WIDTH:0.449999 +G1 F8843.491 +G1 X123.378 Y96.406 E.14714 +;WIDTH:0.487593 +G1 F8095.85 +G1 X123.235 Y96.424 E.00533 +;WIDTH:0.525186 +G1 F7464.767 +G1 X123.092 Y96.443 E.00578 +;WIDTH:0.562779 +G1 F6924.958 +G1 X122.949 Y96.462 E.00624 +;WIDTH:0.600372 +G1 F6457.955 +G1 X122.807 Y96.481 E.00664 +;WIDTH:0.635553 +G1 F6074.586 +G1 X122.743 Y96.498 E.00326 +;WIDTH:0.670734 +G1 F5734.182 +G1 X122.68 Y96.516 E.00342 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.743 Y96.498 E-.01362 +G1 X122.807 Y96.481 E-.01376 +G1 X122.949 Y96.462 E-.02977 +G1 X123.092 Y96.443 E-.02998 +G1 X123.235 Y96.424 E-.02998 +G1 X123.378 Y96.406 E-.02995 +G1 X125.75 Y96.406 E-.49294 +;WIPE_END +G1 X124.363 Y96.886 Z.626 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.596519 +G1 F6502.902 +G1 X125.241 Y96.883 E.04042 +;WIDTH:0.590617 +G1 F6572.978 +G1 X125.978 Y96.876 E.03357 +;WIDTH:0.576223 +G1 F6750.384 +G1 X126.536 Y96.876 E.02474 +G2 X126.139 Y97.415 I1.647 J1.631 E.02979 +G1 X125.798 Y97.408 E.01512 +;WIDTH:0.596519 +G1 F6502.902 +G1 X125.03 Y97.462 E.03544 +G2 X124.525 Y97.009 I-2.3 J2.055 E.03129 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X125.03 Y97.462 I-1.794 J2.508 E-.14127 +G1 X125.798 Y97.408 E-.15999 +G1 X126.139 Y97.415 E-.07088 +G3 X126.536 Y96.876 I2.045 J1.091 E-.1396 +G1 X125.978 Y96.876 E-.11596 +G1 X125.919 Y96.877 E-.0123 +;WIPE_END +G1 X128.184 Y96.503 Z.64 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.645418 +G1 F5975.124 +G1 X128.315 Y96.498 E.00657 +;WIDTH:0.648598 +G1 F5943.752 +G1 X129.816 Y96.498 E.07559 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.315 Y96.498 E-.31193 +G1 X128.184 Y96.503 E-.02724 +;WIPE_END +G1 E-.30083 F2100 +G1 X128.152 Y105.282 Z.753 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G2 X130.868 Y105.301 I2.214 J-118.76 E.09194 +G2 X131.82 Y104.963 I-.293 J-2.331 E.03446 +G1 X132.162 Y104.617 E.01647 +G2 X132.493 Y103.651 I-1.983 J-1.22 E.03485 +G1 X132.491 Y98.594 E.17117 +G2 X132.147 Y97.674 I-2.228 J.309 E.03352 +G1 X133.858 Y99.396 E.08217 +G1 X134.002 Y99.396 E.00487 +G1 X134.002 Y107.475 E.27346 +G2 X133.266 Y106.681 I-2.383 J1.468 E.03688 +G2 X132.262 Y106.17 I-2.025 J2.74 E.03831 +G2 X131.004 Y106.039 I-1.123 J4.67 E.04294 +G2 X129.581 Y106.475 I.197 J3.182 E.05085 +G1 X129.202 Y106.269 E.0146 +G1 X128.541 Y106.083 E.02324 +G2 X128.262 Y105.453 I-1.587 J.325 E.0235 +G1 X128.81 Y105.68 E.02008 +;WIDTH:0.392387 +G1 F10301.401 +G1 X129.875 Y105.67 E.03095 +;WIDTH:0.418339 +G1 F9589.299 +G1 X130.174 Y105.686 E.00935 +;WIDTH:0.44429 +G1 F8969.281 +G1 X130.472 Y105.703 E.00996 +G1 X130.384 Y105.728 E.00305 +;WIDTH:0.432147 +G1 F9249.101 +G1 X129.906 Y105.889 E.01632 +;WIDTH:0.392387 +G1 F10301.401 +G1 X129.586 Y106.045 E.01034 +G2 X128.959 Y105.796 I-1.196 J2.097 E.01967 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X129.586 Y106.045 I-.569 J2.346 E-.14066 +G1 X129.906 Y105.889 E-.07398 +G1 X130.384 Y105.728 E-.10482 +G1 X130.472 Y105.703 E-.01901 +G1 X130.174 Y105.686 E-.06203 +G1 X129.875 Y105.67 E-.06222 +G1 X129.022 Y105.678 E-.17728 +;WIPE_END +G1 X130.472 Y105.703 Z.625 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.44429 +G1 F8969.281 +G1 X130.727 Y105.683 E.00854 +;WIDTH:0.416245 +G1 F9643.073 +G1 X130.981 Y105.663 E.00791 +;WIDTH:0.395838 +G1 F10200.669 +G1 X131.045 Y105.654 E.0019 +;WIDTH:0.416711 +G1 F9631.058 +G1 X131.249 Y105.648 E.00634 +;WIDTH:0.449999 +G1 F8843.491 +G1 X131.573 Y105.64 E.01097 +G3 X132.956 Y106.004 I-.455 J4.535 E.04861 +G3 X133.594 Y106.425 I-3.25 J5.626 E.02589 +G1 X133.594 Y104.263 E.07318 +;WIDTH:0.488044 +G1 F8087.648 +G1 X133.575 Y104.156 E.00402 +;WIDTH:0.526088 +G1 F7450.832 +G1 X133.556 Y104.049 E.00437 +;WIDTH:0.564132 +G1 F6906.982 +G1 X133.537 Y103.941 E.00475 +;WIDTH:0.602176 +G1 F6437.124 +G3 X133.523 Y103.65 I.914 J-.192 E.0136 +;WIDTH:0.594434 +G1 F6527.487 +G1 X133.522 Y99.735 E.17954 +G1 X132.97 Y99.187 E.03567 +G1 X132.972 Y103.65 E.20467 +;WIDTH:0.602176 +G1 F6437.124 +G3 X132.912 Y103.922 I-.582 J.013 E.01308 +;WIDTH:0.564132 +G1 F6906.982 +G1 X132.859 Y104.064 E.00657 +;WIDTH:0.526088 +G1 F7450.832 +G1 X132.806 Y104.206 E.00609 +;WIDTH:0.488044 +G1 F8087.648 +G1 X132.754 Y104.347 E.00556 +;WIDTH:0.449999 +G1 F8843.491 +G3 X132.006 Y105.325 I-2.056 J-.797 E.04224 +G1 X131.554 Y105.556 E.01718 +G1 X131.3 Y105.605 E.00876 +;WIDTH:0.422919 +G1 F9473.722 +G1 X131.045 Y105.654 E.0082 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.3 Y105.605 E-.05396 +G1 X131.554 Y105.556 E-.05376 +G1 X132.006 Y105.325 E-.10549 +G2 X132.754 Y104.347 I-1.307 J-1.775 E-.25932 +G1 X132.806 Y104.206 E-.03123 +G1 X132.859 Y104.064 E-.0315 +G1 X132.912 Y103.922 E-.0315 +G2 X132.972 Y103.65 I-.523 J-.258 E-.05845 +G1 X132.972 Y103.579 E-.01479 +;WIPE_END +G1 X132.57 Y105.398 Z.633 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.47977 +G1 F8240.815 +G1 X132.634 Y105.332 E.00334 +G2 X133.173 Y104.414 I-2.239 J-1.929 E.03888 +G1 X133.173 Y105.643 E.04464 +G1 X132.759 Y105.473 E.01626 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.173 Y105.643 E-.09301 +G1 X133.173 Y104.414 E-.2554 +G3 X132.634 Y105.332 I-2.777 J-1.013 E-.22244 +G1 X132.57 Y105.398 E-.01911 +;WIPE_END +G1 E-.05004 F2100 +G1 X132.13 Y97.653 Z.735 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.428452 +G1 F9337.746 +G1 X132.039 Y97.559 E.00419 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.785 Y96.323 E.0596 +G1 X131.036 Y95.998 E.0139 +G1 X134.002 Y98.964 E.14198 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.824 Y96.787 E-.64 +;WIPE_END +G1 X131.755 Y96.142 Z.611 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X134.002 Y98.388 E.10754 +G1 X134.002 Y95.998 E.0809 +G1 X131.612 Y95.998 E.0809 +M204 P1000 +G1 X131.612 Y95.998 F12000 +G1 X132.702 Y96.45 +M204 P1500 +;WIDTH:0.539394 +G1 F7251.135 +G1 X133.55 Y96.45 E.03501 +G1 X133.55 Y97.298 E.03501 +G1 X132.846 Y96.594 E.0411 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.55 Y97.298 E-.2069 +G1 X133.55 Y96.45 E-.17622 +G1 X132.702 Y96.45 E-.17623 +;WIPE_END +G1 E-.08065 F2100 +G1 X119.423 Y104.714 Z.873 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G2 X118.833 Y104.126 I-2.78 J2.199 E.0236 +G1 X118.481 Y103.726 E.01506 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.833 Y104.126 E-.11073 +G3 X119.423 Y104.714 I-2.19 J2.787 E-.1735 +;WIPE_END +G1 E-.35577 F2100 +G1 X115.998 Y111.577 Z.734 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y114.002 E.08208 +G1 X134.002 Y114.002 E.60941 +G1 X134.002 Y111.845 E.07301 +G3 X131.567 Y113.114 I-2.596 J-2.008 E.09589 +M73 P8 R19 +M73 Q8 S19 +G3 X129.963 Y112.879 I-.191 J-4.285 E.0552 +G1 X129.576 Y112.681 E.01471 +G3 X127.874 Y113.119 I-1.692 J-3.046 E.06014 +G2 X126.874 Y113.043 I-.793 J3.815 E.03404 +G2 X124.501 Y113.043 I-1.186 J61.122 E.08033 +G1 X124.151 Y113.023 E.01187 +G1 X123.348 Y113.124 E.02739 +G3 X121.463 Y112.468 I-.01 J-3.009 E.06886 +G3 X120.275 Y113.006 I-2.571 J-4.094 E.04428 +G3 X117.966 Y112.929 I-1.003 J-4.535 E.07903 +G3 X116.253 Y111.749 I1.371 J-3.826 E.0712 +G1 X115.998 Y111.373 E.01538 +M204 P1000 +G1 X115.998 Y111.373 F12000 +G1 X116.406 Y112.477 +M204 P1500 +G1 F8843.491 +G2 X117.872 Y113.325 I2.905 J-3.333 E.05768 +;WIDTH:0.499938 +G1 F7877.151 +G1 X118.038 Y113.39 E.00677 +;WIDTH:0.549877 +G1 F7101.194 +G1 X118.204 Y113.455 E.00751 +;WIDTH:0.599816 +G1 F6464.403 +G1 X118.37 Y113.52 E.00826 +G1 X118.193 Y113.545 E.00828 +;WIDTH:0.549877 +G1 F7101.194 +G1 X118.017 Y113.57 E.00749 +;WIDTH:0.499938 +G1 F7877.151 +G1 X117.841 Y113.594 E.00675 +;WIDTH:0.449999 +G1 F8843.491 +G1 X116.406 Y113.594 E.04857 +G1 X116.406 Y112.68 E.03094 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.406 Y113.594 E-.18994 +G1 X117.841 Y113.594 E-.29821 +G1 X118.017 Y113.57 E-.03691 +G1 X118.193 Y113.545 E-.03694 +G1 X118.37 Y113.52 E-.03715 +G1 X118.204 Y113.455 E-.03705 +G1 X118.187 Y113.448 E-.0038 +;WIPE_END +G1 X118.37 Y113.52 Z.603 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.599816 +G1 F6464.403 +G1 X118.704 Y113.54 E.01549 +;WIDTH:0.559585 +G1 F6967.765 +G1 X119.039 Y113.56 E.01442 +;WIDTH:0.539594 +G1 F7248.215 +G1 X119.742 Y113.55 E.02904 +;WIDTH:0.584733 +G1 F6644.359 +G1 X120.033 Y113.527 E.01315 +;WIDTH:0.629872 +G1 F6133.381 +G1 X120.325 Y113.505 E.01429 +;WIDTH:0.631098 +G1 F6120.597 +G1 X120.535 Y113.519 E.01029 +;WIDTH:0.600801 +G1 F6452.989 +G1 X120.736 Y113.534 E.00935 +;WIDTH:0.570768 +G1 F6820.151 +G1 X120.937 Y113.549 E.00885 +;WIDTH:0.540734 +G1 F7231.617 +G1 X122.148 Y113.549 E.05013 +;WIDTH:0.553024 +G1 F7057.384 +G1 X122.65 Y113.543 E.02129 +;WIDTH:0.558396 +G1 F6983.837 +G1 X122.771 Y113.54 E.00519 +G1 X122.171 Y113.379 E.02663 +;WIDTH:0.540734 +G1 F7231.617 +G3 X121.43 Y113.013 I1.885 J-4.761 E.03425 +G1 X120.983 Y113.24 E.02075 +;WIDTH:0.574036 +G1 F6778.187 +G1 X120.706 Y113.354 E.01323 +;WIDTH:0.607337 +G1 F6378.263 +G1 X120.428 Y113.467 E.01408 +;WIDTH:0.631098 +G1 F6120.597 +G1 X120.332 Y113.504 E.00503 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.428 Y113.467 E-.02138 +G1 X120.706 Y113.354 E-.06236 +G1 X120.983 Y113.24 E-.06225 +G1 X121.43 Y113.013 E-.10418 +G2 X122.171 Y113.379 I2.63 J-4.393 E-.17194 +G1 X122.771 Y113.54 E-.1291 +G1 X122.65 Y113.543 E-.02515 +G1 X122.344 Y113.547 E-.06364 +;WIPE_END +G1 X122.771 Y113.54 Z.607 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.558396 +G1 F6983.837 +G1 X123.275 Y113.562 E.02162 +;WIDTH:0.559068 +G1 F6974.744 +G1 X123.886 Y113.54 E.02624 +;WIDTH:0.600624 +G1 F6455.037 +G3 X125.482 Y113.521 I1.1 J25.468 E.07403 +;WIDTH:0.607998 +G1 F6370.802 +G1 X126.876 Y113.522 E.0655 +;WIDTH:0.594168 +G1 F6530.636 +G1 X127.319 Y113.54 E.02032 +;WIDTH:0.559691 +G1 F6966.335 +G1 X127.762 Y113.557 E.01905 +;WIDTH:0.555718 +G1 F7020.308 +G1 X128.442 Y113.542 E.029 +;WIDTH:0.564238 +G1 F6905.574 +G1 X128.715 Y113.557 E.01185 +;WIDTH:0.525289 +G1 F7463.166 +G1 X128.878 Y113.573 E.00657 +;WIDTH:0.49385 +G1 F7983.509 +G1 X129.042 Y113.588 E.00617 +;WIDTH:0.46241 +G1 F8581.849 +G1 X129.799 Y113.588 E.0264 +;WIDTH:0.497472 +G1 F7919.891 +G1 X129.972 Y113.571 E.00657 +;WIDTH:0.532534 +G1 F7352.741 +G1 X130.146 Y113.553 E.00712 +;WIDTH:0.567595 +G1 F6861.391 +G1 X130.32 Y113.536 E.00763 +;WIDTH:0.60653 +G1 F6387.395 +G1 X130.428 Y113.516 E.00515 +G1 X130.233 Y113.436 E.00988 +;WIDTH:0.55849 +G1 F6982.563 +G1 X130.039 Y113.355 E.00901 +;WIDTH:0.51045 +G1 F7700.041 +G1 X129.845 Y113.275 E.00816 +;WIDTH:0.46241 +G1 F8581.849 +G1 X129.575 Y113.146 E.01044 +G1 X129.018 Y113.374 E.02099 +;WIDTH:0.50961 +G1 F7713.9 +G1 X128.779 Y113.451 E.00974 +;WIDTH:0.55681 +G1 F7005.39 +G1 X128.54 Y113.528 E.01073 +;WIDTH:0.564238 +G1 F6905.574 +G1 X128.512 Y113.537 E.00127 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.54 Y113.528 E-.00611 +G1 X128.779 Y113.451 E-.05218 +G1 X129.018 Y113.374 E-.05218 +G1 X129.575 Y113.146 E-.12507 +G1 X129.845 Y113.275 E-.06218 +G1 X130.039 Y113.355 E-.04361 +G1 X130.233 Y113.436 E-.04369 +G1 X130.428 Y113.516 E-.0438 +G1 X130.32 Y113.536 E-.02283 +G1 X130.146 Y113.553 E-.03633 +G1 X129.972 Y113.571 E-.03635 +G1 X129.799 Y113.588 E-.03612 +G1 X129.416 Y113.588 E-.07955 +;WIPE_END +G1 X130.428 Y113.516 Z.618 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.60653 +G1 F6387.395 +G1 X130.68 Y113.533 E.01184 +;WIDTH:0.572135 +G1 F6802.529 +G1 X130.932 Y113.551 E.01112 +;WIDTH:0.53774 +G1 F7275.373 +G1 X131.53 Y113.559 E.02461 +;WIDTH:0.556288 +G1 F7012.514 +G1 X131.838 Y113.541 E.01317 +;WIDTH:0.590848 +G1 F6570.207 +G1 X132.147 Y113.524 E.0141 +;WIDTH:0.596036 +G1 F6508.58 +G1 X132.385 Y113.534 E.01096 +;WIDTH:0.57114 +G1 F6815.353 +G1 X132.763 Y113.557 E.01663 +;WIDTH:0.524804 +G1 F7470.678 +G1 X133.557 Y113.557 E.03181 +G1 X133.557 Y112.876 E.02729 +G3 X132.729 Y113.326 I-2.454 J-3.531 E.03784 +;WIDTH:0.553224 +G1 F7054.625 +G1 X132.493 Y113.411 E.01064 +;WIDTH:0.581643 +G1 F6682.47 +G1 X132.257 Y113.496 E.01124 +;WIDTH:0.596036 +G1 F6508.58 +G1 X132.182 Y113.521 E.00364 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.257 Y113.496 E-.01643 +G1 X132.493 Y113.411 E-.05213 +G1 X132.729 Y113.326 E-.05213 +G2 X133.557 Y112.876 I-1.627 J-3.98 E-.19623 +G1 X133.557 Y113.557 E-.14152 +G1 X132.763 Y113.557 E-.165 +G1 X132.683 Y113.552 E-.01656 +;WIPE_END +G1 X117.422 Y112.441 Z.867 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.452999 +G2 X119.192 Y112.72 I2.926 J-12.844 E.06115 +G2 X120.351 Y112.563 I.053 J-3.959 E.04003 +G1 X117.052 Y111.956 E.11438 +G3 X116.536 Y111.445 I1.151 J-1.675 E.0249 +G1 X121.006 Y112.266 E.15497 +G1 X121.495 Y111.976 E.01939 +G2 X122.283 Y112.501 I1.892 J-1.987 E.03245 +G1 X123.438 Y112.713 E.04004 +G2 X124.179 Y112.582 I-.107 J-2.775 E.02574 +G1 X124.494 Y112.636 E.0109 +G1 X125.29 Y112.636 E.02714 +G1 X116.219 Y110.97 E.31448 +G3 X115.998 Y110.512 I1.83 J-1.166 E.01738 +G1 X128.001 Y112.717 E.41613 +G2 X129.05 Y112.493 I-.108 J-3.072 E.03676 +G1 X115.998 Y110.095 E.4525 +G1 X115.999 Y109.678 E.01422 +G1 X132.089 Y112.634 E.55782 +G2 X132.815 Y112.351 I-.655 J-2.749 E.02665 +G1 X115.999 Y109.261 E.583 +G1 X115.999 Y108.844 E.01422 +G1 X133.284 Y112.02 E.59925 +G1 X133.625 Y111.666 E.01676 +G1 X115.999 Y108.427 E.61108 +G1 X115.999 Y108.011 E.01418 +G1 X133.871 Y111.294 E.6196 +G1 X133.92 Y111.2 E.00361 +G2 X134.002 Y110.901 I-.381 J-.265 E.01078 +G1 X115.999 Y107.594 E.62414 +G3 X116.04 Y107.184 I.76 J-.13 E.01422 +G1 X134.002 Y110.484 E.62272 +G1 X134.002 Y110.067 E.01422 +G1 X116.239 Y106.804 E.61582 +G1 X116.482 Y106.431 E.01518 +G1 X134.002 Y109.65 E.6074 +G1 X134.002 Y109.233 E.01422 +G1 X116.775 Y106.069 E.59724 +G1 X117.193 Y105.728 E.01839 +G1 X134.002 Y108.816 E.58275 +G1 X134.002 Y108.399 E.01422 +G1 X124.088 Y106.578 E.3437 +G1 X123.77 Y106.709 E.01173 +M73 P9 R19 +M73 Q9 S19 +G2 X123.105 Y106.511 I-.795 J1.459 E.02383 +G1 X122.111 Y106.511 E.03389 +G1 X121.849 Y106.167 E.01474 +G1 X117.421 Y105.353 E.15352 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.45 Y105.91 E-.64 +;WIPE_END +G1 X121.664 Y105.716 Z.621 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +G1 X118.531 Y105.14 E.10862 +G3 X119.673 Y105.083 I.762 J3.801 E.03913 +G1 X119.723 Y104.942 E.0051 +G1 X121.097 Y105.195 E.04764 +G1 X121.181 Y104.956 E.00864 +G2 X122.034 Y104.95 I.402 J-3.507 E.02916 +G1 X119.81 Y104.541 E.07711 +G3 X119.099 Y103.994 I3.823 J-5.701 E.03061 +G1 X123.273 Y104.761 E.14471 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.244 Y104.204 E-.64 +;WIPE_END +G1 X124.536 Y106.243 Z.683 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +G1 X133.808 Y107.947 E.32145 +G1 X133.526 Y107.478 E.01866 +G1 X124.84 Y105.882 E.30114 +G3 X125.083 Y105.51 I1.055 J.423 E.01525 +G1 X128.131 Y106.07 E.10567 +G2 X127.901 Y105.611 I-1.508 J.47 E.01758 +G1 X124.304 Y104.95 E.1247 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.333 Y105.506 E-.64 +;WIPE_END +G1 X127.841 Y105.183 Z.611 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +G1 X118.71 Y103.505 E.31656 +G1 X118.426 Y103.036 E.0187 +G1 X128.549 Y104.896 E.35095 +G1 X129.387 Y104.889 E.02858 +G3 X129.811 Y104.894 I.202 J.871 E.0146 +G1 X130.818 Y104.896 E.03434 +G1 X118.232 Y102.584 E.43634 +G1 X118.12 Y102.146 E.01542 +G1 X131.599 Y104.623 E.46731 +G2 X131.906 Y104.262 I-.591 J-.813 E.01631 +G1 X118.063 Y101.719 E.47992 +G1 X118.032 Y101.296 E.01446 +G1 X132.055 Y103.872 E.48616 +G2 X132.086 Y103.461 I-1.39 J-.313 E.0141 +G1 X118.043 Y100.881 E.48686 +G1 X118.079 Y100.471 E.01403 +G1 X132.086 Y103.044 E.4856 +G1 X132.086 Y102.627 E.01422 +G1 X118.148 Y100.067 E.48321 +G1 X118.263 Y99.671 E.01406 +G1 X132.086 Y102.21 E.47922 +G1 X132.086 Y101.793 E.01422 +G1 X118.431 Y99.285 E.4734 +G1 X118.65 Y98.908 E.01487 +G1 X132.086 Y101.376 E.46581 +G1 X132.086 Y100.96 E.01418 +G1 X118.902 Y98.537 E.45708 +G3 X119.258 Y98.186 I1.365 J1.025 E.01711 +G1 X132.086 Y100.543 E.44473 +G1 X132.086 Y100.126 E.01422 +G1 X119.701 Y97.85 E.42938 +G3 X120.303 Y97.544 I1.611 J2.419 E.02308 +G1 X123.979 Y98.219 E.12744 +G2 X123.142 Y97.648 I-2.774 J3.171 E.03463 +G1 X120.81 Y97.22 E.08085 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.142 Y97.648 E-.49271 +G3 X123.741 Y98.025 I-1.938 J3.74 E-.14729 +;WIPE_END +G1 X125.591 Y98.515 Z.633 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +G1 X132.072 Y99.706 E.22469 +G1 X132.086 Y99.292 E.01412 +G1 X127.105 Y98.377 E.17269 +G1 X127.269 Y97.99 E.01433 +G1 X132.086 Y98.875 E.167 +G2 X132.06 Y98.453 I-1.531 J-.116 E.01446 +G1 X127.611 Y97.636 E.15424 +G3 X128.622 Y97.404 I.837 J1.335 E.03604 +G1 X131.887 Y98.004 E.11319 +G2 X131.067 Y97.437 I-1.047 J.637 E.03501 +M73 P10 R19 +M73 Q10 S19 +G1 X129.894 Y97.221 E.04067 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.067 Y97.437 E-.24786 +G3 X131.887 Y98.004 I-.227 J1.205 E-.21336 +G1 X131.041 Y97.849 E-.17878 +;WIPE_END +G1 X130.145 Y106.44 Z.751 F12000 +G1 Z.6 F720 +G1 E.8 F1500 +G1 X133.336 Y107.026 E.11063 +M106 S63.75 +;LAYER_CHANGE +;Z:0.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;0.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.307 Y106.47 E-.64 +;WIPE_END +G1 X130.307 Y106.47 Z.6 F12000 +G1 X134.368 Y95.632 Z.802 +;AFTER_LAYER_CHANGE +;0.8 +G1 X134.368 Y95.632 +G1 Z.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X116.109 Y96.031 Z1.128 F12000 +G1 Z.8 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F7116.79 +G2 X116.093 Y96.093 I-.058 J.018 E.01318 +;WIDTH:0.449999 +G1 F8840 +G1 X115.998 Y96.743 E.02224 +G1 X116.743 Y95.998 E.03566 +G1 X117.318 Y95.998 E.01946 +G1 X115.998 Y97.318 E.06319 +G1 X115.998 Y97.894 E.0195 +G1 X117.894 Y95.998 E.09076 +G1 X118.47 Y95.998 E.0195 +G1 X115.998 Y98.47 E.11833 +G1 X115.998 Y99.046 E.0195 +G1 X119.046 Y95.998 E.14591 +G1 X119.621 Y95.998 E.01946 +G1 X115.998 Y99.621 E.17343 +G1 X115.998 Y100.197 E.0195 +G1 X120.197 Y95.998 E.201 +G1 X120.773 Y95.998 E.0195 +G1 X115.998 Y100.773 E.22858 +G1 X115.998 Y101.348 E.01946 +G1 X121.348 Y95.998 E.2561 +G1 X121.924 Y95.998 E.0195 +G1 X115.998 Y101.924 E.28367 +G1 X115.998 Y102.5 E.0195 +G1 X122.5 Y95.998 E.31125 +G1 X123.075 Y95.998 E.01946 +G1 X115.998 Y103.075 E.33877 +G1 X115.998 Y103.651 E.0195 +G1 X123.651 Y95.998 E.36634 +G1 X124.227 Y95.998 E.0195 +G1 X115.998 Y104.227 E.39392 +G1 X115.998 Y104.803 E.0195 +G1 X124.803 Y95.998 E.42149 +G1 X125.378 Y95.998 E.01946 +G1 X115.998 Y105.378 E.44901 +G1 X115.998 Y105.954 E.0195 +G1 X125.954 Y95.998 E.47659 +G1 X126.53 Y95.998 E.0195 +G1 X115.998 Y106.53 E.50416 +G1 X115.998 Y107.105 E.01946 +G1 X127.105 Y95.998 E.53169 +G1 X127.681 Y95.998 E.0195 +G1 X115.998 Y107.681 E.55926 +G1 X115.998 Y108.257 E.0195 +G1 X128.257 Y95.998 E.58683 +G1 X128.832 Y95.998 E.01946 +G1 X115.998 Y108.832 E.61436 +G1 X115.998 Y109.408 E.0195 +G1 X129.408 Y95.998 E.64193 +G1 X129.984 Y95.998 E.0195 +G1 X115.998 Y109.984 E.6695 +G1 X115.998 Y110.559 E.01946 +G1 X130.559 Y95.998 E.69703 +G1 X131.135 Y95.998 E.0195 +G1 X115.998 Y111.135 E.7246 +G1 X115.998 Y111.711 E.0195 +G1 X131.711 Y95.998 E.75217 +G1 X132.287 Y95.998 E.0195 +G1 X115.998 Y112.287 E.77974 +G1 X115.998 Y112.862 E.01946 +G1 X132.862 Y95.998 E.80727 +G1 X133.438 Y95.998 E.0195 +G1 X115.998 Y113.438 E.83484 +G1 X116.011 Y114.002 E.0191 +G1 X134.002 Y96.011 E.86122 +G1 X134.002 Y96.586 E.01946 +G1 X116.586 Y114.002 E.83369 +G1 X117.162 Y114.002 E.0195 +G1 X134.002 Y97.162 E.80612 +G1 X134.002 Y97.738 E.0195 +G1 X117.738 Y114.002 E.77855 +G1 X118.313 Y114.002 E.01946 +G1 X134.002 Y98.313 E.75102 +G1 X134.002 Y98.889 E.0195 +G1 X118.889 Y114.002 E.72345 +G1 X119.465 Y114.002 E.0195 +G1 X134.002 Y99.465 E.69588 +G1 X134.002 Y100.04 E.01946 +G1 X120.04 Y114.002 E.66835 +G1 X120.616 Y114.002 E.0195 +G1 X134.002 Y100.616 E.64078 +G1 X134.002 Y101.192 E.0195 +M73 Q11 S19 +G1 X121.192 Y114.002 E.61321 +M73 P11 R19 +G1 X121.768 Y114.002 E.0195 +G1 X134.002 Y101.768 E.58563 +G1 X134.002 Y102.343 E.01946 +G1 X122.343 Y114.002 E.55811 +G1 X122.919 Y114.002 E.0195 +G1 X134.002 Y102.919 E.53054 +G1 X134.002 Y103.495 E.0195 +G1 X123.495 Y114.002 E.50296 +G1 X124.07 Y114.002 E.01946 +G1 X134.002 Y104.07 E.47544 +G1 X134.002 Y104.646 E.0195 +G1 X124.646 Y114.002 E.44787 +G1 X125.222 Y114.002 E.0195 +G1 X134.002 Y105.222 E.42029 +G1 X134.002 Y105.797 E.01946 +G1 X125.797 Y114.002 E.39277 +G1 X126.373 Y114.002 E.0195 +G1 X134.002 Y106.373 E.3652 +G1 X134.002 Y106.949 E.0195 +G1 X126.949 Y114.002 E.33762 +G1 X127.524 Y114.002 E.01946 +G1 X134.002 Y107.524 E.3101 +G1 X134.002 Y108.1 E.0195 +G1 X128.1 Y114.002 E.28253 +G1 X128.676 Y114.002 E.0195 +G1 X134.002 Y108.676 E.25495 +G1 X134.002 Y109.252 E.0195 +G1 X129.252 Y114.002 E.22738 +G1 X129.827 Y114.002 E.01946 +G1 X134.002 Y109.827 E.19985 +G1 X134.002 Y110.403 E.0195 +G1 X130.403 Y114.002 E.17228 +G1 X130.979 Y114.002 E.0195 +G1 X134.002 Y110.979 E.14471 +G1 X134.002 Y111.554 E.01946 +G1 X131.554 Y114.002 E.11718 +G1 X132.13 Y114.002 E.0195 +G1 X134.002 Y112.13 E.08961 +G1 X134.002 Y114.002 E.06336 +G1 X132.706 Y114.002 E.04387 +G1 X133.858 Y112.85 E.05515 +M204 P1000 +M106 S124.95 +;LAYER_CHANGE +;Z:1 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.706 Y114.002 E-.33856 +G1 X134.002 Y114.002 E-.26933 +G1 X134.002 Y113.847 E-.03211 +;WIPE_END +G1 X134.002 Y113.847 Z.8 F12000 +G1 X134.368 Y95.632 Z1.118 +;AFTER_LAYER_CHANGE +;1 +G1 X134.368 Y95.632 +G1 Z1 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z1.044 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F6600 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X131.797 Y98.203 E.10555 +G3 X131.873 Y99.794 I-3.083 J.945 E.05448 +G1 X131.904 Y103.653 E.13063 +G1 X131.793 Y104.118 E.01618 +G1 X134.002 Y106.326 E.10572 +G1 X134.002 Y103.674 E.08977 +G1 X131.04 Y106.636 E.14179 +G2 X129.576 Y107.2 I.083 J2.396 E.0541 +G2 X128.007 Y106.627 I-1.524 J1.741 E.05785 +G2 X126.949 Y105.327 I-1.096 J-.188 E.06425 +G1 X125.949 Y105.327 E.03385 +G1 X125.468 Y105.468 E.01697 +G1 X124.686 Y104.686 E.03743 +G1 X125.241 Y104.759 E.01895 +G1 X123.288 Y106.712 E.09349 +G1 X122.112 Y106.693 E.03981 +G3 X121.877 Y106.501 I-.034 J-.197 E.01164 +G2 X117.867 Y105.543 I-2.607 J2.04 E.15065 +G1 X115.998 Y103.674 E.08947 +G1 X115.998 Y106.326 E.08977 +G1 X118.895 Y103.429 E.13868 +G3 X118.213 Y101.237 I2.828 J-2.081 E.07916 +G3 X118.893 Y98.893 I3.714 J-.193 E.08417 +G1 X115.998 Y95.998 E.13858 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X122.143 Y97.555 Z1.076 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G3 X123.908 Y98.416 I-.345 J2.948 E.06775 +G1 X126.326 Y95.998 E.11575 +G1 X123.674 Y95.998 E.08977 +G1 X126.604 Y98.928 E.14026 +G1 X127.243 Y99.117 E.02256 +G3 X127.579 Y97.878 I1.388 J-.289 E.04509 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X127.243 Y99.117 I1.052 J.95 E-.27685 +G1 X126.604 Y98.928 E-.13848 +G1 X125.84 Y98.164 E-.22467 +;WIPE_END +G1 X119.355 Y103.561 Z1.147 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.38292 +G1 F7919 +G1 X119.966 Y104.106 E.02315 +;WIDTH:0.449999 +G1 X120.632 Y104.275 E.02326 +;WIDTH:0.38292 +G1 X120.613 Y104.346 E.00208 +G2 X120.673 Y104.286 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X120.514 Y104.209 E.00549 +;WIDTH:0.449999 +G1 X120.356 Y104.131 E.00596 +G1 X118.935 Y102.711 E.068 +M204 P1000 +G1 X118.935 Y102.711 F12000 +G1 X118.678 Y101.879 +M204 P1500 +G1 F7919 +G1 X121.146 Y104.347 E.11814 +G1 X121.75 Y104.374 E.02047 +G1 X118.621 Y101.246 E.14976 +G1 X118.664 Y100.713 E.0181 +G1 X122.265 Y104.314 E.17238 +G1 X122.697 Y104.17 E.01541 +G1 X118.726 Y100.199 E.19009 +G1 X118.866 Y99.763 E.0155 +G1 X123.08 Y103.978 E.20175 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.903 Y101.8 E-.64 +;WIPE_END +G1 X121.194 Y101.612 Z1.006 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G3 X121.134 Y101.672 I-.04 J.019 E.00315 +G1 X121.153 Y101.601 E.00208 +;WIDTH:0.41646 +G1 X121.092 Y101.477 E.00429 +;WIDTH:0.449999 +G1 X121.032 Y101.354 E.00463 +G1 X119.065 Y99.387 E.09416 +G1 X119.286 Y99.033 E.01413 +G1 X121.023 Y100.77 E.08315 +G1 X121.17 Y100.34 E.01538 +G1 X119.546 Y98.716 E.07774 +G1 X119.874 Y98.468 E.01392 +G1 X121.542 Y100.137 E.07987 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.874 Y98.468 E-.49036 +G1 X119.546 Y98.716 E-.08545 +G1 X119.764 Y98.934 E-.06419 +;WIPE_END +G1 X121.464 Y101.965 Z1.061 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G1 X121.445 Y102.036 E.00208 +G2 X121.505 Y101.976 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X121.634 Y102.03 E.00434 +;WIDTH:0.449999 +G1 X121.763 Y102.085 E.00475 +G1 X123.411 Y103.732 E.07886 +M204 P1000 +G1 X123.411 Y103.732 F12000 +G1 X124.582 Y104.232 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G1 X124.563 Y104.303 E.00208 +G2 X124.623 Y104.243 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X124.464 Y104.147 E.00577 +;WIDTH:0.449999 +G1 X124.305 Y104.051 E.00629 +G1 X121.958 Y101.704 E.11235 +G1 X122.17 Y101.34 E.01426 +G1 X125.179 Y104.349 E.14404 +G1 X125.762 Y104.357 E.01974 +G1 X122.298 Y100.893 E.16582 +G1 X122.055 Y100.57 E.01368 +;WIDTH:0.435886 +G1 X122.068 Y100.618 E.00162 +G1 X121.986 Y100.64 E.00277 +G1 X122.008 Y100.558 E.00277 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.986 Y100.64 E-.01764 +G1 X122.068 Y100.618 E-.01764 +G1 X122.055 Y100.57 E-.01033 +G1 X122.298 Y100.893 E-.084 +G1 X124.035 Y102.63 E-.51039 +;WIPE_END +G1 X120.216 Y98.235 Z1.102 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F7919 +G1 X126.213 Y104.232 E.28707 +G1 X126.863 Y104.307 E.02215 +G1 X120.623 Y98.066 E.29873 +G1 X121.103 Y97.971 E.01656 +M73 P12 R19 +M73 Q12 S19 +G1 X127.439 Y104.307 E.3033 +G1 X127.946 Y104.238 E.01732 +G1 X121.623 Y97.915 E.30268 +G1 X122.263 Y97.979 E.02177 +G1 X128.591 Y104.307 E.30292 +G1 X129.166 Y104.307 E.01946 +G1 X124.474 Y99.614 E.22463 +G1 X124.89 Y99.455 E.01507 +G1 X129.728 Y104.293 E.23159 +G1 X130.315 Y104.304 E.01987 +G1 X125.338 Y99.327 E.23825 +G1 X125.868 Y99.281 E.01801 +G1 X130.887 Y104.3 E.24026 +G1 X131.289 Y104.127 E.01481 +G1 X126.483 Y99.321 E.23006 +G1 X127.028 Y99.373 E.01853 +;WIDTH:0.38292 +G1 X127.009 Y99.444 E.00208 +G2 X127.069 Y99.384 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X127.196 Y99.484 E.00502 +;WIDTH:0.449999 +G1 X127.322 Y99.584 E.00544 +G1 X131.488 Y103.75 E.19942 +G1 X131.497 Y103.184 E.01916 +G1 X127.65 Y99.337 E.18415 +G1 X127.65 Y98.761 E.0195 +M73 Q12 S18 +G1 X131.497 Y102.608 E.18415 +G1 X131.497 Y102.032 E.0195 +G1 X127.763 Y98.297 E.17877 +G1 X128.078 Y98.038 E.0138 +G1 X131.497 Y101.456 E.16364 +G1 X131.497 Y100.881 E.01946 +G1 X128.617 Y98.001 E.13786 +G1 X129.187 Y97.995 E.01929 +G1 X131.497 Y100.305 E.11058 +G1 X131.474 Y99.707 E.02026 +G1 X129.774 Y98.006 E.0814 +G1 X130.408 Y98.024 E.02147 +;WIDTH:0.507364 +G1 F7751.203 +G3 X131.198 Y98.157 I.163 J1.447 E.03134 +;WIDTH:0.51103 +G1 F7690.5 +G3 X131.439 Y98.498 I-.492 J.605 E.01645 +G1 X131.466 Y99.079 E.02264 +G1 X130.876 Y98.492 E.03239 +;WIDTH:0.507364 +G1 F7751.203 +G1 X130.552 Y98.168 E.0177 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.876 Y98.492 E-.09522 +G1 X131.466 Y99.079 E-.17296 +G1 X131.439 Y98.498 E-.12087 +M73 P12 R18 +G2 X131.198 Y98.157 I-.734 J.263 E-.08785 +G2 X130.435 Y98.022 I-.627 J1.314 E-.1631 +;WIPE_END +G1 X123.626 Y98.739 Z1.12 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.488252 +G1 F7919 +G1 X123.446 Y98.566 E.00924 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.626 Y98.739 E-.05188 +;WIPE_END +G1 E-.58812 F2100 +G1 X116.958 Y111.207 Z1.247 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G2 X117.654 Y111.846 I5.747 J-5.549 E.02673 +M204 P1000 +G1 X117.654 Y111.846 F12000 +G1 X118.791 Y112.233 +M204 P1500 +G1 F7919 +G1 X118.228 Y112.104 E.01633 +;WIDTH:0.41646 +G1 X118.147 Y111.997 E.00417 +;WIDTH:0.449999 +G1 X118.065 Y111.89 E.00456 +G1 X116.616 Y110.441 E.06936 +G1 X116.518 Y110.297 E.0059 +;WIDTH:0.41646 +G1 X116.42 Y110.153 E.00541 +;WIDTH:0.38292 +G1 X116.079 Y109.74 E.01514 +G1 X115.916 Y109.624 E.00566 +;WIDTH:0.449999 +G1 X115.814 Y109.05 E.01973 +;WIDTH:0.38292 +G1 X115.795 Y109.121 E.00208 +G2 X115.855 Y109.061 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X115.927 Y109.154 E.00365 +;WIDTH:0.449999 +G1 X115.998 Y109.247 E.00396 +G1 X118.836 Y112.085 E.13585 +G1 X119.434 Y112.107 E.02026 +G1 X115.998 Y108.672 E.16446 +G1 X115.854 Y108.079 E.02066 +;WIDTH:0.38292 +G1 X115.835 Y108.15 E.00208 +G2 X115.895 Y108.09 I.02 J-.04 E.00315 +;WIDTH:0.41646 +G1 X115.995 Y108.141 E.00349 +;WIDTH:0.449999 +G1 X116.095 Y108.192 E.0038 +G1 X119.948 Y112.046 E.18446 +G1 X120.378 Y111.9 E.01537 +G1 X116.366 Y107.888 E.19205 +G1 X116.562 Y107.508 E.01447 +G1 X120.761 Y111.707 E.201 +M204 P1000 +G1 X120.761 Y111.707 F12000 +G1 X121.862 Y111.568 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G1 X122.211 Y111.889 E.01341 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.862 Y111.568 E-.09854 +;WIPE_END +G1 E-.54146 F2100 +G1 X118.879 Y109.346 Z1.065 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +G1 F7919 +G3 X118.819 Y109.406 I-.04 J.02 E.00315 +G1 X118.838 Y109.335 E.00208 +;WIDTH:0.41646 +G1 X118.778 Y109.212 E.00425 +;WIDTH:0.449999 +G1 X118.718 Y109.088 E.00466 +G1 X116.746 Y107.117 E.09437 +G1 X116.972 Y106.767 E.0141 +G1 X118.71 Y108.505 E.0832 +G1 X118.866 Y108.085 E.01517 +G1 X117.235 Y106.454 E.07807 +G1 X117.563 Y106.206 E.01392 +G1 X119.239 Y107.882 E.08023 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.563 Y106.206 E-.49256 +G1 X117.235 Y106.454 E-.08545 +G1 X117.446 Y106.665 E-.06199 +;WIPE_END +G1 X119.15 Y109.702 Z1.061 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G1 X119.131 Y109.773 E.00208 +G2 X119.192 Y109.713 I.02 J-.04 E.00321 +;WIDTH:0.41646 +G1 X119.32 Y109.767 E.00431 +;WIDTH:0.449999 +G1 X119.449 Y109.82 E.00472 +G1 X121.099 Y111.47 E.07898 +G1 X121.41 Y111.205 E.01383 +G1 X119.65 Y109.445 E.08425 +G1 X119.859 Y109.078 E.0143 +G1 X122.839 Y112.058 E.14265 +G1 X123.465 Y112.108 E.02126 +G1 X119.984 Y108.628 E.16661 +G1 X119.741 Y108.305 E.01368 +;WIDTH:0.436192 +G1 X119.754 Y108.352 E.00159 +G1 X119.671 Y108.374 E.00281 +G1 X119.693 Y108.292 E.00278 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.671 Y108.374 E-.01764 +G1 X119.754 Y108.352 E-.01784 +G1 X119.741 Y108.305 E-.01013 +G1 X119.984 Y108.628 E-.084 +G1 X121.721 Y110.364 E-.51039 +;WIPE_END +G1 X117.91 Y105.977 Z1.101 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F7919 +G1 X123.942 Y112.01 E.28877 +G1 X124.555 Y112.047 E.02079 +G1 X118.313 Y105.805 E.2988 +G1 X118.792 Y105.708 E.01654 +G1 X125.131 Y112.047 E.30344 +G1 X125.671 Y112.011 E.01832 +G1 X119.319 Y105.66 E.30404 +G1 X119.97 Y105.735 E.02218 +G1 X126.282 Y112.047 E.30215 +G1 X126.858 Y112.047 E.0195 +G1 X121.942 Y107.132 E.2353 +G1 X122.487 Y107.101 E.01848 +G1 X127.43 Y112.043 E.23659 +G1 X128.08 Y112.118 E.02215 +G1 X123.063 Y107.101 E.24016 +G1 X123.496 Y107.107 E.01466 +;WIDTH:0.38292 +G1 X123.611 Y107.211 E.00438 +;WIDTH:0.41646 +G1 X123.753 Y107.284 E.00496 +;WIDTH:0.449999 +G1 X123.894 Y107.356 E.00536 +G1 X128.569 Y112.031 E.22379 +G1 X128.964 Y111.85 E.01471 +G1 X124.246 Y107.133 E.22582 +G1 X124.79 Y107.101 E.01845 +G1 X129.31 Y111.62 E.21635 +M204 P1000 +G1 X129.31 Y111.62 F12000 +G1 X130.405 Y111.997 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G3 X130.345 Y112.057 I-.04 J.02 E.00315 +G1 X130.364 Y111.986 E.00208 +;WIDTH:0.41646 +G1 X130.225 Y111.904 E.00501 +;WIDTH:0.449999 +G1 X130.087 Y111.822 E.00543 +G1 X125.221 Y106.956 E.23293 +G1 X125.296 Y106.455 E.01715 +G1 X130.93 Y112.089 E.2697 +G1 X131.523 Y112.107 E.02008 +G1 X125.423 Y106.007 E.292 +G1 X125.768 Y105.776 E.01405 +G1 X132.02 Y112.028 E.29928 +G1 X132.442 Y111.874 E.01521 +G1 X126.302 Y105.734 E.29392 +G1 X126.76 Y105.687 E.01558 +;WIDTH:0.351385 +G3 X127.313 Y105.803 I.006 J1.341 E.0146 +;WIDTH:0.358241 +G3 X127.604 Y106.178 I-.526 J.71 E.0126 +G1 X127.636 Y106.557 E.00997 +G1 X126.905 Y105.83 E.02703 +M204 P1000 +G1 X127.75 Y106.788 F12000 +M204 P1500 +;WIDTH:0.38292 +G1 F7919 +G1 X127.904 Y106.923 E.00579 +;WIDTH:0.41646 +G1 X128.048 Y106.986 E.00488 +;WIDTH:0.449999 +G1 X128.192 Y107.049 E.00532 +G1 X132.799 Y111.656 E.22053 +G1 X133.089 Y111.37 E.01379 +G1 X128.9 Y107.191 E.20029 +;WIDTH:0.41646 +G1 X128.771 Y107.073 E.00543 +;WIDTH:0.38292 +G3 X128.711 Y107.133 I-.04 J.02 E.00315 +G1 X128.73 Y107.062 E.00208 +M204 P1000 +G1 X128.73 Y107.062 F12000 +G1 X129.82 Y107.526 +M204 P1500 +;WIDTH:0.449999 +G1 F7919 +G1 X133.328 Y111.033 E.1679 +G1 X133.517 Y110.647 E.01455 +G1 X130.163 Y107.292 E.16058 +G1 X130.588 Y107.141 E.01527 +G1 X133.57 Y110.123 E.14275 +G1 X133.616 Y109.594 E.01797 +G1 X131.067 Y107.045 E.12202 +G1 X131.659 Y107.061 E.02005 +G1 X133.53 Y108.933 E.08959 +M204 P1000 +G1 X133.53 Y108.933 F12000 +G1 X133.107 Y107.926 +M204 P1500 +;WIDTH:0.4601 +G1 F7919 +G1 X132.603 Y107.43 E.02453 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.107 Y107.926 E-.14695 +;WIPE_END +G1 E-.49305 F2100 +G1 X121.55 Y106.743 Z1.203 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.477448 +G1 F7919 +G1 X121.137 Y106.307 E.0217 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.55 Y106.743 E-.1248 +;WIPE_END +G1 E-.5152 F2100 +G1 X119.739 Y112.481 Z1.105 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F6600 +G3 X117.792 Y112.208 I-.559 J-3.099 E.06768 +G1 X115.998 Y114.002 E.08588 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X125.222 Y112.454 E.0741 +G1 X124.778 Y112.454 E.01503 +G1 X126.326 Y114.002 E.0741 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X130.388 Y112.392 Z1.086 F12000 +G1 Z1 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G2 X132.357 Y112.357 I.93 J-3.108 E.06773 +G1 X134.002 Y114.002 E.07875 +G1 X131.35 Y114.002 E.08977 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y110.109 E.04201 +G1 X133.971 Y110.206 E.00345 +G3 X133.878 Y110.845 I-1.003 J.181 E.02224 +M204 P1000 +;LAYER_CHANGE +;Z:1.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X133.971 Y110.206 I-.909 J-.459 E-.13655 +G1 X134.002 Y110.109 E-.02116 +G1 X134.002 Y111.35 E-.2579 +G1 X133.239 Y112.113 E-.22439 +;WIPE_END +G1 X133.239 Y112.113 Z1 F12000 +G1 X134.368 Y95.632 Z1.288 +;AFTER_LAYER_CHANGE +;1.2 +G1 X134.368 Y95.632 +G1 Z1.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +M73 P13 R18 +M73 Q13 S18 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z1.244 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F4966 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X131.13 Y98.87 E.13748 +G1 X131.13 Y99.609 E.02501 +G1 X130.945 Y99.794 E.00886 +G1 X131.13 Y99.979 E.00886 +G1 X131.13 Y103.454 E.11762 +G1 X134.002 Y106.326 E.13748 +G1 X134.002 Y103.674 E.08977 +G1 X129.453 Y108.223 E.21776 +G2 X127.469 Y107.469 I-1.462 J.861 E.07762 +G1 X127.233 Y107.233 E.0113 +G1 X127.233 Y106.352 E.02982 +G1 X126.982 Y106.101 E.01202 +G1 X126.101 Y106.101 E.02982 +G1 X123.315 Y103.315 E.13336 +G2 X123.787 Y102.566 I-1.484 J-1.458 E.0302 +G2 X123.764 Y101.837 I-.502 J-.349 E.02646 +G1 X122.681 Y101.494 E.03845 +G2 X122.256 Y102.022 I.15 J.555 E.02453 +G1 X122.177 Y102.177 E.00589 +G1 X120.661 Y100.661 E.07257 +G1 X120.638 Y101.289 E.02127 +G1 X120.667 Y101.657 E.01249 +G1 X122.218 Y100.106 E.07425 +G2 X122.625 Y100.588 I.609 J-.101 E.02241 +G1 X123.73 Y100.324 E.03846 +G1 X123.913 Y99.994 E.01277 +G2 X123.362 Y98.962 I-2.326 J.578 E.04 +G1 X126.326 Y95.998 E.14189 +G1 X123.674 Y95.998 E.08977 +G1 X128.018 Y100.342 E.20794 +G1 X128.018 Y101.982 E.05551 +G1 X127.687 Y102.313 E.01584 +G2 X127.893 Y103.563 I2.468 J.235 E.04336 +G1 X127.639 Y103.939 E.01536 +G1 X126.599 Y103.939 E.0352 +G1 X126.366 Y103.753 E.01009 +G1 X126.119 Y103.881 E.00942 +G1 X122.532 Y107.468 E.17171 +G2 X121.827 Y108.173 I-.197 J.508 E.04261 +G1 X120.67 Y109.33 E.05538 +G1 X121.45 Y109.577 E.02769 +G1 X121.613 Y109.895 E.0121 +G3 X118.373 Y111.627 I-2.365 J-.528 E.14113 +G1 X115.998 Y114.002 E.11369 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X125.996 Y111.679 E.11118 +G3 X125.688 Y111.454 I.08 J-.432 E.01336 +G1 X125.463 Y111.679 E.01077 +G1 X124.463 Y111.679 E.03385 +G2 X124.134 Y111.567 I-.197 J.039 E.01423 +G1 X123.964 Y111.64 E.00626 +G1 X126.326 Y114.002 E.11307 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X131.729 Y111.729 E.10881 +G2 X133.186 Y110.507 I-.272 J-1.804 E.06771 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X131.729 Y111.729 I-1.729 J-.582 E-.41569 +G1 X132.492 Y112.492 E-.22431 +;WIPE_END +G1 X128.194 Y109.482 Z1.292 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4966 +G1 X127.431 Y110.245 E.03652 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.194 Y109.482 E-.22424 +;WIPE_END +G1 E-.41576 F2100 +G1 X124.572 Y107.875 Z1.269 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F4966 +G1 X125.307 Y107.887 E.02488 +G1 X125.3 Y111.266 E.11438 +G1 X124.631 Y111.272 E.02265 +G2 X124.259 Y111.073 I-.389 J.28 E.01479 +;WIDTH:0.46197 +G1 X124.007 Y111.165 E.00935 +;WIDTH:0.487568 +G3 X123.367 Y111.338 I-1.06 J-2.658 E.02457 +;WIDTH:0.463507 +G1 X122.997 Y111.302 E.013 +;WIDTH:0.449999 +G3 X122.472 Y110.973 I.37 J-1.175 E.0212 +G3 X122.239 Y110.241 I1.424 J-.856 E.02624 +G3 X122.255 Y107.875 I45.499 J-.885 E.0801 +G1 X122.989 Y107.887 E.02485 +G1 X122.995 Y109.92 E.06881 +G1 X123.065 Y110.417 E.01699 +G1 X123.246 Y110.677 E.01072 +;WIDTH:0.49087 +G1 X123.596 Y110.84 E.01438 +G1 X123.785 Y110.801 E.00719 +;WIDTH:0.457909 +G1 X123.974 Y110.762 E.00666 +;WIDTH:0.449999 +G1 X124.344 Y110.515 E.01506 +G1 X124.485 Y110.238 E.01052 +G2 X124.551 Y108.253 I-12.933 J-1.424 E.06729 +G1 X124.561 Y108.078 E.00593 +G1 X124.929 Y108.253 E.01379 +;WIDTH:0.391322 +G1 X124.929 Y109.595 E.03888 +;WIDTH:0.425327 +G1 X124.912 Y109.938 E.01092 +;WIDTH:0.459332 +G1 X124.895 Y110.281 E.01189 +;WIDTH:0.506017 +G1 X124.871 Y110.394 E.00445 +;WIDTH:0.552702 +G1 X124.848 Y110.507 E.00489 +;WIDTH:0.599387 +G1 X124.825 Y110.62 E.00534 +;WIDTH:0.646072 +G1 X124.801 Y110.733 E.00579 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X124.825 Y110.62 E-.02401 +G1 X124.848 Y110.507 E-.02396 +G1 X124.871 Y110.394 E-.02396 +G1 X124.895 Y110.281 E-.02401 +G1 X124.912 Y109.938 E-.07137 +G1 X124.929 Y109.595 E-.07137 +G1 X124.929 Y108.253 E-.27888 +G1 X124.561 Y108.078 E-.08468 +G1 X124.551 Y108.253 E-.03643 +G3 X124.551 Y108.259 I-13 J.563 E-.00133 +;WIPE_END +G1 X122.849 Y110.809 Z1.254 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.422726 +G1 F4966 +G1 X122.761 Y110.706 E.00428 +;WIDTH:0.424832 +G1 X122.675 Y110.473 E.00789 +G1 X122.616 Y109.893 E.01851 +;WIDTH:0.390838 +G1 X122.613 Y108.253 E.04744 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.616 Y109.893 E-.34081 +G1 X122.675 Y110.473 E-.12115 +G1 X122.761 Y110.706 E-.05161 +G1 X122.849 Y110.809 E-.02815 +;WIPE_END +G1 E-.09828 F2100 +G1 X122.281 Y111.384 Z1.214 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F4966 +G3 X121.827 Y110.052 I1.26 J-1.173 E.04907 +G1 X121.827 Y109.503 E.01858 +G1 X120.587 Y108.263 E.05936 +G1 X121.416 Y108.065 E.02885 +G2 X121.49 Y107.412 I-.358 J-.371 E.02412 +G2 X118.533 Y106.116 I-2.185 J.965 E.11999 +G1 X118.46 Y106.136 E.00256 +G1 X115.998 Y103.674 E.11785 +G1 X115.998 Y106.326 E.08977 +G1 X119.443 Y102.881 E.16491 +G3 X119.443 Y99.443 I2.886 J-1.719 E.12217 +G1 X115.998 Y95.998 E.16491 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X121.733 Y98.697 Z1.275 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F4966 +G3 X122.963 Y99.153 I-.062 J2.06 E.04519 +G1 X123.277 Y99.529 E.01658 +G1 X123.461 Y99.97 E.01617 +G1 X122.686 Y100.155 E.02697 +G2 X121.889 Y99.387 I-1.216 J.464 E.03869 +G1 X121.455 Y99.356 E.01473 +G1 X120.963 Y99.483 E.0172 +G1 X120.564 Y99.826 E.01781 +G1 X120.307 Y100.384 E.02079 +G2 X120.343 Y102.024 I3.642 J.74 E.05599 +G1 X120.588 Y102.513 E.01851 +G1 X121.009 Y102.845 E.01815 +G1 X121.515 Y102.951 E.0175 +G1 X121.973 Y102.871 E.01574 +G1 X122.327 Y102.635 E.0144 +G2 X122.723 Y101.934 I-1.69 J-1.416 E.02741 +G1 X123.473 Y102.172 E.02663 +G3 X122.842 Y103.206 I-1.999 J-.51 E.04162 +G3 X122.088 Y103.56 I-1.384 J-1.971 E.02834 +G3 X120.568 Y103.387 I-.489 J-2.463 E.05262 +G1 X120.034 Y102.995 E.02242 +G3 X119.517 Y102.104 I2.437 J-2.008 E.03503 +G3 X119.513 Y100.248 I4.042 J-.937 E.06336 +G3 X119.965 Y99.401 I4.262 J1.729 E.03256 +G3 X121.163 Y98.73 I1.607 J1.465 E.04729 +G1 X121.53 Y98.709 E.01244 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.163 Y98.73 E-.07639 +G2 X119.965 Y99.401 I.409 J2.136 E-.29032 +G2 X119.513 Y100.248 I3.809 J2.577 E-.19988 +G2 X119.45 Y100.595 I4.045 J.918 E-.07341 +;WIPE_END +G1 X122.899 Y99.694 Z1.262 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.434998 +G1 F4966 +G1 X122.739 Y99.477 E.00879 +;WIDTH:0.42512 +G1 X122.513 Y99.322 E.00871 +;WIDTH:0.387128 +G1 X122.288 Y99.166 E.00784 +;WIDTH:0.349136 +G1 X122.088 Y99.106 E.00532 +;WIDTH:0.322467 +G1 X121.889 Y99.047 E.00482 +;WIDTH:0.295798 +G1 X121.469 Y99.035 E.00883 +;WIDTH:0.308802 +G1 X120.927 Y99.137 E.01219 +;WIDTH:0.313868 +G1 X120.723 Y99.238 E.00513 +;WIDTH:0.353166 +G1 X120.32 Y99.55 E.01315 +;WIDTH:0.385197 +G1 X120.189 Y99.76 E.00704 +;WIDTH:0.417228 +G1 X120.058 Y99.971 E.00773 +;WIDTH:0.44141 +G1 X119.908 Y100.327 E.0128 +;WIDTH:0.457732 +G1 X119.823 Y100.913 E.02042 +;WIDTH:0.459542 +G2 X119.918 Y102.019 I4.212 J.196 E.03857 +;WIDTH:0.455194 +G1 X119.989 Y102.2 E.00667 +;WIDTH:0.426374 +G1 X120.275 Y102.713 E.01873 +;WIDTH:0.37816 +G1 X120.763 Y103.098 E.01733 +;WIDTH:0.33205 +G1 X120.939 Y103.174 E.00461 +;WIDTH:0.29791 +G1 X121.453 Y103.273 E.0111 +;WIDTH:0.33468 +G1 X121.988 Y103.225 E.01303 +;WIDTH:0.356058 +G1 X122.255 Y103.134 E.00734 +;WIDTH:0.40118 +G1 X122.625 Y102.882 E.01334 +;WIDTH:0.439436 +G1 X122.956 Y102.429 E.0185 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.625 Y102.882 E-.11659 +G1 X122.255 Y103.134 E-.09303 +G1 X121.988 Y103.225 E-.05862 +G1 X121.453 Y103.273 E-.11163 +G1 X120.939 Y103.174 E-.10878 +G1 X120.763 Y103.098 E-.03984 +G1 X120.342 Y102.765 E-.11151 +;WIPE_END +G1 X125.856 Y100.102 Z1.307 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.512841 +G1 F4966 +G1 X125.535 Y100.113 E.01255 +;WIDTH:0.531627 +G1 X125.268 Y100.167 E.01107 +;WIDTH:0.558255 +G1 X124.865 Y100.384 E.01961 +;WIDTH:0.567234 +G2 X124.54 Y100.859 I.806 J.901 E.02534 +G1 X125.104 Y100.961 E.02499 +G1 X125.241 Y100.737 E.01145 +;WIDTH:0.558593 +G1 X125.593 Y100.576 E.0166 +;WIDTH:0.524178 +G1 X126.117 Y100.573 E.02097 +G1 X126.293 Y100.708 E.00888 +;WIDTH:0.487089 +G1 X126.469 Y100.843 E.00819 +;WIDTH:0.449999 +G1 X126.547 Y101.16 E.01105 +G1 X126.431 Y101.368 E.00806 +G1 X126.018 Y101.514 E.01483 +;WIDTH:0.447847 +G1 X125.504 Y101.623 E.01769 +;WIDTH:0.477968 +G3 X125.285 Y101.629 I-.114 J-.183 E.00831 +;WIDTH:0.479741 +G1 X125.212 Y101.698 E.00365 +;WIDTH:0.507211 +G1 X125.016 Y101.797 E.00848 +;WIDTH:0.534681 +G1 X124.82 Y101.896 E.00898 +;WIDTH:0.569398 +G2 X124.397 Y102.404 I.848 J1.134 E.02921 +;WIDTH:0.540778 +G1 X124.371 Y102.605 E.00839 +;WIDTH:0.495389 +G1 X124.345 Y102.806 E.00763 +;WIDTH:0.449999 +G2 X124.682 Y103.379 I1.015 J-.212 E.02291 +;WIDTH:0.475476 +G1 X125.041 Y103.545 E.01423 +G1 X125.633 Y103.565 E.0213 +;WIDTH:0.474002 +G1 X125.899 Y103.514 E.00971 +;WIDTH:0.452406 +G1 X126.307 Y103.334 E.01518 +;WIDTH:0.448563 +G1 X126.46 Y103.334 E.00516 +;WIDTH:0.449999 +G3 X126.75 Y103.532 I-.11 J.473 E.01216 +G1 X127.43 Y103.521 E.02302 +G3 X127.283 Y102.228 I4.495 J-1.167 E.04419 +G1 X127.279 Y101 E.04157 +G1 X127.173 Y100.549 E.01568 +G1 X126.95 Y100.31 E.01106 +;WIDTH:0.451776 +G1 X126.625 Y100.158 E.0122 +;WIDTH:0.483443 +G1 X126.342 Y100.127 E.01043 +;WIDTH:0.51511 +G1 X126.059 Y100.095 E.01118 +M204 P1000 +G1 X126.059 Y100.095 F12000 +G1 X126.767 Y100.628 +M204 P1500 +;WIDTH:0.370226 +G1 F4966 +G1 X126.892 Y100.959 E.00963 +;WIDTH:0.374124 +G1 X126.911 Y101.222 E.00726 +;WIDTH:0.418818 +G1 X126.889 Y101.313 E.00293 +;WIDTH:0.463512 +G1 X126.867 Y101.404 E.00327 +;WIDTH:0.508205 +G1 X126.845 Y101.494 E.00358 +;WIDTH:0.552899 +G1 X126.822 Y101.585 E.00398 +;WIDTH:0.597592 +G2 X126.824 Y101.778 I.242 J.095 E.00912 +;WIDTH:0.551519 +G1 X126.847 Y101.88 E.00442 +;WIDTH:0.505445 +G1 X126.87 Y101.982 E.00402 +;WIDTH:0.459372 +G1 X126.894 Y102.085 E.00366 +;WIDTH:0.413298 +G1 X126.917 Y102.187 E.00322 +;WIDTH:0.415784 +G1 X126.894 Y102.471 E.00883 +;WIDTH:0.464344 +G1 X126.87 Y102.754 E.00995 +;WIDTH:0.500648 +G1 X126.86 Y102.829 E.00288 +;WIDTH:0.536952 +G1 X126.851 Y102.904 E.0031 +;WIDTH:0.573256 +G1 X126.841 Y102.98 E.00338 +M204 P1000 +G1 X126.841 Y102.98 F12000 +G1 X126.108 Y101.902 +M204 P1500 +;WIDTH:0.447878 +G1 F4966 +G1 X126.302 Y101.897 E.00653 +;WIDTH:0.449999 +G3 X126.551 Y102.203 I-.209 J.424 E.01378 +G3 X126.373 Y102.825 I-1.221 J-.013 E.02216 +;WIDTH:0.448563 +G1 X126.059 Y103.038 E.0128 +;WIDTH:0.446972 +G1 X125.857 Y103.09 E.00701 +;WIDTH:0.474002 +G1 X125.654 Y103.143 E.00752 +;WIDTH:0.517665 +G1 X125.393 Y103.099 E.01045 +G1 X125.255 Y102.935 E.00846 +;WIDTH:0.483832 +G1 X125.118 Y102.771 E.00783 +;WIDTH:0.494812 +G1 X125.089 Y102.645 E.00486 +;WIDTH:0.539625 +G1 X125.061 Y102.52 E.00529 +;WIDTH:0.584438 +G3 X125.131 Y102.282 I.154 J-.084 E.0124 +;WIDTH:0.539701 +G1 X125.229 Y102.171 E.00612 +;WIDTH:0.494963 +G1 X125.591 Y102.018 E.01477 +;WIDTH:0.447847 +G1 X125.91 Y101.947 E.011 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.591 Y102.018 E-.06791 +G1 X125.229 Y102.171 E-.08167 +G1 X125.131 Y102.282 E-.03077 +G2 X125.061 Y102.52 I.084 J.154 E-.05724 +G1 X125.089 Y102.645 E-.02662 +G1 X125.118 Y102.771 E-.02687 +G1 X125.255 Y102.935 E-.04441 +G1 X125.393 Y103.099 E-.04454 +G1 X125.654 Y103.143 E-.055 +G1 X125.857 Y103.09 E-.0436 +G1 X126.059 Y103.038 E-.04335 +G1 X126.373 Y102.825 E-.07885 +G2 X126.458 Y102.657 I-1.043 J-.635 E-.03917 +;WIPE_END +G1 X128.446 Y98.768 Z1.276 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4966 +G1 X129.18 Y98.78 E.02485 +G1 X129.18 Y103.155 E.14809 +G1 X129.174 Y103.526 E.01256 +G1 X129.159 Y103.532 E.00055 +G1 X128.425 Y103.52 E.02485 +G1 X128.434 Y98.971 E.15398 +G1 X128.802 Y99.146 E.01379 +;WIDTH:0.391052 +G1 X128.802 Y103.155 E.11605 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.802 Y100.075 E-.64 +;WIPE_END +G1 X126.826 Y106.521 Z1.318 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4966 +G1 X126.826 Y107.713 E.04035 +G1 X126.917 Y107.883 E.00653 +;WIDTH:0.451034 +G1 X127.091 Y107.987 E.00688 +;WIDTH:0.459789 +G1 X127.225 Y108.019 E.00478 +;WIDTH:0.492653 +G1 X127.601 Y107.884 E.01494 +;WIDTH:0.529779 +G1 X127.974 Y107.837 E.01522 +;WIDTH:0.577837 +G1 X128.456 Y107.958 E.0221 +G1 X128.683 Y108.096 E.01182 +;WIDTH:0.552557 +G1 X128.91 Y108.234 E.01126 +;WIDTH:0.527276 +G1 X128.979 Y108.318 E.00438 +;WIDTH:0.52658 +G1 X129.125 Y108.531 E.01039 +;WIDTH:0.48829 +G1 X129.271 Y108.745 E.00959 +;WIDTH:0.449999 +G1 X129.376 Y108.833 E.00464 +G1 X129.653 Y108.851 E.0094 +;WIDTH:0.493835 +G1 X129.76 Y108.814 E.00424 +;WIDTH:0.537671 +G1 X129.868 Y108.777 E.0047 +;WIDTH:0.573971 +G3 X130.401 Y108.121 I2.587 J1.558 E.03745 +;WIDTH:0.551262 +G1 X130.755 Y107.921 E.01719 +;WIDTH:0.53559 +G1 X131.199 Y107.836 E.01852 +;WIDTH:0.519389 +G1 X131.7 Y107.89 E.01996 +;WIDTH:0.537168 +G1 X131.988 Y108.01 E.01282 +;WIDTH:0.551842 +G1 X132.303 Y108.254 E.01686 +;WIDTH:0.561 +G3 X132.666 Y108.894 I-2.156 J1.645 E.03179 +;WIDTH:0.525774 +G1 X132.786 Y109.166 E.01194 +;WIDTH:0.490547 +G1 X132.849 Y109.745 E.02168 +;WIDTH:0.47968 +G1 X132.708 Y109.904 E.00772 +;WIDTH:0.50936 +G1 X132.567 Y110.063 E.00824 +;WIDTH:0.558909 +G1 X132.565 Y110.241 E.00764 +;WIDTH:0.602433 +G1 X132.663 Y110.471 E.01163 +G1 X132.499 Y110.759 E.01542 +;WIDTH:0.598537 +G1 X132.278 Y111.003 E.01521 +;WIDTH:0.564502 +G1 X131.877 Y111.231 E.02001 +;WIDTH:0.526692 +G1 X131.492 Y111.307 E.01579 +;WIDTH:0.509017 +G1 X131.006 Y111.288 E.01885 +;WIDTH:0.555133 +G1 X130.585 Y111.152 E.01884 +;WIDTH:0.572222 +G1 X130.263 Y110.911 E.0177 +;WIDTH:0.601284 +G1 X130.047 Y110.652 E.01566 +;WIDTH:0.607342 +G1 X129.897 Y110.369 E.01503 +G1 X129.852 Y110.352 E.00226 +;WIDTH:0.560818 +G1 X129.806 Y110.336 E.0021 +;WIDTH:0.514293 +G1 X129.761 Y110.319 E.00189 +;WIDTH:0.467768 +G1 X129.448 Y110.288 E.01111 +;WIDTH:0.482848 +G1 X129.252 Y110.38 E.00792 +;WIDTH:0.525243 +G1 X129.161 Y110.491 E.00576 +;WIDTH:0.567638 +G1 X129.07 Y110.603 E.0063 +;WIDTH:0.610032 +G1 X128.684 Y111.038 E.02742 +;WIDTH:0.596665 +G1 X128.41 Y111.206 E.0148 +;WIDTH:0.56868 +G1 X128.01 Y111.297 E.01793 +;WIDTH:0.542592 +G1 X127.661 Y111.273 E.01453 +;WIDTH:0.501997 +G1 X127.201 Y111.081 E.01903 +;WIDTH:0.452059 +G1 X126.965 Y111.092 E.00804 +;WIDTH:0.449999 +G1 X126.746 Y111.272 E.0096 +G1 X126.07 Y111.26 E.02289 +G3 X126.091 Y106.508 I205.956 J-1.471 E.16085 +G1 X126.622 Y106.517 E.01798 +G1 X126.448 Y106.886 E.01381 +;WIDTH:0.39105 +G1 X126.448 Y107.713 E.02394 +;WIDTH:0.399858 +G1 X126.45 Y108.017 E.00902 +;WIDTH:0.394428 +G1 X126.445 Y108.339 E.00941 +;WIDTH:0.39514 +G1 X126.45 Y108.978 E.01872 +;WIDTH:0.40449 +G1 X126.455 Y109.178 E.00601 +G1 X126.638 Y108.63 E.01737 +;WIDTH:0.38584 +G1 X126.888 Y108.302 E.01176 +G1 X126.711 Y108.196 E.00588 +;WIDTH:0.399858 +G1 X126.452 Y107.814 E.0137 +M204 P1000 +G1 X126.452 Y107.814 F12000 +G1 X127.476 Y108.374 +M204 P1500 +;WIDTH:0.475149 +G1 F4966 +G1 X127.318 Y108.423 E.00595 +;WIDTH:0.4598 +G1 X127.026 Y108.701 E.01398 +;WIDTH:0.449999 +G1 X126.839 Y109.189 E.01769 +G2 X126.928 Y110.185 I2.568 J.273 E.03406 +G1 X127.059 Y110.431 E.00943 +;WIDTH:0.45158 +G1 X127.326 Y110.682 E.01245 +;WIDTH:0.496871 +G1 X127.713 Y110.813 E.01542 +;WIDTH:0.542592 +G1 X127.898 Y110.811 E.00769 +;WIDTH:0.586932 +G1 X128.249 Y110.687 E.01684 +;WIDTH:0.610032 +G1 X128.492 Y110.426 E.01682 +G1 X128.515 Y110.298 E.00613 +;WIDTH:0.570024 +G1 X128.538 Y110.169 E.00574 +;WIDTH:0.530016 +G1 X128.562 Y110.041 E.00527 +;WIDTH:0.490008 +G1 X128.585 Y109.913 E.00483 +;WIDTH:0.449999 +G2 X128.529 Y109.038 I-2.428 J-.285 E.02984 +;WIDTH:0.498768 +G1 X128.478 Y108.874 E.00651 +;WIDTH:0.547537 +G1 X128.426 Y108.71 E.00722 +;WIDTH:0.596305 +G1 X128.374 Y108.546 E.00792 +G1 X128.086 Y108.362 E.01573 +;WIDTH:0.555162 +G1 X127.878 Y108.338 E.00892 +;WIDTH:0.52459 +G1 X127.671 Y108.314 E.00835 +M204 P1000 +G1 X127.671 Y108.314 F12000 +G1 X126.455 Y109.178 +M204 P1500 +;WIDTH:0.40449 +G1 F4966 +G1 X126.452 Y109.804 E.01882 +;WIDTH:0.398734 +G1 X126.453 Y109.821 E.0005 +;WIDTH:0.397418 +G1 X126.605 Y110.358 E.01645 +;WIDTH:0.367432 +G1 X126.851 Y110.748 E.01244 +G3 X126.436 Y110.907 I-.438 J-.523 E.01221 +G1 X126.436 Y110.4 E.01368 +;WIDTH:0.388054 +G1 X126.446 Y110.008 E.01125 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.436 Y110.4 E-.08149 +G1 X126.436 Y110.907 E-.10536 +G2 X126.851 Y110.748 I-.023 J-.682 E-.09407 +G1 X126.605 Y110.358 E-.09582 +G1 X126.453 Y109.821 E-.11598 +G1 X126.452 Y109.804 E-.00354 +G1 X126.455 Y109.178 E-.13009 +;WIPE_END +G1 E-.01365 F2100 +G1 X129.04 Y109.363 Z1.245 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.568445 +G1 F4966 +G1 X129.045 Y109.957 E.02596 +G1 X129.385 Y109.826 E.01592 +M73 Q14 S18 +;WIDTH:0.59392 +G1 X129.826 Y109.84 E.02021 +M73 P14 R18 +;WIDTH:0.595435 +G1 X130.023 Y109.906 E.00954 +G1 X130.23 Y109.519 E.02016 +;WIDTH:0.59392 +G1 X130.065 Y109.235 E.01505 +G1 X130.06 Y109.187 E.00221 +G1 X129.829 Y109.307 E.01193 +G1 X129.357 Y109.306 E.02163 +;WIDTH:0.568445 +G1 X129.068 Y109.194 E.01354 +G1 X129.036 Y109.16 E.00204 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.068 Y109.194 E-.0097 +G1 X129.357 Y109.306 E-.06441 +G1 X129.829 Y109.307 E-.09809 +G1 X130.06 Y109.187 E-.0541 +G1 X130.065 Y109.235 E-.01003 +G1 X130.23 Y109.519 E-.06826 +G1 X130.023 Y109.906 E-.09121 +G1 X129.826 Y109.84 E-.04318 +G1 X129.385 Y109.826 E-.09169 +G1 X129.045 Y109.957 E-.07572 +G1 X129.044 Y109.795 E-.03361 +;WIPE_END +G1 X130.707 Y108.556 Z1.236 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.572076 +G1 F4966 +G1 X130.58 Y108.677 E.00772 +;WIDTH:0.589273 +G1 X130.46 Y108.902 E.01158 +G1 X130.462 Y108.943 E.00186 +;WIDTH:0.563472 +G1 X130.464 Y108.984 E.00178 +;WIDTH:0.537671 +G1 X130.554 Y109.129 E.00702 +;WIDTH:0.493835 +G1 X130.644 Y109.275 E.00643 +;WIDTH:0.449999 +G1 X130.812 Y109.358 E.00634 +;WIDTH:0.439693 +G1 X131.772 Y109.358 E.03167 +;WIDTH:0.446946 +G1 X131.891 Y109.323 E.00417 +;WIDTH:0.488601 +G1 X131.973 Y109.206 E.00529 +;WIDTH:0.530256 +G1 X132.054 Y109.088 E.0058 +;WIDTH:0.571911 +G1 X132.135 Y108.971 E.00626 +G1 X131.93 Y108.586 E.01919 +;WIDTH:0.553918 +G1 X131.832 Y108.494 E.00571 +;WIDTH:0.532883 +G1 X131.597 Y108.354 E.01114 +;WIDTH:0.521139 +G1 X131.173 Y108.311 E.01695 +;WIDTH:0.534774 +G1 X130.855 Y108.416 E.0137 +M204 P1000 +G1 X130.855 Y108.416 F12000 +G1 X130.684 Y109.787 +M204 P1500 +;WIDTH:0.446678 +G1 F4966 +G1 X130.812 Y109.755 E.00443 +;WIDTH:0.439739 +G1 X131.78 Y109.755 E.03194 +;WIDTH:0.449999 +G1 X131.947 Y109.837 E.0063 +;WIDTH:0.47968 +G1 X132.029 Y109.944 E.0049 +;WIDTH:0.50936 +G1 X132.112 Y110.051 E.00525 +;WIDTH:0.553949 +G1 X132.085 Y110.241 E.00815 +;WIDTH:0.598537 +G1 X132.059 Y110.43 E.00881 +G1 X131.874 Y110.677 E.01426 +;WIDTH:0.563536 +G1 X131.729 Y110.753 E.00709 +;WIDTH:0.537744 +G1 X131.584 Y110.829 E.00674 +;WIDTH:0.511951 +G1 X131.128 Y110.836 E.01779 +;WIDTH:0.555133 +G1 X130.751 Y110.658 E.01776 +;WIDTH:0.580744 +G1 X130.63 Y110.537 E.00765 +;WIDTH:0.607342 +G1 X130.422 Y110.172 E.01972 +G1 X130.496 Y110.064 E.00614 +;WIDTH:0.562145 +G1 X130.569 Y109.955 E.00566 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.496 Y110.064 E-.02726 +G1 X130.422 Y110.172 E-.02721 +G1 X130.63 Y110.537 E-.0873 +G1 X130.751 Y110.658 E-.03556 +G1 X131.128 Y110.836 E-.08664 +G1 X131.584 Y110.829 E-.09477 +G1 X131.729 Y110.753 E-.03402 +G1 X131.874 Y110.677 E-.03402 +G1 X132.059 Y110.43 E-.06413 +G1 X132.085 Y110.241 E-.03965 +G1 X132.112 Y110.051 E-.03988 +G1 X132.029 Y109.944 E-.02814 +G1 X131.947 Y109.837 E-.02801 +G1 X131.889 Y109.809 E-.01341 +;WIPE_END +G1 X132.398 Y109.602 Z1.21 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.619086 +G1 F4966 +G3 X132.416 Y109.532 I-.048 J-.05 E.01714 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X132.398 Y109.602 I-.066 J.02 E-.07435 +;WIPE_END +G1 E-.56565 F2100 +G1 X129.988 Y98.768 Z1.394 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4966 +G1 X130.723 Y98.78 E.02488 +G1 X130.723 Y99.44 E.02234 +G1 X130.584 Y99.579 E.00665 +;WIDTH:0.448952 +G1 X130.536 Y99.864 E.00976 +;WIDTH:0.447102 +G1 X130.585 Y100.007 E.00508 +;WIDTH:0.449999 +G1 X130.723 Y100.147 E.00665 +G1 X130.723 Y103.155 E.10182 +G1 X130.717 Y103.526 E.01256 +G1 X130.702 Y103.532 E.00055 +G1 X129.967 Y103.52 E.02488 +G1 X129.967 Y100.147 E.11417 +G2 X130.154 Y99.881 I-.38 J-.465 E.01115 +;WIDTH:0.448952 +G1 X130.106 Y99.579 E.01032 +;WIDTH:0.449999 +G1 X129.967 Y99.44 E.00665 +G1 X129.98 Y98.972 E.01585 +M204 P1000 +G1 X129.98 Y98.972 F12000 +G1 X130.345 Y100.304 +M204 P1500 +;WIDTH:0.391054 +G1 F4966 +G1 X130.345 Y103.155 E.08253 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.345 Y100.304 E-.59247 +;WIPE_END +G1 E-.04753 F2100 +G1 X119.419 Y106.438 Z1.419 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4966 +G3 X120.649 Y106.893 I-.062 J2.059 E.04518 +G1 X120.963 Y107.269 E.01658 +G1 X121.147 Y107.71 E.01617 +G1 X120.372 Y107.896 E.02698 +G2 X119.575 Y107.127 I-1.216 J.464 E.03872 +G1 X119.141 Y107.096 E.01473 +G1 X118.649 Y107.223 E.0172 +G1 X118.25 Y107.566 E.01781 +G1 X117.993 Y108.124 E.02079 +G2 X118.029 Y109.764 I3.642 J.74 E.05599 +G1 X118.274 Y110.253 E.01851 +G1 X118.695 Y110.585 E.01815 +G1 X119.201 Y110.691 E.0175 +G1 X119.66 Y110.611 E.01577 +G1 X120.013 Y110.375 E.01437 +G2 X120.409 Y109.674 I-1.689 J-1.416 E.02741 +G1 X121.159 Y109.912 E.02663 +G3 X120.528 Y110.946 I-1.999 J-.51 E.04161 +G3 X119.774 Y111.3 I-1.384 J-1.971 E.02834 +G3 X118.254 Y111.127 I-.489 J-2.462 E.05262 +G1 X117.72 Y110.735 E.02242 +G3 X117.203 Y109.844 I2.437 J-2.008 E.03503 +G3 X117.199 Y107.989 I4.042 J-.937 E.06332 +G3 X117.651 Y107.141 I4.262 J1.728 E.03259 +G3 X118.849 Y106.47 I1.608 J1.465 E.04729 +G1 X119.216 Y106.449 E.01244 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.849 Y106.47 E-.07639 +G2 X117.651 Y107.141 I.409 J2.136 E-.29032 +G2 X117.199 Y107.989 I3.81 J2.575 E-.20006 +G2 X117.136 Y108.336 I4.045 J.918 E-.07323 +;WIPE_END +G1 X120.585 Y107.434 Z1.262 F12000 +G1 Z1.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.434994 +G1 F4966 +G1 X120.425 Y107.217 E.00879 +;WIDTH:0.425128 +G1 X120.199 Y107.062 E.00871 +;WIDTH:0.387132 +G1 X119.974 Y106.906 E.00784 +;WIDTH:0.349136 +G1 X119.774 Y106.847 E.00531 +;WIDTH:0.322469 +G1 X119.575 Y106.787 E.00483 +;WIDTH:0.295802 +G1 X119.155 Y106.775 E.00884 +;WIDTH:0.30881 +G1 X118.613 Y106.877 E.01219 +;WIDTH:0.313868 +G1 X118.409 Y106.978 E.00513 +;WIDTH:0.353164 +G1 X118.006 Y107.29 E.01315 +;WIDTH:0.385193 +G1 X117.875 Y107.501 E.00707 +;WIDTH:0.417222 +G1 X117.744 Y107.711 E.0077 +;WIDTH:0.44141 +G1 X117.594 Y108.067 E.0128 +;WIDTH:0.457728 +G1 X117.509 Y108.654 E.02046 +;WIDTH:0.459542 +G2 X117.604 Y109.759 I4.212 J.195 E.03853 +;WIDTH:0.4552 +G1 X117.676 Y109.94 E.00668 +;WIDTH:0.426374 +G1 X117.961 Y110.453 E.01871 +;WIDTH:0.378166 +G1 X118.449 Y110.838 E.01733 +;WIDTH:0.33205 +G1 X118.625 Y110.914 E.00461 +;WIDTH:0.297916 +G1 X119.139 Y111.013 E.0111 +;WIDTH:0.334676 +G1 X119.674 Y110.965 E.01303 +;WIDTH:0.356052 +G1 X119.941 Y110.874 E.00734 +;WIDTH:0.401182 +G1 X120.311 Y110.622 E.01334 +;WIDTH:0.439426 +G1 X120.642 Y110.17 E.01847 +M204 P1000 +;LAYER_CHANGE +;Z:1.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.311 Y110.622 E-.11642 +G1 X119.941 Y110.874 E-.09303 +G1 X119.674 Y110.965 E-.05862 +G1 X119.139 Y111.013 E-.11163 +G1 X118.625 Y110.914 E-.10878 +G1 X118.449 Y110.838 E-.03984 +G1 X118.027 Y110.505 E-.11168 +;WIPE_END +G1 X118.027 Y110.505 Z1.2 F12000 +G1 X134.368 Y95.632 Z1.586 +;AFTER_LAYER_CHANGE +;1.4 +G1 X134.368 Y95.632 +G1 Z1.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2169 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2169 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z1.444 F12000 +G1 Z1.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2169 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z1.562 F12000 +G1 Z1.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2169 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +M73 P15 R18 +M73 Q15 S18 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:1.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z1.4 F12000 +G1 X134.368 Y95.632 Z1.688 +;AFTER_LAYER_CHANGE +;1.6 +G1 X134.368 Y95.632 +G1 Z1.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z1.644 F12000 +G1 Z1.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z1.762 F12000 +G1 Z1.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:1.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;1.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z1.6 F12000 +G1 X134.368 Y95.632 Z1.888 +;AFTER_LAYER_CHANGE +;1.8 +G1 X134.368 Y95.632 +G1 Z1.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +M73 P16 R18 +M73 Q16 S18 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z1.844 F12000 +G1 Z1.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z1.962 F12000 +G1 Z1.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z1.8 F12000 +G1 X134.368 Y95.632 Z2.088 +;AFTER_LAYER_CHANGE +;2 +G1 X134.368 Y95.632 +G1 Z2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M73 Q16 S17 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +M73 P16 R17 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z2.044 F12000 +G1 Z2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P17 R17 +M73 Q17 S17 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z2.162 F12000 +G1 Z2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z2 F12000 +G1 X134.368 Y95.632 Z2.288 +;AFTER_LAYER_CHANGE +;2.2 +G1 X134.368 Y95.632 +G1 Z2.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z2.244 F12000 +G1 Z2.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z2.362 F12000 +G1 Z2.2 F720 +G1 E.8 F1500 +M73 P18 R17 +M73 Q18 S17 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z2.2 F12000 +G1 X134.368 Y95.632 Z2.488 +;AFTER_LAYER_CHANGE +;2.4 +G1 X134.368 Y95.632 +G1 Z2.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z2.444 F12000 +G1 Z2.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z2.562 F12000 +G1 Z2.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z2.4 F12000 +G1 X134.368 Y95.632 Z2.688 +;AFTER_LAYER_CHANGE +;2.6 +G1 X134.368 Y95.632 +G1 Z2.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +M73 P19 R17 +M73 Q19 S17 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z2.644 F12000 +G1 Z2.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z2.762 F12000 +G1 Z2.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:2.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;2.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z2.6 F12000 +G1 X134.368 Y95.632 Z2.888 +;AFTER_LAYER_CHANGE +;2.8 +G1 X134.368 Y95.632 +G1 Z2.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +M73 P20 R17 +M73 Q20 S17 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z2.844 F12000 +G1 Z2.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z2.962 F12000 +G1 Z2.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:3 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z2.8 F12000 +G1 X134.368 Y95.632 Z3.088 +;AFTER_LAYER_CHANGE +;3 +G1 X134.368 Y95.632 +G1 Z3 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2431 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z3.044 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2431 +G1 X134.002 Y98.65 E.0677 +G1 X131.785 Y96.433 E.10613 +G1 X131.9 Y95.998 E.01523 +G1 X134.002 Y95.998 E.07115 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X132.916 Y104.76 E.44241 +G1 X132.916 Y105.24 E.01625 +G1 X123.674 Y95.998 E.44241 +G1 X126.326 Y95.998 E.08977 +G1 X117.084 Y105.24 E.44241 +G1 X117.084 Y104.76 E.01625 +G1 X126.326 Y114.002 E.44241 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z3.162 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2431 +M73 Q21 S17 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +M73 P21 R17 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.105 Y95.998 E.07132 +G1 X118.219 Y96.429 E.01509 +G1 X115.998 Y98.65 E.10632 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X116.066 Y103.513 Z3.099 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.58567 +G1 F2431 +G1 X116.064 Y103.418 E.00429 +;WIDTH:0.580278 +G1 X116.064 Y102.915 E.02247 +G1 X116.413 Y103.063 E.01694 +G1 X116.545 Y103.217 E.00906 +;WIDTH:0.58567 +G1 X116.609 Y103.498 E.01301 +G1 X116.609 Y106.466 E.13395 +;WIDTH:0.585357 +G1 X116.539 Y106.785 E.01473 +G1 X116.327 Y107.011 E.01398 +G1 X116.066 Y107.087 E.01226 +G1 X116.066 Y106.68 E.01836 +;WIDTH:0.58565 +G1 X116.066 Y103.717 E.13371 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y106.68 E-.61575 +G1 X116.066 Y106.797 E-.02425 +;WIPE_END +G1 X118.608 Y96.066 Z3.192 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585671 +G1 F2431 +G1 X121.315 Y96.065 E.12217 +;WIDTH:0.582136 +G1 X121.836 Y96.065 E.02336 +G3 X121.536 Y96.542 I-.775 J-.154 E.02583 +;WIDTH:0.585671 +G1 X121.256 Y96.609 E.01299 +G1 X119.221 Y96.609 E.09184 +;WIDTH:0.585429 +G1 X118.915 Y96.547 E.01408 +G1 X118.715 Y96.373 E.01196 +G1 X118.675 Y96.258 E.00549 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.715 Y96.373 E-.0253 +G1 X118.915 Y96.547 E-.05509 +G1 X119.221 Y96.609 E-.06488 +G1 X121.256 Y96.609 E-.4229 +G1 X121.536 Y96.542 E-.05983 +G2 X121.581 Y96.506 I-.475 J-.631 E-.012 +;WIPE_END +G1 X128.107 Y96.066 Z3.114 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585055 +G1 F2431 +G1 X128.515 Y96.066 E.01839 +;WIDTH:0.585665 +G1 X130.865 Y96.065 E.10605 +;WIDTH:0.582506 +G1 X131.401 Y96.065 E.02405 +G3 X131.232 Y96.445 I-.911 J-.176 E.01882 +G1 X130.979 Y96.579 E.01285 +;WIDTH:0.585665 +G1 X130.82 Y96.609 E.0073 +G1 X128.724 Y96.608 E.09459 +;WIDTH:0.585055 +G3 X128.248 Y96.403 I.067 J-.81 E.02378 +G1 X128.186 Y96.254 E.00728 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.248 Y96.403 E-.03354 +G2 X128.724 Y96.608 I.543 J-.605 E-.10962 +G1 X130.82 Y96.609 E-.43558 +G1 X130.979 Y96.579 E-.03363 +G1 X131.097 Y96.517 E-.02763 +;WIPE_END +G1 X133.392 Y104.079 Z3.138 F12000 +G1 Z3 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585318 +G1 F2431 +G3 X133.686 Y103.533 I.685 J.018 E.02902 +G1 X133.934 Y103.463 E.01162 +G1 X133.934 Y103.664 E.00907 +;WIDTH:0.58556 +G1 X133.94 Y106.366 E.12192 +;WIDTH:0.573677 +G3 X133.479 Y106.104 I.139 J-.783 E.02386 +;WIDTH:0.58556 +G1 X133.391 Y105.782 E.01506 +G1 X133.391 Y104.282 E.06768 +M204 P1000 +;LAYER_CHANGE +;Z:3.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.391 Y105.782 E-.31172 +G1 X133.479 Y106.104 E-.06937 +G2 X133.94 Y106.366 I.601 J-.52 E-.11235 +G1 X133.938 Y105.661 E-.14656 +;WIPE_END +G1 X133.938 Y105.661 Z3 F12000 +G1 X134.368 Y95.632 Z3.2 +;AFTER_LAYER_CHANGE +;3.2 +G1 X134.368 Y95.632 +G1 Z3.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2434 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +M73 Q21 S16 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +M73 P21 R16 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z3.244 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2434 +G1 X134.002 Y98.65 E.0677 +G1 X131.785 Y96.433 E.10613 +G1 X131.9 Y95.998 E.01523 +G1 X134.002 Y95.998 E.07115 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X132.916 Y104.76 E.44241 +G1 X132.916 Y105.24 E.01625 +G1 X123.674 Y95.998 E.44241 +G1 X126.326 Y95.998 E.08977 +G1 X117.084 Y105.24 E.44241 +G1 X117.084 Y104.76 E.01625 +G1 X126.326 Y114.002 E.44241 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z3.362 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2434 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.105 Y95.998 E.07132 +G1 X118.219 Y96.429 E.01509 +G1 X115.998 Y98.65 E.10632 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X116.064 Y102.915 Z3.289 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.580692 +G1 F2434 +G1 X116.391 Y103.027 E.01546 +G1 X116.573 Y103.314 E.0152 +;WIDTH:0.585671 +G1 X116.609 Y103.515 E.00922 +G1 X116.609 Y106.466 E.13318 +;WIDTH:0.585357 +G1 X116.539 Y106.785 E.01473 +G3 X116.327 Y107.011 I-.684 J-.432 E.01406 +G1 X116.066 Y107.087 E.01226 +;WIDTH:0.585671 +G1 X116.064 Y103.434 E.16486 +;WIDTH:0.580692 +G1 X116.064 Y103.119 E.01409 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.064 Y103.434 E-.06546 +G1 X116.066 Y106.199 E-.57454 +;WIPE_END +G1 X121.379 Y96.066 Z3.4 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.58567 +G1 F2434 +G1 X121.983 Y96.063 E.02726 +;WIDTH:0.578985 +G1 X121.851 Y96.394 E.01588 +G1 X121.698 Y96.535 E.00927 +M73 Q22 S16 +;WIDTH:0.58567 +G1 X121.379 Y96.609 E.01478 +G1 X119.221 Y96.609 E.09739 +M73 P22 R16 +;WIDTH:0.58543 +G1 X118.915 Y96.547 E.01408 +G1 X118.715 Y96.373 E.01196 +G1 X118.608 Y96.066 E.01467 +;WIDTH:0.585653 +G1 X121.175 Y96.066 E.11584 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.608 Y96.066 E-.53345 +G1 X118.715 Y96.373 E-.06756 +G1 X118.857 Y96.496 E-.03899 +;WIPE_END +G1 X128.583 Y96.066 Z3.37 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585664 +G1 F2434 +G1 X130.865 Y96.065 E.10299 +;WIDTH:0.582506 +G1 X131.401 Y96.065 E.02405 +G1 X131.266 Y96.409 E.01658 +G3 X130.979 Y96.579 I-.739 J-.92 E.01502 +;WIDTH:0.585664 +G1 X130.82 Y96.609 E.0073 +G1 X128.583 Y96.608 E.10095 +;WIDTH:0.584926 +G1 X128.236 Y96.527 E.01606 +G1 X128.068 Y96.368 E.01042 +G1 X127.966 Y96.066 E.01437 +G1 X128.38 Y96.066 E.01866 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.966 Y96.066 E-.08603 +G1 X128.068 Y96.368 E-.06624 +G1 X128.236 Y96.527 E-.04807 +G1 X128.583 Y96.608 E-.07405 +G1 X130.342 Y96.609 E-.36561 +;WIPE_END +G1 X133.934 Y103.463 Z3.335 F12000 +G1 Z3.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585563 +G1 F2434 +G1 X133.94 Y106.366 E.13099 +;WIDTH:0.573679 +G3 X133.479 Y106.104 I.139 J-.783 E.02386 +;WIDTH:0.585563 +G1 X133.391 Y105.782 E.01506 +G1 X133.391 Y104.079 E.07684 +;WIDTH:0.58535 +G3 X133.738 Y103.518 I.619 J-.005 E.03138 +M204 P1000 +;LAYER_CHANGE +;Z:3.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X133.391 Y104.079 I.272 J.556 E-.14458 +G1 X133.391 Y105.782 E-.3539 +G1 X133.479 Y106.104 E-.06937 +G2 X133.756 Y106.309 I.6 J-.521 E-.07215 +;WIPE_END +G1 X133.756 Y106.309 Z3.2 F12000 +G1 X134.368 Y95.632 Z3.4 +;AFTER_LAYER_CHANGE +;3.4 +G1 X134.368 Y95.632 +G1 Z3.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2429 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z3.444 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2429 +G1 X134.002 Y98.65 E.0677 +G1 X131.785 Y96.433 E.10613 +G1 X131.9 Y95.998 E.01523 +G1 X134.002 Y95.998 E.07115 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X132.916 Y104.76 E.44241 +G1 X132.916 Y105.24 E.01625 +G1 X123.674 Y95.998 E.44241 +G1 X126.326 Y95.998 E.08977 +G1 X117.084 Y105.24 E.44241 +G1 X117.084 Y104.76 E.01625 +G1 X126.326 Y114.002 E.44241 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z3.562 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2429 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.105 Y95.998 E.07132 +G1 X118.219 Y96.429 E.01509 +G1 X115.998 Y98.65 E.10632 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X116.066 Y103.511 Z3.499 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.585671 +G1 F2429 +G1 X116.064 Y103.429 E.0037 +;WIDTH:0.581367 +G1 X116.064 Y102.916 E.02297 +G1 X116.412 Y103.064 E.01693 +G1 X116.544 Y103.216 E.00901 +;WIDTH:0.585671 +G1 X116.609 Y103.511 E.01363 +G1 X116.609 Y106.466 E.13336 +;WIDTH:0.585357 +G1 X116.539 Y106.785 E.01473 +G1 X116.327 Y107.011 E.01398 +G1 X116.066 Y107.087 E.01226 +G1 X116.066 Y106.875 E.00956 +;WIDTH:0.585652 +G1 X116.066 Y103.714 E.14265 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y106.793 E-.64 +;WIPE_END +G1 X118.608 Y96.066 Z3.592 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585427 +G1 F2429 +G1 X119.221 Y96.066 E.02765 +;WIDTH:0.58567 +G1 X121.616 Y96.064 E.10809 +;WIDTH:0.580525 +G1 X122.127 Y96.064 E.02284 +G3 X121.846 Y96.531 I-.77 J-.145 E.02488 +;WIDTH:0.58567 +G1 X121.541 Y96.609 E.01421 +G1 X119.221 Y96.609 E.1047 +;WIDTH:0.585427 +G1 X118.915 Y96.547 E.01408 +G1 X118.715 Y96.373 E.01196 +G1 X118.675 Y96.258 E.00549 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.715 Y96.373 E-.0253 +G1 X118.915 Y96.547 E-.05509 +G1 X119.221 Y96.609 E-.06488 +G1 X121.541 Y96.609 E-.48212 +G1 X121.6 Y96.594 E-.01261 +;WIPE_END +G1 X127.823 Y96.066 Z3.509 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.584825 +G1 F2429 +G1 X128.273 Y96.066 E.02028 +;WIDTH:0.585662 +G1 X130.865 Y96.065 E.11697 +;WIDTH:0.582506 +G1 X131.401 Y96.065 E.02405 +G3 X131.232 Y96.445 I-.911 J-.176 E.01882 +G1 X130.979 Y96.579 E.01285 +;WIDTH:0.585662 +G1 X130.82 Y96.609 E.0073 +G1 X128.424 Y96.608 E.10813 +;WIDTH:0.584825 +G3 X127.917 Y96.356 I.06 J-.755 E.02615 +G1 X127.886 Y96.259 E.00459 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.917 Y96.356 E-.02116 +G2 X128.424 Y96.608 I.566 J-.503 E-.12059 +G1 X130.82 Y96.609 E-.49792 +G1 X130.822 Y96.609 E-.00033 +;WIPE_END +G1 X133.391 Y104.079 Z3.538 F12000 +G1 Z3.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585351 +G1 F2429 +G3 X133.686 Y103.533 I.687 J.018 E.02904 +G1 X133.934 Y103.463 E.01162 +;WIDTH:0.585562 +G1 X133.94 Y106.366 E.13099 +;WIDTH:0.573678 +G3 X133.479 Y106.104 I.139 J-.783 E.02386 +;WIDTH:0.585562 +G1 X133.391 Y105.782 E.01506 +G1 X133.391 Y104.283 E.06764 +M204 P1000 +;LAYER_CHANGE +;Z:3.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.391 Y105.782 E-.31151 +G1 X133.479 Y106.104 E-.06937 +G2 X133.94 Y106.366 I.601 J-.52 E-.11235 +G1 X133.939 Y105.66 E-.14677 +;WIPE_END +G1 X133.939 Y105.66 Z3.4 F12000 +G1 X134.368 Y95.632 Z3.6 +;AFTER_LAYER_CHANGE +;3.6 +G1 X134.368 Y95.632 +G1 Z3.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2547 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +M73 P23 R16 +M73 Q23 S16 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.946 Y96.851 Z3.671 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.423133 +G1 F2400 +G1 X131.636 Y96.161 E.03085 +M204 P1000 +G1 X131.636 Y96.161 F12000 +G1 X131.446 Y95.814 +M204 P800 +G1 F2400 +G1 X130.398 Y96.862 E.04686 +M204 P1000 +G1 X130.398 Y96.862 F12000 +G1 X129.86 Y96.862 +M204 P800 +G1 F2400 +G1 X130.908 Y95.814 E.04686 +M204 P1000 +G1 X130.908 Y95.814 F12000 +G1 X130.37 Y95.814 +M204 P800 +G1 F2400 +G1 X129.323 Y96.862 E.04683 +M204 P1000 +G1 X129.323 Y96.862 F12000 +G1 X128.785 Y96.862 +M204 P800 +G1 F2400 +G1 X129.833 Y95.814 E.04686 +M204 P1000 +G1 X129.833 Y95.814 F12000 +G1 X129.295 Y95.814 +M204 P800 +G1 F2400 +G1 X128.248 Y96.861 E.04681 +M204 P1000 +G1 X128.248 Y96.861 F12000 +G1 X127.824 Y96.747 +M204 P800 +G1 F2400 +G1 X128.757 Y95.814 E.04171 +M204 P1000 +G1 X128.757 Y95.814 F12000 +G1 X128.22 Y95.814 +M204 P800 +G1 F2400 +G1 X127.549 Y96.484 E.02998 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.22 Y95.814 E-.19705 +;WIPE_END +G1 E-.44295 F2100 +G1 X121.787 Y96.853 Z3.714 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.466216 +G1 F2400 +G1 X122.518 Y96.121 E.03641 +M204 P1000 +G1 X122.518 Y96.121 F12000 +G1 X122.227 Y95.814 +M204 P800 +G1 F2400 +G1 X121.179 Y96.862 E.05217 +M204 P1000 +G1 X121.179 Y96.862 F12000 +G1 X120.581 Y96.862 +M204 P800 +G1 F2400 +G1 X121.628 Y95.814 E.05214 +M204 P1000 +G1 X121.628 Y95.814 F12000 +G1 X121.03 Y95.814 +M204 P800 +G1 F2400 +G1 X119.982 Y96.862 E.05217 +M204 P1000 +G1 X119.982 Y96.862 F12000 +G1 X119.383 Y96.862 +M204 P800 +G1 F2400 +G1 X120.431 Y95.814 E.05217 +M204 P1000 +G1 X120.431 Y95.814 F12000 +G1 X119.833 Y95.814 +M204 P800 +G1 F2400 +G1 X118.85 Y96.797 E.04893 +M204 P1000 +G1 X118.85 Y96.797 F12000 +G1 X118.512 Y96.536 +M204 P800 +G1 F2400 +G1 X119.234 Y95.814 E.03594 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.512 Y96.536 E-.21219 +;WIPE_END +G1 E-.42781 F2100 +G1 X115.998 Y100.65 Z3.684 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2547 +G1 X115.998 Y98.65 E.0677 +G1 X118.219 Y96.429 E.10632 +G1 X118.105 Y95.998 E.01509 +G1 X115.998 Y95.998 E.07132 +G1 X134.002 Y114.002 E.86184 +G1 X134.002 Y111.35 E.08977 +G1 X131.35 Y114.002 E.12695 +G1 X126.326 Y114.002 E.17006 +G1 X117.084 Y104.76 E.44241 +G1 X117.084 Y105.24 E.01625 +G1 X126.326 Y95.998 E.44241 +G1 X123.674 Y95.998 E.08977 +G1 X132.916 Y105.24 E.44241 +G1 X132.916 Y104.76 E.01625 +G1 X123.674 Y114.002 E.44241 +G1 X118.65 Y114.002 E.17006 +G1 X115.998 Y111.35 E.12695 +G1 X115.998 Y114.002 E.08977 +G1 X134.002 Y95.998 E.86184 +G1 X131.9 Y95.998 E.07115 +G1 X131.785 Y96.433 E.01523 +G1 X134.002 Y98.65 E.10613 +G1 X134.002 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y98.65 E-.41563 +G1 X133.239 Y97.887 E-.22437 +;WIPE_END +G1 X133.486 Y106.465 Z3.75 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.448643 +G1 F2400 +G1 X134.186 Y105.764 E.03342 +M204 P1000 +G1 X134.186 Y105.764 F12000 +G1 X134.186 Y105.19 +M204 P800 +G1 F2400 +G1 X133.226 Y106.151 E.04583 +M204 P1000 +G1 X133.226 Y106.151 F12000 +G1 X133.139 Y105.664 +M204 P800 +G1 F2400 +G1 X134.186 Y104.616 E.04998 +M204 P1000 +G1 X134.186 Y104.616 F12000 +G1 X134.186 Y104.043 +M204 P800 +G1 F2400 +G1 X133.139 Y105.09 E.04995 +M204 P1000 +G1 X133.139 Y105.09 F12000 +G1 X133.139 Y104.517 +M204 P800 +G1 F2400 +G1 X134.186 Y103.469 E.04998 +M204 P1000 +G1 X134.186 Y103.469 F12000 +G1 X133.864 Y103.217 +M204 P800 +G1 F2400 +G1 X133.149 Y103.933 E.03414 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.864 Y103.217 E-.21028 +;WIPE_END +G1 E-.42972 F2100 +G1 X116.852 Y106.61 Z3.903 F12000 +G1 Z3.6 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.459022 +G1 F2400 +G1 X116.127 Y107.335 E.03547 +M204 P1000 +G1 X116.127 Y107.335 F12000 +G1 X115.814 Y107.06 +M204 P800 +G1 F2400 +G1 X116.862 Y106.012 E.05128 +M204 P1000 +G1 X116.862 Y106.012 F12000 +G1 X116.862 Y105.424 +M204 P800 +G1 F2400 +G1 X115.814 Y106.472 E.05128 +M204 P1000 +G1 X115.814 Y106.472 F12000 +G1 X115.814 Y105.883 +M204 P800 +G1 F2400 +G1 X116.862 Y104.835 E.05128 +M204 P1000 +G1 X116.862 Y104.835 F12000 +G1 X116.862 Y104.247 +M204 P800 +G1 F2400 +G1 X115.814 Y105.295 E.05128 +M204 P1000 +G1 X115.814 Y105.295 F12000 +G1 X115.814 Y104.706 +M204 P800 +G1 F2400 +G1 X116.862 Y103.658 E.05128 +M204 P1000 +G1 X116.862 Y103.658 F12000 +G1 X116.788 Y103.143 +M204 P800 +G1 F2400 +G1 X115.814 Y104.118 E.04768 +M204 P1000 +G1 X115.814 Y104.118 F12000 +G1 X115.814 Y103.529 +M204 P800 +G1 F2400 +G1 X116.529 Y102.814 E.03499 +M204 P1000 +;LAYER_CHANGE +;Z:3.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;3.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.814 Y103.529 E-.21013 +;WIPE_END +G1 E-.42987 F2100 +G1 X115.814 Y103.529 Z3.6 F12000 +G1 X134.368 Y95.632 Z3.952 +;AFTER_LAYER_CHANGE +;3.8 +G1 X134.368 Y95.632 +G1 Z3.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2185 +G1 X134.368 Y103.419 E.26358 +G1 X133.979 Y103.419 E.01317 +G1 X133.691 Y103.491 E.01005 +G1 X133.557 Y103.588 E.0056 +G1 X133.416 Y103.792 E.00839 +G1 X133.368 Y104.03 E.00822 +G1 X133.368 Y105.793 E.05968 +G1 X133.372 Y105.865 E.00244 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G1 X133.979 Y106.404 E.01103 +G1 X134.368 Y106.404 E.01317 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G1 X116.021 Y107.132 E.01317 +G1 X116.319 Y107.055 E.01042 +G1 X116.453 Y106.954 E.00568 +G1 X116.565 Y106.8 E.00645 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +M73 P24 R16 +G1 X116.557 Y103.186 E.01024 +M73 Q24 S16 +G1 X116.374 Y102.98 E.00933 +G1 X116.021 Y102.868 E.01254 +G1 X115.632 Y102.868 E.01317 +G1 X115.632 Y95.632 E.24493 +G1 X118.557 Y95.632 E.09901 +G1 X118.557 Y96.021 E.01317 +G1 X118.662 Y96.363 E.01211 +G1 X118.922 Y96.58 E.01146 +G1 X119.168 Y96.632 E.00851 +G1 X121.273 Y96.632 E.07125 +G1 X121.525 Y96.578 E.00872 +G1 X121.776 Y96.367 E.0111 +G1 X121.883 Y96.021 E.01226 +G1 X121.883 Y95.632 E.01317 +G1 X128.058 Y95.632 E.20902 +G1 X128.058 Y96.021 E.01317 +G1 X128.165 Y96.367 E.01226 +G1 X128.391 Y96.565 E.01017 +G1 X128.668 Y96.632 E.00965 +G1 X130.837 Y96.632 E.07342 +G1 X131.083 Y96.58 E.00851 +G1 X131.344 Y96.362 E.01151 +G1 X131.448 Y96.021 E.01207 +G1 X131.448 Y95.632 E.01317 +G1 X134.308 Y95.632 E.09681 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2185 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G1 X133.838 Y103.882 E.00514 +G1 X133.775 Y104.03 E.00544 +G1 X133.775 Y105.793 E.05968 +G1 X133.83 Y105.932 E.00506 +G1 X133.979 Y105.996 E.00549 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G1 X116.165 Y106.666 E.00527 +G1 X116.225 Y106.521 E.00531 +G1 X116.225 Y103.479 E.10297 +G1 X116.181 Y103.352 E.00455 +G1 X116.021 Y103.275 E.00601 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X118.964 Y95.225 E.12656 +G1 X118.964 Y96.021 E.02694 +G1 X119.04 Y96.18 E.00597 +G1 X119.168 Y96.225 E.00459 +G1 X121.273 Y96.225 E.07125 +G1 X121.402 Y96.179 E.00464 +G1 X121.476 Y96.021 E.00591 +G1 X121.476 Y95.225 E.02694 +G1 X128.465 Y95.225 E.23657 +G1 X128.465 Y96.021 E.02694 +G1 X128.539 Y96.179 E.00591 +G1 X128.668 Y96.225 E.00464 +G1 X130.837 Y96.225 E.07342 +G1 X130.965 Y96.18 E.00459 +G1 X131.041 Y96.021 E.00597 +G1 X131.041 Y95.225 E.02694 +G1 X134.715 Y95.225 E.12436 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z3.844 F12000 +G1 Z3.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2185 +G1 X134.002 Y98.65 E.0677 +G1 X131.717 Y96.365 E.10938 +G1 X131.814 Y95.998 E.01285 +G1 X134.002 Y95.998 E.07406 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z3.962 F12000 +G1 Z3.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2185 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.191 Y95.998 E.07423 +G1 X118.287 Y96.361 E.01271 +G1 X115.998 Y98.65 E.10957 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.762 Y97.887 E-.22437 +;WIPE_END +G1 X116.762 Y97.887 Z3.8 F12000 +G1 X134.368 Y95.632 Z4.11 +;AFTER_LAYER_CHANGE +;4 +G1 X134.368 Y95.632 +G1 Z4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2177 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G1 X133.557 Y103.588 E.0056 +G1 X133.41 Y103.806 E.0089 +G1 X133.368 Y104.03 E.00771 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.561 Y103.193 E.00997 +G1 X116.374 Y102.98 E.00959 +G2 X115.632 Y102.868 I-.587 J1.376 E.02567 +G1 X115.632 Y95.632 E.24493 +G1 X118.706 Y95.632 E.10405 +G1 X118.706 Y96.021 E.01317 +G1 X118.818 Y96.374 E.01254 +G1 X119.075 Y96.583 E.01121 +G1 X119.316 Y96.632 E.00832 +G1 X121.417 Y96.632 E.07112 +G1 X121.693 Y96.566 E.00961 +G1 X121.927 Y96.356 E.01064 +G1 X122.027 Y96.021 E.01183 +G1 X122.027 Y95.632 E.01317 +G1 X127.917 Y95.632 E.19937 +G1 X127.917 Y96.021 E.01317 +G1 X128.017 Y96.356 E.01183 +G1 X128.251 Y96.566 E.01064 +G1 X128.527 Y96.632 E.00961 +G1 X130.694 Y96.632 E.07335 +G1 X130.935 Y96.582 E.00833 +G1 X131.193 Y96.373 E.01124 +G1 X131.305 Y96.021 E.0125 +G1 X131.305 Y95.632 E.01317 +G1 X134.308 Y95.632 E.10165 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2177 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +M73 P25 R16 +M73 Q25 S16 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01048 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 F1690.049 +G1 X119.113 Y95.225 E.1316 +G1 X119.113 Y96.021 E.02694 +G1 F1708.931 +G1 X119.191 Y96.182 E.00606 +G1 F2177 +G1 X119.316 Y96.225 E.00447 +G1 X121.417 Y96.225 E.07112 +G1 X121.549 Y96.176 E.00477 +G1 X121.62 Y96.021 E.00577 +G1 X121.62 Y95.225 E.02694 +G1 X128.324 Y95.225 E.22692 +G1 X128.324 Y96.021 E.02694 +G1 X128.395 Y96.176 E.00577 +G1 X128.527 Y96.225 E.00477 +G1 X130.694 Y96.225 E.07335 +G1 X130.819 Y96.182 E.00447 +G1 F1741.07 +G1 X130.898 Y96.021 E.00607 +G1 F1723.348 +G1 X130.898 Y95.225 E.02694 +G1 X134.715 Y95.225 E.1292 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z4.044 F12000 +G1 Z4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2177 +G1 X134.002 Y98.65 E.0677 +G1 X131.606 Y96.254 E.1147 +G1 X131.671 Y95.998 E.00894 +G1 X134.002 Y95.998 E.0789 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.339 Y95.998 E.07924 +G1 X118.402 Y96.246 E.00866 +G1 X115.998 Y98.65 E.11508 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.762 Y97.886 E-.22437 +;WIPE_END +G1 X116.762 Y97.886 Z4 F12000 +G1 X134.368 Y95.632 Z4.31 +;AFTER_LAYER_CHANGE +;4.2 +G1 X134.368 Y95.632 +G1 Z4.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2166 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G2 X133.44 Y103.742 I.383 J.634 E.01213 +G1 X133.368 Y104.03 E.01005 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.565 Y103.201 E.00968 +G1 X116.374 Y102.98 E.00989 +G2 X115.632 Y102.868 I-.587 J1.376 E.02567 +G1 X115.632 Y95.632 E.24493 +G1 X118.854 Y95.632 E.10906 +G1 X118.854 Y96.021 E.01317 +G1 X118.973 Y96.384 E.01293 +G1 X119.228 Y96.584 E.01097 +G1 X119.465 Y96.632 E.00819 +G1 X121.616 Y96.63 E.07281 +G1 X121.86 Y96.554 E.00865 +G1 X122.078 Y96.345 E.01022 +G1 X122.171 Y96.021 E.01141 +G1 X122.171 Y95.632 E.01317 +G1 X127.775 Y95.632 E.18969 +G1 X127.775 Y96.021 E.01317 +G1 X127.868 Y96.345 E.01141 +G1 X127.978 Y96.476 E.00579 +G1 X128.112 Y96.567 E.00548 +G1 X128.386 Y96.632 E.00953 +G1 X130.551 Y96.632 E.07328 +G1 X130.788 Y96.584 E.00819 +G1 X131.043 Y96.383 E.01099 +G1 X131.161 Y96.021 E.01289 +G1 X131.161 Y95.632 E.01317 +G1 X134.308 Y95.632 E.10652 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2166 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01048 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X118.438 Y95.225 E.10876 +G1 F1690.049 +G1 X119.261 Y95.225 E.02786 +G1 X119.261 Y96.021 E.02694 +G1 F1708.931 +G1 X119.342 Y96.184 E.00616 +G1 F2166 +G1 X119.465 Y96.225 E.00439 +G1 X121.561 Y96.225 E.07095 +G1 X121.697 Y96.173 E.00493 +G1 X121.764 Y96.021 E.00562 +G1 X121.764 Y95.225 E.02694 +G1 X128.183 Y95.225 E.21728 +G1 X128.183 Y96.021 E.02694 +G1 X128.25 Y96.173 E.00562 +G1 X128.386 Y96.225 E.00493 +G1 X130.551 Y96.225 E.07328 +G1 X130.674 Y96.183 E.0044 +G1 F1741.07 +G1 X130.754 Y96.021 E.00612 +G1 F1723.348 +G1 X130.754 Y95.225 E.02694 +G1 X131.573 Y95.225 E.02772 +G1 F2166 +G1 X134.715 Y95.225 E.10635 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z4.244 F12000 +G1 Z4.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2166 +G1 X134.002 Y98.65 E.0677 +G1 X131.496 Y96.144 E.11996 +G1 X131.528 Y95.998 E.00506 +G1 X134.002 Y95.998 E.08374 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P26 R16 +M73 Q26 S15 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +M73 P26 R15 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.488 Y95.998 E.08428 +G1 X118.516 Y96.132 E.00463 +G1 X115.998 Y98.65 E.12054 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:4.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X116.761 Y97.887 Z4.2 F12000 +G1 X134.368 Y95.632 Z4.51 +;AFTER_LAYER_CHANGE +;4.4 +G1 X134.368 Y95.632 +G1 Z4.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2170 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G2 X133.436 Y103.749 I.384 J.634 E.0124 +G1 X133.368 Y104.03 E.00979 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.446 E.10408 +G1 X116.549 Y103.172 E.00968 +G1 X116.374 Y102.98 E.00879 +G2 X115.632 Y102.868 I-.587 J1.376 E.02567 +G1 X115.632 Y95.632 E.24493 +G1 X119.003 Y95.632 E.1141 +G1 X119.003 Y96.021 E.01317 +G1 X119.129 Y96.394 E.01333 +G1 X119.381 Y96.586 E.01072 +G1 X119.613 Y96.632 E.00801 +G1 X121.754 Y96.63 E.07247 +G1 X122.008 Y96.552 E.00899 +G1 X122.23 Y96.333 E.01056 +G1 X122.315 Y96.021 E.01095 +G1 X122.315 Y95.632 E.01317 +G1 X127.634 Y95.632 E.18004 +G1 X127.634 Y96.021 E.01317 +G1 X127.72 Y96.333 E.01095 +G1 X127.825 Y96.465 E.00571 +G1 X127.974 Y96.568 E.00613 +G1 X128.245 Y96.632 E.00943 +G1 X130.408 Y96.632 E.07321 +G1 X130.64 Y96.586 E.00801 +G1 X130.893 Y96.392 E.01079 +G1 X131.018 Y96.021 E.01325 +G1 X131.018 Y95.632 E.01317 +G1 X134.308 Y95.632 E.11136 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2170 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01048 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X118.586 Y95.225 E.11377 +G1 F1690.042 +G1 X119.41 Y95.225 E.02789 +G1 X119.41 Y96.021 E.02694 +G1 F1708.925 +G1 X119.492 Y96.185 E.00621 +G1 F2170 +G1 X119.613 Y96.225 E.00431 +G1 X121.705 Y96.225 E.07081 +G1 X121.845 Y96.169 E.0051 +G1 X121.908 Y96.021 E.00544 +G1 X121.908 Y95.225 E.02694 +G1 X128.041 Y95.225 E.20759 +G1 X128.041 Y96.021 E.02694 +G1 X128.105 Y96.169 E.00546 +G1 X128.245 Y96.225 E.0051 +G1 X130.408 Y96.225 E.07321 +G1 X130.529 Y96.185 E.00431 +G1 F1741.07 +G1 X130.611 Y96.021 E.00621 +G1 F1723.348 +G1 X130.611 Y95.225 E.02694 +G1 X131.429 Y95.225 E.02769 +G1 F2170 +G1 X134.715 Y95.225 E.11123 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z4.444 F12000 +G1 Z4.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2170 +G1 X134.002 Y98.65 E.0677 +G1 X131.385 Y95.998 E.12611 +G1 X134.002 Y95.998 E.08858 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +M73 P27 R15 +M73 Q27 S15 +G1 X118.636 Y96.012 E.12628 +G2 X119.031 Y96.808 I1.21 J-.104 E.03079 +G2 X120.078 Y96.989 I.759 J-1.273 E.03678 +M204 P1000 +;LAYER_CHANGE +;Z:4.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.031 Y96.808 I-.288 J-1.454 E-.22583 +G3 X118.636 Y96.012 I.815 J-.9 E-.18906 +G1 X117.87 Y96.778 E-.22511 +;WIPE_END +G1 X117.87 Y96.778 Z4.4 F12000 +G1 X134.368 Y95.632 Z4.689 +;AFTER_LAYER_CHANGE +;4.6 +G1 X134.368 Y95.632 +G1 Z4.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2168 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G2 X133.433 Y103.756 I.387 J.635 E.01265 +G1 X133.368 Y104.03 E.00953 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.558 Y103.187 E.0102 +G1 X116.374 Y102.98 E.00937 +G2 X115.632 Y102.868 I-.587 J1.376 E.02567 +G1 X115.632 Y95.632 E.24493 +G1 X119.151 Y95.632 E.11911 +G1 X119.151 Y96.021 E.01317 +G1 X119.285 Y96.403 E.0137 +G1 X119.521 Y96.583 E.01005 +G1 X119.762 Y96.632 E.00832 +G1 X121.893 Y96.63 E.07213 +G1 X122.17 Y96.541 E.00985 +G1 X122.381 Y96.321 E.01032 +G1 X122.459 Y96.021 E.01049 +G1 X122.459 Y95.632 E.01317 +G1 X127.493 Y95.632 E.17039 +G1 X127.493 Y96.021 E.01317 +G1 X127.572 Y96.321 E.0105 +G1 X127.673 Y96.454 E.00565 +G1 X127.811 Y96.557 E.00583 +G1 X128.104 Y96.632 E.01024 +G1 X130.264 Y96.632 E.07311 +G1 X130.493 Y96.588 E.00789 +G1 X130.743 Y96.401 E.01057 +G1 X130.875 Y96.021 E.01362 +G1 X130.875 Y95.632 E.01317 +G1 X134.308 Y95.632 E.1162 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2168 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X118.735 Y95.225 E.11881 +G1 F1690.049 +G1 X119.558 Y95.225 E.02786 +G1 X119.558 Y96.021 E.02694 +G1 F1708.931 +G1 X119.641 Y96.185 E.00622 +G1 F2168 +G1 X119.762 Y96.225 E.00431 +G1 X121.849 Y96.225 E.07064 +G1 X121.967 Y96.187 E.0042 +G1 X122.052 Y96.021 E.00631 +G1 X122.052 Y95.225 E.02694 +G1 X127.9 Y95.225 E.19795 +G1 X127.9 Y96.021 E.02694 +G1 X127.96 Y96.166 E.00531 +G1 X128.104 Y96.225 E.00527 +G1 X130.264 Y96.225 E.07311 +G1 X130.384 Y96.187 E.00426 +G1 F1741.07 +G1 X130.468 Y96.021 E.0063 +G1 F1723.348 +G1 X130.468 Y95.225 E.02694 +G1 X131.286 Y95.225 E.02769 +G1 F2168 +G1 X134.715 Y95.225 E.11607 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z4.644 F12000 +G1 Z4.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2168 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X118.785 Y95.998 E.00457 +G2 X120.078 Y96.998 I1.063 J-.038 E.06309 +M204 P1000 +;LAYER_CHANGE +;Z:4.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X118.785 Y95.998 I-.23 J-1.039 E-.38735 +G1 X118.65 Y95.998 E-.02805 +G1 X117.886 Y96.762 E-.2246 +;WIPE_END +G1 X117.886 Y96.762 Z4.6 F12000 +G1 X134.368 Y95.632 Z4.888 +;AFTER_LAYER_CHANGE +;4.8 +G1 X134.368 Y95.632 +G1 Z4.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2168 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G1 X133.557 Y103.588 E.0056 +G1 X133.415 Y103.796 E.00852 +G1 X133.368 Y104.03 E.00808 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +M73 P28 R15 +M73 Q28 S15 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.566 Y103.203 E.00961 +G1 X116.374 Y102.98 E.00996 +G2 X115.632 Y102.868 I-.587 J1.375 E.02567 +G1 X115.632 Y95.632 E.24493 +G1 X119.299 Y95.632 E.12412 +G1 X119.299 Y96.021 E.01317 +G1 X119.44 Y96.411 E.01404 +G1 X119.662 Y96.579 E.00942 +G1 X119.91 Y96.632 E.00858 +G1 X121.993 Y96.632 E.07051 +G1 X122.234 Y96.582 E.00833 +G1 X122.482 Y96.386 E.0107 +G1 X122.603 Y96.021 E.01302 +G1 X122.603 Y95.632 E.01317 +G1 X127.352 Y95.632 E.16075 +G1 X127.352 Y96.021 E.01317 +G1 X127.473 Y96.386 E.01302 +G1 X127.698 Y96.572 E.00988 +G1 X127.963 Y96.632 E.0092 +G1 X129.972 Y96.632 E.068 +G1 X130.346 Y96.589 E.01274 +G1 X130.593 Y96.409 E.01035 +G1 X130.732 Y96.021 E.01395 +G1 X130.732 Y95.632 E.01317 +G1 X134.308 Y95.632 E.12104 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2168 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.188 Y103.362 E.00415 +G1 X116.021 Y103.275 E.00637 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X118.883 Y95.225 E.12382 +G1 F1690.049 +G1 X119.707 Y95.225 E.02789 +G1 X119.707 Y96.021 E.02694 +G1 F1708.931 +G1 X119.753 Y96.151 E.00467 +G1 F2097.933 +G1 X119.91 Y96.225 E.00587 +G1 F2168 +G1 X121.993 Y96.225 E.07051 +G1 X122.102 Y96.193 E.00385 +G1 X122.196 Y96.021 E.00663 +G1 X122.196 Y95.225 E.02694 +G1 X127.759 Y95.225 E.1883 +G1 X127.759 Y96.021 E.02694 +G1 X127.84 Y96.184 E.00616 +G1 X127.963 Y96.225 E.00439 +G1 X130.121 Y96.225 E.07305 +G1 X130.238 Y96.188 E.00415 +G1 F1741.076 +G1 X130.325 Y96.021 E.00637 +G1 F1723.354 +G1 X130.325 Y95.225 E.02694 +G1 X131.143 Y95.225 E.02769 +G1 F2168 +G1 X134.715 Y95.225 E.12091 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z4.844 F12000 +G1 Z4.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2168 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X118.933 Y95.998 E.00958 +G1 X119.143 Y96.633 E.02264 +G2 X120.079 Y96.998 I.816 J-.707 E.03537 +M204 P1000 +;LAYER_CHANGE +;Z:5 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.143 Y96.633 I-.121 J-1.072 E-.21717 +G1 X118.933 Y95.998 E-.13899 +G1 X118.65 Y95.998 E-.05881 +G1 X117.884 Y96.764 E-.22503 +;WIPE_END +G1 X117.884 Y96.764 Z4.8 F12000 +G1 X134.368 Y95.632 Z5.088 +;AFTER_LAYER_CHANGE +;5 +G1 X134.368 Y95.632 +G1 Z5 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2168 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.71 E.0232 +G2 X133.444 Y103.734 I.381 J.633 E.01184 +G1 X133.368 Y104.03 E.01034 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.574 Y103.218 E.00905 +G1 X116.374 Y102.98 E.01052 +G2 X115.632 Y102.868 I-.587 J1.374 E.02568 +G1 X115.632 Y95.632 E.24493 +G1 X119.448 Y95.632 E.12917 +G1 X119.448 Y96.021 E.01317 +G1 X119.595 Y96.419 E.01436 +G1 X119.873 Y96.603 E.01128 +G1 X120.058 Y96.632 E.00634 +G1 X122.137 Y96.632 E.07037 +G1 X122.398 Y96.574 E.00905 +G1 X122.637 Y96.372 E.01059 +G1 X122.747 Y96.021 E.01245 +G1 X122.747 Y95.632 E.01317 +G1 X127.211 Y95.632 E.1511 +G1 X127.211 Y96.021 E.01317 +G1 X127.321 Y96.372 E.01245 +G1 X127.561 Y96.574 E.01062 +G1 X127.822 Y96.632 E.00905 +G1 X129.972 Y96.632 E.07277 +G1 X130.199 Y96.59 E.00781 +G1 X130.443 Y96.417 E.01012 +G1 X130.589 Y96.021 E.01429 +G1 X130.589 Y95.632 E.01317 +G1 X134.308 Y95.632 E.12588 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2168 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +M73 P29 R15 +M73 Q29 S15 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.192 Y103.367 E.00395 +G1 X116.021 Y103.275 E.00657 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.032 Y95.225 E.12886 +G1 F1690.042 +G1 X119.855 Y95.225 E.02786 +G1 X119.855 Y96.021 E.02694 +G1 F1708.925 +G1 X119.904 Y96.154 E.0048 +G1 F2107.136 +G1 X120.058 Y96.225 E.00574 +G1 F2168 +G1 X122.137 Y96.225 E.07037 +G1 X122.248 Y96.192 E.00392 +G1 X122.34 Y96.021 E.00657 +G1 X122.34 Y95.225 E.02694 +G1 X127.618 Y95.225 E.17865 +G1 X127.618 Y96.021 E.02694 +G1 X127.694 Y96.18 E.00597 +G1 X127.822 Y96.225 E.00459 +G1 X129.972 Y96.225 E.07277 +G1 X130.093 Y96.189 E.00427 +G1 F1741.07 +G1 X130.181 Y96.021 E.00642 +G1 F1723.348 +G1 X130.181 Y95.225 E.02694 +G1 X131 Y95.225 E.02772 +G1 F2168 +G1 X134.715 Y95.225 E.12575 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z5.044 F12000 +G1 Z5 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2168 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.081 Y95.998 E.01459 +G1 X119.302 Y96.647 E.02321 +G2 X120.08 Y96.998 I.784 J-.699 E.02975 +M204 P1000 +;LAYER_CHANGE +;Z:5.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.302 Y96.647 I.006 J-1.05 E-.18266 +G1 X119.081 Y95.998 E-.14248 +G1 X118.65 Y95.998 E-.08957 +G1 X117.884 Y96.764 E-.22529 +;WIPE_END +G1 X117.884 Y96.764 Z5 F12000 +G1 X134.368 Y95.632 Z5.288 +;AFTER_LAYER_CHANGE +;5.2 +G1 X134.368 Y95.632 +G1 Z5.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2168 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.709 E.0232 +G2 X133.437 Y103.748 I.384 J.634 E.01236 +G1 X133.368 Y104.03 E.00983 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.581 Y103.234 E.00847 +G1 X116.375 Y102.981 E.01104 +G2 X115.632 Y102.868 I-.588 J1.372 E.02572 +G1 X115.632 Y95.632 E.24493 +G1 X119.596 Y95.632 E.13418 +G1 X119.596 Y96.021 E.01317 +G1 X119.66 Y96.292 E.00943 +G1 X119.75 Y96.426 E.00546 +G1 X120.01 Y96.6 E.01059 +G1 X120.207 Y96.632 E.00676 +G1 X122.281 Y96.632 E.0702 +G1 X122.564 Y96.563 E.00986 +G1 X122.792 Y96.356 E.01042 +G1 X122.891 Y96.021 E.01182 +G1 X122.891 Y95.632 E.01317 +G1 X127.07 Y95.632 E.14145 +G1 X127.07 Y96.021 E.01317 +G1 X127.17 Y96.356 E.01183 +G1 X127.398 Y96.563 E.01042 +G1 X127.681 Y96.632 E.00986 +G1 X129.117 Y96.632 E.04861 +G1 X129.951 Y96.621 E.02823 +G1 X130.181 Y96.524 E.00845 +G1 X130.383 Y96.29 E.01046 +G1 X130.445 Y96.021 E.00934 +G1 X130.445 Y95.632 E.01317 +G1 X134.308 Y95.632 E.13076 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2168 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.189 Y103.362 E.00414 +G1 X116.021 Y103.275 E.0064 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.18 Y95.225 E.13387 +G1 F1690.049 +G1 X120.003 Y95.225 E.02786 +G1 X120.003 Y96.021 E.02694 +G1 F1708.931 +G1 X120.054 Y96.156 E.00488 +G1 F2115.892 +G1 X120.207 Y96.225 E.00568 +G1 F2168 +G1 X122.281 Y96.225 E.0702 +G1 X122.398 Y96.188 E.00415 +G1 X122.484 Y96.021 E.00636 +G1 X122.484 Y95.225 E.02694 +G1 X127.477 Y95.225 E.16901 +G1 X127.477 Y96.021 E.02694 +G1 X127.548 Y96.176 E.00577 +G1 X127.681 Y96.225 E.0048 +G1 X129.835 Y96.225 E.07291 +G1 F2117.495 +G1 X129.988 Y96.156 E.00568 +G1 F1741.07 +G1 X130.038 Y96.021 E.00487 +G1 F1723.348 +G1 X130.038 Y95.225 E.02694 +G1 X130.856 Y95.225 E.02769 +G1 F2168 +G1 X134.715 Y95.225 E.13062 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z5.244 F12000 +G1 Z5.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2168 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P30 R15 +M73 Q30 S15 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.23 Y95.998 E.01963 +G2 X120.075 Y96.979 I.966 J.022 E.04803 +M204 P1000 +;LAYER_CHANGE +;Z:5.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.23 Y95.998 I.121 J-.959 E-.29485 +G1 X118.65 Y95.998 E-.12053 +G1 X117.886 Y96.762 E-.22462 +;WIPE_END +G1 X117.886 Y96.762 Z5.2 F12000 +G1 X134.368 Y95.632 Z5.488 +;AFTER_LAYER_CHANGE +;5.4 +G1 X134.368 Y95.632 +G1 Z5.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2169 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.709 E.0232 +G2 X133.43 Y103.762 I.389 J.636 E.01287 +G1 X133.368 Y104.03 E.00931 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.436 E.10442 +G1 X116.552 Y103.176 E.0092 +G1 X116.375 Y102.981 E.00891 +G2 X115.632 Y102.868 I-.588 J1.371 E.02572 +G1 X115.632 Y95.632 E.24493 +G1 X119.745 Y95.632 E.13922 +G1 X119.745 Y96.021 E.01317 +G1 X119.811 Y96.299 E.00967 +G1 X119.904 Y96.433 E.00552 +G1 X120.148 Y96.596 E.00993 +G1 X120.355 Y96.632 E.00711 +G1 X122.475 Y96.63 E.07176 +G1 X122.741 Y96.544 E.00946 +G1 X122.947 Y96.339 E.00984 +G1 X123.035 Y96.021 E.01117 +G1 X123.035 Y95.632 E.01317 +G1 X126.929 Y95.632 E.13181 +G1 X126.929 Y96.021 E.01317 +G1 X127.018 Y96.339 E.01118 +G1 X127.125 Y96.47 E.00573 +G1 X127.259 Y96.564 E.00554 +G1 X127.539 Y96.632 E.00975 +G1 X128.974 Y96.632 E.04857 +G1 X129.821 Y96.618 E.02867 +G1 X130.038 Y96.524 E.008 +G1 X130.236 Y96.297 E.0102 +G1 X130.302 Y96.021 E.00961 +G1 X130.302 Y95.632 E.01317 +G1 X134.308 Y95.632 E.1356 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2169 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +M73 Q30 S14 +G1 X116.19 Y103.365 E.00404 +G1 X116.021 Y103.275 E.00648 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.328 Y95.225 E.13888 +G1 F1690.049 +G1 X120.152 Y95.225 E.02789 +G1 X120.152 Y96.021 E.02694 +G1 F1708.931 +G1 X120.205 Y96.159 E.005 +G1 F2124.228 +G1 X120.355 Y96.225 E.00555 +G1 F2169 +G1 X122.425 Y96.225 E.07007 +G1 X122.545 Y96.186 E.00427 +G1 X122.628 Y96.021 E.00625 +G1 X122.628 Y95.225 E.02694 +G1 X127.336 Y95.225 E.15936 +M73 P30 R14 +G1 X127.336 Y96.021 E.02694 +G1 X127.401 Y96.171 E.00553 +G1 X127.539 Y96.225 E.00502 +G1 X129.691 Y96.225 E.07284 +G1 F2125.33 +G1 X129.843 Y96.158 E.00562 +G1 F1741.07 +G1 X129.895 Y96.021 E.00496 +G1 F1723.348 +G1 X129.895 Y95.225 E.02694 +G1 X130.713 Y95.225 E.02769 +G1 F2169 +G1 X134.715 Y95.225 E.13546 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z5.444 F12000 +G1 Z5.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2169 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +M73 P31 R14 +M73 Q31 S14 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.378 Y95.998 E.02464 +G2 X120.078 Y96.952 I.981 J.015 E.043 +M204 P1000 +;LAYER_CHANGE +;Z:5.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.378 Y95.998 I.28 J-.94 E-.26397 +G1 X118.65 Y95.998 E-.15129 +G1 X117.885 Y96.763 E-.22474 +;WIPE_END +G1 X117.885 Y96.763 Z5.4 F12000 +G1 X134.368 Y95.632 Z5.688 +;AFTER_LAYER_CHANGE +;5.6 +G1 X134.368 Y95.632 +G1 Z5.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2168 +G1 X134.368 Y103.419 E.26358 +G2 X133.691 Y103.491 I-.161 J1.708 E.0232 +G1 X133.557 Y103.588 E.0056 +G1 X133.423 Y103.776 E.00781 +G1 X133.368 Y104.03 E.0088 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.561 Y103.192 E.01001 +G1 X116.376 Y102.981 E.0095 +G2 X115.632 Y102.868 I-.589 J1.369 E.02575 +G1 X115.632 Y95.632 E.24493 +G1 X119.893 Y95.632 E.14423 +G1 X119.893 Y96.021 E.01317 +G1 X119.963 Y96.306 E.00993 +G1 X120.059 Y96.44 E.00558 +G1 X120.286 Y96.592 E.00925 +G1 X120.504 Y96.632 E.0075 +G1 X122.611 Y96.631 E.07132 +G1 X122.886 Y96.543 E.00977 +G1 X123.101 Y96.32 E.01049 +G1 X123.179 Y96.021 E.01046 +G1 X123.179 Y95.632 E.01317 +G1 X126.788 Y95.632 E.12216 +G1 X126.788 Y96.021 E.01317 +G1 X126.866 Y96.32 E.01046 +G1 X126.967 Y96.453 E.00565 +G1 X127.121 Y96.566 E.00647 +G1 X127.398 Y96.632 E.00964 +G1 X128.832 Y96.632 E.04854 +G1 X129.598 Y96.63 E.02593 +G1 X129.895 Y96.524 E.01067 +G1 X130.09 Y96.303 E.00998 +G1 X130.159 Y96.021 E.00983 +G1 X130.159 Y95.632 E.01317 +G1 X134.308 Y95.632 E.14044 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2168 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.192 Y103.368 E.00392 +G1 X116.021 Y103.275 E.00659 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.477 Y95.225 E.14392 +G1 F1690.049 +G1 X120.3 Y95.225 E.02786 +G1 X120.3 Y96.021 E.02694 +G1 F1708.931 +G1 X120.355 Y96.161 E.00509 +G1 F2132.173 +G1 X120.504 Y96.225 E.00549 +G1 F2168 +G1 X122.569 Y96.225 E.0699 +G1 X122.692 Y96.184 E.00439 +G1 X122.772 Y96.021 E.00615 +G1 X122.772 Y95.225 E.02694 +G1 X127.195 Y95.225 E.14971 +G1 X127.195 Y96.021 E.02694 +G1 X127.254 Y96.165 E.00527 +G1 X127.398 Y96.225 E.00528 +G1 X129.548 Y96.225 E.07277 +G1 F2132.812 +G1 X129.697 Y96.16 E.0055 +G1 F1741.07 +G1 X129.752 Y96.021 E.00506 +G1 F1723.348 +G1 X129.752 Y95.225 E.02694 +G1 X130.57 Y95.225 E.02769 +G1 F2168 +G1 X134.715 Y95.225 E.1403 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z5.644 F12000 +G1 Z5.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2168 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.674 Y95.998 E.44653 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.527 Y95.998 E.02969 +G2 X120.088 Y96.901 I.993 J.008 E.03797 +M204 P1000 +;LAYER_CHANGE +;Z:5.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.527 Y95.998 I.432 J-.894 E-.23311 +G1 X118.65 Y95.998 E-.18225 +G1 X117.886 Y96.762 E-.22464 +;WIPE_END +G1 X117.886 Y96.762 Z5.6 F12000 +G1 X134.368 Y95.632 Z5.888 +;AFTER_LAYER_CHANGE +;5.8 +G1 X134.368 Y95.632 +G1 Z5.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2167 +G1 X134.368 Y103.419 E.26358 +G2 X133.69 Y103.491 I-.161 J1.707 E.02323 +G1 X133.557 Y103.588 E.00557 +G1 X133.417 Y103.79 E.00832 +G1 X133.368 Y104.03 E.00829 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +M73 P32 R14 +M73 Q32 S14 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.569 Y103.208 E.00942 +G1 X116.377 Y102.982 E.01004 +G2 X115.632 Y102.868 I-.59 J1.367 E.02579 +G1 X115.632 Y95.632 E.24493 +G1 X120.042 Y95.632 E.14927 +G1 X120.042 Y96.021 E.01317 +G1 X120.115 Y96.312 E.01016 +G1 X120.213 Y96.446 E.00562 +G1 X120.424 Y96.588 E.00861 +G1 X120.652 Y96.632 E.00786 +G1 X122.826 Y96.622 E.07359 +G1 X123.089 Y96.502 E.00979 +G1 X123.256 Y96.3 E.00887 +G1 X123.324 Y96.021 E.00972 +G1 X123.324 Y95.632 E.01317 +G1 X126.647 Y95.632 E.11248 +G1 X126.647 Y96.021 E.01317 +G1 X126.714 Y96.3 E.00971 +G1 X126.807 Y96.434 E.00552 +G1 X126.984 Y96.568 E.00751 +G1 X127.257 Y96.632 E.00949 +G1 X128.689 Y96.632 E.04847 +G1 X129.437 Y96.631 E.02532 +G1 X129.719 Y96.545 E.00998 +G1 X129.943 Y96.31 E.01099 +G1 X130.016 Y96.021 E.01009 +G1 X130.016 Y95.632 E.01317 +G1 X134.308 Y95.632 E.14528 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2167 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.625 Y95.225 E.14893 +G1 F1690.042 +G1 X120.449 Y95.225 E.02789 +G1 X120.449 Y96.021 E.02694 +G1 F1708.925 +G1 X120.506 Y96.163 E.00518 +G1 F2139.76 +G1 X120.652 Y96.225 E.00537 +G1 F2167 +G1 X122.713 Y96.225 E.06976 +G1 X122.838 Y96.182 E.00447 +G1 X122.916 Y96.021 E.00606 +G1 X122.916 Y95.225 E.02694 +G1 X127.054 Y95.225 E.14007 +G1 X127.054 Y96.021 E.02694 +G1 X127.107 Y96.159 E.005 +G1 X127.257 Y96.225 E.00555 +G1 X129.405 Y96.225 E.07271 +G1 F2139.965 +G1 X129.552 Y96.162 E.00541 +G1 F1741.076 +G1 X129.609 Y96.021 E.00515 +G1 F1723.354 +G1 X129.609 Y95.225 E.02694 +G1 X130.427 Y95.225 E.02769 +G1 F2167 +G1 X134.715 Y95.225 E.14514 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z5.844 F12000 +G1 Z5.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2167 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.69 Y96.014 E.44576 +G1 X126.28 Y95.998 E.08767 +G1 X116.998 Y105.326 E.44543 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.675 Y95.998 E.0347 +G2 X120.106 Y96.831 I1.039 J-.009 E.03294 +M204 P1000 +;LAYER_CHANGE +;Z:6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.675 Y95.998 I.608 J-.843 E-.20222 +G1 X118.65 Y95.998 E-.21301 +G1 X117.885 Y96.763 E-.22477 +;WIPE_END +G1 X117.885 Y96.763 Z5.8 F12000 +G1 X134.368 Y95.632 Z6.088 +;AFTER_LAYER_CHANGE +;6 +G1 X134.368 Y95.632 +G1 Z6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2165 +G1 X134.368 Y103.419 E.26358 +G2 X133.69 Y103.492 I-.161 J1.705 E.02324 +G2 X133.445 Y103.734 I.382 J.632 E.01176 +G1 X133.368 Y104.03 E.01035 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.576 Y103.223 E.00887 +G1 X116.377 Y102.982 E.01058 +G2 X115.632 Y102.868 I-.59 J1.365 E.02579 +G1 X115.632 Y95.632 E.24493 +G1 X120.19 Y95.632 E.15428 +G1 X120.19 Y96.021 E.01317 +G1 X120.267 Y96.318 E.01039 +G1 X120.367 Y96.451 E.00563 +G1 X120.563 Y96.584 E.00802 +G1 X120.801 Y96.632 E.00822 +G1 X122.857 Y96.632 E.06959 +G1 X123.087 Y96.587 E.00793 +G1 X123.327 Y96.411 E.01007 +G1 X123.468 Y96.021 E.01404 +G1 X123.468 Y95.632 E.01317 +G1 X126.505 Y95.632 E.1028 +G1 X126.505 Y96.021 E.01317 +G1 X126.646 Y96.411 E.01404 +G1 X126.857 Y96.574 E.00902 +G1 X127.116 Y96.632 E.00898 +G1 X128.547 Y96.632 E.04844 +G1 X129.301 Y96.631 E.02552 +G1 X129.578 Y96.544 E.00983 +G1 X129.797 Y96.316 E.0107 +G1 X129.872 Y96.021 E.0103 +G1 X129.872 Y95.632 E.01317 +G1 X134.308 Y95.632 E.15015 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2165 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +M73 P33 R14 +M73 Q33 S14 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.774 Y95.225 E.15398 +G1 F1690.049 +G1 X120.597 Y95.225 E.02786 +G1 X120.597 Y96.021 E.02694 +G1 F1708.931 +G1 X120.656 Y96.165 E.00527 +G1 F2147.004 +G1 X120.801 Y96.225 E.00531 +G1 F2165 +G1 X122.857 Y96.225 E.06959 +G1 X122.971 Y96.19 E.00404 +G1 X123.06 Y96.021 E.00647 +G1 X123.06 Y95.225 E.02694 +G1 X126.913 Y95.225 E.13042 +G1 X126.913 Y96.021 E.02694 +G1 X126.983 Y96.175 E.00573 +G1 X127.116 Y96.225 E.00481 +G1 X129.262 Y96.225 E.07264 +G1 F2146.809 +G1 X129.407 Y96.164 E.00532 +G1 F1741.07 +G1 X129.465 Y96.021 E.00522 +G1 F1723.348 +G1 X129.465 Y95.225 E.02694 +G1 X130.284 Y95.225 E.02772 +G1 F2165 +G1 X134.715 Y95.225 E.14998 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z6.044 F12000 +G1 Z6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2165 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.805 Y96.129 E.44026 +G1 X123.834 Y95.998 E.00454 +G1 X126.139 Y95.998 E.07802 +G1 X126.175 Y96.149 E.00525 +G1 X116.998 Y105.326 E.4393 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.824 Y95.998 E.03974 +G2 X120.14 Y96.738 I1.083 J-.026 E.0279 +M204 P1000 +;LAYER_CHANGE +;Z:6.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.824 Y95.998 I.768 J-.765 E-.17131 +G1 X118.65 Y95.998 E-.24397 +G1 X117.885 Y96.763 E-.22472 +;WIPE_END +G1 X117.885 Y96.763 Z6 F12000 +G1 X134.368 Y95.632 Z6.288 +;AFTER_LAYER_CHANGE +;6.2 +G1 X134.368 Y95.632 +G1 Z6.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2162 +G1 X134.368 Y103.419 E.26358 +G2 X133.689 Y103.492 I-.161 J1.703 E.02327 +G2 X133.437 Y103.748 I.386 J.633 E.01228 +G1 X133.368 Y104.03 E.00983 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.583 Y103.239 E.00829 +G1 X116.378 Y102.983 E.0111 +G2 X115.632 Y102.868 I-.592 J1.361 E.02583 +G1 X115.632 Y95.632 E.24493 +G1 X120.338 Y95.632 E.15929 +G1 X120.338 Y96.021 E.01317 +G1 X120.419 Y96.324 E.01062 +G1 X120.521 Y96.457 E.00567 +G1 X120.703 Y96.58 E.00744 +G1 X120.949 Y96.632 E.00851 +G1 X123.001 Y96.632 E.06946 +G1 X123.253 Y96.578 E.00872 +G1 X123.491 Y96.385 E.01037 +G1 X123.612 Y96.021 E.01298 +G1 X123.612 Y95.632 E.01317 +G1 X126.364 Y95.632 E.09315 +G1 X126.364 Y96.021 E.01317 +G1 X126.485 Y96.385 E.01298 +G1 X126.683 Y96.558 E.0089 +G1 X126.975 Y96.632 E.0102 +G1 X128.404 Y96.632 E.04837 +G1 X129.165 Y96.63 E.02576 +G1 X129.437 Y96.543 E.00967 +G1 X129.651 Y96.321 E.01044 +G1 X129.729 Y96.021 E.01049 +G1 X129.729 Y95.632 E.01317 +G1 X134.308 Y95.632 E.15499 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2162 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X119.922 Y95.225 E.15899 +G1 F1690.049 +G1 X120.746 Y95.225 E.02789 +G1 X120.746 Y96.021 E.02694 +G1 F1708.931 +G1 X120.806 Y96.167 E.00534 +G1 F2153.935 +G1 X120.949 Y96.225 E.00522 +G1 F2162 +G1 X123.001 Y96.225 E.06946 +G1 X123.115 Y96.19 E.00404 +G1 X123.205 Y96.021 E.00648 +G1 X123.205 Y95.225 E.02694 +G1 X126.771 Y95.225 E.1207 +G1 X126.771 Y96.021 E.02694 +G1 X126.839 Y96.173 E.00564 +G1 X126.975 Y96.225 E.00493 +G1 X129.119 Y96.225 E.07257 +G1 F2153.364 +G1 X129.262 Y96.166 E.00524 +G1 F1741.07 +G1 X129.322 Y96.021 E.00531 +G1 F1723.348 +G1 X129.322 Y95.225 E.02694 +G1 X130.14 Y95.225 E.02769 +G1 F2162 +G1 X134.715 Y95.225 E.15486 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z6.244 F12000 +G1 Z6.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2162 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P34 R14 +M73 Q34 S14 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X123.915 Y96.239 E.43499 +G1 X123.978 Y95.998 E.00843 +G1 X125.998 Y95.998 E.06837 +G1 X126.067 Y96.257 E.00907 +G1 X116.998 Y105.326 E.43413 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.972 Y95.998 E.04475 +G2 X120.192 Y96.627 I1.155 J-.051 E.02288 +M204 P1000 +;LAYER_CHANGE +;Z:6.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.972 Y95.998 I.935 J-.68 E-.14047 +G1 X118.65 Y95.998 E-.27473 +G1 X117.885 Y96.763 E-.2248 +;WIPE_END +G1 X117.885 Y96.763 Z6.2 F12000 +G1 X134.368 Y95.632 Z6.488 +;AFTER_LAYER_CHANGE +;6.4 +G1 X134.368 Y95.632 +G1 Z6.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y103.419 E.26358 +G2 X133.689 Y103.492 I-.161 J1.701 E.02327 +G2 X133.43 Y103.762 I.39 J.634 E.0128 +G1 X133.368 Y104.03 E.00931 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.555 Y103.181 E.01042 +G1 X116.379 Y102.984 E.00894 +G2 X115.632 Y102.868 I-.593 J1.358 E.02587 +G1 X115.632 Y95.632 E.24493 +G1 X120.487 Y95.632 E.16434 +G1 X120.487 Y96.021 E.01317 +G1 X120.57 Y96.33 E.01083 +G1 X120.8 Y96.555 E.01089 +G1 X121.097 Y96.632 E.01039 +G1 X123.145 Y96.632 E.06932 +G1 X123.431 Y96.561 E.00997 +G1 X123.657 Y96.355 E.01035 +G1 X123.756 Y96.021 E.01179 +G1 X123.756 Y95.632 E.01317 +G1 X126.223 Y95.632 E.0835 +G1 X126.223 Y96.021 E.01317 +G1 X126.322 Y96.355 E.01179 +G1 X126.547 Y96.561 E.01033 +G1 X126.834 Y96.632 E.01001 +G1 X128.262 Y96.632 E.04834 +G1 X129.029 Y96.63 E.02596 +G1 X129.296 Y96.541 E.00953 +G1 X129.504 Y96.327 E.0101 +G1 X129.586 Y96.021 E.01072 +G1 X129.586 Y95.632 E.01317 +G1 X134.308 Y95.632 E.15983 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.071 Y95.225 E.16403 +G1 F1690.042 +G1 X120.894 Y95.225 E.02786 +G1 X120.894 Y96.021 E.02694 +G1 F1708.925 +G1 X120.957 Y96.168 E.00541 +G1 F2160 +G1 X121.097 Y96.225 E.00512 +G1 X123.145 Y96.225 E.06932 +G1 X123.259 Y96.19 E.00404 +G1 X123.349 Y96.021 E.00648 +G1 X123.349 Y95.225 E.02694 +G1 X126.63 Y95.225 E.11106 +G1 X126.63 Y96.021 E.02694 +G1 X126.694 Y96.17 E.00549 +G1 X126.834 Y96.225 E.00509 +G1 X128.975 Y96.225 E.07247 +G1 F2159.649 +G1 X129.117 Y96.167 E.00519 +G1 F1741.07 +G1 X129.179 Y96.021 E.00537 +G1 F1723.348 +G1 X129.179 Y95.225 E.02694 +G1 X129.997 Y95.225 E.02769 +G1 F2160 +G1 X134.715 Y95.225 E.1597 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z6.444 F12000 +G1 Z6.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.03 Y96.354 E.42948 +G1 X124.122 Y95.998 E.01245 +G1 X125.857 Y95.998 E.05873 +G1 X125.953 Y96.371 E.01304 +G1 X116.998 Y105.326 E.42867 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +M73 Q35 S14 +G1 X115.998 Y95.998 E.86184 +M73 P35 R14 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.12 Y95.998 E.04976 +G1 X120.249 Y96.51 E.01787 +M204 P1000 +;LAYER_CHANGE +;Z:6.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.12 Y95.998 E-.10973 +G1 X118.65 Y95.998 E-.30548 +G1 X117.885 Y96.763 E-.22479 +;WIPE_END +G1 X117.885 Y96.763 Z6.4 F12000 +G1 X134.368 Y95.632 Z6.688 +;AFTER_LAYER_CHANGE +;6.6 +G1 X134.368 Y95.632 +G1 Z6.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2157 +G1 X134.368 Y103.419 E.26358 +G2 X133.688 Y103.492 I-.161 J1.698 E.02331 +G1 X133.555 Y103.59 E.00559 +G1 X133.423 Y103.776 E.00772 +G1 X133.368 Y104.03 E.0088 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.56 Y103.19 E.01008 +G1 X116.38 Y102.984 E.00926 +G2 X115.632 Y102.868 I-.594 J1.356 E.02591 +G1 X115.632 Y95.632 E.24493 +G1 X120.635 Y95.632 E.16935 +G1 X120.635 Y96.021 E.01317 +M73 Q35 S13 +G1 X120.722 Y96.335 E.01103 +G1 X120.943 Y96.552 E.01048 +G1 X121.246 Y96.632 E.01061 +G1 X123.357 Y96.628 E.07145 +G1 X123.631 Y96.527 E.00988 +G1 X123.822 Y96.319 E.00956 +G1 X123.9 Y96.021 E.01043 +G1 X123.9 Y95.632 E.01317 +G1 X126.082 Y95.632 E.07386 +G1 X126.082 Y96.021 E.01317 +G1 X126.159 Y96.319 E.01042 +G1 X126.265 Y96.457 E.00589 +G1 X126.413 Y96.564 E.00618 +G1 X126.693 Y96.632 E.00975 +G1 X128.119 Y96.632 E.04827 +G1 X128.893 Y96.629 E.0262 +G1 X129.154 Y96.54 E.00933 +G1 X129.358 Y96.332 E.00986 +G1 X129.443 Y96.021 E.01091 +G1 X129.443 Y95.632 E.01317 +G1 X134.308 Y95.632 E.16467 +G1 X134.775 Y95.225 F12000 +M73 P35 R13 +M204 P800 +;TYPE:External perimeter +G1 F2157 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.219 Y95.225 E.16904 +G1 F1690.049 +G1 X121.042 Y95.225 E.02786 +G1 X121.042 Y96.021 E.02694 +G1 F1708.931 +G1 X121.107 Y96.17 E.0055 +G1 F2157 +G1 X121.246 Y96.225 E.00506 +G1 X123.289 Y96.225 E.06915 +G1 X123.432 Y96.167 E.00522 +G1 X123.493 Y96.021 E.00536 +G1 X123.493 Y95.225 E.02694 +G1 X126.489 Y95.225 E.10141 +G1 X126.489 Y96.021 E.02694 +G1 X126.55 Y96.167 E.00536 +G1 X126.693 Y96.225 E.00522 +G1 X128.832 Y96.225 E.0724 +G1 X128.972 Y96.169 E.0051 +G1 F1741.07 +G1 X129.036 Y96.021 E.00546 +G1 F1723.348 +G1 X129.036 Y95.225 E.02694 +G1 X129.854 Y95.225 E.02769 +G1 F2157 +G1 X134.715 Y95.225 E.16454 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z6.644 F12000 +G1 Z6.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2157 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.153 Y96.477 E.4236 +G1 X124.266 Y95.998 E.01666 +G1 X125.716 Y95.998 E.04908 +G1 X125.833 Y96.491 E.01715 +G1 X116.998 Y105.326 E.42293 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.269 Y95.998 E.0548 +G1 X120.358 Y96.367 E.01285 +M204 P1000 +;LAYER_CHANGE +;Z:6.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.269 Y95.998 E-.07888 +G1 X118.65 Y95.998 E-.33645 +G1 X117.886 Y96.762 E-.22467 +;WIPE_END +G1 X117.886 Y96.762 Z6.6 F12000 +G1 X134.368 Y95.632 Z6.888 +;AFTER_LAYER_CHANGE +;6.8 +G1 X134.368 Y95.632 +G1 Z6.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2155 +G1 X134.368 Y103.419 E.26358 +G2 X133.688 Y103.493 I-.16 J1.696 E.02331 +G1 X133.554 Y103.591 E.00562 +G1 X133.417 Y103.79 E.00818 +G1 X133.368 Y104.03 E.00829 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +M73 P36 R13 +M73 Q36 S13 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.564 Y103.199 E.00975 +G1 X116.381 Y102.985 E.00953 +G2 X115.632 Y102.868 I-.595 J1.352 E.02595 +G1 X115.632 Y95.632 E.24493 +G1 X120.784 Y95.632 E.17439 +G1 X120.784 Y96.021 E.01317 +G1 X120.874 Y96.34 E.01122 +G1 X121.087 Y96.549 E.0101 +G1 X121.394 Y96.632 E.01076 +G1 X123.491 Y96.629 E.07098 +G1 X123.772 Y96.53 E.01008 +G1 X123.988 Y96.276 E.01129 +G1 X124.044 Y96.021 E.00884 +G1 X124.044 Y95.632 E.01317 +G1 X125.941 Y95.632 E.06421 +G1 X125.941 Y96.021 E.01317 +G1 X125.997 Y96.276 E.00884 +G1 X126.115 Y96.449 E.00709 +G1 X126.281 Y96.569 E.00693 +G1 X126.552 Y96.632 E.00942 +G1 X127.976 Y96.632 E.0482 +G1 X128.756 Y96.628 E.0264 +G1 X129.013 Y96.539 E.00921 +G1 X129.211 Y96.337 E.00957 +G1 X129.3 Y96.021 E.01111 +G1 X129.3 Y95.632 E.01317 +G1 X134.308 Y95.632 E.16951 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2155 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.367 Y95.225 E.17405 +G1 F1690.049 +G1 X121.191 Y95.225 E.02789 +G1 X121.191 Y96.021 E.02694 +G1 F1708.931 +G1 X121.257 Y96.171 E.00555 +G1 F2155 +G1 X121.394 Y96.225 E.00498 +G1 X123.433 Y96.225 E.06902 +G1 X123.578 Y96.164 E.00532 +G1 X123.637 Y96.021 E.00524 +G1 X123.637 Y95.225 E.02694 +G1 X126.348 Y95.225 E.09176 +G1 X126.348 Y96.021 E.02694 +G1 X126.406 Y96.164 E.00522 +G1 X126.552 Y96.225 E.00536 +G1 X128.689 Y96.225 E.07233 +G1 X128.827 Y96.171 E.00502 +G1 F1741.076 +G1 X128.892 Y96.021 E.00553 +G1 F1723.354 +G1 X128.892 Y95.225 E.02694 +G1 X129.711 Y95.225 E.02772 +G1 F2155 +G1 X134.715 Y95.225 E.16938 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z6.844 F12000 +G1 Z6.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2155 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.235 Y96.559 E.41967 +G2 X124.41 Y95.998 I-.83 J-.568 E.02019 +G1 X125.575 Y95.998 E.03943 +G2 X125.756 Y96.568 I1.002 J-.006 E.02056 +G1 X116.998 Y105.326 E.41924 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.417 Y95.998 E.05981 +G1 X120.468 Y96.224 E.00784 +M204 P1000 +;LAYER_CHANGE +;Z:7 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.417 Y95.998 E-.04815 +G1 X118.65 Y95.998 E-.3672 +G1 X117.886 Y96.762 E-.22465 +;WIPE_END +G1 X117.886 Y96.762 Z6.8 F12000 +G1 X134.368 Y95.632 Z7.088 +;AFTER_LAYER_CHANGE +;7 +G1 X134.368 Y95.632 +G1 Z7 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2153 +G1 X134.368 Y103.419 E.26358 +G2 X133.687 Y103.493 I-.16 J1.693 E.02335 +G2 X133.442 Y103.738 I.385 J.631 E.01184 +G1 X133.368 Y104.03 E.0102 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.569 Y103.208 E.00942 +G1 X116.382 Y102.986 E.00983 +G2 X115.632 Y102.868 I-.596 J1.348 E.02599 +G1 X115.632 Y95.632 E.24493 +G1 X120.932 Y95.632 E.1794 +G1 X120.932 Y96.021 E.01317 +G1 X121.025 Y96.345 E.01141 +G1 X121.135 Y96.476 E.00579 +G1 X121.328 Y96.593 E.00764 +G1 X121.543 Y96.632 E.0074 +G1 X123.754 Y96.606 E.07484 +G1 X124.021 Y96.441 E.01062 +G1 X124.152 Y96.226 E.00852 +G1 X124.188 Y95.632 E.02014 +G1 X125.8 Y95.632 E.05456 +G1 X125.835 Y96.226 E.02014 +G1 X125.967 Y96.441 E.00854 +G1 X126.151 Y96.574 E.00768 +G1 X126.411 Y96.632 E.00902 +G1 X127.834 Y96.632 E.04817 +G1 X128.619 Y96.628 E.02657 +G1 X128.871 Y96.538 E.00906 +G1 X129.065 Y96.342 E.00933 +G1 X129.156 Y96.021 E.01129 +G1 X129.156 Y95.632 E.01317 +G1 X134.308 Y95.632 E.17439 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2153 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +M73 P37 R13 +M73 Q37 S13 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.238 J.034 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.516 Y95.225 E.17909 +G1 F1690.042 +G1 X121.339 Y95.225 E.02786 +G1 X121.339 Y96.021 E.02694 +G1 F1708.925 +G1 X121.407 Y96.173 E.00564 +G1 F2153 +G1 X121.543 Y96.225 E.00493 +G1 X123.577 Y96.225 E.06885 +G1 X123.725 Y96.161 E.00546 +G1 X123.781 Y96.021 E.0051 +G1 X123.781 Y95.225 E.02694 +G1 X126.207 Y95.225 E.08212 +G1 X126.207 Y96.021 E.02694 +G1 X126.263 Y96.161 E.0051 +G1 X126.411 Y96.225 E.00546 +G1 X128.546 Y96.225 E.07227 +G1 X128.683 Y96.172 E.00497 +G1 F1741.07 +G1 X128.749 Y96.021 E.00558 +G1 F1723.348 +G1 X128.749 Y95.225 E.02694 +G1 X129.567 Y95.225 E.02769 +G1 F2153 +G1 X134.715 Y95.225 E.17425 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z7.044 F12000 +G1 Z7 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2153 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.321 Y96.645 E.41555 +G2 X124.554 Y95.998 I-.815 J-.66 E.02372 +G1 X125.434 Y95.998 E.02979 +G2 X125.672 Y96.652 I1.061 J-.016 E.024 +G1 X116.998 Y105.326 E.41522 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.566 Y95.998 E.06485 +G1 X120.576 Y96.081 E.00283 +M204 P1000 +;LAYER_CHANGE +;Z:7.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.566 Y95.998 E-.01737 +G1 X118.65 Y95.998 E-.39817 +G1 X117.886 Y96.762 E-.22446 +;WIPE_END +G1 X117.886 Y96.762 Z7 F12000 +G1 X134.368 Y95.632 Z7.288 +;AFTER_LAYER_CHANGE +;7.2 +G1 X134.368 Y95.632 +G1 Z7.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2151 +G1 X134.368 Y103.419 E.26358 +G2 X133.686 Y103.494 I-.16 J1.69 E.02339 +G2 X133.438 Y103.746 I.388 J.63 E.01208 +G1 X133.368 Y104.03 E.0099 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.573 Y103.217 E.00909 +G1 X116.383 Y102.987 E.0101 +G2 X115.632 Y102.868 I-.597 J1.345 E.02603 +G1 X115.632 Y95.632 E.24493 +G1 X121.081 Y95.632 E.18444 +G1 X121.081 Y96.021 E.01317 +G1 X121.177 Y96.35 E.0116 +G1 X121.288 Y96.48 E.00579 +G1 X121.463 Y96.588 E.00696 +G1 X121.691 Y96.632 E.00786 +G1 X123.721 Y96.632 E.06871 +G1 X123.949 Y96.588 E.00786 +G1 X124.197 Y96.404 E.01045 +G1 X124.314 Y96.167 E.00895 +G1 X124.332 Y95.632 E.01812 +G1 X125.659 Y95.632 E.04492 +G1 X125.676 Y96.167 E.01812 +G1 X125.794 Y96.404 E.00896 +G1 X125.988 Y96.563 E.00849 +G1 X126.269 Y96.632 E.00979 +G1 X127.691 Y96.632 E.04813 +G1 X128.481 Y96.627 E.02674 +G1 X128.729 Y96.537 E.00893 +G1 X128.919 Y96.347 E.0091 +G1 X129.013 Y96.021 E.01148 +G1 X129.013 Y95.632 E.01317 +G1 X134.308 Y95.632 E.17923 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2151 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.664 Y95.225 E.1841 +G1 F1690.049 +G1 X121.488 Y95.225 E.02789 +G1 X121.488 Y96.021 E.02694 +G1 F1708.931 +G1 X121.557 Y96.174 E.00568 +G1 F2151 +G1 X121.691 Y96.225 E.00485 +G1 X123.75 Y96.223 E.06969 +G1 X123.88 Y96.149 E.00506 +G1 X123.925 Y96.021 E.00459 +G1 X123.925 Y95.225 E.02694 +G1 X126.066 Y95.225 E.07247 +G1 X126.072 Y96.07 E.0286 +G1 X126.149 Y96.186 E.00471 +G1 X126.269 Y96.225 E.00427 +G1 X128.402 Y96.225 E.0722 +G1 X128.538 Y96.173 E.00493 +G1 F1741.07 +G1 X128.606 Y96.021 E.00564 +G1 F1723.348 +G1 X128.606 Y95.225 E.02694 +G1 X129.424 Y95.225 E.02769 +G1 F2151 +G1 X134.715 Y95.225 E.17909 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z7.244 F12000 +G1 Z7.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2151 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P38 R13 +M73 Q38 S13 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.395 Y96.719 E.41201 +G2 X124.688 Y95.998 I-.658 J-.688 E.02714 +G1 X125.302 Y95.998 E.02078 +G2 X125.6 Y96.724 I.952 J.033 E.02738 +G1 X116.998 Y105.326 E.41177 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:7.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z7.2 F12000 +G1 X134.368 Y95.632 Z7.488 +;AFTER_LAYER_CHANGE +;7.4 +G1 X134.368 Y95.632 +G1 Z7.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2149 +G1 X134.368 Y103.419 E.26358 +G2 X133.686 Y103.494 I-.16 J1.686 E.02339 +G2 X133.434 Y103.753 I.39 J.63 E.01236 +G1 X133.368 Y104.03 E.00964 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.577 Y103.225 E.0088 +G1 X116.384 Y102.987 E.01037 +G2 X115.632 Y102.868 I-.598 J1.342 E.02607 +G1 X115.632 Y95.632 E.24493 +G1 X121.229 Y95.632 E.18945 +G1 X121.229 Y96.021 E.01317 +G1 X121.328 Y96.355 E.01179 +G1 X121.441 Y96.484 E.0058 +G1 X121.599 Y96.583 E.00631 +G1 X121.84 Y96.632 E.00832 +G1 X123.865 Y96.632 E.06854 +G1 X124.132 Y96.571 E.00927 +G1 X124.349 Y96.394 E.00948 +G1 X124.471 Y96.097 E.01087 +G1 X124.476 Y95.632 E.01574 +G1 X125.518 Y95.632 E.03527 +G1 X125.518 Y96.021 E.01317 +G1 X125.644 Y96.394 E.01333 +G1 X125.862 Y96.571 E.0095 +G1 X126.128 Y96.632 E.00924 +G1 X128.259 Y96.632 E.07213 +G1 X128.55 Y96.558 E.01016 +G1 X128.773 Y96.352 E.01028 +G1 X128.87 Y96.021 E.01168 +G1 X128.87 Y95.632 E.01317 +G1 X134.308 Y95.632 E.18407 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2149 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.813 Y95.225 E.18915 +G1 F1690.049 +G1 X121.636 Y95.225 E.02786 +G1 X121.636 Y96.021 E.02694 +G1 F1708.931 +G1 X121.707 Y96.176 E.00577 +G1 F2149 +G1 X121.84 Y96.225 E.0048 +G1 X123.865 Y96.225 E.06854 +G1 X123.983 Y96.188 E.00419 +G1 X124.069 Y96.021 E.00636 +G1 X124.069 Y95.225 E.02694 +G1 X125.925 Y95.225 E.06282 +G1 X125.925 Y96.021 E.02694 +G1 X126.011 Y96.188 E.00636 +G1 X126.128 Y96.225 E.00415 +G1 X128.259 Y96.225 E.07213 +G1 X128.393 Y96.175 E.00484 +G1 F1741.07 +G1 X128.463 Y96.021 E.00573 +G1 F1723.348 +G1 X128.463 Y95.225 E.02694 +G1 X129.281 Y95.225 E.02769 +G1 F2149 +G1 X134.715 Y95.225 E.18393 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z7.444 F12000 +G1 Z7.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2149 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.464 Y96.788 E.40871 +G2 X124.839 Y95.998 I-.671 J-.802 E.03054 +G1 X125.155 Y95.998 E.0107 +G2 X125.533 Y96.791 I1.048 J-.013 E.03068 +G1 X116.998 Y105.326 E.40857 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +M73 Q39 S13 +G1 X115.998 Y95.998 E.86184 +M73 P39 R13 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:7.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z7.4 F12000 +G1 X134.368 Y95.632 Z7.688 +;AFTER_LAYER_CHANGE +;7.6 +G1 X134.368 Y95.632 +G1 Z7.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2142 +G1 X134.368 Y103.419 E.26358 +G2 X133.685 Y103.494 I-.16 J1.683 E.02342 +G2 X133.43 Y103.761 I.393 J.631 E.01263 +G1 X133.368 Y104.028 E.00928 +G1 X133.372 Y105.865 E.06218 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.581 Y103.234 E.00847 +G1 X116.385 Y102.988 E.01065 +G2 X115.632 Y102.868 I-.599 J1.338 E.02611 +G1 X115.632 Y95.632 E.24493 +G1 X121.377 Y95.632 E.19446 +G1 X121.377 Y96.021 E.01317 +G1 X121.48 Y96.36 E.01199 +G1 X121.594 Y96.488 E.0058 +G1 X121.735 Y96.577 E.00564 +G1 X121.988 Y96.632 E.00876 +G1 X124.009 Y96.632 E.06841 +G1 X124.257 Y96.58 E.00858 +G1 X124.497 Y96.388 E.0104 +;WIDTH:0.494655 +G1 X124.55 Y96.312 E.00348 +;WIDTH:0.539311 +G1 X124.602 Y96.235 E.00384 +;WIDTH:0.583967 +G1 X124.654 Y96.159 E.00414 +;WIDTH:0.628623 +G1 X124.706 Y96.082 E.00453 +G1 X124.707 Y95.72 E.01763 +;WIDTH:0.624847 +G1 X125.289 Y95.72 E.02816 +;WIDTH:0.628623 +G1 X125.29 Y96.082 E.01763 +G1 X125.342 Y96.159 E.00453 +;WIDTH:0.583967 +G1 X125.395 Y96.235 E.00417 +;WIDTH:0.539311 +G1 X125.447 Y96.312 E.00384 +;WIDTH:0.494655 +G1 X125.499 Y96.388 E.00346 +;WIDTH:0.449999 +G1 X125.74 Y96.58 E.01043 +G1 X125.986 Y96.632 E.00851 +G1 X128.116 Y96.632 E.0721 +G1 X128.411 Y96.556 E.01031 +G1 X128.627 Y96.356 E.00996 +G1 X128.727 Y96.021 E.01183 +G1 X128.727 Y95.632 E.01317 +G1 X134.308 Y95.632 E.18891 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2142 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.029 I.031 J.235 E.01047 +G1 X133.775 Y105.793 E.05971 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X120.961 Y95.225 E.19416 +G1 F1690.049 +G1 X121.785 Y95.225 E.02789 +G1 X121.785 Y96.021 E.02694 +G1 F1708.931 +G1 X121.857 Y96.177 E.00582 +G1 F2142 +G1 X121.988 Y96.225 E.00472 +G1 X124.032 Y96.224 E.06919 +G1 X124.172 Y96.144 E.00546 +G1 X124.213 Y96.021 E.00439 +G1 X124.213 Y95.225 E.02694 +G1 X125.784 Y95.225 E.05318 +G1 X125.784 Y96.039 E.02755 +G1 X125.874 Y96.191 E.00598 +G1 X125.987 Y96.225 E.00399 +G1 X128.116 Y96.225 E.07206 +G1 X128.248 Y96.176 E.00477 +G1 F1741.07 +G1 X128.32 Y96.021 E.00578 +G1 F1723.348 +G1 X128.32 Y95.225 E.02694 +G1 X129.138 Y95.225 E.02769 +G1 F2142 +G1 X134.715 Y95.225 E.18877 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z7.644 F12000 +G1 Z7.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2142 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.525 Y96.849 E.40579 +G1 X124.998 Y96.361 E.023 +G1 X125.473 Y96.851 E.0231 +G1 X116.998 Y105.326 E.40569 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:7.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +M73 Q39 S12 +;WIPE_END +G1 X117.887 Y96.761 Z7.6 F12000 +G1 X134.368 Y95.632 Z7.888 +;AFTER_LAYER_CHANGE +;7.8 +G1 X134.368 Y95.632 +G1 Z7.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y103.419 E.26358 +G2 X133.684 Y103.495 I-.16 J1.679 E.02346 +G1 X133.55 Y103.594 E.00564 +G1 X133.426 Y103.769 E.00726 +G1 X133.368 Y104.03 E.00905 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +M73 P39 R12 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +M73 Q40 S12 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +M73 P40 R12 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.585 Y103.243 E.00815 +G1 X116.387 Y102.989 E.0109 +G2 X115.632 Y102.868 I-.602 J1.334 E.02619 +G1 X115.632 Y95.632 E.24493 +G1 X121.526 Y95.632 E.1995 +G1 X121.526 Y96.021 E.01317 +G1 X121.631 Y96.364 E.01214 +G1 X121.871 Y96.572 E.01075 +G1 X122.136 Y96.632 E.0092 +G1 X124.153 Y96.632 E.06827 +G1 X124.37 Y96.592 E.00747 +G1 X124.612 Y96.425 E.00995 +;WIDTH:0.485509 +G1 X124.779 Y96.076 E.01424 +G1 X124.78 Y95.648 E.01575 +;WIDTH:0.482276 +G1 X125.219 Y95.648 E.01604 +G1 X125.219 Y96.021 E.01363 +G1 X125.388 Y96.425 E.016 +;WIDTH:0.449999 +G1 X125.63 Y96.592 E.00995 +G1 X125.846 Y96.632 E.00744 +G1 X127.305 Y96.632 E.04939 +G1 X128.018 Y96.63 E.02413 +G1 X128.272 Y96.554 E.00897 +G1 X128.48 Y96.361 E.0096 +G1 X128.583 Y96.021 E.01203 +G1 X128.583 Y95.632 E.01317 +G1 X134.308 Y95.632 E.19378 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01047 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.11 Y95.225 E.1992 +G1 F1690.042 +G1 X121.933 Y95.225 E.02786 +G1 X121.933 Y96.021 E.02694 +G1 F1708.925 +G1 X122.007 Y96.178 E.00587 +G1 F2141 +G1 X122.136 Y96.225 E.00465 +G1 X124.153 Y96.225 E.06827 +G1 X124.306 Y96.156 E.00568 +G1 X124.357 Y96.021 E.00488 +G1 X124.357 Y95.225 E.02694 +G1 X125.643 Y95.225 E.04353 +G1 X125.643 Y96.021 E.02694 +G1 X125.693 Y96.156 E.00487 +G1 X125.846 Y96.225 E.00568 +G1 X127.973 Y96.225 E.072 +G1 X128.104 Y96.177 E.00472 +G1 F1741.076 +G1 X128.176 Y96.021 E.00582 +G1 F1723.354 +G1 X128.176 Y95.225 E.02694 +G1 X128.995 Y95.225 E.02772 +G1 F2141 +G1 X134.715 Y95.225 E.19361 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z7.844 F12000 +G1 Z7.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.573 Y96.897 E.40349 +G2 X125 Y96.455 I-.3 J-.717 E.02139 +G2 X125.427 Y96.897 I.726 J-.275 E.02139 +G1 X116.998 Y105.326 E.40349 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z7.8 F12000 +G1 X134.368 Y95.632 Z8.088 +;AFTER_LAYER_CHANGE +;8 +G1 X134.368 Y95.632 +G1 Z8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2204 +G1 X134.368 Y103.419 E.26358 +G2 X133.683 Y103.495 I-.16 J1.676 E.02349 +G1 X133.55 Y103.595 E.00563 +G1 X133.423 Y103.777 E.00751 +G1 X133.368 Y104.03 E.00876 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.556 Y103.183 E.01034 +G1 X116.388 Y102.99 E.00866 +G2 X115.632 Y102.868 I-.603 J1.33 E.02623 +G1 X115.632 Y95.632 E.24493 +G1 X121.674 Y95.632 E.20451 +G1 X121.674 Y96.021 E.01317 +G1 X121.782 Y96.368 E.0123 +G1 X122.008 Y96.566 E.01017 +G1 X122.285 Y96.632 E.00964 +G1 X124.437 Y96.616 E.07284 +G1 X124.716 Y96.466 E.01072 +;WIDTH:0.497967 +G1 X124.788 Y96.37 E.00454 +;WIDTH:0.545934 +G1 X124.859 Y96.274 E.00499 +;WIDTH:0.593901 +G1 X124.93 Y96.179 E.00543 +;WIDTH:0.641868 +G1 X125.001 Y96.083 E.00595 +G1 X125.072 Y96.179 E.00595 +;WIDTH:0.593901 +G1 X125.143 Y96.274 E.00543 +;WIDTH:0.545934 +G1 X125.215 Y96.37 E.00502 +;WIDTH:0.497967 +G1 X125.286 Y96.466 E.00452 +;WIDTH:0.449999 +G1 X125.444 Y96.574 E.00648 +G1 X125.705 Y96.632 E.00905 +G1 X126.9 Y96.632 E.04045 +G1 X127.877 Y96.63 E.03307 +G1 X128.133 Y96.551 E.00907 +G1 X128.334 Y96.365 E.00927 +G1 X128.44 Y96.021 E.01218 +G1 X128.44 Y95.632 E.01317 +G1 X134.308 Y95.632 E.19862 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.368 Y95.632 E-.01247 +G1 X134.368 Y98.652 E-.62753 +;WIPE_END +G1 X125.001 Y95.725 Z8.171 F12000 +G1 Z8 F720 +G1 E.8 F1500 +;WIDTH:0.641868 +G1 F2100 +G1 X125.001 Y96.083 E.01783 +G1 E-.16 +;WIPE_START +G1 F9600 +G1 X125.001 Y95.725 E-.0744 +G1 X125.001 Y96.083 E-.0744 +;WIPE_END +G1 E-.4912 F2100 +G1 X134.775 Y95.225 Z8.171 F12000 +G1 Z8 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2204 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +M73 P41 R12 +M73 Q41 S12 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.258 Y95.225 E.20421 +G1 F1690.049 +G1 X122.081 Y95.225 E.02786 +G1 X122.081 Y96.021 E.02694 +G1 F1708.931 +G1 X122.156 Y96.179 E.00592 +G1 F2204 +G1 X122.285 Y96.225 E.00464 +G1 X124.297 Y96.225 E.0681 +G1 X124.437 Y96.169 E.0051 +G1 X124.501 Y96.021 E.00546 +G1 X124.501 Y95.225 E.02694 +G1 X125.501 Y95.225 E.03385 +G1 X125.501 Y96.021 E.02694 +G1 X125.565 Y96.169 E.00546 +G1 X125.705 Y96.225 E.0051 +G1 X127.83 Y96.225 E.07193 +G1 F2201.906 +G1 X127.959 Y96.178 E.00465 +G1 F1741.07 +G1 X128.033 Y96.021 E.00587 +G1 F1723.348 +G1 X128.033 Y95.225 E.02694 +G1 X128.851 Y95.225 E.02769 +G1 F2204 +G1 X134.715 Y95.225 E.19849 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z8.044 F12000 +G1 Z8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2204 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.615 Y96.939 E.40148 +G1 X125.001 Y96.688 E.01559 +G1 X125.386 Y96.938 E.01554 +G1 X116.998 Y105.326 E.40153 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z8 F12000 +G1 X134.368 Y95.632 Z8.288 +;AFTER_LAYER_CHANGE +;8.2 +G1 X134.368 Y95.632 +G1 Z8.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2206 +G1 X134.368 Y103.419 E.26358 +G2 X133.682 Y103.496 I-.16 J1.672 E.02353 +G1 X133.549 Y103.596 E.00563 +G1 X133.419 Y103.785 E.00776 +G1 X133.368 Y104.03 E.00847 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.625 Y103.387 E.10608 +G1 X116.511 Y103.113 E.01005 +G1 X116.31 Y102.94 E.00898 +G2 X115.632 Y102.868 I-.517 J1.633 E.02323 +G1 X115.632 Y95.632 E.24493 +G1 X121.823 Y95.632 E.20956 +G1 X121.823 Y96.021 E.01317 +G1 X121.934 Y96.373 E.01249 +G1 X122.146 Y96.56 E.00957 +G1 X122.433 Y96.632 E.01002 +G1 X124.503 Y96.629 E.07007 +G1 X124.805 Y96.509 E.011 +G1 X124.903 Y96.29 E.00812 +;WIDTH:0.404784 +G1 X125.001 Y96.071 E.00722 +G1 X125.098 Y96.29 E.00721 +;WIDTH:0.449999 +G1 X125.196 Y96.509 E.00812 +G1 X125.348 Y96.593 E.00588 +G1 X125.564 Y96.632 E.00743 +G1 X126.828 Y96.632 E.04278 +G1 X127.736 Y96.63 E.03073 +G1 X127.994 Y96.549 E.00915 +G1 X128.188 Y96.369 E.00896 +G1 X128.297 Y96.021 E.01234 +G1 X128.297 Y95.632 E.01317 +G1 X134.308 Y95.632 E.20346 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.368 Y95.632 E-.01247 +G1 X134.368 Y98.652 E-.62753 +;WIPE_END +G1 X125.001 Y95.585 Z8.372 F12000 +G1 Z8.2 F720 +G1 E.8 F1500 +;WIDTH:0.359568 +G1 F2100 +G1 X125.001 Y96.071 E.0128 +G1 E-.16 +;WIPE_START +G1 F9600 +G1 X125.001 Y95.585 E-.101 +G1 X125.001 Y96.071 E-.101 +;WIPE_END +G1 E-.438 F2100 +G1 X134.775 Y95.225 Z8.371 F12000 +G1 Z8.2 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2206 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.406 Y95.225 E.20922 +G1 F1690.042 +G1 X122.23 Y95.225 E.02789 +G1 X122.23 Y96.021 E.02694 +G1 F1708.925 +G1 X122.306 Y96.18 E.00597 +G1 F2206 +G1 X122.433 Y96.225 E.00456 +G1 X124.437 Y96.225 E.06783 +G1 X124.56 Y96.184 E.00439 +G1 X124.641 Y96.021 E.00616 +G1 X124.641 Y95.225 E.02694 +G1 X125.36 Y95.225 E.02434 +G1 X125.36 Y96.021 E.02694 +G1 X125.441 Y96.184 E.00616 +G1 X125.564 Y96.225 E.00439 +G1 X127.686 Y96.225 E.07183 +G1 X127.815 Y96.179 E.00464 +G1 F1741.07 +G1 X127.89 Y96.021 E.00592 +G1 F1723.348 +G1 X127.89 Y95.225 E.02694 +G1 X128.708 Y95.225 E.02769 +G1 F2206 +G1 X134.715 Y95.225 E.20333 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z8.244 F12000 +M73 Q42 S12 +G1 Z8.2 F720 +M73 P42 R12 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2206 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.645 Y96.97 E.40002 +G1 X124.784 Y96.937 E.00484 +G3 X125.217 Y96.937 I.217 J.194 E.01653 +G1 X125.355 Y96.969 E.0048 +G1 X116.998 Y105.326 E.40004 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z8.2 F12000 +G1 X134.368 Y95.632 Z8.488 +;AFTER_LAYER_CHANGE +;8.4 +G1 X134.368 Y95.632 +G1 Z8.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2128 +G1 X134.368 Y103.419 E.26358 +G2 X133.681 Y103.496 I-.16 J1.668 E.02357 +G2 X133.444 Y103.734 I.391 J.626 E.01147 +G1 X133.368 Y104.03 E.01034 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.627 Y103.401 E.10561 +G1 X116.512 Y103.115 E.01043 +G1 X116.311 Y102.941 E.009 +G2 X115.632 Y102.868 I-.518 J1.627 E.02327 +G1 X115.632 Y95.632 E.24493 +G1 X121.971 Y95.632 E.21457 +G1 X121.971 Y96.021 E.01317 +G1 X122.085 Y96.377 E.01265 +G1 X122.283 Y96.554 E.00899 +G1 X122.582 Y96.632 E.01046 +G1 X124.57 Y96.632 E.06729 +G1 X124.874 Y96.551 E.01065 +G1 X124.999 Y96.355 E.00787 +G1 X125.123 Y96.551 E.00785 +G1 X125.428 Y96.632 E.01068 +G1 X126.757 Y96.632 E.04499 +G1 X127.595 Y96.63 E.02837 +G1 X127.854 Y96.547 E.00921 +G1 X128.042 Y96.373 E.00867 +G1 X128.154 Y96.021 E.0125 +G1 X128.154 Y95.632 E.01317 +G1 X134.308 Y95.632 E.20831 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2128 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.239 J.035 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.555 Y95.225 E.21426 +G1 F1690.049 +G1 X122.378 Y95.225 E.02786 +G1 X122.378 Y96.021 E.02694 +G1 F1708.931 +G1 X122.456 Y96.181 E.00603 +G1 F2128 +G1 X122.582 Y96.225 E.00452 +G1 X124.57 Y96.225 E.06729 +G1 X124.626 Y96.217 E.00191 +;WIDTH:0.47357 +G1 X124.784 Y96.04 E.0085 +G1 X124.784 Y95.236 E.02879 +;WIDTH:0.471932 +G1 X125.213 Y95.236 E.0153 +G1 X125.213 Y96.021 E.028 +G1 X125.326 Y96.198 E.00749 +;WIDTH:0.449999 +G1 X125.428 Y96.225 E.00357 +G1 X127.543 Y96.225 E.07159 +G1 X127.67 Y96.181 E.00455 +G1 F1741.07 +G1 X127.747 Y96.021 E.00601 +G1 F1723.348 +G1 X127.747 Y95.225 E.02694 +G1 X128.565 Y95.225 E.02769 +G1 F2128 +G1 X134.715 Y95.225 E.20817 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z8.444 F12000 +G1 Z8.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2128 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.667 Y96.991 E.39899 +G3 X125.333 Y96.991 I.332 J.853 E.02307 +G1 X116.998 Y105.326 E.39899 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +M73 P43 R12 +M73 Q43 S12 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z8.4 F12000 +G1 X124.996 Y95.319 Z8.6 +;AFTER_LAYER_CHANGE +;8.6 +G1 X124.996 Y95.319 +G1 Z8.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.640752 +G1 F2098.722 +G1 X124.996 Y96.048 E.03624 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X124.996 Y95.319 E-.1515 +G1 X124.996 Y96.048 E-.1515 +;WIPE_END +G1 E-.337 F2100 +G1 X134.368 Y95.632 Z8.764 F12000 +G1 Z8.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2145 +G1 X134.368 Y103.419 E.26358 +G2 X133.68 Y103.497 I-.16 J1.664 E.02361 +G2 X133.442 Y103.739 I.392 J.625 E.01159 +G1 X133.368 Y104.03 E.01016 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.632 Y103.479 E.10297 +G1 X116.619 Y103.351 E.00435 +G1 X116.513 Y103.116 E.00873 +G1 X116.313 Y102.942 E.00897 +G2 X115.632 Y102.868 I-.521 J1.62 E.02335 +G1 X115.632 Y95.632 E.24493 +G1 X122.12 Y95.632 E.21961 +G1 X122.12 Y96.021 E.01317 +G1 X122.236 Y96.381 E.0128 +G1 X122.357 Y96.504 E.00584 +G1 X122.509 Y96.591 E.00593 +G1 X122.73 Y96.632 E.00761 +G1 X124.699 Y96.632 E.06665 +G1 X124.925 Y96.589 E.00779 +G1 X124.996 Y96.492 E.00407 +G1 X125.068 Y96.589 E.00409 +G1 X125.294 Y96.632 E.00779 +G1 X127.4 Y96.632 E.07129 +G1 X127.652 Y96.578 E.00872 +G1 X127.896 Y96.377 E.0107 +G1 X128.011 Y96.021 E.01266 +G1 X128.011 Y95.632 E.01317 +G1 X134.308 Y95.632 E.21315 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2145 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.703 Y95.225 E.21927 +G1 F1690.049 +G1 X122.527 Y95.225 E.02789 +G1 X122.527 Y96.021 E.02694 +G1 F1708.931 +G1 X122.606 Y96.182 E.00607 +G1 F2145 +G1 X122.73 Y96.225 E.00444 +G1 X124.699 Y96.225 E.06665 +G1 X124.705 Y96.225 E.0002 +;WIDTH:0.497688 +G1 X124.778 Y96.181 E.00322 +;WIDTH:0.545376 +G1 X124.851 Y96.136 E.00358 +;WIDTH:0.593064 +G1 X124.924 Y96.092 E.0039 +;WIDTH:0.640752 +G1 X124.996 Y96.048 E.00419 +G1 X125.052 Y96.088 E.00342 +;WIDTH:0.593064 +G1 X125.108 Y96.129 E.00317 +;WIDTH:0.545376 +G1 X125.163 Y96.17 E.00287 +;WIDTH:0.497688 +G1 X125.219 Y96.211 E.00262 +;WIDTH:0.449999 +G1 X125.294 Y96.225 E.00258 +G1 X127.4 Y96.225 E.07129 +G1 X127.526 Y96.182 E.00451 +G1 F1741.07 +G1 X127.603 Y96.021 E.00604 +G1 F1723.348 +G1 X127.603 Y95.225 E.02694 +G1 X128.422 Y95.225 E.02772 +G1 F2145 +G1 X134.715 Y95.225 E.21301 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z8.644 F12000 +G1 Z8.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2145 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G3 X125.326 Y96.998 I.326 J1.523 E.02223 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:8.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z8.6 F12000 +G1 X124.994 Y95.192 Z8.8 +;AFTER_LAYER_CHANGE +;8.8 +G1 X124.994 Y95.192 +G1 Z8.8 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.384676 +G1 F2098.783 +G1 X124.994 Y96.036 E.02398 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X124.994 Y95.192 E-.17539 +G1 X124.994 Y96.036 E-.17539 +;WIPE_END +G1 E-.28922 F2100 +G1 X134.368 Y95.632 Z8.964 F12000 +G1 Z8.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2147 +G1 X134.368 Y103.419 E.26358 +G2 X133.679 Y103.498 I-.16 J1.66 E.02365 +G2 X133.439 Y103.743 I.394 J.624 E.01172 +G1 X133.368 Y104.03 E.01001 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +M73 Q44 S12 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +M73 P44 R12 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.625 Y103.385 E.10615 +G1 X116.514 Y103.117 E.00982 +G1 X116.314 Y102.942 E.009 +G2 X115.632 Y102.868 I-.522 J1.615 E.02338 +G1 X115.632 Y95.632 E.24493 +G1 X122.268 Y95.632 E.22462 +G1 X122.268 Y96.021 E.01317 +G1 X122.388 Y96.384 E.01294 +G1 X122.621 Y96.575 E.0102 +G1 X122.879 Y96.632 E.00894 +G1 X124.824 Y96.632 E.06584 +G1 X124.87 Y96.63 E.00156 +G1 X124.994 Y96.577 E.00456 +G1 X125.029 Y96.617 E.0018 +G1 X125.164 Y96.632 E.0046 +G1 X127.257 Y96.632 E.07085 +G1 X127.519 Y96.573 E.00909 +G1 X127.75 Y96.381 E.01017 +G1 X127.867 Y96.021 E.01281 +G1 X127.867 Y95.632 E.01317 +G1 X134.308 Y95.632 E.21802 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2147 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X121.852 Y95.225 E.22432 +G1 F1690.049 +G1 X122.675 Y95.225 E.02786 +G1 X122.675 Y96.021 E.02694 +G1 F1708.931 +G1 X122.755 Y96.183 E.00612 +G1 F2147 +G1 X122.879 Y96.225 E.00443 +G1 X124.824 Y96.225 E.06584 +G1 X124.833 Y96.225 E.0003 +G1 X124.914 Y96.131 E.0042 +;WIDTH:0.417338 +G1 X124.994 Y96.036 E.00387 +G1 X125.057 Y96.128 E.00347 +;WIDTH:0.449999 +G1 X125.119 Y96.22 E.00376 +G1 X125.164 Y96.225 E.00153 +G1 X127.257 Y96.225 E.07085 +G1 X127.381 Y96.183 E.00443 +G1 F1741.076 +G1 X127.46 Y96.021 E.0061 +G1 F1723.354 +G1 X127.46 Y95.225 E.02694 +G1 X128.278 Y95.225 E.02769 +G1 F2147 +G1 X134.715 Y95.225 E.21788 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z8.844 F12000 +G1 Z8.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2147 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +M73 Q44 S11 +G1 X124.674 Y96.998 E.39866 +G1 X124.994 Y96.981 E.01085 +G1 X125.326 Y96.998 E.01125 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +M73 P44 R11 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:9 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z8.8 F12000 +G1 X134.368 Y95.632 Z9.088 +;AFTER_LAYER_CHANGE +;9 +G1 X134.368 Y95.632 +G1 Z9 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2113 +G1 X134.368 Y103.419 E.26358 +G2 X133.678 Y103.498 I-.16 J1.656 E.02368 +G2 X133.437 Y103.748 I.396 J.624 E.01186 +G1 X133.368 Y104.03 E.00983 +G1 X133.372 Y105.865 E.06211 +G1 X133.471 Y106.133 E.00967 +G1 X133.665 Y106.317 E.00905 +G2 X134.368 Y106.404 I.544 J-1.522 E.02417 +G1 X134.368 Y114.368 E.26957 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.627 Y103.397 E.10574 +G1 X116.515 Y103.119 E.01014 +G1 X116.315 Y102.943 E.00902 +G2 X115.632 Y102.868 I-.523 J1.608 E.02342 +G1 X115.632 Y95.632 E.24493 +G1 X122.416 Y95.632 E.22963 +G1 X122.416 Y96.021 E.01317 +G1 X122.488 Y96.309 E.01005 +G1 X122.585 Y96.443 E.0056 +G1 X122.761 Y96.571 E.00737 +G1 X123.027 Y96.632 E.00924 +G1 X124.947 Y96.632 E.06499 +G1 X125.031 Y96.632 E.00284 +G1 X127.113 Y96.632 E.07047 +G1 X127.386 Y96.568 E.00949 +G1 X127.604 Y96.384 E.00966 +G1 X127.724 Y96.021 E.01294 +G1 X127.724 Y95.632 E.01317 +G1 X134.308 Y95.632 E.22286 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2113 +G1 X134.775 Y103.826 E.29113 +G1 X133.979 Y103.826 E.02694 +G2 X133.775 Y104.03 I.031 J.235 E.0105 +G1 X133.775 Y105.793 E.05968 +G2 X133.979 Y105.996 I.235 J-.032 E.01046 +G1 X134.775 Y105.996 E.02694 +G1 X134.775 Y114.775 E.29716 +G1 X115.225 Y114.775 E.66174 +M73 Q45 S11 +G1 X115.225 Y106.725 E.27248 +M73 P45 R11 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122 Y95.225 E.22933 +G1 F1690.049 +G1 X122.824 Y95.225 E.02789 +G1 X122.824 Y96.021 E.02694 +G1 F1708.931 +G1 X122.88 Y96.162 E.00514 +G1 F2113 +G1 X123.027 Y96.225 E.00541 +G1 X124.947 Y96.225 E.06499 +G1 X124.992 Y96.193 E.00187 +G1 X125.034 Y96.225 E.00179 +G1 X127.113 Y96.225 E.07037 +G1 X127.237 Y96.183 E.00443 +G1 F1741.07 +G1 X127.317 Y96.021 E.00612 +G1 F1723.348 +G1 X127.317 Y95.225 E.02694 +G1 X128.135 Y95.225 E.02769 +G1 F2113 +G1 X134.715 Y95.225 E.22272 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z9.044 F12000 +G1 Z9 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2113 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.669 Y96.993 E.3989 +G1 X125.329 Y96.995 E.02234 +G1 X116.998 Y105.326 E.3988 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:9.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z9 F12000 +G1 X134.368 Y95.632 Z9.288 +;AFTER_LAYER_CHANGE +;9.2 +G1 X134.368 Y95.632 +G1 Z9.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y103.357 E.26148 +G1 X133.979 Y103.357 E.01317 +G1 X133.678 Y103.436 E.01053 +G1 X133.545 Y103.538 E.00567 +G1 X133.434 Y103.691 E.0064 +G1 X133.368 Y103.967 E.00961 +G1 X133.372 Y105.926 E.06631 +G1 X133.472 Y106.199 E.00984 +G1 X133.667 Y106.382 E.00905 +G1 X133.979 Y106.468 E.01095 +G1 X134.368 Y106.468 E.01317 +G1 X134.368 Y114.368 E.26741 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.62 Y103.36 E.107 +G1 X116.516 Y103.12 E.00885 +G1 X116.317 Y102.944 E.00899 +G2 X115.632 Y102.868 I-.525 J1.602 E.02349 +G1 X115.632 Y95.632 E.24493 +G1 X122.565 Y95.632 E.23467 +G1 X122.565 Y96.021 E.01317 +G1 X122.639 Y96.312 E.01016 +G1 X122.737 Y96.446 E.00562 +G1 X122.902 Y96.568 E.00695 +G1 X123.175 Y96.632 E.00949 +G1 X126.033 Y96.632 E.09674 +G1 X127.099 Y96.618 E.03609 +G1 X127.336 Y96.51 E.00882 +G1 X127.509 Y96.309 E.00898 +G1 X127.581 Y96.021 E.01005 +G1 X127.581 Y95.632 E.01317 +G1 X134.308 Y95.632 E.2277 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.764 E.28903 +G1 X133.979 Y103.764 E.02694 +G1 X133.834 Y103.824 E.00531 +G1 X133.775 Y103.967 E.00524 +G1 X133.775 Y105.857 E.06397 +G1 X133.831 Y105.997 E.0051 +G1 X133.979 Y106.061 E.00546 +G1 X134.775 Y106.061 E.02694 +G1 X134.775 Y114.775 E.29496 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.149 Y95.225 E.23437 +G1 F1690.042 +G1 X122.972 Y95.225 E.02786 +G1 X122.972 Y96.021 E.02694 +G1 F1708.925 +G1 X123.029 Y96.163 E.00518 +G1 F2140.273 +G1 X123.175 Y96.225 E.00537 +G1 F2141 +G1 X126.97 Y96.225 E.12846 +G1 X127.092 Y96.184 E.00436 +G1 F1741.07 +G1 X127.174 Y96.021 E.00618 +G1 F1723.348 +G1 X127.174 Y95.225 E.02694 +G1 X127.992 Y95.225 E.02769 +G1 F2141 +G1 X134.715 Y95.225 E.22757 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z9.244 F12000 +G1 Z9.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P46 R11 +M73 Q46 S11 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.669 Y96.993 E.3989 +G1 X125.33 Y96.994 E.02237 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z9.362 F12000 +G1 Z9.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2141 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:9.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z9.2 F12000 +G1 X134.368 Y95.632 Z9.488 +;AFTER_LAYER_CHANGE +;9.4 +G1 X134.368 Y95.632 +G1 Z9.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y103.223 E.25695 +G1 X133.979 Y103.223 E.01317 +G1 X133.679 Y103.302 E.0105 +G1 X133.546 Y103.403 E.00565 +G1 X133.418 Y103.592 E.00773 +G1 X133.368 Y103.834 E.00836 +G1 X133.371 Y106.056 E.07521 +G1 X133.474 Y106.34 E.01023 +G1 X133.67 Y106.523 E.00908 +G1 X133.979 Y106.607 E.01084 +G1 X134.368 Y106.607 E.01317 +G1 X134.368 Y114.368 E.2627 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.622 Y103.37 E.10666 +G1 X116.517 Y103.121 E.00915 +G1 X116.318 Y102.945 E.00899 +G2 X115.632 Y102.868 I-.526 J1.595 E.02353 +G1 X115.632 Y95.632 E.24493 +G1 X122.713 Y95.632 E.23968 +G1 X122.713 Y96.021 E.01317 +G1 X122.789 Y96.316 E.01031 +G1 X122.888 Y96.449 E.00561 +G1 X123.044 Y96.564 E.00656 +G1 X123.324 Y96.632 E.00975 +G1 X126.166 Y96.632 E.0962 +G1 X126.914 Y96.626 E.02532 +G1 X127.19 Y96.513 E.01009 +G1 X127.364 Y96.312 E.009 +G1 X127.438 Y96.021 E.01016 +G1 X127.438 Y95.632 E.01317 +G1 X134.308 Y95.632 E.23254 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.63 E.2845 +G1 X133.979 Y103.63 E.02694 +G1 X133.834 Y103.69 E.00531 +G1 X133.775 Y103.834 E.00527 +G1 X133.775 Y105.996 E.07318 +G1 X133.831 Y106.137 E.00514 +G1 X133.979 Y106.199 E.00543 +G1 X134.775 Y106.199 E.02694 +G1 X134.775 Y114.775 E.29029 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.297 Y95.225 E.23938 +G1 F1690.049 +G1 X123.12 Y95.225 E.02786 +G1 X123.12 Y96.021 E.02694 +G1 F1708.931 +G1 X123.179 Y96.164 E.00524 +G1 F2141 +G1 X123.324 Y96.225 E.00532 +G1 X126.827 Y96.225 E.11857 +G1 X126.948 Y96.185 E.00431 +G1 F1741.07 +G1 X127.031 Y96.021 E.00622 +G1 F1723.348 +G1 X127.031 Y95.225 E.02694 +G1 X127.849 Y95.225 E.02769 +G1 F2141 +G1 X134.715 Y95.225 E.23241 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z9.444 F12000 +G1 Z9.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.668 Y96.992 E.39894 +G1 X125.33 Y96.994 E.02241 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z9.562 F12000 +G1 Z9.4 F720 +M73 Q47 S11 +G1 E.8 F1500 +M73 P47 R11 +M204 P2000 +G1 F2141 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:9.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z9.4 F12000 +G1 X134.368 Y95.632 Z9.688 +;AFTER_LAYER_CHANGE +;9.6 +G1 X134.368 Y95.632 +G1 Z9.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y103.089 E.25241 +G1 X133.979 Y103.089 E.01317 +G1 X133.679 Y103.168 E.0105 +G1 X133.546 Y103.269 E.00565 +G1 X133.443 Y103.407 E.00583 +G1 X133.368 Y103.7 E.01024 +G1 X133.371 Y106.195 E.08445 +G1 X133.476 Y106.482 E.01034 +G1 X133.672 Y106.663 E.00903 +G1 X133.979 Y106.745 E.01076 +G1 X134.368 Y106.745 E.01317 +G1 X134.368 Y114.368 E.25803 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.624 Y103.381 E.10629 +G1 X116.518 Y103.123 E.00944 +G1 X116.319 Y102.945 E.00904 +G2 X115.632 Y102.868 I-.527 J1.59 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X122.862 Y95.632 E.24473 +G1 X122.862 Y96.021 E.01317 +G1 X122.939 Y96.319 E.01042 +G1 X123.04 Y96.452 E.00565 +G1 X123.185 Y96.56 E.00612 +G1 X123.472 Y96.632 E.01002 +G1 X126.3 Y96.632 E.09572 +G1 X126.727 Y96.631 E.01445 +G1 X127.012 Y96.536 E.01017 +G1 X127.219 Y96.316 E.01022 +G1 X127.294 Y96.021 E.0103 +G1 X127.294 Y95.632 E.01317 +G1 X134.308 Y95.632 E.23742 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.496 E.27996 +G1 X133.979 Y103.496 E.02694 +G1 X133.834 Y103.556 E.00531 +G1 X133.775 Y103.7 E.00527 +G1 X133.775 Y106.134 E.08239 +G1 X133.832 Y106.276 E.00518 +G1 X133.979 Y106.338 E.0054 +G1 X134.775 Y106.338 E.02694 +G1 X134.775 Y114.775 E.28558 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.445 Y95.225 E.24439 +G1 F1690.049 +G1 X123.269 Y95.225 E.02789 +G1 X123.269 Y96.021 E.02694 +G1 F1708.931 +G1 X123.328 Y96.165 E.00527 +G1 F2141 +G1 X123.472 Y96.225 E.00528 +G1 X126.684 Y96.225 E.10872 +G1 X126.804 Y96.186 E.00427 +G1 F1741.07 +G1 X126.887 Y96.021 E.00625 +G1 F1723.348 +G1 X126.887 Y95.225 E.02694 +G1 X127.706 Y95.225 E.02772 +G1 F2141 +G1 X134.715 Y95.225 E.23725 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z9.644 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.668 Y96.992 E.39894 +G1 X125.33 Y96.994 E.02241 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z9.762 F12000 +G1 Z9.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2141 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:9.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z9.6 F12000 +G1 X134.368 Y95.632 Z9.888 +;AFTER_LAYER_CHANGE +;9.8 +G1 X134.368 Y95.632 +G1 Z9.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2114 +G1 X134.368 Y102.956 E.24791 +G1 X133.979 Y102.956 E.01317 +G1 X133.68 Y103.034 E.01046 +G1 X133.547 Y103.135 E.00565 +G1 X133.44 Y103.278 E.00605 +G1 X133.368 Y103.566 E.01005 +G1 X133.371 Y106.334 E.09369 +G1 X133.478 Y106.623 E.01043 +G1 X133.675 Y106.803 E.00903 +G1 X133.979 Y106.883 E.01064 +G1 X134.368 Y106.883 E.01317 +G1 X134.368 Y114.368 E.25336 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +M73 P48 R11 +M73 Q48 S11 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.625 Y103.384 E.10618 +G1 X116.526 Y103.134 E.0091 +G1 X116.319 Y102.945 E.00949 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.01 Y95.632 E.24974 +G1 X123.01 Y96.021 E.01317 +G1 X123.089 Y96.321 E.0105 +G1 X123.19 Y96.454 E.00565 +G1 X123.332 Y96.56 E.006 +G1 X123.621 Y96.632 E.01008 +G1 X126.434 Y96.632 E.09522 +G1 X126.636 Y96.625 E.00684 +G1 X126.888 Y96.524 E.00919 +G1 X127.074 Y96.319 E.00937 +G1 X127.151 Y96.021 E.01042 +G1 X127.151 Y95.632 E.01317 +G1 X134.308 Y95.632 E.24226 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2114 +G1 X134.775 Y103.363 E.27546 +G1 X133.979 Y103.363 E.02694 +G1 X133.835 Y103.422 E.00527 +G1 X133.775 Y103.563 E.00519 +G1 X133.775 Y106.273 E.09173 +G1 X133.833 Y106.415 E.00519 +G1 X133.979 Y106.476 E.00536 +G1 X134.775 Y106.476 E.02694 +G1 X134.775 Y114.775 E.28091 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.594 Y95.225 E.24943 +G1 F1690.042 +G1 X123.417 Y95.225 E.02786 +G1 X123.417 Y96.021 E.02694 +G1 F1708.925 +G1 X123.477 Y96.166 E.00531 +G1 F2114 +G1 X123.621 Y96.225 E.00527 +G1 X126.434 Y96.225 E.09522 +G1 X126.659 Y96.187 E.00772 +G1 F1741.076 +G1 X126.744 Y96.021 E.00631 +G1 F1723.354 +G1 X126.744 Y95.225 E.02694 +G1 X127.562 Y95.225 E.02769 +G1 F2114 +G1 X134.715 Y95.225 E.24212 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z9.844 F12000 +G1 Z9.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2114 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.667 Y96.991 E.39899 +G1 X125.33 Y96.994 E.02244 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:10 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z9.8 F12000 +G1 X134.368 Y95.632 Z10.088 +;AFTER_LAYER_CHANGE +;10 +G1 X134.368 Y95.632 +G1 Z10 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y102.822 E.24337 +G1 X133.979 Y102.822 E.01317 +G1 X133.682 Y102.899 E.01039 +G1 X133.548 Y102.999 E.00566 +G1 X133.434 Y103.155 E.00654 +G1 X133.368 Y103.432 E.00964 +G1 X133.382 Y106.54 E.1052 +G1 X133.48 Y106.764 E.00828 +G1 X133.678 Y106.943 E.00903 +G1 X133.979 Y107.022 E.01053 +G1 X134.368 Y107.022 E.01317 +G1 X134.368 Y114.368 E.24865 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.435 E.10446 +G1 X116.534 Y103.146 E.01032 +G1 X116.319 Y102.945 E.00996 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.159 Y95.632 E.25478 +G1 X123.159 Y96.021 E.01317 +G1 X123.238 Y96.323 E.01057 +G1 X123.34 Y96.456 E.00567 +G1 X123.48 Y96.559 E.00588 +G1 X123.769 Y96.632 E.01009 +G1 X126.397 Y96.632 E.08895 +G1 X126.506 Y96.622 E.00371 +G1 X126.752 Y96.519 E.00903 +G1 X126.93 Y96.32 E.00904 +G1 X127.008 Y96.021 E.01046 +G1 X127.008 Y95.632 E.01317 +G1 X134.308 Y95.632 E.2471 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.229 E.27093 +G1 X133.979 Y103.229 E.02694 +G1 X133.835 Y103.288 E.00527 +G1 X133.775 Y103.432 E.00528 +G1 X133.775 Y106.411 E.10084 +G1 X133.813 Y106.529 E.0042 +G1 X133.979 Y106.615 E.00633 +G1 X134.775 Y106.615 E.02694 +G1 X134.775 Y114.775 E.27621 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +M73 P49 R11 +M73 Q49 S11 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.742 Y95.225 E.25444 +G1 F1690.049 +G1 X123.566 Y95.225 E.02789 +G1 X123.566 Y96.021 E.02694 +G1 F1708.931 +G1 X123.626 Y96.166 E.00531 +G1 F2141 +G1 X123.769 Y96.225 E.00524 +G1 X126.397 Y96.225 E.08895 +G1 X126.515 Y96.187 E.0042 +G1 F1741.07 +G1 X126.601 Y96.021 E.00633 +G1 F1723.348 +G1 X126.601 Y95.225 E.02694 +G1 X127.419 Y95.225 E.02769 +G1 F2141 +G1 X134.715 Y95.225 E.24696 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z10.044 F12000 +G1 Z10 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +M73 Q49 S10 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P49 R10 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G1 X125.326 Y96.998 E.02207 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z10.162 F12000 +G1 Z10 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2141 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:10.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z10 F12000 +G1 X134.368 Y95.632 Z10.288 +;AFTER_LAYER_CHANGE +;10.2 +G1 X134.368 Y95.632 +G1 Z10.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2141 +G1 X134.368 Y102.688 E.23884 +G1 X133.979 Y102.688 E.01317 +G1 X133.685 Y102.763 E.01027 +G1 X133.551 Y102.863 E.00566 +G1 X133.427 Y103.037 E.00723 +G1 X133.368 Y103.299 E.00909 +G1 X133.382 Y106.68 E.11444 +G1 X133.483 Y106.906 E.00838 +G1 X133.681 Y107.083 E.00899 +G1 X133.979 Y107.16 E.01042 +G1 X134.368 Y107.16 E.01317 +G1 X134.368 Y114.368 E.24398 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.436 E.10442 +G1 X116.541 Y103.158 E.00989 +G1 X116.319 Y102.945 E.01041 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.307 Y95.632 E.25979 +G1 X123.307 Y96.021 E.01317 +G1 X123.388 Y96.325 E.01065 +G1 X123.49 Y96.458 E.00567 +G1 X123.627 Y96.559 E.00576 +G1 X123.918 Y96.632 E.01016 +G1 X126.254 Y96.632 E.07907 +G1 X126.333 Y96.627 E.00268 +G1 X126.608 Y96.519 E.01 +G1 X126.786 Y96.321 E.00901 +G1 X126.865 Y96.021 E.0105 +G1 X126.865 Y95.632 E.01317 +G1 X134.308 Y95.632 E.25194 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2141 +G1 X134.775 Y103.095 E.26639 +G1 X133.979 Y103.095 E.02694 +G1 X133.836 Y103.153 E.00522 +G1 X133.775 Y103.299 E.00536 +G1 X133.775 Y106.55 E.11004 +G1 X133.813 Y106.669 E.00423 +G1 X133.979 Y106.753 E.0063 +G1 X134.775 Y106.753 E.02694 +G1 X134.775 Y114.775 E.27153 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.891 Y95.225 E.25948 +G1 F1690.042 +G1 X123.714 Y95.225 E.02786 +G1 X123.714 Y96.021 E.02694 +G1 F1708.925 +G1 X123.775 Y96.167 E.00536 +G1 F2141 +G1 X123.918 Y96.225 E.00522 +G1 X126.254 Y96.225 E.07907 +G1 X126.372 Y96.187 E.0042 +G1 F1741.07 +G1 X126.458 Y96.021 E.00633 +G1 F1723.348 +G1 X126.458 Y95.225 E.02694 +G1 X127.276 Y95.225 E.02769 +G1 F2141 +G1 X134.715 Y95.225 E.2518 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z10.244 F12000 +G1 Z10.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2141 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P50 R10 +M73 Q50 S10 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G1 X125.326 Y96.998 E.02207 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z10.362 F12000 +G1 Z10.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2141 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:10.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z10.2 F12000 +G1 X134.368 Y95.632 Z10.488 +;AFTER_LAYER_CHANGE +;10.4 +G1 X134.368 Y95.632 +G1 Z10.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2139 +G1 X134.368 Y102.554 E.2343 +G1 X133.979 Y102.554 E.01317 +G1 X133.688 Y102.628 E.01016 +G1 X133.554 Y102.726 E.00562 +G1 X133.419 Y102.92 E.008 +G1 X133.368 Y103.165 E.00847 +G1 X133.382 Y106.82 E.12372 +G1 X133.485 Y107.048 E.00847 +G1 X133.684 Y107.223 E.00897 +G1 X133.979 Y107.299 E.01031 +G1 X134.368 Y107.299 E.01317 +G1 X134.368 Y114.368 E.23928 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.437 E.10439 +G1 X116.547 Y103.167 E.00957 +G1 X116.319 Y102.945 E.01077 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.419 Y95.632 E.26358 +G2 X123.501 Y96.327 I1.638 J.159 E.02387 +G1 X123.604 Y96.459 E.00567 +G1 X123.739 Y96.558 E.00567 +G1 X124.03 Y96.632 E.01016 +G1 X126.111 Y96.632 E.07044 +G1 X126.21 Y96.624 E.00336 +G1 X126.464 Y96.52 E.00929 +G1 X126.642 Y96.322 E.00901 +G1 X126.721 Y96.021 E.01053 +G1 X126.721 Y95.632 E.01317 +G1 X134.308 Y95.632 E.25681 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2139 +G1 X134.775 Y102.961 E.26185 +G1 X133.979 Y102.961 E.02694 +G1 X133.837 Y103.019 E.00519 +G1 X133.775 Y103.165 E.00537 +G1 X133.775 Y106.688 E.11925 +G1 X133.814 Y106.808 E.00427 +G1 X133.979 Y106.892 E.00627 +G1 X134.775 Y106.892 E.02694 +G1 X134.775 Y114.775 E.26683 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.039 Y95.225 E.26449 +G1 F1922.471 +G1 X123.826 Y95.225 E.02664 +G1 X123.826 Y96.021 E.02694 +G1 F1931.082 +G1 X123.888 Y96.167 E.00537 +G1 F2139 +G1 X124.03 Y96.225 E.00519 +G1 X126.111 Y96.225 E.07044 +G1 X126.228 Y96.188 E.00415 +G1 F1741.07 +G1 X126.314 Y96.021 E.00636 +G1 F1723.348 +G1 X126.314 Y95.225 E.02694 +G1 X127.133 Y95.225 E.02772 +G1 F2139 +G1 X134.715 Y95.225 E.25664 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z10.444 F12000 +G1 Z10.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2139 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G1 X125.326 Y96.998 E.02207 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z10.562 F12000 +G1 Z10.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2139 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +M73 Q51 S10 +G1 X115.998 Y95.998 E.86184 +M73 P51 R10 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:10.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z10.4 F12000 +G1 X134.368 Y95.632 Z10.688 +;AFTER_LAYER_CHANGE +;10.6 +G1 X134.368 Y95.632 +G1 Z10.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2131 +G1 X134.368 Y102.421 E.2298 +G1 X133.979 Y102.421 E.01317 +G1 X133.691 Y102.493 E.01005 +G1 X133.557 Y102.589 E.00558 +G1 X133.446 Y102.733 E.00615 +G1 X133.368 Y103.031 E.01043 +G1 X133.383 Y106.959 E.13296 +G1 X133.487 Y107.189 E.00854 +G1 X133.688 Y107.364 E.00902 +G1 X133.979 Y107.437 E.01016 +G1 X134.368 Y107.437 E.01317 +G1 X134.368 Y114.368 E.23461 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.631 Y103.436 E.10442 +G1 X116.54 Y103.156 E.00997 +G1 X116.319 Y102.945 E.01034 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.276 Y95.632 E.25874 +G1 X123.276 Y96.021 E.01317 +G1 X123.357 Y96.325 E.01065 +G1 X123.46 Y96.457 E.00567 +G1 X123.597 Y96.559 E.00578 +G1 X123.887 Y96.632 E.01012 +G1 X126.221 Y96.628 E.079 +G1 X126.509 Y96.519 E.01042 +G1 X126.686 Y96.32 E.00901 +G1 X126.765 Y96.021 E.01047 +G1 X126.765 Y95.632 E.01317 +G1 X134.308 Y95.632 E.25532 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2131 +G1 X134.775 Y102.828 E.25735 +G1 X133.979 Y102.828 E.02694 +G1 X133.838 Y102.884 E.00514 +G1 X133.775 Y103.031 E.00541 +G1 X133.775 Y104.971 E.06567 +G3 X133.775 Y105.063 I-.025 J.046 E.00382 +G1 X133.775 Y106.827 E.05971 +G1 X133.815 Y106.948 E.00431 +G1 X133.979 Y107.03 E.00621 +G1 X134.775 Y107.03 E.02694 +G1 X134.775 Y114.775 E.26216 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.684 Y95.225 E.28633 +G1 X123.684 Y96.021 E.02694 +G1 X123.745 Y96.167 E.00536 +G1 X123.887 Y96.225 E.00519 +G1 X126.154 Y96.225 E.07674 +G1 X126.272 Y96.187 E.0042 +G1 X126.358 Y96.021 E.00633 +G1 X126.358 Y95.225 E.02694 +G1 X134.715 Y95.225 E.28287 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z10.644 F12000 +G1 Z10.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2131 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G1 X125.327 Y96.997 E.0221 +G1 X116.998 Y105.326 E.3987 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z10.762 F12000 +G1 Z10.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2131 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:10.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z10.6 F12000 +G1 X134.815 Y105.021 Z10.929 +;AFTER_LAYER_CHANGE +;10.8 +G1 X134.815 Y105.021 +G1 Z10.8 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1703.247 +G1 X133.964 Y105.021 E.02406 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.815 Y105.021 E-.17685 +G1 X133.964 Y105.021 E-.17685 +;WIPE_END +G1 E-.2863 F2100 +G1 X134.368 Y95.632 Z10.964 F12000 +G1 Z10.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2153 +G1 X134.368 Y102.287 E.22526 +G1 X133.979 Y102.287 E.01317 +G1 X133.615 Y102.407 E.01297 +G1 X133.492 Y102.528 E.00584 +G1 X133.407 Y102.682 E.00595 +G1 X133.368 Y102.898 E.00743 +G1 X133.368 Y104.857 E.06631 +G1 X133.369 Y104.891 E.00115 +G1 X133.42 Y105.021 E.00473 +G1 X133.382 Y105.054 E.0017 +G1 X133.368 Y105.185 E.00446 +G1 X133.383 Y107.099 E.06479 +G1 X133.49 Y107.331 E.00865 +G1 X133.691 Y107.504 E.00898 +G1 X133.979 Y107.576 E.01005 +G1 X134.368 Y107.576 E.01317 +G1 X134.368 Y114.368 E.2299 +G1 X115.632 Y114.368 E.63419 +M73 Q52 S10 +G1 X115.632 Y107.132 E.24493 +M73 P52 R10 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.625 Y103.386 E.10612 +G1 X116.532 Y103.144 E.00878 +G1 X116.319 Y102.945 E.00987 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X123.134 Y95.632 E.25393 +G1 X123.134 Y96.021 E.01317 +G1 X123.214 Y96.323 E.01057 +G1 X123.315 Y96.456 E.00565 +G1 X123.455 Y96.559 E.00588 +G1 X123.745 Y96.632 E.01012 +G1 X126.303 Y96.632 E.08659 +G1 X126.432 Y96.618 E.00439 +G1 X126.661 Y96.516 E.00849 +G1 X126.837 Y96.317 E.00899 +G1 X126.914 Y96.021 E.01035 +G1 X126.914 Y95.632 E.01317 +G1 X134.308 Y95.632 E.25028 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2153 +G1 X134.775 Y102.694 E.25282 +G1 X133.979 Y102.694 E.02694 +G1 X133.816 Y102.774 E.00615 +G1 X133.775 Y102.898 E.00442 +G1 X133.775 Y104.866 E.06661 +G1 F1995.9 +G1 X133.87 Y104.944 E.00416 +;WIDTH:0.41646 +G1 F1724.704 +G1 X133.964 Y105.021 E.00377 +G1 X133.872 Y105.081 E.00341 +;WIDTH:0.449999 +G1 F1987.074 +G1 X133.78 Y105.141 E.00372 +G1 F2153 +G1 X133.775 Y105.185 E.0015 +G1 X133.775 Y106.965 E.06025 +G1 X133.816 Y107.087 E.00436 +G1 X133.979 Y107.169 E.00618 +G1 X134.775 Y107.169 E.02694 +G1 X134.775 Y114.775 E.25745 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.541 Y95.225 E.28149 +G1 X123.541 Y96.021 E.02694 +G1 X123.602 Y96.166 E.00532 +G1 X123.745 Y96.225 E.00524 +G1 X126.303 Y96.225 E.08659 +G1 X126.422 Y96.186 E.00424 +G1 X126.507 Y96.021 E.00628 +G1 X126.507 Y95.225 E.02694 +G1 X134.715 Y95.225 E.27783 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z10.844 F12000 +G1 Z10.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2153 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G1 X133.002 Y105.326 E.02207 +G1 X124.674 Y96.998 E.39866 +G1 X125.326 Y96.998 E.02207 +G1 X116.998 Y105.326 E.39866 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:11 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;11 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z10.8 F12000 +G1 X134.699 Y105.023 Z11.127 +;AFTER_LAYER_CHANGE +;11 +G1 X134.699 Y105.023 +G1 Z11 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.604532 +G1 F1958.012 +G1 X133.954 Y105.023 E.03479 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.699 Y105.023 E-.15482 +G1 X133.954 Y105.023 E-.15482 +;WIPE_END +G1 E-.33036 F2100 +G1 X134.368 Y95.632 Z11.164 F12000 +G1 Z11 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2334 +G1 X134.368 Y102.153 E.22073 +G1 X133.979 Y102.153 E.01317 +G1 X133.619 Y102.271 E.01282 +G1 X133.495 Y102.391 E.00584 +G1 X133.408 Y102.545 E.00599 +G1 X133.368 Y102.764 E.00754 +G1 X133.368 Y104.743 E.06699 +G1 X133.407 Y104.957 E.00736 +G1 X133.493 Y105.023 E.00367 +G1 X133.407 Y105.089 E.00367 +G1 X133.368 Y105.303 E.00736 +G1 X133.368 Y107.104 E.06096 +G1 X133.441 Y107.393 E.01009 +G1 X133.615 Y107.594 E.009 +G1 X133.979 Y107.714 E.01297 +G1 X134.368 Y107.714 E.01317 +G1 X134.368 Y114.368 E.22523 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.625 Y103.383 E.10622 +G1 X116.525 Y103.133 E.00911 +G1 X116.319 Y102.945 E.00944 +G2 X115.632 Y102.868 I-.527 J1.589 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X122.992 Y95.632 E.24913 +G1 X122.992 Y96.021 E.01317 +G1 X123.07 Y96.321 E.01049 +G1 X123.171 Y96.454 E.00565 +G1 X123.314 Y96.56 E.00603 +G1 X123.602 Y96.632 E.01005 +G1 X126.452 Y96.632 E.09647 +G1 X126.581 Y96.618 E.00439 +G1 X126.813 Y96.514 E.00861 +G1 X126.988 Y96.314 E.009 +G1 X127.063 Y96.021 E.01024 +G1 X127.063 Y95.632 E.01317 +G1 X134.308 Y95.632 E.24523 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2334 +G1 X134.775 Y102.56 E.24828 +G1 X133.979 Y102.56 E.02694 +G1 X133.817 Y102.64 E.00612 +G1 X133.775 Y102.764 E.00443 +G1 X133.775 Y104.751 E.06726 +;WIDTH:0.488633 +G1 F2147.775 +G1 X133.82 Y104.819 E.00302 +;WIDTH:0.527266 +G1 F1966.856 +G1 X133.865 Y104.887 E.00328 +;WIDTH:0.565899 +G1 F1930.888 +G1 X133.909 Y104.955 E.00352 +;WIDTH:0.604532 +G1 F1912.857 +G1 X133.954 Y105.023 E.00381 +G1 F1940.132 +G1 X133.913 Y105.075 E.00309 +;WIDTH:0.565899 +G1 F1952.979 +G1 X133.871 Y105.127 E.00291 +;WIDTH:0.527266 +G1 F1990.565 +G1 X133.829 Y105.179 E.00269 +;WIDTH:0.488633 +G1 F2118.368 +G1 X133.788 Y105.231 E.00245 +;WIDTH:0.449999 +G1 F2334 +G1 X133.775 Y105.303 E.00248 +G1 X133.775 Y107.104 E.06096 +G1 X133.816 Y107.227 E.00439 +G1 X133.979 Y107.307 E.00615 +G1 X134.775 Y107.307 E.02694 +G1 X134.775 Y114.775 E.25278 +G1 X115.225 Y114.775 E.66174 +M73 Q53 S10 +G1 X115.225 Y106.725 E.27248 +M73 P53 R10 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.399 Y95.225 E.27668 +G1 X123.399 Y96.021 E.02694 +G1 X123.459 Y96.166 E.00531 +G1 X123.599 Y96.225 E.00514 +G1 X126.452 Y96.225 E.09657 +G1 X126.573 Y96.186 E.0043 +G1 X126.656 Y96.021 E.00625 +G1 X126.656 Y95.225 E.02694 +G1 X134.715 Y95.225 E.27279 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z11.044 F12000 +G1 Z11 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2334 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.002 Y104.674 E.44653 +G3 X133.002 Y105.326 I-1.964 J.325 E.02217 +G1 X124.667 Y96.991 E.39899 +G1 X125.33 Y96.994 E.02244 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z11.162 F12000 +G1 Z11 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2334 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X116.065 Y100.177 Z11.068 F12000 +G1 Z11 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.583955 +G1 F2334 +G1 X116.065 Y99.608 E.0256 +G1 X116.327 Y99.685 E.01229 +G3 X116.606 Y100.144 I-.53 J.637 E.02461 +;WIDTH:0.584321 +G1 X116.607 Y102.622 E.11155 +G1 X116.564 Y102.589 E.00244 +G1 X116.066 Y102.441 E.02339 +G1 X116.065 Y100.38 E.09278 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y102.441 E-.4283 +G1 X116.564 Y102.589 E-.10796 +G1 X116.607 Y102.622 E-.01126 +G1 X116.607 Y102.177 E-.09248 +;WIPE_END +G1 X116.066 Y109.775 Z11.133 F12000 +G1 Z11 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585468 +G1 F2334 +G1 X116.066 Y107.581 E.09898 +;WIDTH:0.585254 +G2 X116.608 Y107.378 I-.389 J-1.859 E.0262 +G1 X116.608 Y107.772 E.01777 +;WIDTH:0.585468 +G1 X116.609 Y109.775 E.09036 +G3 X116.294 Y110.33 I-.711 J-.036 E.02985 +G1 X116.066 Y110.391 E.01065 +G1 X116.066 Y109.979 E.01859 +M204 P1000 +;LAYER_CHANGE +;Z:11.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;11.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y110.391 E-.08562 +G1 X116.294 Y110.33 E-.04905 +G2 X116.609 Y109.775 I-.396 J-.592 E-.13752 +G1 X116.608 Y108.005 E-.36781 +;WIPE_END +G1 X116.608 Y108.005 Z11 F12000 +G1 X134.368 Y95.632 Z11.378 +;AFTER_LAYER_CHANGE +;11.2 +G1 X134.368 Y95.632 +G1 Z11.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2310 +G1 X134.368 Y102.02 E.21623 +G1 X133.979 Y102.02 E.01317 +G1 X133.622 Y102.134 E.01269 +G1 X133.498 Y102.254 E.00584 +G1 X133.409 Y102.409 E.00605 +G1 X133.368 Y102.63 E.00761 +G1 X133.368 Y104.626 E.06756 +G1 X133.44 Y104.913 E.01002 +G1 X133.608 Y105.025 E.00683 +G1 X133.44 Y105.136 E.00682 +G1 X133.368 Y105.423 E.01002 +G1 X133.368 Y107.242 E.06157 +G1 X133.443 Y107.535 E.01024 +G1 X133.619 Y107.735 E.00902 +G1 X133.979 Y107.853 E.01282 +G1 X134.368 Y107.853 E.01317 +G1 X134.368 Y114.368 E.22052 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.624 Y103.38 E.10632 +G1 X116.518 Y103.123 E.00941 +G1 X116.319 Y102.945 E.00904 +G2 X115.632 Y102.868 I-.527 J1.59 E.02357 +G1 X115.632 Y95.632 E.24493 +G1 X122.849 Y95.632 E.24429 +G1 X122.849 Y96.021 E.01317 +G1 X122.926 Y96.319 E.01042 +G1 X123.027 Y96.452 E.00565 +G1 X123.173 Y96.561 E.00617 +G1 X123.46 Y96.632 E.01001 +G1 X126.601 Y96.632 E.10632 +G1 X126.731 Y96.618 E.00443 +M73 Q53 S9 +G1 X126.966 Y96.511 E.00874 +G1 X127.139 Y96.31 E.00898 +G1 X127.212 Y96.021 E.01009 +G1 X127.212 Y95.632 E.01317 +G1 X134.308 Y95.632 E.24019 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2310 +G1 X134.775 Y101.752 E.22093 +G1 X134.775 Y102.427 E.02285 +G1 X133.979 Y102.427 E.02694 +G1 X133.818 Y102.505 E.00606 +G1 X133.775 Y102.63 E.00447 +G1 X133.775 Y104.626 E.06756 +G1 F2264.878 +M73 P53 R9 +G1 X133.782 Y104.679 E.00181 +G1 F1909.422 +G1 X133.961 Y104.826 E.00784 +;WIDTH:0.442707 +G1 F1875.289 +G1 X134.779 Y104.826 E.02719 +;WIDTH:0.441185 +G1 F1854.908 +G1 X134.779 Y105.224 E.01318 +G1 X133.979 Y105.224 E.02649 +;WIDTH:0.449999 +G1 F1856.606 +G1 X133.799 Y105.327 E.00702 +G1 F2238.659 +G1 X133.775 Y105.423 E.00335 +G1 F2298.669 +G1 X133.775 Y107.242 E.06157 +G1 F2310 +G1 X133.817 Y107.366 E.00443 +G1 X133.979 Y107.446 E.00612 +G1 X134.775 Y107.446 E.02694 +G1 X134.775 Y108.121 E.02285 +G1 X134.775 Y114.775 E.22523 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G1 X116.187 Y103.36 E.00423 +G1 X116.021 Y103.275 E.00631 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.256 Y95.225 E.27184 +G1 X123.256 Y96.021 E.02694 +G1 X123.315 Y96.165 E.00527 +G1 X123.46 Y96.225 E.00531 +G1 X126.601 Y96.225 E.10632 +G1 X126.723 Y96.185 E.00435 +G1 X126.805 Y96.021 E.00621 +G1 X126.805 Y95.225 E.02694 +G1 X134.715 Y95.225 E.26774 +M204 P1000 +G1 E-.16 F2100 +M73 Q54 S9 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z11.244 F12000 +M73 P54 R9 +G1 Z11.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2310 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.003 Y104.673 E.44657 +G3 X133.009 Y105.333 I-.955 J.339 E.02276 +G1 X124.668 Y96.992 E.39928 +G1 X125.33 Y96.994 E.02241 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z11.362 F12000 +G1 Z11.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2310 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X116.065 Y99.608 Z11.259 F12000 +G1 Z11.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.583955 +G1 F2310 +G1 X116.327 Y99.685 E.01229 +G3 X116.606 Y100.144 I-.53 J.637 E.02461 +;WIDTH:0.58432 +G1 X116.607 Y102.622 E.11155 +G1 X116.563 Y102.589 E.00248 +G1 X116.066 Y102.441 E.02334 +G1 X116.065 Y100.177 E.10192 +;WIDTH:0.583955 +G1 X116.065 Y99.812 E.01642 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.065 Y100.177 E-.07585 +G1 X116.066 Y102.441 E-.47049 +G1 X116.498 Y102.57 E-.09366 +;WIPE_END +G1 X116.608 Y107.378 Z11.284 F12000 +G1 Z11.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585468 +G1 F2310 +G1 X116.609 Y109.775 E.10814 +G3 X116.294 Y110.33 I-.678 J-.018 E.02997 +G1 X116.066 Y110.391 E.01065 +G1 X116.066 Y107.581 E.12677 +;WIDTH:0.585256 +G1 X116.428 Y107.47 E.01707 +M204 P1000 +;LAYER_CHANGE +;Z:11.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;11.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y107.581 E-.07869 +G1 X116.066 Y110.282 E-.56131 +;WIPE_END +G1 X116.066 Y110.282 Z11.2 F12000 +G1 X134.368 Y95.632 Z11.609 +;AFTER_LAYER_CHANGE +;11.4 +G1 X134.368 Y95.632 +G1 Z11.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2316 +G1 X134.368 Y101.886 E.21169 +G1 X133.979 Y101.886 E.01317 +G1 X133.626 Y101.998 E.01254 +G1 X133.5 Y102.117 E.00587 +G1 X133.41 Y102.272 E.00607 +G1 X133.368 Y102.496 E.00771 +G1 X133.368 Y104.509 E.06814 +G1 X133.37 Y104.556 E.00159 +G1 X133.477 Y104.857 E.01081 +G1 X133.769 Y105.027 E.01144 +G1 X133.477 Y105.197 E.01144 +G1 X133.402 Y105.344 E.00559 +G1 X133.368 Y105.544 E.00687 +G1 X133.368 Y107.381 E.06218 +G1 X133.444 Y107.676 E.01031 +G1 X133.622 Y107.877 E.00909 +G1 X133.979 Y107.991 E.01269 +G1 X134.368 Y107.991 E.01317 +G1 X134.368 Y114.368 E.21585 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.622 Y103.37 E.10666 +G1 X116.517 Y103.121 E.00915 +G1 X116.318 Y102.945 E.00899 +G2 X115.632 Y102.868 I-.526 J1.595 E.02353 +G1 X115.632 Y95.632 E.24493 +G1 X122.707 Y95.632 E.23948 +G1 X122.707 Y96.021 E.01317 +G1 X122.782 Y96.316 E.0103 +G1 X122.881 Y96.449 E.00561 +G1 X123.037 Y96.564 E.00656 +G1 X123.317 Y96.632 E.00975 +G1 X126.75 Y96.632 E.1162 +G1 X127.035 Y96.562 E.00993 +G1 X127.24 Y96.386 E.00915 +G1 X127.361 Y96.021 E.01302 +G1 X127.361 Y95.632 E.01317 +G1 X134.308 Y95.632 E.23515 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2316 +G1 X134.775 Y102.293 E.23924 +G1 X133.979 Y102.293 E.02694 +G1 X133.819 Y102.37 E.00601 +G1 X133.775 Y102.496 E.00452 +G1 X133.775 Y104.509 E.06814 +G1 X133.78 Y104.555 E.00157 +;WIDTH:0.487496 +G1 F2080.259 +G1 X133.839 Y104.626 E.00341 +;WIDTH:0.524992 +G1 F1943.735 +G1 X133.897 Y104.697 E.00367 +;WIDTH:0.562488 +G1 F1905.233 +G1 X133.956 Y104.768 E.00399 +G1 F1995.818 +G1 X134.72 Y104.768 E.03301 +;WIDTH:0.56051 +G1 F1984.739 +G1 X134.72 Y105.285 E.02225 +G1 X133.979 Y105.285 E.03189 +G1 F1896.455 +G1 X133.923 Y105.333 E.00317 +;WIDTH:0.523673 +G1 F1936.964 +G1 X133.867 Y105.381 E.00295 +;WIDTH:0.486836 +G1 F2021.737 +G1 X133.811 Y105.428 E.0027 +;WIDTH:0.449999 +G1 F2248.494 +G1 X133.775 Y105.544 E.00411 +G1 F2316 +G1 X133.775 Y107.381 E.06218 +G1 X133.818 Y107.506 E.00447 +G1 X133.979 Y107.584 E.00606 +G1 X134.775 Y107.584 E.02694 +G1 X134.775 Y114.775 E.24341 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X123.114 Y95.225 E.26703 +G1 X123.114 Y96.021 E.02694 +G1 X123.172 Y96.164 E.00522 +G1 X123.317 Y96.225 E.00532 +G1 X126.75 Y96.225 E.1162 +G1 X126.873 Y96.184 E.00439 +G1 X126.954 Y96.021 E.00616 +G1 X126.954 Y95.225 E.02694 +G1 X134.715 Y95.225 E.2627 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z11.444 F12000 +G1 Z11.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2316 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.017 Y104.659 E.44724 +G1 X133.055 Y104.83 E.00593 +G3 X133.054 Y105.223 I-.198 J.197 E.01476 +G1 X133.027 Y105.351 E.00443 +G1 X124.669 Y96.993 E.40009 +M73 Q55 S9 +G1 X125.33 Y96.994 E.02237 +M73 P55 R9 +G1 X116.998 Y105.326 E.39885 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z11.562 F12000 +G1 Z11.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2316 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X116.065 Y100.177 Z11.468 F12000 +G1 Z11.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.583955 +G1 F2316 +G1 X116.065 Y99.608 E.0256 +G1 X116.327 Y99.685 E.01229 +G3 X116.606 Y100.144 I-.53 J.637 E.02461 +;WIDTH:0.584319 +G1 X116.607 Y102.622 E.11155 +G1 X116.561 Y102.588 E.00258 +G1 X116.066 Y102.441 E.02325 +G1 X116.065 Y100.38 E.09278 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y102.441 E-.4283 +G1 X116.561 Y102.588 E-.10731 +G1 X116.607 Y102.622 E-.01189 +G1 X116.607 Y102.177 E-.0925 +;WIPE_END +G1 X116.066 Y110.112 Z11.539 F12000 +G1 Z11.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585467 +G1 F2316 +G1 X116.066 Y107.581 E.11418 +;WIDTH:0.585254 +G2 X116.608 Y107.373 I-.328 J-1.663 E.02631 +;WIDTH:0.585467 +G1 X116.609 Y109.775 E.10836 +G3 X116.294 Y110.33 I-.711 J-.036 E.02985 +G1 X116.066 Y110.391 E.01065 +G1 X116.066 Y110.315 E.00343 +M204 P1000 +;LAYER_CHANGE +;Z:11.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;11.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.066 Y110.391 E-.01579 +G1 X116.294 Y110.33 E-.04905 +G2 X116.609 Y109.775 I-.396 J-.592 E-.13752 +G1 X116.608 Y107.669 E-.43764 +;WIPE_END +G1 X116.608 Y107.669 Z11.4 F12000 +G1 X134.368 Y95.632 Z11.774 +;AFTER_LAYER_CHANGE +;11.6 +G1 X134.368 Y95.632 +G1 Z11.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2453 +G1 X134.368 Y101.752 E.20715 +G1 X133.979 Y101.752 E.01317 +G1 X133.629 Y101.862 E.01242 +G1 X133.503 Y101.979 E.00582 +G1 X133.412 Y102.136 E.00614 +G1 X133.368 Y102.363 E.00783 +G1 X133.368 Y104.391 E.06865 +G1 X133.377 Y104.494 E.0035 +G1 X133.514 Y104.787 E.01095 +;WIDTH:0.478845 +G1 X133.718 Y104.908 E.0086 +;WIDTH:0.50769 +G1 X133.923 Y105.028 E.00918 +G1 X133.718 Y105.149 E.0092 +;WIDTH:0.478845 +G1 X133.514 Y105.269 E.00858 +;WIDTH:0.449999 +G1 X133.417 Y105.426 E.00625 +G1 X133.368 Y105.665 E.00826 +G1 X133.368 Y107.519 E.06276 +G1 X133.446 Y107.818 E.01046 +G1 X133.626 Y108.018 E.00911 +G1 X133.979 Y108.13 E.01254 +G1 X134.368 Y108.13 E.01317 +G1 X134.368 Y114.368 E.21115 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.132 E.24493 +G2 X116.319 Y107.055 I.16 J-1.666 E.02357 +G2 X116.565 Y106.8 I-.394 J-.626 E.01211 +G1 X116.632 Y106.521 E.00971 +G1 X116.62 Y103.36 E.107 +G1 X116.516 Y103.12 E.00885 +G1 X116.317 Y102.944 E.00899 +G2 X115.632 Y102.868 I-.525 J1.602 E.02349 +G1 X115.632 Y95.632 E.24493 +G1 X122.564 Y95.632 E.23464 +G1 X122.564 Y96.021 E.01317 +G1 X122.638 Y96.312 E.01016 +G1 X122.736 Y96.446 E.00562 +G1 X122.902 Y96.568 E.00697 +G1 X123.175 Y96.632 E.00949 +G1 X126.899 Y96.632 E.12605 +G1 X127.187 Y96.56 E.01005 +G1 X127.392 Y96.382 E.00919 +G1 X127.51 Y96.021 E.01286 +G1 X127.51 Y95.632 E.01317 +G1 X134.308 Y95.632 E.2301 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.368 Y95.632 E-.01247 +G1 X134.368 Y98.652 E-.62753 +;WIPE_END +G1 X133.923 Y105.028 Z11.712 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +;WIDTH:0.50769 +G1 F2057.359 +G1 X134.342 Y105.028 E.01619 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.923 Y105.028 E-.08707 +G1 X134.342 Y105.028 E-.08707 +;WIPE_END +G1 E-.46586 F2100 +G1 X134.775 Y95.225 Z11.771 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2399.997 +G1 X134.775 Y101.484 E.21186 +G1 F2395.038 +G1 X134.775 Y102.159 E.02285 +G1 F2390.378 +G1 X133.979 Y102.159 E.02694 +G1 F2389.776 +G1 X133.82 Y102.235 E.00597 +G1 F2388.095 +G1 X133.775 Y102.363 E.00459 +G1 F2361.167 +G1 X133.775 Y104.391 E.06865 +G1 F2207.84 +G1 X133.824 Y104.523 E.00477 +G1 F1900.609 +G1 X133.979 Y104.595 E.00578 +G1 F1887.015 +G1 X134.775 Y104.595 E.02694 +G1 F1864.135 +G1 X134.775 Y105.462 E.02935 +G1 X133.979 Y105.462 E.02694 +G1 F1878.188 +G1 X133.824 Y105.533 E.00577 +G1 F2204.948 +G1 X133.775 Y105.665 E.00477 +G1 F2324.6 +G1 X133.775 Y107.519 E.06276 +G1 F2362.24 +G1 X133.819 Y107.646 E.00455 +G1 F2365.218 +G1 X133.979 Y107.723 E.00601 +G1 F2366.339 +G1 X134.775 Y107.723 E.02694 +G1 F2375.72 +G1 X134.775 Y108.398 E.02285 +G1 F2399.997 +G1 X134.775 Y114.775 E.21585 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.725 E.27248 +G1 X116.021 Y106.725 E.02694 +G1 F2400 +G2 X116.225 Y106.521 I-.031 J-.235 E.0105 +G1 X116.225 Y103.479 E.10297 +G2 X116.021 Y103.275 I-.24 J.036 E.01046 +G1 F2399.993 +G1 X115.225 Y103.275 E.02694 +G1 X115.225 Y95.225 E.27248 +G1 X122.971 Y95.225 E.26219 +G1 X122.971 Y96.021 E.02694 +G1 F2400 +G1 X123.029 Y96.163 E.00519 +G1 X123.175 Y96.225 E.00537 +G1 X126.899 Y96.225 E.12605 +G1 X127.023 Y96.183 E.00443 +G1 X127.103 Y96.021 E.00612 +G1 F2399.997 +G1 X127.103 Y95.225 E.02694 +G1 X134.715 Y95.225 E.25766 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z11.644 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2453 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.04 Y104.636 E.44835 +G1 X133.079 Y104.777 E.00495 +G3 X133.079 Y105.28 I-.189 J.251 E.01973 +G1 X133.052 Y105.376 E.00338 +G1 X124.669 Y96.993 E.40129 +G1 X125.33 Y96.995 E.02237 +G1 X116.998 Y105.326 E.39882 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z11.762 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2453 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +M73 Q56 S9 +G1 X115.998 Y95.998 E.86184 +M73 P56 R9 +G1 X118.65 Y95.998 E.08977 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y99.116 E.01577 +G3 X117.069 Y100.028 I.016 J1.066 E.052 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X115.998 Y99.116 I-1.055 J.154 E-.31924 +G1 X115.998 Y98.65 E-.09684 +G1 X116.76 Y97.888 E-.22392 +;WIPE_END +G1 X116.859 Y102.102 Z11.674 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.456332 +G1 F2400 +G1 X116.222 Y102.739 E.03097 +M204 P1000 +G1 X116.222 Y102.739 F12000 +G1 X115.814 Y102.562 +M204 P800 +G1 F2400 +G1 X116.859 Y101.517 E.0508 +M204 P1000 +G1 X116.859 Y101.517 F12000 +G1 X116.858 Y100.933 +M204 P800 +G1 F2400 +G1 X115.814 Y101.978 E.05078 +M204 P1000 +G1 X115.814 Y101.978 F12000 +G1 X115.814 Y101.393 +M204 P800 +G1 F2400 +G1 X116.858 Y100.348 E.05078 +M204 P1000 +G1 X116.858 Y100.348 F12000 +G1 X116.786 Y99.836 +M204 P800 +G1 F2400 +G1 X115.814 Y100.808 E.04725 +M204 P1000 +G1 X115.814 Y100.808 F12000 +G1 X115.814 Y100.224 +M204 P800 +G1 F2400 +G1 X116.525 Y99.513 E.03456 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.814 Y100.224 E-.20896 +;WIPE_END +G1 E-.43104 F2100 +G1 X116.854 Y109.918 Z11.77 F12000 +G1 Z11.6 F720 +G1 E.8 F1500 +M204 P800 +;WIDTH:0.443403 +G1 F2400 +G1 X116.135 Y110.636 E.03384 +M204 P1000 +G1 X116.135 Y110.636 F12000 +G1 X115.814 Y110.391 +M204 P800 +G1 F2400 +G1 X116.861 Y109.343 E.04933 +M204 P1000 +G1 X116.861 Y109.343 F12000 +G1 X116.861 Y108.777 +M204 P800 +G1 F2400 +G1 X115.814 Y109.825 E.04933 +M204 P1000 +G1 X115.814 Y109.825 F12000 +G1 X115.814 Y109.258 +M204 P800 +G1 F2400 +G1 X116.861 Y108.211 E.04931 +M204 P1000 +G1 X116.861 Y107.645 F12000 +M204 P800 +G1 F2400 +G1 X115.814 Y108.692 E.04931 +M204 P1000 +G1 X115.814 Y108.692 F12000 +G1 X115.814 Y108.126 +M204 P800 +G1 F2400 +G1 X116.861 Y107.079 E.04931 +M204 P1000 +;LAYER_CHANGE +;Z:11.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;11.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.814 Y108.126 E-.3077 +;WIPE_END +G1 E-.3323 F2100 +G1 X115.814 Y108.126 Z11.6 F12000 +G1 X125.091 Y96.027 Z11.866 +;AFTER_LAYER_CHANGE +;11.8 +G1 X125.091 Y96.027 +G1 Z11.8 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1200 +G1 X125.091 Y96 E.00076 +G1 F900 +G1 X125.091 Y95.8 E.00565 +M204 P1000 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X125.091 Y95.081 E.02033 +M106 S124.95 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.091 Y96.027 E-.19659 +G1 X125.091 Y96 E-.00561 +G1 X125.091 Y95.8 E-.04156 +;WIPE_END +G1 E-.39624 F2100 +G1 X134.368 Y95.632 Z11.962 F12000 +G1 Z11.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2231 +G1 X134.368 Y101.618 E.20262 +G1 X133.979 Y101.618 E.01317 +G1 X133.633 Y101.725 E.01226 +G1 X133.506 Y101.842 E.00584 +G1 X133.413 Y101.999 E.00618 +G1 X133.368 Y102.229 E.00793 +G1 X133.368 Y104.268 E.06902 +G1 X133.387 Y104.419 E.00515 +G1 X133.551 Y104.704 E.01113 +G1 X133.739 Y104.778 E.00684 +;WIDTH:0.424913 +G1 X133.928 Y104.852 E.00645 +;WIDTH:0.399826 +G1 X134.394 Y104.852 E.01383 +;WIDTH:0.396919 +G1 X134.394 Y105.206 E.01042 +G1 X133.979 Y105.206 E.01222 +;WIDTH:0.423459 +G1 X133.765 Y105.281 E.00718 +;WIDTH:0.449999 +G1 X133.551 Y105.355 E.00766 +G1 X133.434 Y105.513 E.00665 +G1 X133.368 Y105.791 E.00967 +G1 X133.368 Y107.657 E.06316 +G1 X133.372 Y107.725 E.00231 +G1 X133.448 Y107.959 E.00833 +G1 X133.63 Y108.159 E.00915 +G1 X133.979 Y108.268 E.01238 +G1 X134.368 Y108.268 E.01317 +G1 X134.368 Y114.368 E.20648 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y110.432 E.13323 +G1 X116.021 Y110.432 E.01317 +G1 X116.291 Y110.369 E.00938 +G1 X116.425 Y110.28 E.00545 +G1 X116.592 Y110.039 E.00992 +G1 X116.632 Y109.821 E.0075 +G1 X116.632 Y100.892 E.30224 +G1 X116.629 Y100.122 E.02606 +G1 X116.525 Y99.832 E.01043 +G1 X116.291 Y99.631 E.01044 +G1 X116.021 Y99.568 E.00938 +G1 X115.632 Y99.568 E.01317 +G1 X115.632 Y95.632 E.13323 +G1 X122.422 Y95.632 E.22983 +G1 X122.422 Y96.021 E.01317 +G1 X122.527 Y96.363 E.01211 +G1 X122.739 Y96.557 E.00973 +G1 X123.032 Y96.632 E.01024 +G1 X125.016 Y96.632 E.06716 +G1 X125.145 Y96.632 E.00437 +G1 X127.048 Y96.632 E.06441 +G1 X127.339 Y96.559 E.01016 +G1 X127.544 Y96.379 E.00923 +G1 X127.659 Y96.021 E.01273 +G1 X127.659 Y95.632 E.01317 +G1 X134.308 Y95.632 E.22506 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2231 +G1 X134.775 Y102.025 E.23017 +G1 X133.979 Y102.025 E.02694 +G1 X133.821 Y102.1 E.00592 +G1 X133.775 Y102.229 E.00464 +G1 X133.775 Y104.268 E.06902 +G1 F2164.314 +G1 X133.836 Y104.414 E.00536 +G1 F1867.293 +G1 X133.979 Y104.472 E.00522 +G1 F1853.006 +G1 X134.775 Y104.472 E.02694 +G1 F1839.182 +G1 X134.775 Y105.587 E.03774 +G1 X133.979 Y105.587 E.02694 +G1 F1853.771 +G1 X133.836 Y105.645 E.00522 +G1 F2161.993 +G1 X133.775 Y105.791 E.00536 +G1 F2231 +G1 X133.775 Y107.657 E.06316 +G1 X133.82 Y107.785 E.00459 +G1 X133.979 Y107.861 E.00597 +G1 X134.775 Y107.861 E.02694 +G1 X134.775 Y114.775 E.23403 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y110.025 E.16078 +G1 X116.021 Y110.025 E.02694 +G1 X116.156 Y109.974 E.00488 +G1 X116.225 Y109.821 E.00568 +G1 X116.225 Y100.179 E.32637 +G1 X116.156 Y100.026 E.00568 +G1 X116.021 Y99.975 E.00488 +G1 X115.225 Y99.975 E.02694 +G1 X115.225 Y95.225 E.16078 +G1 X122.154 Y95.225 E.23454 +G1 X122.829 Y95.225 E.02285 +G1 X122.829 Y96.021 E.02694 +G1 X122.902 Y96.178 E.00586 +G1 X123.032 Y96.225 E.00468 +G1 X125.031 Y96.225 E.06766 +G1 F1976.948 +G1 X125.061 Y96.126 E.0035 +;WIDTH:0.41646 +G1 F1384.594 +G1 X125.091 Y96.027 E.00321 +G1 X125.119 Y96.126 E.0032 +;WIDTH:0.449999 +G1 F1976.888 +G1 X125.148 Y96.225 E.00349 +G1 F2231 +G1 X127.048 Y96.225 E.06431 +G1 X127.174 Y96.182 E.00451 +G1 X127.252 Y96.021 E.00606 +G1 X127.252 Y95.225 E.02694 +G1 X127.927 Y95.225 E.02285 +G1 X134.715 Y95.225 E.22977 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z11.844 F12000 +G1 Z11.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2231 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.068 Y104.608 E.44969 +G2 X133.428 Y105.029 I.602 J-.15 E.01944 +G2 X133.084 Y105.408 I.217 J.543 E.01793 +G1 X124.674 Y96.998 E.40258 +G1 X125.326 Y96.998 E.02207 +G1 X116.997 Y105.327 E.3987 +G1 X116.997 Y104.673 E.02214 +G1 X126.326 Y114.002 E.44657 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.562 Y113.238 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z11.962 F12000 +G1 Z11.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2231 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.65 Y95.998 E.08977 +M73 P57 R9 +M73 Q57 S9 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y99.202 E.01868 +G3 X116.992 Y100.073 I.012 J.989 E.04899 +M204 P1000 +;LAYER_CHANGE +;Z:12 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;12 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G2 X115.998 Y99.202 I-.982 J.118 E-.30075 +G1 X115.998 Y98.65 E-.11471 +G1 X116.762 Y97.886 E-.22454 +;WIPE_END +G1 X116.762 Y97.886 Z11.8 F12000 +G1 X125.095 Y95.206 Z12 +;AFTER_LAYER_CHANGE +;12 +G1 X125.095 Y95.206 +G1 Z12 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.413246 +G1 F1741.305 +G1 X125.095 Y96.038 E.02562 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.095 Y95.206 E-.1729 +G1 X125.095 Y96.038 E-.1729 +;WIPE_END +G1 E-.2942 F2100 +G1 X134.368 Y95.632 Z12.162 F12000 +G1 Z12 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2200 +G1 X134.368 Y101.485 E.19812 +G1 X133.979 Y101.485 E.01317 +G1 X133.637 Y101.589 E.0121 +G1 X133.51 Y101.704 E.0058 +G1 X133.414 Y101.862 E.00626 +G1 X133.368 Y102.095 E.00804 +G1 X133.368 Y104.145 E.06939 +G1 X133.411 Y104.37 E.00775 +G1 X133.585 Y104.612 E.01009 +;WIDTH:0.487415 +G1 X133.754 Y104.702 E.00708 +;WIDTH:0.52483 +G1 X133.922 Y104.791 E.00762 +G1 X134.332 Y104.792 E.01643 +;WIDTH:0.521448 +G1 X134.332 Y105.27 E.01902 +G1 X133.979 Y105.27 E.01405 +G1 X133.782 Y105.36 E.00862 +;WIDTH:0.485724 +G1 X133.585 Y105.45 E.00797 +;WIDTH:0.449999 +G1 X133.411 Y105.692 E.01009 +G1 X133.368 Y105.917 E.00775 +G1 X133.368 Y107.796 E.0636 +G1 X133.372 Y107.864 E.00231 +G1 X133.45 Y108.101 E.00845 +G1 X133.634 Y108.3 E.00917 +G1 X133.979 Y108.407 E.01223 +G1 X134.368 Y108.407 E.01317 +G1 X134.368 Y114.368 E.20177 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y110.7 E.12416 +G1 X115.632 Y110.232 E.01584 +G1 X116.021 Y110.232 E.01317 +G1 X116.3 Y110.165 E.00971 +G1 X116.434 Y110.072 E.00552 +G1 X116.594 Y109.835 E.00968 +G1 X116.632 Y109.621 E.00736 +G1 X116.632 Y101.216 E.2845 +G1 X116.628 Y100.305 E.03084 +G1 X116.519 Y100.025 E.01017 +G1 X116.3 Y99.835 E.00981 +G1 X116.021 Y99.768 E.00971 +G1 X115.632 Y99.768 E.01317 +G1 X115.632 Y99.3 E.01584 +G1 X115.632 Y95.632 E.12416 +G1 X122.279 Y95.632 E.22499 +G1 X122.279 Y96.021 E.01317 +G1 X122.382 Y96.361 E.01203 +G1 X122.63 Y96.574 E.01107 +G1 X122.89 Y96.632 E.00902 +G1 X124.91 Y96.632 E.06837 +G1 X124.985 Y96.627 E.00254 +G1 X125.095 Y96.57 E.00419 +G1 X125.132 Y96.614 E.00195 +G1 X125.279 Y96.632 E.00501 +G1 X127.197 Y96.632 E.06492 +G1 X127.491 Y96.557 E.01027 +G1 X127.696 Y96.375 E.00928 +G1 X127.808 Y96.021 E.01257 +G1 X127.808 Y95.632 E.01317 +G1 X134.308 Y95.632 E.22002 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2200 +G1 X134.775 Y101.892 E.22567 +G1 X133.979 Y101.892 E.02694 +G1 X133.822 Y101.965 E.00586 +G1 X133.775 Y102.095 E.00468 +G1 X133.775 Y104.145 E.06939 +G1 F2130.966 +G1 X133.847 Y104.301 E.00582 +G1 F1867.293 +G1 X133.979 Y104.349 E.00475 +G1 F1853.006 +G1 X134.775 Y104.349 E.02694 +G1 F1833.012 +G1 X134.775 Y105.713 E.04617 +G2 X133.847 Y105.761 I-.284 J3.508 E.03155 +G1 F2126.626 +G1 X133.775 Y105.917 E.00582 +G1 F2200 +G1 X133.775 Y107.796 E.0636 +G1 X133.821 Y107.925 E.00464 +G1 X133.979 Y107.999 E.00591 +G1 X134.775 Y107.999 E.02694 +G1 X134.775 Y114.775 E.22936 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y110.7 E.13793 +G1 F1359.994 +G1 X115.225 Y109.825 E.02962 +G1 X116.021 Y109.825 E.02694 +G1 F1410.859 +G1 X116.159 Y109.772 E.005 +G1 F2118.392 +G1 X116.225 Y109.621 E.00558 +G1 F2200 +G1 X116.225 Y100.379 E.31283 +G1 F2118.388 +G1 X116.159 Y100.229 E.00555 +G1 F1410.859 +G1 X116.021 Y100.175 E.00502 +G1 F1359.994 +G1 X115.225 Y100.175 E.02694 +G1 X115.225 Y99.3 E.02962 +G1 F2200 +G1 X115.225 Y95.225 E.13793 +G1 X122.686 Y95.225 E.25255 +G1 X122.686 Y96.021 E.02694 +G1 X122.759 Y96.177 E.00583 +G1 X122.89 Y96.225 E.00472 +G1 X124.92 Y96.225 E.06871 +G1 F1752.278 +G1 X125.095 Y96.038 E.00867 +G1 X125.23 Y96.219 E.00764 +G1 F2200 +G1 X125.279 Y96.225 E.00167 +G1 X127.197 Y96.225 E.06492 +G1 X127.324 Y96.181 E.00455 +G1 X127.401 Y96.021 E.00601 +G1 X127.401 Y95.225 E.02694 +G1 X134.715 Y95.225 E.24757 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z12.044 F12000 +G1 Z12 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2200 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.106 Y104.57 E.4515 +G2 X133.58 Y105.031 I.849 J-.398 E.02287 +G2 X133.124 Y105.448 I.415 J.912 E.02126 +G1 X124.674 Y96.998 E.4045 +G1 X125.095 Y96.978 E.01427 +G1 X125.326 Y96.998 E.00785 +G1 X116.996 Y105.328 E.39875 +G1 X116.995 Y104.671 E.02224 +G1 X126.326 Y114.002 E.44667 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:12.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;12.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z12 F12000 +G1 X134.368 Y95.632 Z12.288 +;AFTER_LAYER_CHANGE +;12.2 +G1 X134.368 Y95.632 +G1 Z12.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2192 +G1 X134.368 Y101.351 E.19358 +G1 X133.979 Y101.351 E.01317 +G1 X133.641 Y101.453 E.01195 +G1 X133.513 Y101.567 E.0058 +G1 X133.415 Y101.725 E.00629 +G1 X133.368 Y101.961 E.00815 +G1 X133.368 Y104.021 E.06973 +G1 X133.422 Y104.272 E.00869 +G1 X133.613 Y104.51 E.01033 +G1 X133.925 Y104.63 E.01132 +G1 X134.368 Y104.632 E.015 +G1 X134.368 Y105.432 E.02708 +G1 X133.979 Y105.432 E.01317 +G1 X133.613 Y105.554 E.01306 +G1 X133.422 Y105.792 E.01033 +G1 X133.368 Y106.04 E.00859 +G1 X133.368 Y107.934 E.06411 +G1 X133.372 Y108.003 E.00234 +G1 X133.451 Y108.243 E.00855 +G1 X133.638 Y108.441 E.00922 +G1 X133.979 Y108.545 E.01207 +G1 X134.368 Y108.545 E.01317 +G1 X134.368 Y114.368 E.1971 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y110.5 E.13093 +G1 X115.632 Y110.032 E.01584 +G1 X116.021 Y110.032 E.01317 +G1 X116.309 Y109.96 E.01005 +G1 X116.443 Y109.864 E.00558 +G1 X116.595 Y109.63 E.00944 +G1 X116.632 Y109.421 E.00718 +G1 X116.632 Y101.302 E.27482 +G1 X116.625 Y100.488 E.02755 +M73 P58 R9 +M73 Q58 S9 +G1 X116.542 Y100.259 E.00824 +G1 X116.309 Y100.04 E.01082 +G1 X116.021 Y99.968 E.01005 +G1 X115.632 Y99.968 E.01317 +G1 X115.632 Y99.5 E.01584 +G1 X115.632 Y95.632 E.13093 +G1 X122.137 Y95.632 E.22019 +G1 X122.137 Y96.021 E.01317 +G1 X122.239 Y96.359 E.01195 +G1 X122.353 Y96.487 E.0058 +G1 X122.521 Y96.589 E.00665 +G1 X122.748 Y96.632 E.00782 +G1 X124.787 Y96.632 E.06902 +G1 X125.023 Y96.585 E.00815 +G1 X125.098 Y96.481 E.00434 +G1 X125.174 Y96.585 E.00436 +G1 X125.41 Y96.632 E.00815 +G1 X127.347 Y96.632 E.06557 +G1 X127.643 Y96.555 E.01035 +G1 X127.847 Y96.371 E.0093 +G1 X127.957 Y96.021 E.01242 +G1 X127.957 Y95.632 E.01317 +G1 X134.308 Y95.632 E.21497 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2192 +G1 X134.775 Y101.758 E.22113 +G1 X133.979 Y101.758 E.02694 +G1 X133.823 Y101.83 E.00582 +G1 X133.775 Y101.961 E.00472 +G1 X133.775 Y104.021 E.06973 +G1 X133.81 Y104.135 E.00404 +G1 F1863.72 +G1 X133.976 Y104.225 E.00639 +G1 F1846.356 +G1 X134.775 Y104.225 E.02705 +G1 F1833.006 +G1 X134.775 Y105.839 E.05463 +G1 X133.979 Y105.839 E.02694 +G1 F1847.734 +G1 X133.81 Y105.929 E.00648 +G1 F2192 +G1 X133.775 Y106.042 E.004 +G1 X133.775 Y107.934 E.06404 +G1 X133.822 Y108.065 E.00471 +G1 X133.979 Y108.138 E.00586 +G1 X134.775 Y108.138 E.02694 +G1 X134.775 Y114.775 E.22465 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y110.5 E.1447 +G1 F1359.987 +G1 X115.225 Y109.625 E.02962 +G1 X116.021 Y109.625 E.02694 +G1 F1410.854 +G1 X116.162 Y109.569 E.00514 +G1 F2130.574 +G1 X116.225 Y109.421 E.00544 +G1 F2192 +G1 X116.225 Y100.579 E.29929 +G1 F2130.574 +G1 X116.162 Y100.431 E.00544 +G1 F1410.854 +G1 X116.021 Y100.375 E.00514 +G1 F1359.987 +G1 X115.225 Y100.375 E.02694 +G1 X115.225 Y99.5 E.02962 +G1 F2192 +G1 X115.225 Y95.225 E.1447 +G1 X121.869 Y95.225 E.22489 +G1 X122.544 Y95.225 E.02285 +G1 X122.544 Y96.021 E.02694 +G1 X122.616 Y96.177 E.00582 +G1 X122.748 Y96.225 E.00475 +G1 X124.787 Y96.225 E.06902 +G1 X124.865 Y96.209 E.0027 +G1 F1965.19 +G1 X124.904 Y96.122 E.00323 +;WIDTH:0.402893 +G1 F1659.698 +G1 X124.943 Y96.035 E.00285 +;WIDTH:0.355787 +G1 F1636.208 +G1 X124.943 Y95.177 E.02232 +;WIDTH:0.354597 +G1 F1575.808 +G1 X125.254 Y95.177 E.00806 +G1 X125.254 Y96.021 E.02187 +;WIDTH:0.402298 +G1 F1565.14 +G1 X125.293 Y96.115 E.00304 +;WIDTH:0.449999 +G1 F1933.119 +G1 X125.331 Y96.209 E.00343 +G1 F2192 +G1 X125.41 Y96.225 E.00273 +G1 X127.347 Y96.225 E.06557 +G1 X127.474 Y96.18 E.00456 +G1 X127.55 Y96.021 E.00597 +G1 X127.55 Y95.225 E.02694 +G1 X128.225 Y95.225 E.02285 +G1 X134.715 Y95.225 E.21968 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z12.244 F12000 +G1 Z12.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2192 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 Q58 S8 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.147 Y104.529 E.45347 +G2 X133.877 Y104.995 I.907 J-.614 E.03014 +M73 P58 R8 +G3 X134.002 Y105.068 I.049 J.058 E.00642 +G2 X133.169 Y105.493 I.031 J1.087 E.03272 +G1 X124.674 Y96.998 E.40665 +G3 X125.333 Y96.991 I.351 J2.102 E.0224 +G1 X116.997 Y105.327 E.39904 +G1 X116.996 Y104.672 E.02217 +G1 X126.326 Y114.002 E.44662 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:12.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;12.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z12.2 F12000 +G1 X134.368 Y95.632 Z12.488 +;AFTER_LAYER_CHANGE +;12.4 +G1 X134.368 Y95.632 +G1 Z12.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2187 +G1 X134.368 Y101.217 E.18905 +G1 X133.979 Y101.217 E.01317 +G1 X133.645 Y101.316 E.01179 +G1 X133.516 Y101.429 E.0058 +G1 X133.417 Y101.588 E.00634 +G1 X133.368 Y101.828 E.00829 +G1 X133.368 Y103.895 E.06997 +G1 X133.429 Y104.162 E.00927 +G1 X133.605 Y104.378 E.00943 +G1 X133.9 Y104.501 E.01082 +G1 X134.368 Y104.506 E.01584 +G1 X134.368 Y105.561 E.03571 +G1 X133.979 Y105.561 E.01317 +G1 X133.605 Y105.689 E.01338 +G1 X133.429 Y105.905 E.00943 +G1 X133.368 Y106.171 E.00924 +G1 X133.368 Y108.073 E.06438 +G1 X133.372 Y108.142 E.00234 +G1 X133.453 Y108.384 E.00864 +G1 X133.642 Y108.582 E.00927 +G1 X133.979 Y108.683 E.01191 +G1 X134.368 Y108.683 E.01317 +G1 X134.368 Y114.368 E.19243 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y110.3 E.1377 +G1 X115.632 Y109.832 E.01584 +G1 X116.021 Y109.832 E.01317 +G1 X116.317 Y109.756 E.01034 +G1 X116.451 Y109.656 E.00566 +G1 X116.597 Y109.426 E.00922 +G1 X116.632 Y109.221 E.00704 +G1 X116.632 Y101.692 E.25485 +G1 X116.623 Y100.672 E.03453 +G1 X116.533 Y100.445 E.00827 +G1 X116.317 Y100.244 E.00999 +G1 X116.021 Y100.168 E.01034 +G1 X115.632 Y100.168 E.01317 +G1 X115.632 Y99.7 E.01584 +G1 X115.632 Y95.632 E.1377 +G1 X121.994 Y95.632 E.21535 +G1 X121.994 Y96.021 E.01317 +G1 X122.095 Y96.357 E.01188 +G1 X122.315 Y96.559 E.01011 +G1 X122.605 Y96.632 E.01012 +G1 X124.659 Y96.632 E.06953 +G1 X124.685 Y96.632 E.00088 +G1 X124.972 Y96.546 E.01014 +G1 X125.102 Y96.338 E.0083 +G1 X125.233 Y96.546 E.00832 +G1 X125.371 Y96.607 E.00511 +G1 X125.546 Y96.632 E.00598 +G1 X127.496 Y96.632 E.06601 +G1 X127.795 Y96.553 E.01047 +G1 X127.999 Y96.367 E.00934 +G1 X128.106 Y96.021 E.01226 +G1 X128.106 Y95.632 E.01317 +G1 X134.308 Y95.632 E.20993 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2187 +G1 X134.775 Y101.624 E.2166 +G1 X133.979 Y101.624 E.02694 +G1 X133.824 Y101.695 E.00577 +G1 X133.775 Y101.828 E.0048 +G1 X133.775 Y103.895 E.06997 +G1 X133.812 Y104.012 E.00415 +G1 F1846.265 +G1 X133.979 Y104.099 E.00637 +G1 F1831.502 +G1 X134.775 Y104.099 E.02694 +G1 F1816.02 +G1 X134.775 Y105.968 E.06326 +G1 X133.979 Y105.968 E.02694 +G1 F1831.145 +G1 X133.812 Y106.054 E.00636 +G1 F2187 +G1 X133.775 Y106.171 E.00415 +G1 X133.775 Y108.073 E.06438 +G1 X133.824 Y108.205 E.00477 +G1 X133.979 Y108.276 E.00577 +G1 X134.775 Y108.276 E.02694 +G1 X134.775 Y114.775 E.21998 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y110.3 E.15147 +G1 F1359.994 +M73 Q59 S8 +G1 X115.225 Y109.425 E.02962 +G1 X116.021 Y109.425 E.02694 +G1 F1410.859 +M73 P59 R8 +G1 X116.164 Y109.366 E.00524 +G1 F2141.829 +G1 X116.225 Y109.221 E.00532 +G1 F2187 +G1 X116.225 Y100.779 E.28575 +G1 F2141.829 +G1 X116.164 Y100.634 E.00532 +G1 F1410.859 +G1 X116.021 Y100.575 E.00524 +G1 F1359.994 +G1 X115.225 Y100.575 E.02694 +G1 X115.225 Y99.7 E.02962 +G1 F2187 +G1 X115.225 Y95.225 E.15147 +G1 X122.402 Y95.225 E.24293 +G1 X122.402 Y96.021 E.02694 +G1 X122.473 Y96.176 E.00577 +G1 X122.605 Y96.225 E.00477 +G1 X124.659 Y96.225 E.06953 +G1 X124.717 Y96.217 E.00198 +;WIDTH:0.487941 +G1 F1921.783 +G1 X124.881 Y96.041 E.0089 +G1 F1881.261 +G1 X124.881 Y95.243 E.02953 +;WIDTH:0.486248 +G1 F1836.86 +G1 X125.324 Y95.243 E.01633 +G1 X125.324 Y96.021 E.02868 +G1 F1849.42 +G1 X125.442 Y96.196 E.00778 +;WIDTH:0.449999 +G1 F2187 +G1 X125.546 Y96.225 E.00365 +G1 X127.496 Y96.225 E.06601 +G1 X127.625 Y96.179 E.00464 +G1 X127.699 Y96.021 E.00591 +G1 X127.699 Y95.225 E.02694 +G1 X134.715 Y95.225 E.23748 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z12.444 F12000 +G1 Z12.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2187 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.197 Y104.479 E.45586 +G2 X134.002 Y104.868 I.818 J-.663 E.03125 +G1 X134.002 Y105.198 E.01117 +G2 X133.23 Y105.554 I.007 J1.031 E.02966 +G1 X124.673 Y96.997 E.40962 +G1 X124.717 Y96.997 E.00149 +G3 X125.35 Y96.974 I.345 J.762 E.02199 +G1 X116.997 Y105.327 E.39985 +G1 X116.995 Y104.671 E.0222 +G1 X126.326 Y114.002 E.44667 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:12.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;12.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z12.4 F12000 +G1 X134.368 Y95.632 Z12.688 +;AFTER_LAYER_CHANGE +;12.6 +G1 X134.368 Y95.632 +G1 Z12.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2280 +G1 X134.368 Y101.083 E.18451 +G1 X133.979 Y101.083 E.01317 +G1 X133.65 Y101.18 E.01161 +G1 X133.52 Y101.291 E.00579 +G1 X133.418 Y101.451 E.00642 +G1 X133.368 Y101.694 E.0084 +G1 X133.368 Y103.769 E.07024 +G1 X133.411 Y103.995 E.00779 +G1 X133.595 Y104.244 E.01048 +G1 X133.837 Y104.363 E.00913 +G1 X134.368 Y104.379 E.01798 +G1 X134.368 Y105.69 E.04438 +G1 X133.837 Y105.706 E.01798 +G1 X133.595 Y105.825 E.00913 +G1 X133.436 Y106.021 E.00854 +G1 X133.368 Y106.3 E.00972 +G1 X133.368 Y108.211 E.06468 +G1 X133.372 Y108.281 E.00237 +G1 X133.455 Y108.526 E.00876 +G1 X133.647 Y108.724 E.00934 +G1 X133.979 Y108.822 E.01172 +G1 X134.368 Y108.822 E.01317 +G1 X134.368 Y114.368 E.18773 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y110.1 E.14447 +G1 X115.632 Y109.632 E.01584 +G1 X116.021 Y109.632 E.01317 +G1 X116.325 Y109.551 E.01065 +G1 X116.458 Y109.449 E.00567 +G1 X116.598 Y109.223 E.009 +G1 X116.632 Y109.021 E.00693 +G1 X116.632 Y101.721 E.2471 +G1 X116.629 Y100.921 E.02708 +G1 X116.524 Y100.631 E.01044 +G1 X116.325 Y100.449 E.00913 +G1 X116.021 Y100.368 E.01065 +G1 X115.632 Y100.368 E.01317 +G1 X115.632 Y99.9 E.01584 +G1 X115.632 Y95.9 E.1354 +G1 X115.632 Y95.632 E.00907 +G1 X121.852 Y95.632 E.21054 +G1 X121.852 Y96.021 E.01317 +G1 X121.952 Y96.356 E.01183 +G1 X122.192 Y96.569 E.01086 +G1 X122.463 Y96.632 E.00942 +G1 X124.526 Y96.632 E.06983 +G1 X124.605 Y96.627 E.00268 +G1 X124.902 Y96.503 E.01089 +G1 X125.004 Y96.287 E.00809 +;WIDTH:0.423181 +G1 X125.107 Y96.072 E.00754 +G1 X125.21 Y96.287 E.00754 +;WIDTH:0.449999 +G1 X125.313 Y96.503 E.0081 +G1 X125.467 Y96.59 E.00599 +G1 X125.689 Y96.632 E.00765 +G1 X127.645 Y96.632 E.06621 +G1 X127.712 Y96.628 E.00227 +G1 X127.948 Y96.552 E.00839 +G1 X128.151 Y96.362 E.00941 +G1 X128.255 Y96.021 E.01207 +G1 X128.255 Y95.632 E.01317 +G1 X134.308 Y95.632 E.20489 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.368 Y95.632 E-.01247 +G1 X134.368 Y98.652 E-.62753 +;WIPE_END +G1 X125.107 Y95.603 Z12.77 F12000 +G1 Z12.6 F720 +G1 E.8 F1500 +;WIDTH:0.396362 +G1 F2062.044 +G1 X125.107 Y96.072 E.01378 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.107 Y95.603 E-.09746 +G1 X125.107 Y96.072 E-.09746 +;WIPE_END +G1 E-.44508 F2100 +G1 X134.775 Y95.225 Z12.769 F12000 +G1 Z12.6 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2280 +G1 X134.775 Y101.49 E.21206 +G1 X133.979 Y101.49 E.02694 +G1 X133.826 Y101.56 E.0057 +G1 X133.775 Y101.694 E.00485 +G1 X133.775 Y103.769 E.07024 +G1 X133.775 Y103.783 E.00047 +G1 F2116.447 +G1 X133.851 Y103.927 E.00551 +G1 F1846.265 +G1 X133.979 Y103.972 E.00459 +G1 F1831.502 +G1 X134.775 Y103.972 E.02694 +G1 F1815.066 +G1 X134.775 Y106.097 E.07193 +G1 X133.931 Y106.102 E.02857 +G1 F1924.71 +G1 X133.814 Y106.181 E.00478 +G1 F2235.558 +G1 X133.775 Y106.3 E.00424 +G1 F2280 +G1 X133.775 Y108.211 E.06468 +G1 X133.825 Y108.345 E.00484 +G1 X133.979 Y108.415 E.00573 +G1 X134.775 Y108.415 E.02694 +G1 X134.775 Y114.775 E.21528 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y110.1 E.15824 +G1 F1360 +G1 X115.225 Y109.225 E.02962 +G1 X116.021 Y109.225 E.02694 +G1 F1410.864 +G1 X116.167 Y109.164 E.00536 +G1 F2152.257 +G1 X116.225 Y109.021 E.00522 +G1 F2280 +G1 X116.225 Y101.654 E.24936 +G1 X116.225 Y100.979 E.02285 +G1 F2152.257 +G1 X116.167 Y100.836 E.00522 +G1 F1410.859 +G1 X116.021 Y100.775 E.00536 +G1 F1359.994 +G1 X115.225 Y100.775 E.02694 +G1 X115.225 Y99.9 E.02962 +G1 F2280 +G1 X115.225 Y95.9 E.1354 +G1 X115.225 Y95.225 E.02285 +G1 X121.584 Y95.225 E.21524 +G1 X122.259 Y95.225 E.02285 +G1 X122.259 Y96.021 E.02694 +G1 X122.33 Y96.176 E.00577 +G1 X122.463 Y96.225 E.0048 +G1 X124.526 Y96.225 E.06983 +G1 F2217.96 +G1 X124.651 Y96.182 E.00447 +G1 F1802.209 +G1 X124.729 Y96.021 E.00606 +G1 F1786.337 +G1 X124.729 Y95.225 E.02694 +G1 F1724.046 +G1 X125.485 Y95.225 E.02559 +G1 X125.485 Y96.021 E.02694 +G1 F1741.745 +G1 X125.564 Y96.182 E.00607 +G1 F2216.01 +G1 X125.689 Y96.225 E.00447 +G1 F2280 +G1 X127.645 Y96.225 E.06621 +G1 X127.775 Y96.178 E.00468 +G1 X127.848 Y96.021 E.00586 +G1 X127.848 Y95.225 E.02694 +G1 X128.523 Y95.225 E.02285 +G1 X134.715 Y95.225 E.20959 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z12.644 F12000 +G1 Z12.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2280 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P60 R8 +M73 Q60 S8 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.258 Y104.418 E.45878 +G2 X134.002 Y104.736 I.713 J-.639 E.02827 +G1 X134.002 Y105.333 E.02021 +G2 X133.291 Y105.615 I-.031 J.957 E.02663 +G1 X124.663 Y96.987 E.41302 +G1 X124.883 Y96.932 E.00768 +G3 X125.379 Y96.945 I.242 J.255 E.01865 +G1 X116.998 Y105.326 E.40119 +G1 X116.998 Y104.673 E.0221 +G1 X126.326 Y114.002 E.44655 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:12.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;12.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z12.6 F12000 +G1 X134.368 Y95.632 Z12.888 +;AFTER_LAYER_CHANGE +;12.8 +G1 X134.368 Y95.632 +G1 Z12.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2210 +G1 X134.368 Y100.95 E.18001 +G1 X133.979 Y100.95 E.01317 +G1 X133.654 Y101.043 E.01144 +G1 X133.524 Y101.153 E.00576 +G1 X133.42 Y101.314 E.00649 +G1 X133.368 Y101.56 E.00851 +G1 X133.368 Y103.64 E.07041 +G1 X133.416 Y103.878 E.00822 +G1 X133.585 Y104.106 E.00961 +G1 X133.782 Y104.218 E.00767 +G1 X134.368 Y104.25 E.01986 +G1 X134.368 Y105.819 E.05311 +G1 X133.782 Y105.851 E.01986 +G1 X133.585 Y105.963 E.00767 +G1 X133.441 Y106.14 E.00772 +G1 X133.368 Y106.43 E.01012 +G1 X133.368 Y108.35 E.06499 +G1 X133.372 Y108.421 E.00241 +G1 X133.458 Y108.668 E.00885 +G1 X133.651 Y108.865 E.00934 +G1 X133.979 Y108.96 E.01156 +G1 X134.368 Y108.96 E.01317 +G1 X134.368 Y114.368 E.18305 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y109.9 E.15124 +G1 X115.632 Y109.432 E.01584 +G1 X116.021 Y109.432 E.01317 +G1 X116.333 Y109.347 E.01095 +G1 X116.465 Y109.241 E.00573 +G1 X116.599 Y109.019 E.00878 +G1 X116.632 Y108.821 E.00679 +G1 X116.632 Y101.179 E.25867 +G1 X116.554 Y100.879 E.01049 +G1 X116.333 Y100.653 E.0107 +G1 X116.021 Y100.568 E.01095 +G1 X115.632 Y100.568 E.01317 +G1 X115.632 Y100.1 E.01584 +G1 X115.632 Y95.9 E.14216 +G1 X115.632 Y95.632 E.00907 +G1 X121.71 Y95.632 E.20573 +G1 X121.71 Y96.021 E.01317 +G1 X121.809 Y96.355 E.01179 +G1 X121.922 Y96.484 E.0058 +G1 X122.069 Y96.578 E.00591 +G1 X122.32 Y96.632 E.00869 +G1 X124.39 Y96.632 E.07007 +G1 X124.534 Y96.615 E.00491 +G1 X124.815 Y96.46 E.01086 +G1 X124.884 Y96.265 E.007 +;WIDTH:0.405158 +G1 X124.953 Y96.071 E.0062 +;WIDTH:0.360317 +G1 X124.954 Y95.586 E.0128 +;WIDTH:0.357561 +G1 X125.269 Y95.586 E.00824 +G1 X125.269 Y96.021 E.01138 +;WIDTH:0.40378 +G1 X125.338 Y96.241 E.00692 +;WIDTH:0.449999 +G1 X125.408 Y96.46 E.00778 +G1 X125.567 Y96.571 E.00656 +G1 X125.833 Y96.632 E.00924 +G1 X127.794 Y96.632 E.06638 +G1 X127.861 Y96.628 E.00227 +G1 X128.1 Y96.55 E.00851 +G1 X128.303 Y96.358 E.00946 +G1 X128.404 Y96.021 E.01191 +G1 X128.404 Y95.632 E.01317 +G1 X134.308 Y95.632 E.19984 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2210 +G1 X134.775 Y101.357 E.20756 +G1 X133.979 Y101.357 E.02694 +G1 X133.827 Y101.425 E.00564 +G1 X133.775 Y101.56 E.0049 +G1 X133.775 Y103.64 E.07041 +G1 F2123.116 +G1 X133.847 Y103.795 E.00578 +G1 F1829.671 +G1 X133.979 Y103.843 E.00475 +G1 F1814.51 +G1 X134.775 Y103.843 E.02694 +G1 X134.775 Y106.226 E.08066 +G1 X133.979 Y106.226 E.02694 +G1 F1828.234 +G1 X133.847 Y106.274 E.00475 +G1 F2122.822 +G1 X133.775 Y106.43 E.00582 +G1 F2210 +G1 X133.775 Y108.35 E.06499 +G1 X133.826 Y108.485 E.00488 +G1 X133.979 Y108.553 E.00567 +G1 X134.775 Y108.553 E.02694 +G1 X134.775 Y114.775 E.21061 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y109.9 E.16501 +G1 F1359.994 +G1 X115.225 Y109.025 E.02962 +G1 X116.021 Y109.025 E.02694 +G1 F1410.859 +G1 X116.169 Y108.961 E.00546 +G1 F2161.938 +G1 X116.225 Y108.821 E.0051 +G1 F2210 +G1 X116.225 Y101.854 E.23582 +G1 X116.225 Y101.179 E.02285 +G1 F2161.938 +G1 X116.169 Y101.039 E.0051 +G1 F1410.864 +G1 X116.021 Y100.975 E.00546 +G1 F1360 +G1 X115.225 Y100.975 E.02694 +G1 X115.225 Y100.1 E.02962 +G1 F2210 +G1 X115.225 Y95.9 E.14216 +G1 X115.225 Y95.225 E.02285 +G1 X122.117 Y95.225 E.23329 +G1 X122.117 Y96.021 E.02694 +G1 X122.187 Y96.176 E.00576 +G1 X122.32 Y96.225 E.0048 +G1 X124.39 Y96.225 E.07007 +G1 F2164.478 +G1 X124.531 Y96.168 E.00515 +G1 F1786.168 +G1 X124.593 Y96.021 E.0054 +G1 F1769.844 +G1 X124.593 Y95.225 E.02694 +G1 F1716.34 +G1 X125.63 Y95.225 E.0351 +G1 X125.63 Y96.021 E.02694 +G1 F1734.294 +G1 X125.691 Y96.168 E.00539 +G1 F2159.634 +G1 X125.833 Y96.225 E.00518 +G1 F2210 +G1 X127.794 Y96.225 E.06638 +G1 X127.926 Y96.176 E.00477 +G1 X127.997 Y96.021 E.00577 +G1 X127.997 Y95.225 E.02694 +G1 X134.715 Y95.225 E.2274 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.936 Y100.589 Z12.843 F12000 +G1 Z12.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2210 +G1 X134.002 Y100.583 E.00224 +G1 X134.002 Y98.65 E.06543 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.319 Y104.357 E.4617 +G2 X134.002 Y104.617 I.693 J-.793 E.02526 +G1 X134.002 Y105.453 E.0283 +G2 X133.353 Y105.677 I.026 J1.124 E.02362 +G1 X124.64 Y96.965 E.41706 +G2 X125.111 Y96.656 I-.193 J-.81 E.01945 +G1 X125.418 Y96.906 E.0134 +G1 X116.998 Y105.326 E.40306 +G1 X116.997 Y104.673 E.0221 +G1 X126.326 Y114.002 E.44657 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +M73 P61 R8 +M73 Q61 S8 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:13 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;13 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z12.8 F12000 +G1 X134.368 Y95.632 Z13.088 +;AFTER_LAYER_CHANGE +;13 +G1 X134.368 Y95.632 +G1 Z13 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2216 +G1 X134.368 Y100.816 E.17547 +G1 X133.979 Y100.816 E.01317 +G1 X133.659 Y100.907 E.01126 +G1 X133.528 Y101.015 E.00575 +G1 X133.421 Y101.176 E.00654 +G1 X133.368 Y101.427 E.00868 +G1 X133.368 Y103.509 E.07047 +G1 X133.372 Y103.58 E.00241 +G1 X133.469 Y103.845 E.00955 +G1 X133.735 Y104.069 E.01177 +G1 X133.979 Y104.12 E.00844 +G1 X134.368 Y104.12 E.01317 +G1 X134.368 Y105.951 E.06198 +G1 X133.979 Y105.951 E.01317 +G1 X133.735 Y106.002 E.00844 +G1 X133.552 Y106.124 E.00744 +G1 X133.429 Y106.294 E.0071 +G1 X133.368 Y106.561 E.00927 +G1 X133.368 Y108.488 E.06523 +G1 X133.372 Y108.56 E.00244 +G1 X133.46 Y108.811 E.009 +G1 X133.656 Y109.006 E.00936 +G1 X133.979 Y109.099 E.01138 +G1 X134.368 Y109.099 E.01317 +G1 X134.368 Y114.368 E.17835 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y109.7 E.15801 +G1 X115.632 Y109.232 E.01584 +G1 X116.021 Y109.232 E.01317 +G1 X116.34 Y109.143 E.01121 +G1 X116.551 Y108.925 E.01027 +G1 X116.632 Y108.621 E.01065 +G1 X116.632 Y102.178 E.21809 +G1 X116.628 Y101.31 E.02938 +G1 X116.535 Y101.048 E.00941 +G1 X116.34 Y100.857 E.00924 +G1 X116.021 Y100.768 E.01121 +G1 X115.632 Y100.768 E.01317 +G1 X115.632 Y100.3 E.01584 +G1 X115.632 Y95.9 E.14893 +G1 X115.632 Y95.632 E.00907 +G1 X121.567 Y95.632 E.20089 +G1 X121.567 Y96.021 E.01317 +G1 X121.666 Y96.355 E.01179 +G1 X121.779 Y96.484 E.0058 +G1 X121.948 Y96.587 E.0067 +G1 X122.178 Y96.632 E.00793 +G1 X124.251 Y96.632 E.07017 +G1 X124.471 Y96.591 E.00757 +G1 X124.714 Y96.42 E.01006 +;WIDTH:0.477649 +G1 X124.8 Y96.248 E.00695 +;WIDTH:0.505299 +G1 X124.887 Y96.077 E.00738 +G1 X124.887 Y95.658 E.01611 +;WIDTH:0.501991 +G1 X125.346 Y95.658 E.01752 +G1 X125.346 Y96.021 E.01386 +G1 X125.433 Y96.221 E.00833 +;WIDTH:0.475995 +G1 X125.52 Y96.42 E.00782 +;WIDTH:0.449999 +G1 X125.762 Y96.591 E.01003 +G1 X125.983 Y96.632 E.00761 +G1 X127.943 Y96.632 E.06634 +G1 X128.01 Y96.628 E.00227 +G1 X128.253 Y96.548 E.00866 +G1 X128.455 Y96.353 E.0095 +G1 X128.553 Y96.021 E.01172 +G1 X128.553 Y95.632 E.01317 +G1 X134.308 Y95.632 E.1948 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2216 +G1 X134.775 Y101.223 E.20302 +G1 X133.979 Y101.223 E.02694 +G1 X133.828 Y101.289 E.00558 +G1 X133.775 Y101.427 E.005 +G1 X133.775 Y103.509 E.07047 +G1 F2155.154 +G1 X133.836 Y103.655 E.00536 +G1 F1818.514 +G1 X133.979 Y103.713 E.00522 +G1 F1803.073 +G1 X134.775 Y103.713 E.02694 +G1 F1796.564 +G1 X134.775 Y106.358 E.08953 +G1 X133.979 Y106.358 E.02694 +G1 F1812.17 +G1 X133.836 Y106.416 E.00522 +G1 F2154.225 +G1 X133.775 Y106.561 E.00532 +G1 F2216 +G1 X133.775 Y108.488 E.06523 +G1 X133.827 Y108.625 E.00496 +G1 X133.979 Y108.692 E.00562 +G1 X134.775 Y108.692 E.02694 +G1 X134.775 Y114.775 E.2059 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y109.7 E.17178 +G1 F1359.994 +G1 X115.225 Y108.825 E.02962 +G1 X116.021 Y108.825 E.02694 +G1 F1410.859 +G1 X116.171 Y108.759 E.00555 +G1 F2170.945 +G1 X116.225 Y108.621 E.00502 +G1 F2216 +G1 X116.225 Y107.946 E.02285 +G1 X116.225 Y102.054 E.19944 +G1 X116.225 Y101.379 E.02285 +G1 F2170.945 +G1 X116.171 Y101.241 E.00502 +G1 F1410.859 +G1 X116.021 Y101.175 E.00555 +G1 F1359.994 +G1 X115.225 Y101.175 E.02694 +G1 X115.225 Y100.3 E.02962 +G1 F2216 +G1 X115.225 Y95.9 E.14893 +G1 X115.225 Y95.225 E.02285 +G1 X121.974 Y95.225 E.22845 +G1 X121.974 Y96.021 E.02694 +G1 X122.045 Y96.176 E.00577 +G1 X122.178 Y96.225 E.0048 +G1 X124.251 Y96.225 E.07017 +G1 F2116.303 +G1 X124.405 Y96.154 E.00574 +G1 F1767.613 +G1 X124.454 Y96.021 E.0048 +G1 F1750.74 +G1 X124.454 Y95.225 E.02694 +G1 F1680.545 +G1 X125.78 Y95.225 E.04488 +G1 X125.78 Y96.021 E.02694 +G1 F1699.787 +G1 X125.829 Y96.154 E.0048 +G1 F2106.98 +G1 X125.983 Y96.225 E.00574 +G1 F2216 +G1 X127.943 Y96.225 E.06634 +G1 X128.076 Y96.175 E.00481 +G1 X128.146 Y96.021 E.00573 +G1 X128.146 Y95.225 E.02694 +G1 X134.715 Y95.225 E.22235 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.807 Y100.492 Z13.043 F12000 +G1 Z13 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2216 +G1 X134.002 Y100.45 E.00675 +G1 X134.002 Y98.65 E.06093 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.397 Y104.279 E.46543 +G2 X134.002 Y104.486 I.608 J-.789 E.02203 +G1 X134.002 Y105.584 E.03717 +G2 X133.439 Y105.763 I-.002 J.97 E.02032 +G1 X124.6 Y96.924 E.42312 +G2 X125.117 Y96.437 I-.274 J-.808 E.0248 +G2 X125.472 Y96.852 I.828 J-.349 E.01878 +G1 X116.998 Y105.326 E.40565 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:13.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;13.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z13 F12000 +G1 X134.368 Y95.632 Z13.288 +;AFTER_LAYER_CHANGE +;13.2 +G1 X134.368 Y95.632 +G1 Z13.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2223 +G1 X134.368 Y100.682 E.17094 +G1 X133.979 Y100.682 E.01317 +G1 X133.663 Y100.77 E.0111 +G1 X133.532 Y100.877 E.00573 +G1 X133.423 Y101.039 E.00661 +G1 X133.368 Y101.293 E.0088 +G1 X133.368 Y103.378 E.07057 +G1 X133.373 Y103.454 E.00258 +G1 X133.471 Y103.718 E.00953 +G1 X133.694 Y103.918 E.01014 +G1 X133.979 Y103.989 E.00994 +G1 X134.368 Y103.989 E.01317 +G1 X134.368 Y106.083 E.07088 +G1 X133.979 Y106.083 E.01317 +G1 X133.694 Y106.153 E.00993 +G1 X133.544 Y106.264 E.00632 +G1 X133.433 Y106.418 E.00643 +G1 X133.368 Y106.693 E.00956 +G1 X133.368 Y108.627 E.06546 +G1 X133.449 Y108.931 E.01065 +G1 X133.66 Y109.148 E.01025 +G1 X133.979 Y109.237 E.01121 +G1 X134.368 Y109.237 E.01317 +G1 X134.368 Y114.368 E.17368 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y109.5 E.16478 +M73 P62 R8 +M73 Q62 S8 +G1 X115.632 Y109.032 E.01584 +G1 X116.021 Y109.032 E.01317 +G1 X116.347 Y108.938 E.01148 +G1 X116.477 Y108.828 E.00576 +G1 X116.586 Y108.655 E.00692 +G1 X116.632 Y108.421 E.00807 +G1 X116.632 Y102.035 E.21616 +G1 X116.628 Y101.505 E.01794 +G1 X116.514 Y101.217 E.01048 +G1 X116.347 Y101.062 E.00771 +G1 X116.021 Y100.968 E.01148 +G1 X115.632 Y100.968 E.01317 +G1 X115.632 Y100.5 E.01584 +G1 X115.632 Y95.632 E.16478 +G1 X121.425 Y95.632 E.19609 +G1 X121.425 Y96.021 E.01317 +G1 X121.524 Y96.355 E.01179 +G1 X121.636 Y96.484 E.00578 +G1 X121.828 Y96.596 E.00752 +G1 X122.035 Y96.632 E.00711 +G1 X124.11 Y96.632 E.07024 +G1 X124.361 Y96.578 E.00869 +G1 X124.599 Y96.387 E.01033 +G1 X124.718 Y96.075 E.0113 +G1 X124.721 Y95.632 E.015 +G1 X125.523 Y95.632 E.02715 +G1 X125.523 Y96.021 E.01317 +G1 X125.644 Y96.387 E.01305 +G1 X125.882 Y96.578 E.01033 +G1 X126.133 Y96.632 E.00869 +G1 X128.092 Y96.632 E.06631 +G1 X128.159 Y96.628 E.00227 +G1 X128.405 Y96.546 E.00878 +G1 X128.607 Y96.349 E.00955 +G1 X128.702 Y96.021 E.01156 +G1 X128.702 Y95.632 E.01317 +G1 X134.308 Y95.632 E.18976 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2223 +G1 X134.775 Y101.089 E.19849 +G1 X133.979 Y101.089 E.02694 +G1 X133.83 Y101.154 E.0055 +G1 X133.775 Y101.293 E.00506 +G1 X133.775 Y103.378 E.07057 +G1 F2163.635 +G1 X133.834 Y103.521 E.00524 +G1 F1818.508 +G1 X133.979 Y103.582 E.00532 +G1 F1803.066 +G1 X134.775 Y103.582 E.02694 +G1 F1796.551 +G1 X134.775 Y106.49 E.09843 +G1 X133.979 Y106.49 E.02694 +G1 F1812.158 +G1 X133.834 Y106.55 E.00531 +G1 F2162.782 +G1 X133.775 Y106.693 E.00524 +G1 F2223 +G1 X133.775 Y108.627 E.06546 +G1 X133.829 Y108.764 E.00498 +G1 X133.979 Y108.83 E.00555 +G1 X134.775 Y108.83 E.02694 +G1 X134.775 Y114.775 E.20123 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y109.5 E.17855 +G1 F1359.987 +G1 X115.225 Y108.625 E.02962 +G1 X116.021 Y108.625 E.02694 +G1 F1410.854 +G1 X116.173 Y108.557 E.00564 +G1 F2179.346 +G1 X116.225 Y108.421 E.00493 +G1 F2223 +G1 X116.225 Y101.579 E.23159 +G1 X116.186 Y101.458 E.0043 +G1 F1410.854 +G1 X116.021 Y101.375 E.00625 +G1 F1359.987 +G1 X115.225 Y101.375 E.02694 +G1 X115.225 Y100.5 E.02962 +G1 F2223 +G1 X115.225 Y95.225 E.17855 +G1 X121.832 Y95.225 E.22364 +G1 X121.832 Y96.021 E.02694 +G1 X121.902 Y96.176 E.00576 +G1 X122.035 Y96.225 E.0048 +G1 X124.11 Y96.225 E.07024 +G1 X124.224 Y96.19 E.00404 +G1 F1760.282 +G1 X124.314 Y96.024 E.00639 +G1 F1739.214 +G1 X124.314 Y95.225 E.02705 +G1 F1677.754 +G1 X125.93 Y95.225 E.0547 +G1 X125.93 Y96.021 E.02694 +G1 F1697.104 +G1 X126.02 Y96.19 E.00648 +G1 F2223 +G1 X126.133 Y96.225 E.004 +G1 X128.092 Y96.225 E.06631 +G1 X128.227 Y96.174 E.00488 +G1 X128.295 Y96.021 E.00567 +G1 X128.295 Y95.225 E.02694 +G1 X134.715 Y95.225 E.21731 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.678 Y100.393 Z13.242 F12000 +G1 Z13.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2223 +G1 X134.002 Y100.316 E.01127 +G1 X134.002 Y98.65 E.05639 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.47 Y104.206 E.46893 +G1 X134.002 Y104.355 E.0187 +G1 X134.002 Y105.716 E.04607 +G1 X133.511 Y105.835 E.0171 +G1 X124.558 Y96.882 E.42858 +G2 X125.084 Y96.123 I-.499 J-.907 E.0324 +G3 X125.158 Y95.998 I.058 J-.049 E.00643 +G2 X125.537 Y96.787 I1.055 J-.021 E.03055 +G1 X116.998 Y105.326 E.40876 +G1 X116.997 Y104.673 E.0221 +G1 X126.326 Y114.002 E.44657 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:13.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;13.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z13.2 F12000 +G1 X134.368 Y95.632 Z13.488 +;AFTER_LAYER_CHANGE +;13.4 +G1 X134.368 Y95.632 +G1 Z13.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2232 +G1 X134.368 Y100.548 E.1664 +G1 X133.979 Y100.548 E.01317 +G1 X133.668 Y100.633 E.01091 +G1 X133.536 Y100.738 E.00571 +G1 X133.425 Y100.901 E.00668 +G1 X133.368 Y101.159 E.00894 +G1 X133.368 Y103.247 E.07068 +G1 X133.373 Y103.328 E.00275 +G1 X133.472 Y103.588 E.00942 +G1 X133.659 Y103.768 E.00879 +G1 X133.979 Y103.858 E.01125 +G1 X134.368 Y103.858 E.01317 +G1 X134.368 Y106.214 E.07975 +G1 X133.979 Y106.214 E.01317 +G1 X133.659 Y106.305 E.01126 +G1 X133.536 Y106.404 E.00534 +G1 X133.437 Y106.543 E.00578 +G1 X133.368 Y106.825 E.00983 +G1 X133.368 Y108.765 E.06567 +G1 X133.451 Y109.073 E.0108 +G1 X133.665 Y109.289 E.01029 +G1 X133.979 Y109.376 E.01103 +G1 X134.368 Y109.376 E.01317 +G1 X134.368 Y114.368 E.16897 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y109.3 E.17155 +G1 X115.632 Y108.832 E.01584 +G1 X116.021 Y108.832 E.01317 +G1 X116.353 Y108.734 E.01172 +G1 X116.482 Y108.622 E.00578 +G1 X116.587 Y108.451 E.00679 +G1 X116.632 Y108.221 E.00793 +G1 X116.632 Y101.893 E.21419 +G1 X116.63 Y101.728 E.00559 +G1 X116.543 Y101.461 E.00951 +G1 X116.353 Y101.266 E.00922 +G1 X116.021 Y101.168 E.01172 +G1 X115.632 Y101.168 E.01317 +G1 X115.632 Y100.7 E.01584 +G1 X115.632 Y95.9 E.16247 +G1 X115.632 Y95.632 E.00907 +G1 X121.282 Y95.632 E.19125 +G1 X121.282 Y96.021 E.01317 +G1 X121.381 Y96.355 E.01179 +G1 X121.494 Y96.484 E.0058 +G1 X121.639 Y96.577 E.00583 +G1 X121.893 Y96.632 E.0088 +G1 X123.969 Y96.632 E.07027 +G1 X124.237 Y96.57 E.00931 +G1 X124.451 Y96.397 E.00931 +G1 X124.573 Y96.11 E.01056 +G1 X124.579 Y95.632 E.01618 +G1 X125.674 Y95.632 E.03706 +G1 X125.674 Y96.021 E.01317 +G1 X125.803 Y96.397 E.01346 +G1 X126.016 Y96.57 E.00929 +G1 X126.285 Y96.632 E.00934 +G1 X128.241 Y96.632 E.06621 +G1 X128.308 Y96.628 E.00227 +G1 X128.558 Y96.543 E.00894 +G1 X128.76 Y96.344 E.0096 +G1 X128.851 Y96.021 E.01136 +G1 X128.851 Y95.632 E.01317 +G1 X134.308 Y95.632 E.18471 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2232 +G1 X134.775 Y100.956 E.19399 +G1 X133.979 Y100.956 E.02694 +G1 X133.831 Y101.019 E.00544 +G1 X133.775 Y101.159 E.0051 +G1 X133.775 Y103.247 E.07068 +G1 F2172.597 +G1 X133.831 Y103.388 E.00514 +G1 F1818.514 +G1 X133.979 Y103.451 E.00544 +G1 F1803.073 +G1 X134.775 Y103.451 E.02694 +G1 F1796.564 +G1 X134.775 Y106.621 E.1073 +G1 X133.979 Y106.621 E.02694 +G1 F1812.17 +G1 X133.831 Y106.685 E.00546 +G1 F2171.825 +G1 X133.775 Y106.825 E.0051 +G1 F2232 +G1 X133.775 Y108.765 E.06567 +G1 X133.83 Y108.905 E.00509 +M73 Q63 S8 +G1 X133.979 Y108.969 E.00549 +G1 X134.775 Y108.969 E.02694 +G1 X134.775 Y114.775 E.19653 +G1 X115.225 Y114.775 E.66174 +M73 P63 R8 +G1 X115.225 Y109.3 E.18532 +M73 P63 R7 +M73 Q63 S7 +G1 F1359.994 +G1 X115.225 Y108.425 E.02962 +G1 X116.021 Y108.425 E.02694 +G1 F1410.859 +G1 X116.175 Y108.355 E.00573 +G1 F2187.197 +G1 X116.225 Y108.221 E.00484 +G1 F2232 +G1 X116.225 Y107.546 E.02285 +G1 X116.225 Y102.454 E.17236 +G1 X116.225 Y101.779 E.02285 +G1 F2202.275 +G1 X116.179 Y101.649 E.00467 +G1 F1410.859 +G1 X116.021 Y101.575 E.00591 +G1 F1359.994 +G1 X115.225 Y101.575 E.02694 +G1 X115.225 Y100.7 E.02962 +G1 F2232 +G1 X115.225 Y95.9 E.16247 +G1 X115.225 Y95.225 E.02285 +G1 X121.689 Y95.225 E.2188 +G1 X121.689 Y96.021 E.02694 +G1 X121.76 Y96.176 E.00577 +G1 X121.893 Y96.225 E.0048 +G1 X123.969 Y96.225 E.07027 +G1 X124.087 Y96.187 E.0042 +G1 F1753.342 +G1 X124.172 Y96.021 E.00631 +G1 F1736.026 +G1 X124.172 Y95.225 E.02694 +G1 F1669.3 +G1 X126.082 Y95.225 E.06465 +G1 X126.082 Y96.021 E.02694 +G1 F1688.984 +G1 X126.167 Y96.187 E.00631 +G1 F2232 +G1 X126.285 Y96.225 E.0042 +G1 X128.241 Y96.225 E.06621 +G1 X128.377 Y96.172 E.00494 +G1 X128.444 Y96.021 E.00559 +G1 X128.444 Y95.225 E.02694 +G1 X134.715 Y95.225 E.21227 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.244 E-.62753 +;WIPE_END +G1 X133.549 Y100.294 Z13.442 F12000 +G1 Z13.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2232 +G1 X134.002 Y100.182 E.0158 +G1 X134.002 Y98.65 E.05186 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.563 Y104.113 E.47338 +G1 X134.002 Y104.224 E.01533 +G1 X134.002 Y105.848 E.05497 +G1 X133.619 Y105.943 E.01336 +G1 X124.509 Y96.834 E.43607 +G2 X124.941 Y95.998 I-.597 J-.838 E.03308 +G1 X125.313 Y95.998 E.01259 +G2 X125.609 Y96.715 I1.012 J.001 E.02694 +G1 X116.996 Y105.328 E.4123 +G1 X116.996 Y104.672 E.0222 +G1 X126.326 Y114.002 E.44662 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:13.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;13.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z13.4 F12000 +G1 X134.368 Y95.632 Z13.688 +;AFTER_LAYER_CHANGE +;13.6 +G1 X134.368 Y95.632 +G1 Z13.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2237 +G1 X134.368 Y100.415 E.1619 +G1 X133.979 Y100.415 E.01317 +G1 X133.673 Y100.497 E.01072 +G1 X133.54 Y100.6 E.00569 +G1 X133.427 Y100.764 E.00674 +G1 X133.368 Y101.025 E.00906 +G1 X133.368 Y103.117 E.07081 +G1 X133.44 Y103.404 E.01002 +G1 X133.629 Y103.617 E.00964 +G1 X133.979 Y103.727 E.01242 +G1 X134.368 Y103.727 E.01317 +G1 X134.368 Y106.346 E.08865 +G1 X133.979 Y106.346 E.01317 +G1 X133.629 Y106.456 E.01242 +G1 X133.44 Y106.67 E.00966 +G1 X133.368 Y106.957 E.01002 +G1 X133.368 Y108.904 E.0659 +G1 X133.375 Y108.997 E.00316 +G1 X133.453 Y109.215 E.00784 +G1 X133.67 Y109.431 E.01036 +G1 X133.979 Y109.514 E.01083 +G1 X134.368 Y109.514 E.01317 +G1 X134.368 Y114.368 E.1643 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y109.1 E.17832 +G1 X115.632 Y108.632 E.01584 +G1 X116.021 Y108.632 E.01317 +G1 X116.359 Y108.53 E.01195 +G1 X116.488 Y108.416 E.00583 +G1 X116.589 Y108.247 E.00666 +G1 X116.632 Y108.021 E.00779 +G1 X116.632 Y101.979 E.20451 +G1 X116.557 Y101.685 E.01027 +G1 X116.356 Y101.468 E.01001 +G1 X116.021 Y101.368 E.01183 +G1 X115.632 Y101.368 E.01317 +G1 X115.632 Y100.9 E.01584 +G1 X115.632 Y95.9 E.16924 +G1 X115.632 Y95.632 E.00907 +G1 X121.14 Y95.632 E.18644 +G1 X121.14 Y96.021 E.01317 +G1 X121.237 Y96.352 E.01168 +G1 X121.478 Y96.568 E.01095 +G1 X121.75 Y96.632 E.00946 +G1 X123.827 Y96.632 E.0703 +G1 X124.057 Y96.587 E.00793 +G1 X124.3 Y96.408 E.01022 +G1 X124.417 Y96.18 E.00867 +G1 X124.438 Y95.632 E.01856 +G1 X125.826 Y95.632 E.04698 +G1 X125.847 Y96.18 E.01856 +G1 X125.964 Y96.408 E.00867 +G1 X126.154 Y96.563 E.0083 +G1 X126.437 Y96.632 E.00986 +G1 X128.39 Y96.632 E.06611 +G1 X128.457 Y96.628 E.00227 +G1 X128.711 Y96.541 E.00909 +G1 X128.912 Y96.339 E.00965 +G1 X129.001 Y96.021 E.01118 +G1 X129.001 Y95.632 E.01317 +G1 X134.308 Y95.632 E.17964 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2237 +G1 X134.775 Y100.822 E.18945 +G1 X133.979 Y100.822 E.02694 +G1 X133.833 Y100.884 E.00537 +G1 X133.775 Y101.025 E.00516 +G1 X133.775 Y103.117 E.07081 +G1 X133.81 Y103.23 E.004 +G1 F1818.514 +G1 X133.979 Y103.32 E.00648 +G1 F1803.073 +G1 X134.775 Y103.32 E.02694 +G1 F1796.558 +G1 X134.775 Y106.753 E.1162 +G1 X133.979 Y106.753 E.02694 +G1 F1812.164 +G1 X133.828 Y106.819 E.00558 +G1 F2181.106 +G1 X133.775 Y106.957 E.005 +G1 F2237 +G1 X133.775 Y108.904 E.0659 +G1 X133.832 Y109.045 E.00515 +G1 X133.979 Y109.107 E.0054 +G1 X134.775 Y109.107 E.02694 +G1 X134.775 Y114.775 E.19185 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y109.1 E.19209 +G1 F1360 +G1 X115.225 Y108.225 E.02962 +G1 X116.021 Y108.225 E.02694 +G1 F1410.864 +G1 X116.177 Y108.153 E.00582 +G1 F2194.544 +G1 X116.225 Y108.021 E.00475 +G1 F2237 +G1 X116.225 Y107.346 E.02285 +G1 X116.225 Y102.654 E.15882 +G1 X116.225 Y101.979 E.02285 +G1 F2190.815 +G1 X116.176 Y101.846 E.0048 +G1 F1410.859 +G1 X116.021 Y101.775 E.00577 +G1 F1359.994 +G1 X115.225 Y101.775 E.02694 +G1 X115.225 Y100.9 E.02962 +G1 F2237 +G1 X115.225 Y95.9 E.16924 +G1 X115.225 Y95.225 E.02285 +G1 X121.547 Y95.225 E.21399 +G1 X121.547 Y96.021 E.02694 +G1 X121.617 Y96.175 E.00573 +G1 X121.75 Y96.225 E.00481 +G1 X123.827 Y96.225 E.0703 +G1 X123.856 Y96.223 E.00098 +G1 F2101.052 +G1 X123.985 Y96.15 E.00502 +G1 F1751.83 +G1 X124.031 Y96.021 E.00464 +G1 F1734.465 +G1 X124.031 Y95.225 E.02694 +G1 F1669.306 +G1 X126.233 Y95.225 E.07453 +G1 X126.24 Y96.074 E.02874 +G1 F1837.749 +G1 X126.317 Y96.186 E.0046 +G1 F2232.411 +G1 X126.437 Y96.225 E.00427 +G1 F2237 +G1 X128.39 Y96.225 E.06611 +G1 X128.528 Y96.171 E.00502 +G1 X128.593 Y96.021 E.00553 +G1 X128.593 Y95.225 E.02694 +G1 X134.715 Y95.225 E.20722 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.434 Y100.221 Z13.642 F12000 +M73 Q64 S7 +G1 Z13.6 F720 +G1 E.8 F1500 +M73 P64 R7 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2237 +G1 X134.002 Y100.048 E.0201 +G1 X134.002 Y98.65 E.04732 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.671 Y104.005 E.47855 +G1 X134.002 Y104.094 E.0116 +G1 X134.002 Y105.98 E.06384 +G1 X133.727 Y106.051 E.00961 +G1 X124.447 Y96.771 E.44423 +G2 X124.804 Y95.998 I-.664 J-.776 E.02973 +G1 X125.46 Y95.998 E.0222 +G2 X125.681 Y96.643 I1.133 J-.029 E.02344 +G1 X116.997 Y105.327 E.4157 +G1 X116.995 Y104.671 E.0222 +G1 X126.326 Y114.002 E.44667 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:13.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;13.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z13.6 F12000 +G1 X134.368 Y95.632 Z13.888 +;AFTER_LAYER_CHANGE +;13.8 +G1 X134.368 Y95.632 +G1 Z13.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2272 +G1 X134.368 Y100.281 E.15736 +G1 X133.979 Y100.281 E.01317 +G1 X133.678 Y100.36 E.01053 +G1 X133.545 Y100.461 E.00565 +G1 X133.429 Y100.626 E.00683 +G1 X133.368 Y100.892 E.00924 +G1 X133.368 Y102.986 E.07088 +G1 X133.423 Y103.239 E.00876 +G1 X133.602 Y103.467 E.00981 +G1 X133.979 Y103.597 E.0135 +G1 X134.368 Y103.597 E.01317 +G1 X134.368 Y106.478 E.09752 +G1 X133.979 Y106.478 E.01317 +G1 X133.602 Y106.607 E.01349 +G1 X133.442 Y106.797 E.00841 +G1 X133.368 Y107.089 E.0102 +G1 X133.368 Y109.042 E.06611 +G1 X133.375 Y109.137 E.00322 +G1 X133.456 Y109.357 E.00794 +G1 X133.676 Y109.572 E.01041 +G1 X133.979 Y109.653 E.01062 +G1 X134.368 Y109.653 E.01317 +G1 X134.368 Y114.368 E.1596 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y108.9 E.18509 +G1 X115.632 Y108.432 E.01584 +G1 X116.021 Y108.432 E.01317 +G1 X116.365 Y108.326 E.01218 +G1 X116.492 Y108.21 E.00582 +G1 X116.59 Y108.044 E.00653 +G1 X116.632 Y107.821 E.00768 +G1 X116.632 Y102.179 E.19097 +G1 X116.629 Y102.119 E.00203 +G1 X116.551 Y101.875 E.00867 +G1 X116.358 Y101.669 E.00956 +G1 X116.021 Y101.568 E.01191 +G1 X115.632 Y101.568 E.01317 +G1 X115.632 Y101.1 E.01584 +G1 X115.632 Y95.9 E.17601 +G1 X115.632 Y95.632 E.00907 +G1 X120.997 Y95.632 E.1816 +G1 X120.997 Y96.021 E.01317 +G1 X121.092 Y96.348 E.01153 +G1 X121.202 Y96.478 E.00576 +G1 X121.357 Y96.578 E.00624 +G1 X121.608 Y96.632 E.00869 +G1 X123.686 Y96.632 E.07034 +G1 X123.864 Y96.605 E.00609 +G1 X124.126 Y96.444 E.01041 +G1 X124.256 Y96.239 E.00822 +G1 X124.296 Y96.021 E.0075 +G1 X124.296 Y95.632 E.01317 +G1 X125.978 Y95.632 E.05693 +G1 X125.978 Y96.021 E.01317 +G1 X126.018 Y96.239 E.0075 +G1 X126.148 Y96.444 E.00822 +G1 X126.327 Y96.574 E.00749 +G1 X126.588 Y96.632 E.00905 +G1 X128.539 Y96.632 E.06604 +G1 X128.606 Y96.628 E.00227 +G1 X128.864 Y96.538 E.00925 +G1 X129.064 Y96.333 E.00969 +G1 X129.15 Y96.021 E.01095 +G1 X129.15 Y95.632 E.01317 +G1 X134.308 Y95.632 E.17459 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2272 +G1 X134.775 Y100.688 E.18492 +G1 X133.979 Y100.688 E.02694 +G1 X133.834 Y100.748 E.00531 +G1 X133.775 Y100.892 E.00527 +G1 X133.775 Y102.986 E.07088 +G1 F2252.772 +G1 X133.81 Y103.099 E.004 +G1 F1818.514 +G1 X133.979 Y103.189 E.00648 +G1 F1803.073 +G1 X134.775 Y103.189 E.02694 +G1 F1796.564 +G1 X134.775 Y106.885 E.12511 +G1 X133.979 Y106.885 E.02694 +G1 F1812.17 +G1 X133.826 Y106.954 E.00568 +G1 F2190.471 +G1 X133.775 Y107.089 E.00488 +G1 F2272 +G1 X133.775 Y109.042 E.06611 +G1 X133.833 Y109.185 E.00522 +G1 X133.979 Y109.246 E.00536 +G1 X134.775 Y109.246 E.02694 +G1 X134.775 Y114.775 E.18715 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y108.9 E.19886 +G1 F1359.994 +G1 X115.225 Y108.025 E.02962 +G1 X116.021 Y108.025 E.02694 +G1 F1410.859 +G1 X116.178 Y107.951 E.00587 +G1 F2201.439 +G1 X116.225 Y107.821 E.00468 +G1 F2272 +G1 X116.225 Y107.146 E.02285 +G1 X116.225 Y102.854 E.14528 +G1 X116.225 Y102.179 E.02285 +G1 F2192.812 +G1 X116.176 Y102.047 E.00477 +G1 F1410.864 +G1 X116.021 Y101.975 E.00578 +G1 F1360 +G1 X115.225 Y101.975 E.02694 +G1 X115.225 Y101.1 E.02962 +G1 F2272 +G1 X115.225 Y95.9 E.17601 +G1 X115.225 Y95.225 E.02285 +G1 X121.404 Y95.225 E.20915 +G1 X121.404 Y96.021 E.02694 +G1 X121.473 Y96.174 E.00568 +G1 X121.608 Y96.225 E.00488 +G1 X123.686 Y96.225 E.07034 +G1 F2142.146 +G1 X123.833 Y96.162 E.00541 +G1 F1751.823 +G1 X123.889 Y96.021 E.00514 +G1 F1734.458 +G1 X123.889 Y95.225 E.02694 +G1 F1669.3 +G1 X126.385 Y95.225 E.08449 +G1 X126.385 Y96.021 E.02694 +G1 F1688.984 +G1 X126.442 Y96.162 E.00515 +G1 F2136.377 +G1 X126.588 Y96.225 E.00538 +G1 F2272 +G1 X128.539 Y96.225 E.06604 +G1 X128.678 Y96.17 E.00506 +G1 X128.743 Y96.021 E.0055 +G1 X128.743 Y95.225 E.02694 +G1 X134.715 Y95.225 E.20214 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.328 Y100.169 Z13.842 F12000 +G1 Z13.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2272 +G3 X134.002 Y99.915 I.719 J.885 E.0248 +G1 X134.002 Y98.65 E.04282 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.774 Y103.902 E.48348 +G1 X134.002 Y103.963 E.00799 +G1 X134.002 Y106.112 E.07274 +G1 X133.829 Y106.153 E.00602 +G1 X124.382 Y96.706 E.45222 +G2 X124.663 Y95.998 I-.813 J-.731 E.02634 +G1 X125.611 Y95.998 E.03209 +G2 X125.781 Y96.543 I.909 J.015 E.01966 +G1 X116.998 Y105.326 E.42044 +G1 X116.998 Y104.674 E.02207 +M73 P65 R7 +M73 Q65 S7 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X133.38 Y109.805 Z13.949 F12000 +G1 Z13.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2272 +G2 X134.002 Y110.019 I.676 J-.951 E.02257 +G1 X134.002 Y111.35 E.04505 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.631 Y96.017 E.06706 +M204 P1000 +;LAYER_CHANGE +;Z:14 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;14 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.4117 +G1 X117.873 Y96.775 E-.2283 +;WIPE_END +G1 X117.873 Y96.775 Z13.8 F12000 +G1 X134.368 Y95.632 Z14.089 +;AFTER_LAYER_CHANGE +;14 +G1 X134.368 Y95.632 +G1 Z14 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2249 +G1 X134.368 Y100.147 E.15283 +G1 X133.979 Y100.147 E.01317 +G1 X133.684 Y100.223 E.01031 +G1 X133.55 Y100.323 E.00566 +G1 X133.431 Y100.487 E.00686 +G1 X133.368 Y100.758 E.00942 +G1 X133.368 Y102.855 E.07098 +G1 X133.413 Y103.085 E.00793 +G1 X133.58 Y103.318 E.0097 +G1 X133.979 Y103.466 E.0144 +G1 X134.368 Y103.466 E.01317 +G1 X134.368 Y106.61 E.10642 +G1 X133.979 Y106.61 E.01317 +G1 X133.58 Y106.758 E.0144 +G1 X133.443 Y106.926 E.00734 +G1 X133.368 Y107.22 E.01027 +G1 X133.368 Y109.181 E.06638 +G1 X133.376 Y109.277 E.00326 +G1 X133.458 Y109.5 E.00804 +G1 X133.681 Y109.714 E.01046 +G1 X133.979 Y109.791 E.01042 +G1 X134.368 Y109.791 E.01317 +G1 X134.368 Y114.368 E.15493 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y108.7 E.19185 +G1 X115.632 Y108.232 E.01584 +G1 X116.021 Y108.232 E.01317 +G1 X116.371 Y108.122 E.01242 +G1 X116.497 Y108.004 E.00584 +G1 X116.591 Y107.84 E.0064 +G1 X116.632 Y107.621 E.00754 +G1 X116.632 Y102.379 E.17744 +G1 X116.625 Y102.285 E.00319 +G1 X116.546 Y102.066 E.00788 +G1 X116.36 Y101.87 E.00915 +G1 X116.021 Y101.768 E.01198 +G1 X115.632 Y101.768 E.01317 +G1 X115.632 Y101.3 E.01584 +G1 X115.632 Y95.9 E.18278 +G1 X115.632 Y95.632 E.00907 +G1 X120.855 Y95.632 E.17679 +G1 X120.855 Y96.021 E.01317 +G1 X120.946 Y96.343 E.01133 +G1 X121.055 Y96.474 E.00577 +G1 X121.237 Y96.588 E.00727 +G1 X121.465 Y96.632 E.00786 +G1 X123.544 Y96.632 E.07037 +G1 X123.6 Y96.63 E.0019 +G1 X123.882 Y96.53 E.01013 +G1 X124.094 Y96.288 E.01089 +G1 X124.155 Y96.021 E.00927 +G1 X124.155 Y95.632 E.01317 +G1 X126.129 Y95.632 E.06682 +G1 X126.129 Y96.021 E.01317 +G1 X126.191 Y96.288 E.00928 +G1 X126.308 Y96.453 E.00685 +G1 X126.469 Y96.569 E.00672 +G1 X126.74 Y96.632 E.00942 +G1 X128.688 Y96.632 E.06594 +G1 X128.755 Y96.628 E.00227 +G1 X129.017 Y96.536 E.0094 +G1 X129.216 Y96.328 E.00974 +G1 X129.299 Y96.021 E.01076 +G1 X129.299 Y95.632 E.01317 +G1 X134.308 Y95.632 E.16955 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2249 +G1 X134.775 Y100.554 E.18038 +G1 X133.979 Y100.554 E.02694 +G1 X133.836 Y100.613 E.00524 +G1 X133.775 Y100.758 E.00532 +G1 X133.775 Y102.855 E.07098 +G1 X133.809 Y102.968 E.00399 +G1 F1818.514 +G1 X133.979 Y103.059 E.00653 +G1 F1803.073 +G1 X134.775 Y103.059 E.02694 +G1 F1796.564 +G1 X134.775 Y107.017 E.13397 +G1 X133.979 Y107.017 E.02694 +G1 F1812.17 +G1 X133.846 Y107.066 E.0048 +G1 F2124.677 +G1 X133.775 Y107.22 E.00574 +G1 F2249 +G1 X133.775 Y109.181 E.06638 +G1 X133.835 Y109.325 E.00528 +G1 X133.979 Y109.384 E.00527 +G1 X134.775 Y109.384 E.02694 +G1 X134.775 Y114.775 E.18248 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y108.7 E.20563 +G1 F1359.994 +G1 X115.225 Y107.825 E.02962 +G1 X116.021 Y107.825 E.02694 +G1 F1410.859 +G1 X116.18 Y107.749 E.00597 +G1 F2207.916 +G1 X116.225 Y107.621 E.00459 +G1 F2249 +G1 X116.225 Y106.946 E.02285 +G1 X116.225 Y103.054 E.13174 +G1 X116.225 Y102.379 E.02285 +G1 F2195.18 +G1 X116.177 Y102.247 E.00475 +G1 F1410.859 +G1 X116.021 Y102.175 E.00582 +G1 F1359.994 +G1 X115.225 Y102.175 E.02694 +G1 X115.225 Y101.3 E.02962 +G1 F2249 +G1 X115.225 Y95.9 E.18278 +G1 X115.225 Y95.225 E.02285 +G1 X121.262 Y95.225 E.20434 +G1 X121.262 Y96.021 E.02694 +G1 X121.329 Y96.172 E.00559 +G1 X121.465 Y96.225 E.00494 +G1 X123.544 Y96.225 E.07037 +G1 F2152.212 +G1 X123.688 Y96.165 E.00528 +G1 F1751.83 +G1 X123.748 Y96.021 E.00528 +G1 F1734.465 +G1 X123.748 Y95.225 E.02694 +G1 F1669.3 +G1 X126.537 Y95.225 E.0944 +G1 X126.537 Y96.021 E.02694 +G1 F1688.984 +G1 X126.596 Y96.165 E.00527 +G1 F2147.246 +G1 X126.74 Y96.225 E.00528 +G1 F2249 +G1 X128.688 Y96.225 E.06594 +G1 X128.827 Y96.17 E.00506 +G1 X128.892 Y96.021 E.0055 +G1 X128.892 Y95.225 E.02694 +G1 X134.715 Y95.225 E.1971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.239 Y100.133 Z14.042 F12000 +G1 Z14 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2249 +G3 X134.002 Y99.781 I.765 J.656 E.02934 +G1 X134.002 Y98.65 E.03828 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X133.872 Y103.804 E.48817 +G1 X134.002 Y103.832 E.0045 +G1 X134.002 Y106.243 E.08161 +G1 X133.927 Y106.251 E.00255 +G1 X124.301 Y96.625 E.46079 +G2 X124.521 Y95.998 I-.823 J-.642 E.02289 +G1 X125.763 Y95.998 E.04204 +G1 X125.866 Y96.458 E.01596 +G1 X116.998 Y105.326 E.42451 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.488 Y95.998 E.06221 +G1 X120.52 Y96.155 E.00542 +M204 P1000 +;LAYER_CHANGE +;Z:14.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;14.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.488 Y95.998 E-.0333 +G1 X118.65 Y95.998 E-.38196 +G1 X117.885 Y96.763 E-.22474 +;WIPE_END +G1 X117.885 Y96.763 Z14 F12000 +G1 X134.368 Y95.632 Z14.288 +;AFTER_LAYER_CHANGE +;14.2 +G1 X134.368 Y95.632 +M73 Q66 S7 +G1 Z14.2 F720 +G1 E.8 F1500 +M73 P66 R7 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2280 +G1 X134.368 Y100.014 E.14833 +G1 X133.979 Y100.014 E.01317 +G1 X133.689 Y100.086 E.01011 +G1 X133.556 Y100.184 E.00559 +G1 X133.433 Y100.349 E.00697 +G1 X133.368 Y100.624 E.00956 +G1 X133.368 Y102.724 E.07108 +G1 X133.376 Y102.824 E.0034 +G1 X133.495 Y103.097 E.01008 +G1 X133.694 Y103.265 E.00882 +G1 X133.979 Y103.335 E.00993 +G1 X134.368 Y103.335 E.01317 +G1 X134.368 Y106.741 E.11529 +G1 X133.979 Y106.741 E.01317 +G1 X133.694 Y106.812 E.00994 +G1 X133.56 Y106.907 E.00556 +G1 X133.431 Y107.081 E.00733 +G1 X133.368 Y107.352 E.00942 +G1 X133.368 Y109.319 E.06658 +G1 X133.376 Y109.416 E.00329 +G1 X133.461 Y109.643 E.0082 +G1 X133.687 Y109.855 E.01049 +G1 X133.979 Y109.93 E.0102 +G1 X134.368 Y109.93 E.01317 +G1 X134.368 Y114.368 E.15022 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y108.5 E.19862 +G1 X115.632 Y108.032 E.01584 +G1 X116.021 Y108.032 E.01317 +G1 X116.377 Y107.918 E.01265 +G1 X116.555 Y107.718 E.00906 +G1 X116.632 Y107.421 E.01039 +G1 X116.632 Y103.403 E.136 +G1 X116.629 Y102.517 E.02999 +G1 X116.541 Y102.257 E.00929 +G1 X116.362 Y102.072 E.00871 +G1 X116.021 Y101.968 E.01207 +G1 X115.632 Y101.968 E.01317 +G1 X115.632 Y101.5 E.01584 +G1 X115.632 Y95.9 E.18955 +G1 X115.632 Y95.632 E.00907 +G1 X120.712 Y95.632 E.17195 +G1 X120.712 Y96.021 E.01317 +G1 X120.801 Y96.338 E.01114 +G1 X121.038 Y96.562 E.01104 +G1 X121.323 Y96.632 E.00993 +G1 X122.579 Y96.632 E.04251 +G1 X123.464 Y96.629 E.02996 +G1 X123.743 Y96.529 E.01003 +G1 X123.93 Y96.33 E.00924 +G1 X124.013 Y96.021 E.01083 +G1 X124.013 Y95.632 E.01317 +G1 X126.281 Y95.632 E.07677 +G1 X126.281 Y96.021 E.01317 +G1 X126.365 Y96.33 E.01084 +G1 X126.468 Y96.462 E.00567 +G1 X126.613 Y96.565 E.00602 +G1 X126.892 Y96.632 E.00971 +G1 X128.837 Y96.632 E.06584 +G1 X128.904 Y96.628 E.00227 +G1 X129.171 Y96.533 E.00959 +G1 X129.368 Y96.322 E.00977 +G1 X129.448 Y96.021 E.01054 +G1 X129.448 Y95.632 E.01317 +G1 X134.308 Y95.632 E.1645 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2280 +G1 X134.775 Y100.421 E.17588 +G1 X133.979 Y100.421 E.02694 +G1 X133.838 Y100.477 E.00514 +G1 X133.775 Y100.624 E.00541 +G1 X133.775 Y102.724 E.07108 +G1 F2222.496 +G1 X133.817 Y102.848 E.00443 +G1 F1818.508 +G1 X133.979 Y102.928 E.00612 +G1 F1803.066 +G1 X134.775 Y102.928 E.02694 +G1 X134.775 Y103.734 E.02728 +G1 F2280 +G1 X134.775 Y106.342 E.08828 +G1 F1796.551 +G1 X134.775 Y107.149 E.02732 +G1 X133.979 Y107.149 E.02694 +G1 F1812.158 +G1 X133.839 Y107.204 E.00509 +G1 F2145.443 +G1 X133.775 Y107.352 E.00546 +G1 F2280 +G1 X133.775 Y109.319 E.06658 +G1 X133.837 Y109.465 E.00537 +G1 X133.979 Y109.523 E.00519 +G1 X134.775 Y109.523 E.02694 +G1 X134.775 Y114.775 E.17777 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y108.5 E.2124 +G1 F1359.987 +G1 X115.225 Y107.625 E.02962 +G1 X116.021 Y107.625 E.02694 +G1 F1410.854 +G1 X116.181 Y107.547 E.00603 +G1 F2214.013 +G1 X116.225 Y107.421 E.00452 +G1 F2280 +G1 X116.225 Y106.746 E.02285 +G1 X116.225 Y103.254 E.1182 +G1 F2226.988 +G1 X116.225 Y102.579 E.02285 +G1 F2194.616 +G1 X116.178 Y102.448 E.00471 +G1 F1410.854 +G1 X116.021 Y102.375 E.00586 +G1 F1359.987 +G1 X115.225 Y102.375 E.02694 +G1 X115.225 Y101.5 E.02962 +G1 F2280 +G1 X115.225 Y95.9 E.18955 +G1 X115.225 Y95.225 E.02285 +G1 X121.119 Y95.225 E.1995 +G1 X121.119 Y96.021 E.02694 +G1 X121.185 Y96.171 E.00555 +G1 X121.323 Y96.225 E.00502 +G1 X123.403 Y96.225 E.07041 +G1 F2163.073 +G1 X123.544 Y96.168 E.00515 +G1 F1751.83 +G1 X123.606 Y96.021 E.0054 +G1 F1734.465 +G1 X123.606 Y95.225 E.02694 +G1 F1669.293 +G1 X126.688 Y95.225 E.10432 +G1 X126.688 Y96.021 E.02694 +G1 F1688.978 +G1 X126.751 Y96.168 E.00541 +G1 F2158.965 +G1 X126.892 Y96.225 E.00515 +G1 F2280 +G1 X128.837 Y96.225 E.06584 +G1 X128.976 Y96.17 E.00506 +G1 X129.041 Y96.021 E.0055 +G1 X129.041 Y95.225 E.02694 +G1 X134.715 Y95.225 E.19206 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.16 Y100.107 Z14.243 F12000 +G1 Z14.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2280 +G3 X134.002 Y99.647 I.835 J.528 E.03391 +G1 X134.002 Y98.65 E.03375 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.701 E.49375 +G1 X134.002 Y106.326 E.08885 +G1 X124.223 Y96.547 E.46812 +G1 X124.38 Y95.998 E.01933 +G1 X125.915 Y95.998 E.05196 +G1 X125.993 Y96.331 E.01158 +G1 X116.998 Y105.326 E.43059 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X133.188 Y109.886 Z14.345 F12000 +G1 Z14.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2280 +G2 X134.002 Y110.296 I.818 J-.61 E.03198 +G1 X134.002 Y111.35 E.03568 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.346 Y95.998 E.05741 +G1 X120.415 Y96.293 E.01025 +M204 P1000 +;LAYER_CHANGE +;Z:14.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;14.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.346 Y95.998 E-.06296 +G1 X118.65 Y95.998 E-.35245 +G1 X117.886 Y96.762 E-.22459 +;WIPE_END +G1 X117.886 Y96.762 Z14.2 F12000 +G1 X134.368 Y95.632 Z14.488 +;AFTER_LAYER_CHANGE +;14.4 +G1 X134.368 Y95.632 +G1 Z14.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2252 +G1 X134.368 Y99.88 E.14379 +G1 X133.979 Y99.88 E.01317 +G1 X133.695 Y99.95 E.0099 +G1 X133.561 Y100.045 E.00556 +G1 X133.436 Y100.211 E.00703 +G1 X133.368 Y100.49 E.00972 +G1 X133.368 Y102.594 E.07122 +G1 X133.369 Y102.626 E.00108 +G1 X133.455 Y102.908 E.00998 +G1 X133.676 Y103.124 E.01046 +G1 X133.979 Y103.204 E.01061 +G1 X134.368 Y103.204 E.01317 +G1 X134.368 Y106.873 E.12419 +G1 X133.979 Y106.873 E.01317 +G1 X133.676 Y106.954 E.01062 +G1 X133.543 Y107.056 E.00567 +G1 X133.433 Y107.209 E.00638 +G1 X133.368 Y107.484 E.00956 +G1 X133.368 Y109.457 E.06678 +G1 X133.376 Y109.556 E.00336 +G1 X133.464 Y109.786 E.00834 +G1 X133.693 Y109.997 E.01054 +G1 X133.979 Y110.068 E.00997 +G1 X134.368 Y110.068 E.01317 +G1 X134.368 Y114.368 E.14555 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y108.3 E.20539 +G1 X115.632 Y107.832 E.01584 +G1 X116.021 Y107.832 E.01317 +G1 X116.382 Y107.714 E.01286 +G1 X116.558 Y107.514 E.00902 +G1 X116.632 Y107.221 E.01023 +G1 X116.632 Y102.779 E.15036 +G1 X116.569 Y102.509 E.00938 +G1 X116.365 Y102.274 E.01053 +G1 X116.021 Y102.168 E.01218 +G1 X115.632 Y102.168 E.01317 +G1 X115.632 Y101.7 E.01584 +G1 X115.632 Y95.632 E.20539 +G1 X120.57 Y95.632 E.16715 +G1 X120.57 Y96.021 E.01317 +M73 Q67 S7 +G1 X120.655 Y96.333 E.01095 +G1 X120.761 Y96.465 E.00573 +G1 X120.929 Y96.578 E.00685 +G1 X121.181 Y96.632 E.00872 +G1 X123.261 Y96.632 E.07041 +G1 X123.546 Y96.561 E.00994 +M73 P67 R7 +G1 X123.766 Y96.365 E.00997 +G1 X123.872 Y96.021 E.01218 +G1 X123.872 Y95.632 E.01317 +G1 X126.433 Y95.632 E.08669 +G1 X126.433 Y96.021 E.01317 +G1 X126.539 Y96.365 E.01218 +G1 X126.758 Y96.561 E.00995 +G1 X127.043 Y96.632 E.00994 +G1 X128.986 Y96.632 E.06577 +G1 X129.054 Y96.628 E.00231 +G1 X129.324 Y96.53 E.00972 +G1 X129.521 Y96.317 E.00982 +G1 X129.597 Y96.021 E.01034 +G1 X129.597 Y95.632 E.01317 +G1 X134.308 Y95.632 E.15946 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2252 +G1 X134.775 Y100.287 E.17134 +G1 X133.979 Y100.287 E.02694 +G1 X133.839 Y100.342 E.00509 +G1 X133.775 Y100.49 E.00546 +G1 X133.775 Y102.594 E.07122 +G1 F2229.558 +G1 X133.815 Y102.715 E.00431 +G1 F1818.514 +G1 X133.979 Y102.797 E.00621 +G1 F1803.073 +G1 X134.775 Y102.797 E.02694 +G1 X134.775 Y103.603 E.02728 +G1 F2252 +G1 X134.775 Y106.474 E.09718 +G1 F1796.564 +G1 X134.775 Y107.28 E.02728 +G1 X133.979 Y107.28 E.02694 +G1 F1812.17 +G1 X133.833 Y107.341 E.00536 +G1 F2164.348 +G1 X133.775 Y107.484 E.00522 +G1 F2252 +G1 X133.775 Y109.457 E.06678 +G1 X133.839 Y109.605 E.00546 +G1 X133.979 Y109.661 E.0051 +G1 X134.775 Y109.661 E.02694 +G1 X134.775 Y114.775 E.1731 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y108.3 E.21917 +G1 F1359.994 +G1 X115.225 Y107.425 E.02962 +G1 X116.021 Y107.425 E.02694 +G1 F1410.859 +G1 X116.183 Y107.346 E.0061 +G1 F2219.76 +G1 X116.225 Y107.221 E.00446 +G1 F2252 +G1 X116.225 Y102.779 E.15036 +G1 F2200.987 +G1 X116.178 Y102.649 E.00468 +G1 F1410.859 +G1 X116.021 Y102.575 E.00587 +G1 F1359.994 +G1 X115.225 Y102.575 E.02694 +G1 X115.225 Y101.7 E.02962 +G1 F2252 +G1 X115.225 Y95.225 E.21917 +G1 X120.977 Y95.225 E.1947 +G1 X120.977 Y96.021 E.02694 +G1 X121.041 Y96.169 E.00546 +G1 X121.181 Y96.225 E.0051 +G1 X123.261 Y96.225 E.07041 +G1 X123.375 Y96.19 E.00404 +G1 F1751.823 +G1 X123.465 Y96.021 E.00648 +G1 F1734.458 +G1 X123.465 Y95.225 E.02694 +G1 F1669.3 +G1 X126.84 Y95.225 E.11424 +G1 X126.84 Y96.021 E.02694 +G1 F1688.984 +G1 X126.906 Y96.171 E.00555 +G1 F2170.989 +G1 X127.043 Y96.225 E.00498 +G1 F2252 +G1 X128.986 Y96.225 E.06577 +G1 X129.125 Y96.17 E.00506 +G1 X129.19 Y96.021 E.0055 +G1 X129.19 Y95.225 E.02694 +G1 X134.715 Y95.225 E.18701 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.092 Y100.087 Z14.444 F12000 +G1 Z14.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2252 +G3 X134.002 Y99.513 I.908 J.432 E.03843 +G1 X134.002 Y98.65 E.02921 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X124.117 Y96.441 E.47319 +G1 X124.238 Y95.998 E.01554 +G1 X126.066 Y95.998 E.06188 +G1 X126.116 Y96.208 E.00731 +G1 X116.998 Y105.326 E.43647 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.204 Y95.998 E.0526 +G1 X120.31 Y96.43 E.01506 +M204 P1000 +;LAYER_CHANGE +;Z:14.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;14.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.204 Y95.998 E-.09244 +G1 X118.65 Y95.998 E-.32294 +G1 X117.886 Y96.762 E-.22462 +;WIPE_END +G1 X117.886 Y96.762 Z14.4 F12000 +G1 X134.368 Y95.632 Z14.688 +;AFTER_LAYER_CHANGE +;14.6 +G1 X134.368 Y95.632 +M73 Q67 S6 +G1 Z14.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2284 +G1 X134.368 Y99.746 E.13925 +G1 X133.979 Y99.746 E.01317 +G1 X133.701 Y99.813 E.00968 +G1 X133.567 Y99.906 E.00552 +G1 X133.438 Y100.072 E.00712 +G1 X133.368 Y100.357 E.00993 +G1 X133.368 Y102.463 E.07129 +G1 X133.369 Y102.494 E.00105 +G1 X133.454 Y102.776 E.00997 +G1 X133.659 Y102.983 E.00986 +G1 X133.979 Y103.073 E.01125 +G1 X134.368 Y103.073 E.01317 +G1 X134.368 Y107.005 E.13309 +G1 X133.979 Y107.005 E.01317 +M73 P67 R6 +G1 X133.659 Y107.095 E.01125 +G1 X133.528 Y107.204 E.00577 +G1 X133.434 Y107.339 E.00557 +G1 X133.368 Y107.616 E.00964 +G1 X133.368 Y109.596 E.06702 +G1 X133.376 Y109.696 E.0034 +G1 X133.466 Y109.929 E.00845 +G1 X133.699 Y110.139 E.01062 +G1 X133.979 Y110.207 E.00975 +G1 X134.368 Y110.207 E.01317 +G1 X134.368 Y114.368 E.14084 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y108.1 E.21216 +G1 X115.632 Y107.632 E.01584 +G1 X116.021 Y107.632 E.01317 +G1 X116.387 Y107.511 E.01305 +G1 X116.56 Y107.31 E.00898 +G1 X116.632 Y107.021 E.01008 +G1 X116.632 Y102.979 E.13682 +G1 X116.561 Y102.693 E.00997 +G1 X116.368 Y102.476 E.00983 +G1 X116.021 Y102.368 E.0123 +G1 X115.632 Y102.368 E.01317 +G1 X115.632 Y101.9 E.01584 +G1 X115.632 Y95.632 E.21216 +G1 X120.427 Y95.632 E.1623 +G1 X120.427 Y96.021 E.01317 +G1 X120.51 Y96.327 E.01073 +G1 X120.613 Y96.46 E.00569 +G1 X120.821 Y96.592 E.00834 +G1 X121.038 Y96.632 E.00747 +G1 X123.12 Y96.632 E.07047 +G1 X123.371 Y96.578 E.00869 +G1 X123.603 Y96.395 E.01 +G1 X123.73 Y96.021 E.01337 +G1 X123.73 Y95.632 E.01317 +G1 X126.585 Y95.632 E.09664 +G1 X126.585 Y96.021 E.01317 +G1 X126.712 Y96.395 E.01337 +G1 X126.905 Y96.559 E.00857 +G1 X127.195 Y96.632 E.01012 +G1 X129.135 Y96.632 E.06567 +G1 X129.203 Y96.628 E.00231 +G1 X129.478 Y96.527 E.00992 +G1 X129.673 Y96.311 E.00985 +G1 X129.746 Y96.021 E.01012 +G1 X129.746 Y95.632 E.01317 +G1 X134.308 Y95.632 E.15442 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2284 +G1 X134.775 Y100.153 E.16681 +G1 X133.979 Y100.153 E.02694 +G1 X133.841 Y100.206 E.005 +G1 X133.775 Y100.357 E.00558 +G1 X133.775 Y102.463 E.07129 +G1 F2236.602 +G1 X133.814 Y102.582 E.00424 +G1 F1818.514 +G1 X133.979 Y102.666 E.00627 +G1 F1803.073 +G1 X134.775 Y102.666 E.02694 +G1 X134.775 Y103.472 E.02728 +G1 F2284 +G1 X134.775 Y106.605 E.10605 +G1 F1796.558 +G1 X134.775 Y107.412 E.02732 +G1 X133.979 Y107.412 E.02694 +G1 F1812.164 +G1 X133.828 Y107.478 E.00558 +G1 F2181.591 +G1 X133.775 Y107.616 E.005 +G1 F2284 +G1 X133.775 Y109.596 E.06702 +G1 X133.841 Y109.746 E.00555 +G1 X133.979 Y109.799 E.005 +G1 X134.775 Y109.799 E.02694 +G1 X134.775 Y114.775 E.16843 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y108.1 E.22594 +G1 F1360 +G1 X115.225 Y107.225 E.02962 +G1 X116.021 Y107.225 E.02694 +G1 F1410.864 +G1 X116.184 Y107.144 E.00616 +G1 F2225.187 +G1 X116.225 Y107.021 E.00439 +G1 F2284 +G1 X116.225 Y102.979 E.13682 +G1 F2204.387 +M73 Q68 S6 +G1 X116.179 Y102.85 E.00464 +G1 F1410.859 +G1 X116.021 Y102.775 E.00592 +G1 F1359.994 +G1 X115.225 Y102.775 E.02694 +G1 X115.225 Y101.9 E.02962 +G1 F2284 +M73 P68 R6 +G1 X115.225 Y95.225 E.22594 +G1 X120.835 Y95.225 E.18989 +G1 X120.835 Y96.021 E.02694 +G1 X120.896 Y96.168 E.00539 +G1 X121.038 Y96.225 E.00518 +G1 X122.979 Y96.225 E.0657 +G1 F2253.087 +G1 X123.233 Y96.191 E.00867 +G1 F1751.83 +G1 X123.323 Y96.021 E.00651 +G1 F1734.465 +G1 X123.323 Y95.225 E.02694 +G1 F1669.306 +G1 X126.992 Y95.225 E.12419 +G1 X126.992 Y96.021 E.02694 +G1 F1688.99 +G1 X127.06 Y96.174 E.00567 +G1 F2182.953 +G1 X127.195 Y96.225 E.00488 +G1 F2284 +G1 X129.135 Y96.225 E.06567 +G1 X129.274 Y96.17 E.00506 +G1 X129.339 Y96.021 E.0055 +G1 X129.339 Y95.225 E.02694 +G1 X134.715 Y95.225 E.18197 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.041 Y100.077 Z14.644 F12000 +G1 Z14.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2284 +G3 X134.002 Y99.38 I.959 J.31 E.04297 +G1 X134.002 Y98.65 E.02471 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X124.002 Y96.326 E.47869 +G1 X124.097 Y95.998 E.01156 +G1 X126.218 Y95.998 E.07179 +G1 X126.233 Y96.091 E.00319 +G1 X116.998 Y105.326 E.44207 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X133.058 Y109.921 Z14.743 F12000 +G1 Z14.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2284 +G2 X134.002 Y110.573 I.937 J-.348 E.04136 +G1 X134.002 Y111.35 E.0263 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.061 Y95.998 E.04776 +G1 X120.226 Y96.556 E.0197 +M204 P1000 +;LAYER_CHANGE +;Z:14.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;14.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.061 Y95.998 E-.12092 +G1 X118.65 Y95.998 E-.29322 +G1 X117.882 Y96.766 E-.22586 +;WIPE_END +G1 X117.882 Y96.766 Z14.6 F12000 +G1 X134.368 Y95.632 Z14.888 +;AFTER_LAYER_CHANGE +;14.8 +G1 X134.368 Y95.632 +G1 Z14.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2258 +G1 X134.368 Y99.612 E.13472 +G1 X133.979 Y99.612 E.01317 +G1 X133.707 Y99.676 E.00946 +G1 X133.573 Y99.766 E.00546 +G1 X133.441 Y99.933 E.00721 +G1 X133.368 Y100.223 E.01012 +G1 X133.368 Y102.332 E.07139 +G1 X133.435 Y102.611 E.00971 +G1 X133.643 Y102.842 E.01052 +G1 X133.979 Y102.943 E.01188 +G1 X134.368 Y102.943 E.01317 +G1 X134.368 Y107.137 E.14196 +G1 X133.979 Y107.137 E.01317 +G1 X133.643 Y107.237 E.01187 +G1 X133.435 Y107.468 E.01052 +G1 X133.368 Y107.747 E.00971 +G1 X133.368 Y109.734 E.06726 +G1 X133.376 Y109.836 E.00346 +G1 X133.47 Y110.072 E.0086 +G1 X133.705 Y110.28 E.01062 +G1 X133.979 Y110.345 E.00953 +G1 X134.368 Y110.345 E.01317 +G1 X134.368 Y114.368 E.13617 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.9 E.21893 +G1 X115.632 Y107.432 E.01584 +G1 X116.021 Y107.432 E.01317 +G1 X116.312 Y107.358 E.01016 +G1 X116.446 Y107.26 E.00562 +G1 X116.562 Y107.106 E.00653 +G1 X116.632 Y106.821 E.00993 +G1 X116.632 Y103.161 E.12389 +G1 X116.553 Y102.878 E.00995 +G1 X116.371 Y102.678 E.00915 +G1 X116.021 Y102.568 E.01242 +G1 X115.632 Y102.568 E.01317 +G1 X115.632 Y102.1 E.01584 +G1 X115.632 Y95.632 E.21893 +G1 X120.285 Y95.632 E.1575 +G1 X120.285 Y96.021 E.01317 +G1 X120.364 Y96.322 E.01053 +G1 X120.589 Y96.55 E.01084 +G1 X120.896 Y96.632 E.01076 +G1 X122.978 Y96.632 E.07047 +G1 X123.208 Y96.587 E.00793 +G1 X123.441 Y96.42 E.0097 +G1 X123.589 Y96.021 E.0144 +G1 X123.589 Y95.632 E.01317 +G1 X126.736 Y95.632 E.10652 +G1 X126.736 Y96.021 E.01317 +G1 X126.885 Y96.42 E.01442 +G1 X127.089 Y96.575 E.00867 +G1 X127.347 Y96.632 E.00894 +G1 X129.284 Y96.632 E.06557 +G1 X129.352 Y96.628 E.00231 +G1 X129.632 Y96.524 E.01011 +G1 X129.825 Y96.304 E.00991 +G1 X129.895 Y96.021 E.00987 +G1 X129.895 Y95.632 E.01317 +G1 X134.308 Y95.632 E.14937 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2258 +G1 X134.775 Y100.019 E.16227 +G1 X133.979 Y100.019 E.02694 +G1 X133.843 Y100.071 E.00493 +G1 X133.775 Y100.223 E.00564 +G1 X133.775 Y102.332 E.07139 +G1 F2243.564 +G1 X133.812 Y102.449 E.00415 +G1 F1818.514 +G1 X133.979 Y102.536 E.00637 +G1 F1803.073 +G1 X134.775 Y102.536 E.02694 +G1 X134.775 Y103.341 E.02725 +G1 F2258 +G1 X134.775 Y106.737 E.11495 +G1 F1796.564 +G1 X134.775 Y107.544 E.02732 +G1 X133.979 Y107.544 E.02694 +G1 F1812.17 +G1 X133.824 Y107.615 E.00577 +G1 F2197.356 +G1 X133.775 Y107.747 E.00477 +G1 F2258 +G1 X133.775 Y109.734 E.06726 +G1 X133.843 Y109.886 E.00564 +G1 X133.979 Y109.938 E.00493 +G1 X134.775 Y109.938 E.02694 +G1 X134.775 Y114.775 E.16373 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y107.9 E.23271 +G1 F1359.994 +G1 X115.225 Y107.025 E.02962 +G1 X116.021 Y107.025 E.02694 +G1 F1410.859 +G1 X116.163 Y106.968 E.00518 +G1 F2135.502 +G1 X116.225 Y106.821 E.0054 +G1 F2258 +G1 X116.225 Y103.179 E.12328 +G1 F2208.095 +G1 X116.18 Y103.051 E.00459 +G1 F1410.864 +G1 X116.021 Y102.975 E.00597 +G1 F1360 +G1 X115.225 Y102.975 E.02694 +G1 X115.225 Y102.1 E.02962 +G1 F2258 +G1 X115.225 Y95.225 E.23271 +G1 X120.692 Y95.225 E.18505 +G1 X120.692 Y96.021 E.02694 +G1 X120.752 Y96.166 E.00531 +G1 X120.896 Y96.225 E.00527 +G1 X122.978 Y96.225 E.07047 +G1 F2254.636 +G1 X123.091 Y96.191 E.00399 +G1 F1751.83 +G1 X123.182 Y96.021 E.00653 +G1 F1734.465 +G1 X123.182 Y95.225 E.02694 +G1 F1669.3 +G1 X127.143 Y95.225 E.13407 +G1 X127.143 Y96.021 E.02694 +G1 F1688.984 +G1 X127.193 Y96.154 E.00481 +G1 F2106.847 +G1 X127.347 Y96.225 E.00574 +G1 F2258 +G1 X129.284 Y96.225 E.06557 +G1 X129.424 Y96.17 E.00509 +G1 X129.488 Y96.021 E.00549 +G1 X129.488 Y95.225 E.02694 +G1 X134.715 Y95.225 E.17693 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.022 Y100.075 Z14.844 F12000 +G1 Z14.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2258 +G3 X134.002 Y99.246 I.961 J.142 E.04746 +G1 X134.002 Y98.65 E.02017 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 P69 R6 +M73 Q69 S6 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.893 Y96.217 E.48391 +G1 X123.955 Y95.998 E.0077 +G1 X126.326 Y95.998 E.08026 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.919 Y95.998 E.04295 +G2 X120.171 Y96.669 I1.138 J-.045 E.02468 +M204 P1000 +;LAYER_CHANGE +;Z:15 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;15 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.919 Y95.998 I.887 J-.716 E-.15152 +G1 X118.65 Y95.998 E-.26371 +G1 X117.885 Y96.763 E-.22477 +;WIPE_END +G1 X117.885 Y96.763 Z14.8 F12000 +G1 X134.368 Y95.632 Z15.088 +;AFTER_LAYER_CHANGE +;15 +G1 X134.368 Y95.632 +G1 Z15 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2254 +G1 X134.368 Y99.479 E.13022 +G1 X133.979 Y99.479 E.01317 +G1 X133.58 Y99.627 E.0144 +G1 X133.444 Y99.794 E.00729 +G1 X133.368 Y100.089 E.01031 +G1 X133.368 Y102.201 E.07149 +G1 X133.424 Y102.458 E.0089 +G1 X133.629 Y102.702 E.01079 +G1 X133.979 Y102.812 E.01242 +G1 X134.368 Y102.812 E.01317 +G1 X134.368 Y107.269 E.15086 +G1 X133.979 Y107.269 E.01317 +G1 X133.629 Y107.379 E.01242 +G1 X133.436 Y107.599 E.00991 +G1 X133.368 Y107.879 E.00975 +G1 X133.368 Y109.873 E.06749 +G1 X133.409 Y110.092 E.00754 +G1 X133.578 Y110.334 E.00999 +G1 X133.979 Y110.483 E.01448 +G1 X134.368 Y110.483 E.01317 +G1 X134.368 Y114.368 E.1315 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.7 E.2257 +G1 X115.632 Y107.232 E.01584 +G1 X116.021 Y107.232 E.01317 +G1 X116.317 Y107.156 E.01034 +G1 X116.45 Y107.056 E.00563 +G1 X116.564 Y106.902 E.00649 +G1 X116.632 Y106.621 E.00979 +G1 X116.632 Y103.379 E.10974 +G1 X116.577 Y103.125 E.0088 +G1 X116.375 Y102.881 E.01072 +G1 X116.021 Y102.768 E.01258 +G1 X115.632 Y102.768 E.01317 +G1 X115.632 Y102.3 E.01584 +G1 X115.632 Y95.632 E.2257 +G1 X120.143 Y95.632 E.15269 +G1 X120.143 Y96.021 E.01317 +G1 X120.218 Y96.316 E.0103 +G1 X120.318 Y96.45 E.00566 +G1 X120.512 Y96.582 E.00794 +G1 X120.753 Y96.632 E.00833 +G1 X122.142 Y96.632 E.04702 +G1 X122.883 Y96.63 E.02508 +G1 X123.152 Y96.545 E.00955 +G1 X123.376 Y96.308 E.01104 +G1 X123.447 Y96.021 E.01001 +G1 X123.447 Y95.632 E.01317 +G1 X126.888 Y95.632 E.11647 +G1 X126.888 Y96.021 E.01317 +G1 X126.959 Y96.308 E.01001 +G1 X127.056 Y96.442 E.0056 +G1 X127.227 Y96.569 E.00721 +G1 X127.498 Y96.632 E.00942 +G1 X129.433 Y96.632 E.0655 +G1 X129.501 Y96.628 E.00231 +G1 X129.786 Y96.52 E.01032 +G1 X129.978 Y96.298 E.00993 +G1 X130.044 Y96.021 E.00964 +G1 X130.044 Y95.632 E.01317 +G1 X134.308 Y95.632 E.14433 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2254 +G1 X134.775 Y99.886 E.15777 +G1 X133.979 Y99.886 E.02694 +G1 X133.846 Y99.935 E.0048 +G1 X133.775 Y100.089 E.00574 +G1 X133.775 Y102.201 E.07149 +G1 F2250.406 +G1 X133.81 Y102.316 E.00407 +G1 F1818.514 +G1 X133.979 Y102.405 E.00647 +G1 F1803.073 +G1 X134.775 Y102.405 E.02694 +G1 X134.775 Y103.211 E.02728 +G1 F2254 +G1 X134.775 Y106.869 E.12382 +G1 F1796.564 +G1 X134.775 Y107.676 E.02732 +G1 X133.979 Y107.676 E.02694 +G1 F1812.17 +G1 X133.82 Y107.752 E.00597 +G1 F2211.799 +G1 X133.775 Y107.879 E.00456 +G1 F2254 +G1 X133.775 Y109.873 E.06749 +G1 X133.81 Y109.987 E.00404 +G1 X133.979 Y110.076 E.00647 +G1 X134.775 Y110.076 E.02694 +G1 X134.775 Y114.775 E.15906 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y107.7 E.23948 +G1 F1359.994 +G1 X115.225 Y106.825 E.02962 +G1 X116.021 Y106.825 E.02694 +G1 F1410.859 +G1 X116.164 Y106.766 E.00524 +G1 F2141.637 +G1 X116.225 Y106.621 E.00532 +G1 F2254 +G1 X116.225 Y103.379 E.10974 +G1 F2212.084 +G1 X116.181 Y103.252 E.00455 +G1 F1410.859 +G1 X116.021 Y103.175 E.00601 +G1 F1359.994 +G1 X115.225 Y103.175 E.02694 +G1 X115.225 Y102.3 E.02962 +G1 F2254 +G1 X115.225 Y95.225 E.23948 +G1 X120.55 Y95.225 E.18024 +G1 X120.55 Y96.021 E.02694 +G1 X120.608 Y96.164 E.00522 +G1 X120.753 Y96.225 E.00532 +G1 X122.837 Y96.225 E.07054 +G1 F2221.306 +G1 X122.961 Y96.183 E.00443 +G1 F1751.83 +G1 X123.04 Y96.021 E.0061 +G1 F1734.465 +G1 X123.04 Y95.225 E.02694 +G1 X123.857 Y95.225 E.02765 +G1 F2254 +G1 X126.468 Y95.225 E.08838 +G1 F1669.3 +G1 X127.295 Y95.225 E.02799 +G1 X127.295 Y96.021 E.02694 +G1 F1688.984 +G1 X127.351 Y96.162 E.00514 +G1 F2133.922 +G1 X127.498 Y96.225 E.00541 +G1 F2254 +G1 X129.433 Y96.225 E.0655 +G1 X129.573 Y96.17 E.00509 +G1 X129.637 Y96.021 E.00549 +G1 X129.637 Y95.225 E.02694 +G1 X134.715 Y95.225 E.17188 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.002 Y100.081 Z15.045 F12000 +G1 Z15 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2254 +G1 X133.045 Y99.798 E.00969 +G3 X134.002 Y99.112 I.991 J.373 E.04226 +G1 X134.002 Y98.65 E.01564 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.795 Y96.119 E.4886 +G1 X123.814 Y95.998 E.00415 +G1 X126.326 Y95.998 E.08503 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +M73 Q70 S6 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +M73 P70 R6 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.776 Y95.998 E.03811 +G2 X120.129 Y96.768 I1.042 J-.012 E.02953 +M204 P1000 +;LAYER_CHANGE +;Z:15.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;15.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.776 Y95.998 I.689 J-.782 E-.18128 +G1 X118.65 Y95.998 E-.234 +G1 X117.885 Y96.763 E-.22472 +;WIPE_END +G1 X117.885 Y96.763 Z15 F12000 +G1 X134.368 Y95.632 Z15.288 +;AFTER_LAYER_CHANGE +;15.2 +G1 X134.368 Y95.632 +G1 Z15.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2285 +G1 X134.368 Y99.345 E.12568 +G1 X133.979 Y99.345 E.01317 +G1 X133.587 Y99.487 E.01411 +G1 X133.447 Y99.655 E.0074 +G1 X133.368 Y99.956 E.01053 +G1 X133.368 Y102.071 E.07159 +G1 X133.415 Y102.305 E.00808 +G1 X133.615 Y102.561 E.011 +G1 X133.979 Y102.681 E.01297 +G1 X134.368 Y102.681 E.01317 +G1 X134.368 Y107.4 E.15973 +G1 X133.979 Y107.4 E.01317 +G1 X133.615 Y107.52 E.01297 +G1 X133.441 Y107.722 E.00902 +G1 X133.368 Y108.011 E.01009 +G1 X133.368 Y110.011 E.0677 +G1 X133.41 Y110.234 E.00768 +G1 X133.585 Y110.478 E.01016 +G1 X133.979 Y110.622 E.0142 +G1 X134.368 Y110.622 E.01317 +G1 X134.368 Y114.368 E.1268 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.5 E.23247 +G1 X115.632 Y107.032 E.01584 +G1 X116.021 Y107.032 E.01317 +G1 X116.321 Y106.953 E.0105 +G1 X116.455 Y106.852 E.00568 +G1 X116.566 Y106.698 E.00643 +G1 X116.632 Y106.421 E.00964 +G1 X116.632 Y103.579 E.0962 +G1 X116.563 Y103.297 E.00983 +G1 X116.379 Y103.083 E.00955 +G1 X116.021 Y102.968 E.01273 +G1 X115.632 Y102.968 E.01317 +G1 X115.632 Y102.5 E.01584 +G1 X115.632 Y95.632 E.23247 +G1 X120 Y95.632 E.14785 +G1 X120 Y96.021 E.01317 +G1 X120.073 Y96.31 E.01009 +G1 X120.17 Y96.444 E.0056 +G1 X120.393 Y96.592 E.00906 +G1 X120.611 Y96.632 E.0075 +G1 X122.001 Y96.632 E.04705 +G1 X122.736 Y96.631 E.02488 +G1 X123.01 Y96.545 E.00972 +G1 X123.223 Y96.329 E.01027 +G1 X123.306 Y96.021 E.0108 +G1 X123.306 Y95.632 E.01317 +G1 X127.04 Y95.632 E.12639 +G1 X127.04 Y96.021 E.01317 +G1 X127.122 Y96.329 E.01079 +G1 X127.226 Y96.461 E.00569 +G1 X127.375 Y96.567 E.00619 +G1 X127.65 Y96.632 E.00956 +G1 X129.582 Y96.632 E.0654 +G1 X129.65 Y96.628 E.00231 +G1 X129.94 Y96.516 E.01052 +G1 X130.13 Y96.291 E.00997 +G1 X130.193 Y96.021 E.00938 +G1 X130.193 Y95.632 E.01317 +G1 X134.308 Y95.632 E.13929 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2285 +G1 X134.775 Y99.752 E.15323 +G1 X133.979 Y99.752 E.02694 +G1 X133.848 Y99.799 E.00471 +G1 X133.775 Y99.956 E.00586 +G1 X133.775 Y102.071 E.07159 +G1 F2266.344 +G1 X133.806 Y102.179 E.0038 +G1 F1818.508 +G1 X133.979 Y102.274 E.00668 +G1 F1803.066 +G1 X134.775 Y102.274 E.02694 +G1 X134.775 Y103.08 E.02728 +G1 F2285 +G1 X134.775 Y107.001 E.13272 +G1 F1796.551 +G1 X134.775 Y107.807 E.02728 +G1 X133.979 Y107.807 E.02694 +G1 F1812.158 +G1 X133.816 Y107.888 E.00616 +G1 F2225.063 +G1 X133.775 Y108.011 E.00439 +G1 F2285 +G1 X133.775 Y110.011 E.0677 +G1 X133.811 Y110.127 E.00411 +G1 X133.979 Y110.215 E.00642 +G1 X134.775 Y110.215 E.02694 +G1 X134.775 Y114.775 E.15435 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y107.5 E.24625 +G1 F1359.987 +G1 X115.225 Y106.625 E.02962 +G1 X116.021 Y106.625 E.02694 +G1 F1410.854 +G1 X116.166 Y106.565 E.00531 +G1 F2147.504 +G1 X116.225 Y106.421 E.00527 +G1 F2285 +G1 X116.225 Y103.579 E.0962 +G1 F2264.227 +G1 X116.193 Y103.469 E.00388 +G1 F1410.854 +G1 X116.021 Y103.375 E.00663 +G1 F1359.987 +G1 X115.225 Y103.375 E.02694 +G1 X115.225 Y102.5 E.02962 +G1 F2285 +G1 X115.225 Y95.225 E.24625 +G1 X120.407 Y95.225 E.1754 +G1 X120.407 Y96.021 E.02694 +G1 X120.464 Y96.162 E.00515 +G1 X120.611 Y96.225 E.00541 +G1 X122.695 Y96.225 E.07054 +G1 F2229.735 +G1 X122.816 Y96.185 E.00431 +G1 F1751.823 +G1 X122.899 Y96.021 E.00622 +G1 F1734.458 +G1 X122.899 Y95.225 E.02694 +G1 X123.715 Y95.225 E.02762 +G1 F2285 +G1 X126.62 Y95.225 E.09833 +G1 F1669.293 +G1 X127.447 Y95.225 E.02799 +G1 X127.447 Y96.021 E.02694 +G1 F1688.978 +G1 X127.509 Y96.168 E.0054 +G1 F2158.21 +G1 X127.65 Y96.225 E.00515 +G1 F2285 +G1 X129.582 Y96.225 E.0654 +G1 X129.722 Y96.17 E.00509 +G1 X129.786 Y96.021 E.00549 +G1 X129.786 Y95.225 E.02694 +G1 X134.715 Y95.225 E.16684 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.002 Y100.08 Z15.245 F12000 +G1 Z15.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2285 +G1 X133.046 Y99.659 E.01433 +G3 X134.002 Y98.979 I.988 J.376 E.0421 +G1 X134.002 Y98.65 E.01114 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X133.002 Y109.921 Z15.342 F12000 +G1 Z15.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2285 +G1 X133.068 Y110.369 E.01533 +G2 X134.002 Y110.988 I.965 J-.441 E.03996 +G1 X134.002 Y111.35 E.01225 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.65 Y95.998 E.08977 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:15.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;15.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X116.761 Y97.887 Z15.2 F12000 +G1 X134.368 Y95.632 Z15.51 +;AFTER_LAYER_CHANGE +;15.4 +G1 X134.368 Y95.632 +G1 Z15.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2259 +G1 X134.368 Y99.211 E.12114 +G1 X133.979 Y99.211 E.01317 +G1 X133.594 Y99.348 E.01383 +G1 X133.451 Y99.515 E.00744 +G1 X133.368 Y99.822 E.01076 +G1 X133.368 Y101.94 E.07169 +G1 X133.371 Y102.005 E.0022 +G1 X133.458 Y102.26 E.00912 +G1 X133.682 Y102.473 E.01046 +G1 X133.979 Y102.55 E.01039 +G1 X134.368 Y102.55 E.01317 +G1 X134.368 Y107.532 E.16863 +G1 X133.979 Y107.532 E.01317 +G1 X133.682 Y107.609 E.01039 +M73 Q71 S6 +G1 X133.548 Y107.709 E.00566 +G1 X133.441 Y107.854 E.0061 +G1 X133.368 Y108.143 E.01009 +G1 X133.368 Y110.15 E.06793 +G1 X133.411 Y110.376 E.00779 +G1 X133.592 Y110.623 E.01037 +G1 X133.979 Y110.76 E.0139 +G1 X134.368 Y110.76 E.01317 +G1 X134.368 Y114.368 E.12213 +G1 X115.632 Y114.368 E.63419 +M73 P71 R6 +G1 X115.632 Y107.3 E.23924 +G1 X115.632 Y106.832 E.01584 +G1 X116.021 Y106.832 E.01317 +G1 X116.326 Y106.751 E.01068 +G1 X116.458 Y106.648 E.00567 +G1 X116.567 Y106.495 E.00636 +G1 X116.632 Y106.221 E.00953 +G1 X116.632 Y103.779 E.08266 +G1 X116.584 Y103.541 E.00822 +G1 X116.383 Y103.286 E.01099 +G1 X116.021 Y103.168 E.01289 +G1 X115.632 Y103.168 E.01317 +G1 X115.632 Y102.7 E.01584 +G1 X115.632 Y95.632 E.23924 +G1 X119.858 Y95.632 E.14304 +G1 X119.858 Y96.021 E.01317 +G1 X119.927 Y96.304 E.00986 +G1 X120.022 Y96.438 E.00556 +G1 X120.275 Y96.601 E.01019 +G1 X120.468 Y96.632 E.00662 +G1 X122.554 Y96.632 E.07061 +G1 X122.832 Y96.565 E.00968 +G1 X123.071 Y96.347 E.01095 +G1 X123.165 Y96.021 E.01148 +G1 X123.165 Y95.632 E.01317 +G1 X127.191 Y95.632 E.13628 +G1 X127.191 Y96.021 E.01317 +G1 X127.285 Y96.347 E.01148 +G1 X127.524 Y96.565 E.01095 +G1 X127.802 Y96.632 E.00968 +G1 X129.731 Y96.632 E.06529 +G1 X129.885 Y96.613 E.00525 +G1 X130.149 Y96.467 E.01021 +G1 X130.342 Y96.021 E.01645 +G1 X130.342 Y95.632 E.01317 +G1 X134.308 Y95.632 E.13424 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2259 +G1 X134.775 Y99.618 E.1487 +G1 X133.979 Y99.618 E.02694 +G1 X133.83 Y99.683 E.0055 +G1 X133.775 Y99.822 E.00506 +G1 X133.775 Y101.94 E.07169 +G1 F2237.492 +G1 X133.813 Y102.059 E.00423 +G1 F1818.514 +G1 X133.979 Y102.143 E.0063 +G1 F1803.073 +G1 X134.775 Y102.143 E.02694 +G1 X134.775 Y102.949 E.02728 +G1 F2259 +G1 X134.775 Y107.132 E.14159 +G1 F1796.564 +G1 X134.775 Y107.939 E.02732 +G1 X133.979 Y107.939 E.02694 +G1 F1812.17 +G1 X133.835 Y107.998 E.00527 +G1 F2158.191 +G1 X133.775 Y108.143 E.00531 +G1 F2259 +G1 X133.775 Y110.15 E.06793 +G1 X133.812 Y110.267 E.00415 +G1 X133.979 Y110.353 E.00636 +G1 X134.775 Y110.353 E.02694 +G1 X134.775 Y114.775 E.14968 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y107.3 E.25302 +G1 F1359.994 +G1 X115.225 Y106.425 E.02962 +G1 X116.021 Y106.425 E.02694 +G1 F1410.859 +G1 X116.167 Y106.364 E.00536 +G1 F2153.115 +G1 X116.225 Y106.221 E.00522 +G1 F2259 +G1 X116.225 Y103.779 E.08266 +G1 F2253.159 +G1 X116.191 Y103.665 E.00403 +G1 F1410.859 +G1 X116.021 Y103.575 E.00651 +G1 F1359.994 +G1 X115.225 Y103.575 E.02694 +G1 X115.225 Y102.7 E.02962 +G1 F2259 +G1 X115.225 Y95.225 E.25302 +G1 X120.265 Y95.225 E.1706 +G1 X120.265 Y96.021 E.02694 +G1 X120.319 Y96.16 E.00505 +G1 X120.468 Y96.225 E.0055 +G1 X122.554 Y96.225 E.07061 +G1 F2238.094 +G1 X122.672 Y96.187 E.0042 +G1 F1751.83 +G1 X122.757 Y96.021 E.00631 +G1 F1734.465 +G1 X122.757 Y95.225 E.02694 +G1 X123.574 Y95.225 E.02765 +G1 F2259 +G1 X126.772 Y95.225 E.10825 +G1 F1669.306 +G1 X127.598 Y95.225 E.02796 +G1 X127.598 Y96.021 E.02694 +G1 F1688.99 +G1 X127.666 Y96.173 E.00564 +G1 F2180.07 +G1 X127.802 Y96.225 E.00493 +G1 F2259 +G1 X129.731 Y96.225 E.06529 +G1 X129.853 Y96.185 E.00435 +G1 X129.935 Y96.021 E.00621 +G1 X129.935 Y95.225 E.02694 +G1 X134.715 Y95.225 E.1618 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.002 Y100.08 Z15.445 F12000 +G1 Z15.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2259 +G3 X134.002 Y98.845 I1.026 J-.191 E.06112 +G1 X134.002 Y98.65 E.0066 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.491 Y95.998 E.02847 +G2 X120.086 Y96.915 I.99 J.01 E.0392 +M204 P1000 +;LAYER_CHANGE +;Z:15.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;15.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.491 Y95.998 I.394 J-.907 E-.24067 +G1 X118.65 Y95.998 E-.17477 +G1 X117.886 Y96.762 E-.22456 +;WIPE_END +G1 X117.886 Y96.762 Z15.4 F12000 +G1 X134.368 Y95.632 Z15.688 +;AFTER_LAYER_CHANGE +;15.6 +G1 X134.368 Y95.632 +G1 Z15.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2312 +G1 X134.368 Y99.077 E.11661 +G1 X133.979 Y99.077 E.01317 +G1 X133.602 Y99.208 E.01351 +G1 X133.54 Y99.263 E.00281 +G1 X133.4 Y99.493 E.00911 +G1 X133.368 Y99.688 E.00669 +G1 X133.368 Y101.809 E.07179 +G1 X133.372 Y101.876 E.00227 +G1 X133.455 Y102.123 E.00882 +G1 X133.67 Y102.336 E.01024 +G1 X133.979 Y102.42 E.01084 +G1 X134.368 Y102.42 E.01317 +G1 X134.368 Y107.664 E.1775 +G1 X133.979 Y107.664 E.01317 +G1 X133.67 Y107.748 E.01084 +G1 X133.538 Y107.852 E.00569 +G1 X133.44 Y107.987 E.00565 +G1 X133.368 Y108.275 E.01005 +G1 X133.368 Y110.288 E.06814 +G1 X133.413 Y110.518 E.00793 +G1 X133.6 Y110.768 E.01057 +G1 X133.979 Y110.899 E.01357 +G1 X134.368 Y110.899 E.01317 +G1 X134.368 Y114.368 E.11742 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y107.1 E.24601 +G1 X115.632 Y106.632 E.01584 +G1 X116.021 Y106.632 E.01317 +G1 X116.33 Y106.548 E.01084 +G1 X116.462 Y106.444 E.00569 +G1 X116.569 Y106.291 E.00632 +G1 X116.632 Y106.021 E.00938 +G1 X116.632 Y103.979 E.06912 +G1 X116.572 Y103.714 E.0092 +G1 X116.387 Y103.489 E.00986 +G1 X116.021 Y103.368 E.01305 +G1 X115.632 Y103.368 E.01317 +G1 X115.632 Y102.9 E.01584 +G1 X115.632 Y95.632 E.24601 +G1 X119.715 Y95.632 E.1382 +G1 X119.715 Y96.021 E.01317 +G1 X119.781 Y96.298 E.00964 +G1 X119.874 Y96.432 E.00552 +G1 X120.087 Y96.583 E.00884 +G1 X120.326 Y96.632 E.00826 +G1 X122.412 Y96.632 E.07061 +G1 X122.693 Y96.564 E.00979 +G1 X122.918 Y96.364 E.01019 +G1 X123.023 Y96.021 E.01214 +G1 X123.023 Y95.632 E.01317 +G1 X127.343 Y95.632 E.14623 +G1 X127.343 Y96.021 E.01317 +G1 X127.448 Y96.364 E.01214 +G1 X127.673 Y96.564 E.01019 +G1 X127.953 Y96.632 E.00975 +G1 X129.881 Y96.632 E.06526 +G1 X130.139 Y96.575 E.00894 +G1 X130.351 Y96.41 E.00909 +G1 X130.491 Y96.021 E.01399 +G1 X130.491 Y95.632 E.01317 +G1 X134.308 Y95.632 E.1292 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2312 +G1 X134.775 Y99.485 E.1442 +G1 X133.979 Y99.485 E.02694 +G1 X133.832 Y99.546 E.00539 +G1 X133.775 Y99.688 E.00518 +G1 X133.775 Y101.809 E.07179 +G1 F2171.011 +M73 Q72 S6 +G1 X133.832 Y101.95 E.00515 +G1 F1818.514 +G1 X133.979 Y102.013 E.00541 +G1 F1803.073 +G1 X134.775 Y102.013 E.02694 +G1 X134.775 Y102.818 E.02725 +G1 F2312 +G1 X134.775 Y107.264 E.15049 +G1 F1796.558 +M73 P72 R6 +G1 X134.775 Y108.071 E.02732 +G1 X133.979 Y108.071 E.02694 +G1 F1812.164 +G1 X133.832 Y108.134 E.00541 +G1 F2170.224 +G1 X133.775 Y108.275 E.00515 +G1 F2312 +G1 X133.775 Y110.288 E.06814 +G1 X133.814 Y110.408 E.00427 +G1 X133.979 Y110.492 E.00627 +G1 X134.775 Y110.492 E.02694 +G1 X134.775 Y114.775 E.14497 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y107.1 E.25979 +G1 F1360 +G1 X115.225 Y106.225 E.02962 +G1 X116.021 Y106.225 E.02694 +G1 F1410.864 +G1 X116.168 Y106.162 E.00541 +G1 F2158.495 +G1 X116.225 Y106.021 E.00515 +G1 F2312 +G1 X116.225 Y103.979 E.06912 +G1 F2258.425 +G1 X116.192 Y103.867 E.00395 +G1 F1410.859 +G1 X116.021 Y103.775 E.00657 +G1 F1359.994 +G1 X115.225 Y103.775 E.02694 +G1 X115.225 Y102.9 E.02962 +G1 F2312 +G1 X115.225 Y95.225 E.25979 +G1 X120.122 Y95.225 E.16576 +G1 X120.122 Y96.021 E.02694 +G1 X120.175 Y96.158 E.00497 +G1 X120.326 Y96.225 E.00559 +G1 X122.412 Y96.225 E.07061 +G1 F2246.184 +G1 X122.528 Y96.189 E.00411 +G1 F1751.83 +G1 X122.616 Y96.021 E.00642 +G1 F1734.465 +G1 X122.616 Y95.225 E.02694 +G1 X123.432 Y95.225 E.02762 +G1 F2312 +G1 X126.923 Y95.225 E.11817 +G1 F1669.3 +G1 X127.75 Y95.225 E.02799 +G1 X127.75 Y96.021 E.02694 +G1 F1688.984 +G1 X127.823 Y96.178 E.00586 +G1 F2199.805 +G1 X127.953 Y96.225 E.00468 +G1 F2312 +G1 X129.881 Y96.225 E.06526 +G1 X130.004 Y96.184 E.00439 +G1 X130.084 Y96.021 E.00615 +G1 X130.084 Y95.225 E.02694 +G1 X134.715 Y95.225 E.15675 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.244 E-.62753 +;WIPE_END +G1 X133.143 Y107.776 Z15.769 F12000 +G1 Z15.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2312 +G3 X134.002 Y107.298 I.86 J.534 E.03474 +G1 X134.002 Y106.326 E.0329 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +M73 Q72 S5 +G1 X126.326 Y114.002 E.44653 +G1 X123.674 Y114.002 E.08977 +M73 P72 R5 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y102.786 E.03006 +G3 X133.002 Y101.823 I-.011 J-.99 E.05207 +G1 X133.002 Y99.675 E.07271 +G1 X133.139 Y99.186 E.01719 +G3 X133.951 Y98.711 I.904 J.613 E.03292 +G2 X134.002 Y98.65 I.018 J-.037 E.00368 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X120.65 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y114.002 E-.41563 +G1 X117.887 Y113.239 E-.22437 +;WIPE_END +G1 X129.35 Y114.002 Z15.801 F12000 +G1 Z15.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2312 +G1 X131.35 Y114.002 E.0677 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.349 Y95.998 E.02366 +G2 X120.076 Y96.964 I.989 J.012 E.04402 +M204 P1000 +;LAYER_CHANGE +;Z:15.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;15.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.349 Y95.998 I.262 J-.954 E-.27029 +G1 X118.65 Y95.998 E-.14526 +G1 X117.886 Y96.762 E-.22445 +;WIPE_END +G1 X117.886 Y96.762 Z15.6 F12000 +G1 X134.368 Y95.632 Z15.888 +;AFTER_LAYER_CHANGE +;15.8 +G1 X134.368 Y95.632 +G1 Z15.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2259 +G1 X134.368 Y98.944 E.11211 +G1 X133.979 Y98.944 E.01317 +G1 X133.61 Y99.067 E.01317 +G1 X133.547 Y99.122 E.00283 +G1 X133.402 Y99.355 E.00929 +G1 X133.368 Y99.554 E.00683 +G1 X133.368 Y101.701 E.07267 +G1 X133.445 Y101.976 E.00967 +G1 X133.659 Y102.199 E.01046 +G1 X133.979 Y102.289 E.01125 +G1 X134.368 Y102.289 E.01317 +G1 X134.368 Y107.796 E.18641 +G1 X133.979 Y107.796 E.01317 +G1 X133.659 Y107.886 E.01125 +G1 X133.528 Y107.994 E.00575 +G1 X133.43 Y108.139 E.00592 +G1 X133.368 Y108.406 E.00928 +G1 X133.368 Y110.427 E.06841 +G1 X133.414 Y110.66 E.00804 +G1 X133.609 Y110.913 E.01081 +G1 X133.979 Y111.037 E.01321 +G1 X134.368 Y111.037 E.01317 +G1 X134.368 Y114.368 E.11275 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y106.9 E.25278 +G1 X115.632 Y106.432 E.01584 +G1 X116.021 Y106.432 E.01317 +G1 X116.334 Y106.346 E.01099 +G1 X116.466 Y106.24 E.00573 +G1 X116.571 Y106.088 E.00625 +G1 X116.632 Y105.821 E.00927 +G1 X116.632 Y104.711 E.03757 +G1 X116.622 Y104.067 E.0218 +G1 X116.516 Y103.82 E.0091 +G1 X116.313 Y103.642 E.00914 +G1 X116.021 Y103.568 E.0102 +G1 X115.632 Y103.568 E.01317 +G1 X115.632 Y103.1 E.01584 +G1 X115.632 Y95.632 E.25278 +G1 X119.573 Y95.632 E.1334 +G1 X119.573 Y96.021 E.01317 +G1 X119.636 Y96.291 E.00938 +G1 X119.725 Y96.425 E.00545 +G1 X119.962 Y96.591 E.00979 +G1 X120.178 Y96.632 E.00744 +G1 X122.271 Y96.632 E.07085 +G1 X122.53 Y96.574 E.00898 +G1 X122.766 Y96.38 E.01034 +G1 X122.882 Y96.021 E.01277 +G1 X122.882 Y95.632 E.01317 +G1 X127.495 Y95.632 E.15614 +G1 X127.495 Y96.021 E.01317 +G1 X127.611 Y96.38 E.01277 +G1 X127.815 Y96.559 E.00919 +G1 X128.105 Y96.632 E.01012 +G1 X130.03 Y96.632 E.06516 +G1 X130.292 Y96.573 E.00909 +G1 X130.507 Y96.402 E.0093 +G1 X130.64 Y96.021 E.01366 +G1 X130.64 Y95.632 E.01317 +G1 X134.308 Y95.632 E.12416 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2259 +G1 X134.775 Y99.351 E.13966 +G1 X133.979 Y99.351 E.02694 +G1 X133.835 Y99.41 E.00527 +G1 X133.775 Y99.554 E.00528 +G1 X133.775 Y101.678 E.07189 +G1 F2182.233 +G1 X133.828 Y101.816 E.005 +G1 F1818.514 +G1 X133.979 Y101.882 E.00558 +G1 F1803.073 +G1 X134.775 Y101.882 E.02694 +G1 X134.775 Y102.688 E.02728 +G1 F2259 +G1 X134.775 Y107.396 E.15936 +G1 F1796.564 +G1 X134.775 Y108.203 E.02732 +G1 X133.979 Y108.203 E.02694 +G1 F1812.17 +G1 X133.828 Y108.269 E.00558 +G1 F2181.546 +G1 X133.775 Y108.406 E.00497 +G1 F2259 +G1 X133.775 Y110.427 E.06841 +G1 X133.815 Y110.548 E.00431 +G1 X133.979 Y110.63 E.00621 +G1 X134.775 Y110.63 E.02694 +G1 X134.775 Y114.775 E.1403 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.9 E.26656 +G1 F1359.994 +G1 X115.225 Y106.025 E.02962 +G1 X116.021 Y106.025 E.02694 +G1 F1410.859 +G1 X116.17 Y105.961 E.00549 +G1 F2163.649 +G1 X116.225 Y105.821 E.00509 +G1 F2259 +G1 X116.225 Y104.179 E.05558 +G1 F2234.71 +G1 X116.186 Y104.059 E.00427 +G1 F1410.864 +G1 X116.021 Y103.975 E.00627 +G1 F1360 +G1 X115.225 Y103.975 E.02694 +G1 X115.225 Y103.1 E.02962 +G1 F2259 +G1 X115.225 Y95.225 E.26656 +G1 X119.98 Y95.225 E.16095 +G1 X119.98 Y96.021 E.02694 +M73 Q73 S5 +G1 X120.031 Y96.156 E.00488 +G1 X120.182 Y96.225 E.00562 +G1 X122.271 Y96.225 E.07071 +G1 X122.38 Y96.193 E.00385 +G1 F1751.83 +G1 X122.474 Y96.021 E.00663 +G1 F1734.465 +G1 X122.474 Y95.225 E.02694 +G1 X123.291 Y95.225 E.02765 +G1 F2259 +M73 P73 R5 +G1 X127.075 Y95.225 E.12808 +G1 F1669.3 +G1 X127.902 Y95.225 E.02799 +G1 X127.902 Y96.021 E.02694 +G1 F1688.984 +G1 X127.98 Y96.182 E.00606 +G1 F2217.354 +G1 X128.105 Y96.225 E.00447 +G1 F2259 +G1 X130.03 Y96.225 E.06516 +G1 X130.155 Y96.182 E.00447 +G1 X130.233 Y96.021 E.00606 +G1 X130.233 Y95.225 E.02694 +G1 X134.715 Y95.225 E.15171 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X129.921 Y96.998 Z15.887 F12000 +G1 Z15.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2259 +G1 X130.451 Y96.904 E.01822 +G2 X131.007 Y95.998 I-.51 J-.936 E.03767 +G1 X131.35 Y95.998 E.01161 +G1 X133.935 Y98.583 E.12374 +G1 X134.002 Y98.577 E.00228 +G1 X134.002 Y95.998 E.0873 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X133.949 Y111.403 E.12441 +G1 X134.002 Y111.404 E.00179 +G1 X134.002 Y114.002 E.08794 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.206 Y95.998 E.01882 +G2 X120.073 Y96.99 I.984 J.015 E.04886 +M204 P1000 +;LAYER_CHANGE +;Z:16 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;16 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.206 Y95.998 I.116 J-.977 E-.3 +G1 X118.65 Y95.998 E-.11554 +G1 X117.886 Y96.762 E-.22446 +;WIPE_END +G1 X117.886 Y96.762 Z15.8 F12000 +G1 X134.368 Y95.632 Z16.088 +;AFTER_LAYER_CHANGE +;16 +G1 X134.368 Y95.632 +G1 Z16 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2257 +G1 X134.368 Y98.81 E.10757 +G1 X133.979 Y98.81 E.01317 +G1 X133.619 Y98.927 E.01281 +G1 X133.555 Y98.98 E.00281 +G1 X133.403 Y99.216 E.0095 +G1 X133.368 Y99.421 E.00704 +G1 X133.368 Y101.547 E.07196 +G1 X133.43 Y101.816 E.00934 +G1 X133.648 Y102.061 E.0111 +G1 X133.979 Y102.158 E.01168 +G1 X134.368 Y102.158 E.01317 +G1 X134.368 Y107.928 E.19531 +G1 X133.979 Y107.928 E.01317 +G1 X133.648 Y108.025 E.01168 +G1 X133.443 Y108.244 E.01015 +G1 X133.368 Y108.538 E.01027 +G1 X133.368 Y110.565 E.06861 +G1 X133.416 Y110.803 E.00822 +G1 X133.618 Y111.058 E.01101 +G1 X133.979 Y111.176 E.01286 +G1 X134.368 Y111.176 E.01317 +G1 X134.368 Y114.368 E.10805 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y106.7 E.25955 +G1 X115.632 Y106.232 E.01584 +G1 X116.021 Y106.232 E.01317 +G1 X116.338 Y106.144 E.01114 +G1 X116.469 Y106.036 E.00575 +G1 X116.571 Y105.888 E.00608 +G1 X116.632 Y105.621 E.00927 +G1 X116.632 Y104.812 E.02738 +G1 X116.627 Y104.299 E.01737 +G1 X116.516 Y104.021 E.01013 +G1 X116.317 Y103.844 E.00901 +G1 X116.021 Y103.768 E.01034 +G1 X115.632 Y103.768 E.01317 +G1 X115.632 Y103.3 E.01584 +G1 X115.632 Y95.632 E.25955 +G1 X119.43 Y95.632 E.12856 +G1 X119.43 Y96.021 E.01317 +G1 X119.577 Y96.418 E.01433 +G1 X119.82 Y96.591 E.0101 +G1 X120.041 Y96.632 E.00761 +G1 X121.21 Y96.632 E.03957 +G1 X122.188 Y96.629 E.0331 +G1 X122.451 Y96.54 E.0094 +G1 X122.665 Y96.315 E.01051 +G1 X122.74 Y96.021 E.01027 +G1 X122.74 Y95.632 E.01317 +G1 X127.646 Y95.632 E.16606 +G1 X127.646 Y96.021 E.01317 +G1 X127.721 Y96.315 E.01027 +G1 X127.82 Y96.448 E.00561 +G1 X127.967 Y96.559 E.00623 +G1 X128.257 Y96.632 E.01012 +G1 X130.179 Y96.632 E.06506 +G1 X130.445 Y96.571 E.00924 +G1 X130.663 Y96.393 E.00953 +G1 X130.789 Y96.021 E.01329 +G1 X130.789 Y95.632 E.01317 +G1 X134.308 Y95.632 E.11911 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2257 +G1 X134.775 Y99.217 E.13512 +G1 X133.979 Y99.217 E.02694 +G1 X133.837 Y99.274 E.00518 +G1 X133.775 Y99.421 E.0054 +G1 X133.775 Y101.547 E.07196 +G1 F2192.804 +G1 X133.825 Y101.681 E.00484 +G1 F1818.514 +G1 X133.979 Y101.751 E.00573 +G1 F1803.073 +G1 X134.775 Y101.751 E.02694 +G1 X134.775 Y102.557 E.02728 +G1 F2257 +G1 X134.775 Y107.528 E.16826 +G1 F1796.564 +G1 X134.775 Y108.335 E.02732 +G1 X133.979 Y108.335 E.02694 +G1 F1812.17 +G1 X133.825 Y108.404 E.00571 +G1 F2192.211 +G1 X133.775 Y108.538 E.00484 +G1 F2257 +G1 X133.775 Y110.565 E.06861 +G1 X133.817 Y110.689 E.00443 +G1 X133.979 Y110.769 E.00612 +G1 X134.775 Y110.769 E.02694 +G1 X134.775 Y114.775 E.1356 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.7 E.27333 +G1 F1359.994 +G1 X115.225 Y105.825 E.02962 +G1 X116.021 Y105.825 E.02694 +G1 F1410.859 +G1 X116.171 Y105.76 E.00553 +G1 F2168.598 +G1 X116.225 Y105.621 E.00505 +G1 F2257 +G1 X116.225 Y104.379 E.04204 +G1 F2235.171 +G1 X116.186 Y104.259 E.00427 +G1 F1410.859 +G1 X116.021 Y104.175 E.00627 +G1 F1359.994 +G1 X115.225 Y104.175 E.02694 +G1 X115.225 Y103.3 E.02962 +G1 F2257 +G1 X115.225 Y95.225 E.27333 +G1 X119.837 Y95.225 E.15611 +G1 X119.837 Y96.021 E.02694 +G1 X119.886 Y96.154 E.0048 +G1 X120.041 Y96.225 E.00577 +G1 X122.129 Y96.225 E.07068 +G1 F2232.773 +G1 X122.25 Y96.186 E.0043 +G1 F1751.83 +G1 X122.333 Y96.021 E.00625 +G1 F1734.465 +G1 X122.333 Y95.225 E.02694 +G1 X123.149 Y95.225 E.02762 +G1 F2257 +G1 X127.227 Y95.225 E.13804 +G1 F1669.3 +G1 X128.053 Y95.225 E.02796 +G1 X128.053 Y96.021 E.02694 +G1 F1688.984 +G1 X128.111 Y96.164 E.00522 +G1 F2141.657 +G1 X128.257 Y96.225 E.00536 +G1 F2257 +G1 X130.179 Y96.225 E.06506 +G1 X130.306 Y96.18 E.00456 +G1 X130.382 Y96.021 E.00597 +G1 X130.382 Y95.225 E.02694 +G1 X134.715 Y95.225 E.14667 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X129.922 Y96.998 Z16.087 F12000 +G1 Z16 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2257 +G2 X131.156 Y95.998 I.194 J-1.021 E.06118 +G1 X131.35 Y95.998 E.00657 +G1 X133.834 Y98.482 E.11891 +G1 X134.002 Y98.444 E.00583 +G1 X134.002 Y95.998 E.08279 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +M73 P74 R5 +M73 Q74 S5 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.998 Y105.326 E.44653 +G1 X116.998 Y104.674 E.02207 +G1 X126.326 Y114.002 E.44653 +G1 X131.35 Y114.002 E.17006 +G1 X133.845 Y111.507 E.11943 +G1 X134.002 Y111.542 E.00544 +G1 X134.002 Y114.002 E.08327 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X119.064 Y95.998 E.01401 +G1 X119.283 Y96.644 E.02309 +G2 X120.08 Y96.998 I.777 J-.674 E.03048 +M204 P1000 +;LAYER_CHANGE +;Z:16.2 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;16.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.283 Y96.644 I-.02 J-1.028 E-.18715 +G1 X119.064 Y95.998 E-.14175 +G1 X118.65 Y95.998 E-.08603 +G1 X117.884 Y96.764 E-.22507 +;WIPE_END +G1 X117.884 Y96.764 Z16 F12000 +G1 X134.368 Y95.632 Z16.288 +;AFTER_LAYER_CHANGE +;16.2 +G1 X134.368 Y95.632 +G1 Z16.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2255 +G1 X134.368 Y98.676 E.10304 +G1 X133.979 Y98.676 E.01317 +G1 X133.564 Y98.839 E.01509 +G1 X133.405 Y99.078 E.00972 +G1 X133.368 Y99.287 E.00718 +G1 X133.368 Y101.417 E.0721 +G1 X133.431 Y101.687 E.00938 +G1 X133.638 Y101.924 E.01065 +G1 X133.979 Y102.027 E.01206 +G1 X134.368 Y102.027 E.01317 +G1 X134.368 Y108.059 E.20418 +G1 X133.979 Y108.059 E.01317 +G1 X133.638 Y108.163 E.01207 +G1 X133.441 Y108.38 E.00992 +G1 X133.368 Y108.67 E.01012 +G1 X133.368 Y110.704 E.06885 +G1 X133.418 Y110.945 E.00833 +G1 X133.628 Y111.203 E.01126 +G1 X133.979 Y111.314 E.01246 +G1 X134.368 Y111.314 E.01317 +G1 X134.368 Y114.368 E.10337 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y106.5 E.26632 +G1 X115.632 Y106.032 E.01584 +G1 X116.021 Y106.032 E.01317 +G1 X116.342 Y105.941 E.01129 +G1 X116.473 Y105.833 E.00575 +G1 X116.616 Y105.559 E.01046 +G1 X116.632 Y105.043 E.01747 +G1 X116.626 Y104.49 E.01872 +G1 X116.519 Y104.225 E.00967 +G1 X116.321 Y104.047 E.00901 +G1 X116.021 Y103.968 E.0105 +G1 X115.632 Y103.968 E.01317 +G1 X115.632 Y103.5 E.01584 +G1 X115.632 Y95.632 E.26632 +G1 X119.288 Y95.632 E.12375 +G1 X119.288 Y96.021 E.01317 +G1 X119.428 Y96.41 E.01399 +G1 X119.674 Y96.589 E.0103 +G1 X119.899 Y96.632 E.00775 +G1 X121.215 Y96.632 E.04454 +G1 X122.036 Y96.63 E.02779 +G1 X122.304 Y96.544 E.00953 +G1 X122.516 Y96.328 E.01024 +G1 X122.599 Y96.021 E.01076 +G1 X122.599 Y95.632 E.01317 +G1 X127.798 Y95.632 E.17598 +G1 X127.798 Y96.021 E.01317 +G1 X127.88 Y96.328 E.01076 +G1 X127.984 Y96.461 E.00571 +G1 X128.119 Y96.559 E.00565 +G1 X128.408 Y96.632 E.01009 +G1 X130.328 Y96.632 E.06499 +G1 X130.599 Y96.568 E.00943 +G1 X130.817 Y96.387 E.00959 +G1 X130.938 Y96.021 E.01305 +G1 X130.938 Y95.632 E.01317 +G1 X134.308 Y95.632 E.11407 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2255 +G1 X134.775 Y99.083 E.13059 +G1 X133.979 Y99.083 E.02694 +G1 X133.84 Y99.138 E.00506 +G1 X133.775 Y99.287 E.0055 +G1 X133.775 Y101.417 E.0721 +G1 F2202.776 +G1 X133.823 Y101.547 E.00469 +G1 F1818.514 +G1 X133.979 Y101.62 E.00583 +G1 F1803.073 +G1 X134.775 Y101.62 E.02694 +G1 X134.775 Y102.426 E.02728 +G1 F2255 +G1 X134.775 Y107.66 E.17716 +G1 F1796.551 +G1 X134.775 Y108.466 E.02728 +G1 X133.979 Y108.466 E.02694 +G1 F1812.158 +G1 X133.823 Y108.539 E.00583 +G1 F2202.269 +G1 X133.775 Y108.67 E.00472 +G1 F2255 +G1 X133.775 Y110.704 E.06885 +G1 X133.818 Y110.829 E.00447 +G1 X133.979 Y110.907 E.00606 +G1 X134.775 Y110.907 E.02694 +G1 X134.775 Y114.775 E.13093 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.5 E.2801 +G1 F1359.987 +G1 X115.225 Y105.625 E.02962 +G1 X116.021 Y105.625 E.02694 +G1 F1410.854 +G1 X116.172 Y105.559 E.00558 +G1 F2173.347 +G1 X116.225 Y105.421 E.005 +G1 F2255 +G1 X116.225 Y104.618 E.02718 +G1 F2239.775 +G1 X116.187 Y104.461 E.00547 +G1 F1410.854 +G1 X116.021 Y104.375 E.00633 +G1 F1359.987 +G1 X115.225 Y104.375 E.02694 +G1 X115.225 Y103.5 E.02962 +G1 F2255 +G1 X115.225 Y95.225 E.2801 +G1 X119.695 Y95.225 E.1513 +G1 X119.695 Y96.021 E.02694 +G1 X119.742 Y96.151 E.00468 +G1 X119.899 Y96.225 E.00587 +G1 X121.988 Y96.225 E.07071 +G1 F2161.811 +G1 X122.129 Y96.168 E.00515 +G1 F1751.823 +G1 X122.191 Y96.021 E.0054 +G1 F1734.458 +G1 X122.191 Y95.225 E.02694 +G1 X123.008 Y95.225 E.02765 +G1 F2255 +G1 X127.378 Y95.225 E.14792 +G1 F1669.293 +G1 X128.205 Y95.225 E.02799 +G1 X128.205 Y96.021 E.02694 +G1 F1688.978 +G1 X128.267 Y96.168 E.0054 +G1 F2157.604 +G1 X128.408 Y96.225 E.00515 +G1 F2255 +G1 X130.328 Y96.225 E.06499 +G1 X130.457 Y96.178 E.00465 +G1 X130.531 Y96.021 E.00587 +G1 X130.531 Y95.225 E.02694 +G1 X134.715 Y95.225 E.14162 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X129.922 Y96.998 Z16.287 F12000 +G1 Z16.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2255 +G1 X130.339 Y96.998 E.01411 +G2 X131.305 Y95.998 I-.028 J-.993 E.05212 +G1 X133.732 Y98.38 E.11511 +G1 X134.002 Y98.31 E.00944 +G1 X134.002 Y95.998 E.07826 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.994 Y105.33 E.44672 +G1 X116.997 Y104.673 E.02224 +G1 X126.326 Y114.002 E.44657 +G1 X131.35 Y114.002 E.17006 +G1 X133.739 Y111.613 E.11436 +G1 X134.002 Y111.681 E.00919 +G1 X134.002 Y114.002 E.07856 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +M73 P75 R5 +M73 Q75 S5 +G1 X118.65 Y95.998 E.12695 +G1 X118.922 Y95.998 E.00921 +G1 X119.131 Y96.633 E.02263 +G2 X120.079 Y96.988 I.794 J-.676 E.03578 +M204 P1000 +;LAYER_CHANGE +;Z:16.4 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;16.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.131 Y96.633 I-.154 J-1.031 E-.21965 +G1 X118.922 Y95.998 E-.13892 +G1 X118.65 Y95.998 E-.05652 +G1 X117.885 Y96.763 E-.22491 +;WIPE_END +G1 X117.885 Y96.763 Z16.2 F12000 +G1 X134.368 Y95.632 Z16.488 +;AFTER_LAYER_CHANGE +;16.4 +G1 X134.368 Y95.632 +G1 Z16.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2332 +G1 X134.368 Y98.543 E.09853 +G1 X133.979 Y98.543 E.01317 +G1 X133.573 Y98.697 E.0147 +G1 X133.407 Y98.939 E.00993 +G1 X133.368 Y99.153 E.00736 +G1 X133.368 Y101.286 E.0722 +G1 X133.425 Y101.543 E.00891 +G1 X133.629 Y101.786 E.01074 +G1 X133.979 Y101.897 E.01243 +G1 X134.368 Y101.897 E.01317 +G1 X134.368 Y108.191 E.21304 +G1 X133.979 Y108.191 E.01317 +G1 X133.629 Y108.301 E.01242 +G1 X133.439 Y108.515 E.00969 +G1 X133.368 Y108.802 E.01001 +G1 X133.368 Y110.842 E.06905 +G1 X133.42 Y111.088 E.00851 +G1 X133.638 Y111.349 E.01151 +G1 X133.979 Y111.453 E.01207 +G1 X134.368 Y111.453 E.01317 +G1 X134.368 Y114.368 E.09867 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y106.3 E.27309 +G1 X115.632 Y105.832 E.01584 +G1 X116.021 Y105.832 E.01317 +G1 X116.345 Y105.739 E.01141 +G1 X116.476 Y105.629 E.00579 +G1 X116.571 Y105.487 E.00578 +G1 X116.632 Y105.221 E.00924 +G1 X116.628 Y104.707 E.0174 +G1 X116.522 Y104.43 E.01004 +G1 X116.326 Y104.249 E.00903 +G1 X116.021 Y104.168 E.01068 +G1 X115.632 Y104.168 E.01317 +G1 X115.632 Y103.7 E.01584 +G1 X115.632 Y95.632 E.27309 +G1 X119.145 Y95.632 E.11891 +G1 X119.145 Y96.021 E.01317 +G1 X119.279 Y96.402 E.01367 +G1 X119.528 Y96.588 E.01052 +G1 X119.756 Y96.632 E.00786 +G1 X121.22 Y96.632 E.04955 +G1 X121.897 Y96.63 E.02292 +G1 X122.145 Y96.554 E.00878 +G1 X122.367 Y96.341 E.01041 +G1 X122.457 Y96.021 E.01125 +G1 X122.457 Y95.632 E.01317 +G1 X127.95 Y95.632 E.18593 +G1 X127.95 Y96.021 E.01317 +G1 X128.04 Y96.341 E.01125 +G1 X128.148 Y96.472 E.00575 +G1 X128.291 Y96.57 E.00587 +G1 X128.56 Y96.632 E.00934 +G1 X130.477 Y96.632 E.06489 +G1 X130.753 Y96.566 E.00961 +G1 X130.966 Y96.387 E.00942 +G1 X131.087 Y96.021 E.01305 +G1 X131.087 Y95.632 E.01317 +G1 X134.308 Y95.632 E.10903 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2332 +G1 X134.775 Y98.95 E.12609 +G1 X133.979 Y98.95 E.02694 +G1 X133.843 Y99.001 E.00492 +G1 X133.775 Y99.153 E.00564 +G1 X133.775 Y101.286 E.0722 +G1 F2212.188 +G1 X133.82 Y101.414 E.00459 +G1 F1818.514 +G1 X133.979 Y101.489 E.00595 +G1 F1803.073 +G1 X134.775 Y101.489 E.02694 +G1 X134.775 Y102.295 E.02728 +G1 F2332 +G1 X134.775 Y107.791 E.18603 +G1 F1796.57 +G1 X134.775 Y108.598 E.02732 +G1 X133.979 Y108.598 E.02694 +G1 F1812.176 +G1 X133.82 Y108.674 E.00597 +G1 F2211.762 +G1 X133.775 Y108.802 E.00459 +G1 F2332 +G1 X133.775 Y110.842 E.06905 +G1 X133.82 Y110.97 E.00459 +G1 X133.979 Y111.046 E.00597 +G1 X134.775 Y111.046 E.02694 +G1 X134.775 Y114.775 E.12622 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.3 E.28687 +G1 F1360 +G1 X115.225 Y105.425 E.02962 +G1 X116.021 Y105.425 E.02694 +G1 F1410.864 +G1 X116.129 Y105.394 E.0038 +G1 F1992.38 +G1 X116.213 Y105.289 E.00455 +G1 F2332 +G1 X116.225 Y104.779 E.01727 +G1 F2153.115 +G1 X116.167 Y104.636 E.00522 +G1 F1410.864 +G1 X116.021 Y104.575 E.00536 +G1 F1360 +G1 X115.225 Y104.575 E.02694 +G1 X115.225 Y103.7 E.02962 +G1 F2332 +G1 X115.225 Y95.225 E.28687 +G1 X119.553 Y95.225 E.1465 +G1 X119.553 Y96.021 E.02694 +G1 X119.637 Y96.187 E.0063 +G1 X119.756 Y96.225 E.00423 +G1 X121.846 Y96.225 E.07074 +G1 F2175.598 +G1 X121.984 Y96.172 E.005 +G1 F1751.836 +G1 X122.05 Y96.021 E.00558 +G1 F1734.471 +G1 X122.05 Y95.225 E.02694 +G1 X122.866 Y95.225 E.02762 +G1 F2332 +G1 X127.53 Y95.225 E.15787 +G1 F1669.306 +G1 X128.357 Y95.225 E.02799 +G1 X128.357 Y96.021 E.02694 +G1 F1688.99 +G1 X128.423 Y96.172 E.00558 +G1 F2172.467 +G1 X128.56 Y96.225 E.00497 +G1 F2332 +G1 X130.477 Y96.225 E.06489 +G1 X130.609 Y96.177 E.00475 +G1 X130.68 Y96.021 E.0058 +G1 X130.68 Y95.225 E.02694 +G1 X134.715 Y95.225 E.13658 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.244 E-.62753 +;WIPE_END +G1 X129.934 Y96.998 Z16.487 F12000 +G1 Z16.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2332 +G1 X130.488 Y96.998 E.01875 +G2 X131.44 Y96.088 I-.018 J-.972 E.04897 +G1 X133.628 Y98.276 E.10474 +G1 X134.002 Y98.176 E.0131 +G1 X134.002 Y95.998 E.07372 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X116.989 Y105.335 E.44696 +G1 X116.99 Y104.665 E.02268 +G1 X126.326 Y114.002 E.44693 +G1 X131.35 Y114.002 E.17006 +G1 X133.631 Y111.721 E.10919 +G1 X134.002 Y111.819 E.01299 +G1 X134.002 Y114.002 E.07389 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X118.779 Y95.998 E.00437 +G2 X120.079 Y96.988 I1.048 J-.027 E.06342 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X118.779 Y95.998 I-.252 J-1.017 E-.38938 +G1 X118.65 Y95.998 E-.02681 +G1 X117.889 Y96.759 E-.22381 +;WIPE_END +G1 X115.998 Y101.674 Z16.492 F12000 +G1 Z16.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2332 +G1 X115.998 Y103.674 E.0677 +G1 X116.156 Y103.832 E.00756 +G3 X116.942 Y104.469 I-.085 J.908 E.0363 +M204 P1000 +G1 X116.942 Y104.469 F12000 +G1 X116.943 Y105.533 +M204 P2000 +G1 F2332 +G3 X116.532 Y106.058 I-.971 J-.336 E.02298 +G2 X115.998 Y106.326 I.052 J.77 E.02077 +G1 X115.998 Y108.326 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:16.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;16.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y106.326 E-.41563 +G3 X116.532 Y106.058 I.586 J.502 E-.1275 +G2 X116.853 Y105.725 I-.56 J-.861 E-.09687 +;WIPE_END +G1 X116.853 Y105.725 Z16.4 F12000 +G1 X134.368 Y95.632 Z16.753 +;AFTER_LAYER_CHANGE +;16.6 +G1 X134.368 Y95.632 +G1 Z16.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2351 +G1 X134.368 Y98.409 E.094 +G1 X133.979 Y98.409 E.01317 +G1 X133.583 Y98.555 E.01429 +G1 X133.409 Y98.8 E.01017 +G1 X133.368 Y99.019 E.00754 +G1 X133.368 Y101.155 E.0723 +G1 X133.373 Y101.231 E.00258 +G1 X133.479 Y101.506 E.00998 +G1 X133.676 Y101.685 E.00901 +G1 X133.979 Y101.766 E.01062 +G1 X134.368 Y101.766 E.01317 +G1 X134.368 Y108.323 E.22195 +G1 X133.979 Y108.323 E.01317 +G1 X133.676 Y108.403 E.01061 +G1 X133.543 Y108.506 E.00569 +G1 X133.444 Y108.639 E.00561 +G1 X133.368 Y108.934 E.01031 +G1 X133.368 Y110.981 E.06929 +G1 X133.379 Y111.098 E.00398 +G1 X133.509 Y111.371 E.01023 +G1 X133.649 Y111.495 E.00633 +G1 X133.979 Y111.591 E.01163 +G1 X134.368 Y111.591 E.01317 +G1 X134.368 Y114.368 E.094 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y106.1 E.27986 +M73 Q76 S5 +G1 F1200 +M73 P76 R5 +G1 X115.632 Y105.2 E.03046 +G1 F900 +G1 X115.632 Y105 E.00677 +G1 X115.632 Y104.8 E.00677 +G1 F1200 +G1 X115.632 Y103.9 E.03046 +G1 F2351 +G1 X115.632 Y95.632 E.27986 +G1 X119.003 Y95.632 E.1141 +G1 X119.003 Y96.021 E.01317 +G1 X119.13 Y96.394 E.01334 +G1 X119.381 Y96.586 E.0107 +G1 X119.614 Y96.632 E.00804 +G1 X121.705 Y96.632 E.07078 +G1 X121.997 Y96.558 E.0102 +G1 X122.218 Y96.352 E.01023 +G1 X122.316 Y96.021 E.01168 +G1 X122.316 Y95.632 E.01317 +G1 X128.101 Y95.632 E.19582 +G1 X128.101 Y96.021 E.01317 +G1 X128.199 Y96.352 E.01168 +G1 X128.42 Y96.558 E.01023 +G1 X128.712 Y96.632 E.0102 +G1 X130.626 Y96.632 E.06479 +G1 X130.907 Y96.563 E.00979 +G1 X131.115 Y96.387 E.00922 +G1 X131.236 Y96.021 E.01305 +G1 X131.236 Y95.632 E.01317 +G1 X134.308 Y95.632 E.10398 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2351 +G1 X134.775 Y98.816 E.12155 +G1 X133.979 Y98.816 E.02694 +G1 X133.847 Y98.864 E.00475 +G1 X133.775 Y99.019 E.00578 +G1 X133.775 Y101.155 E.0723 +G1 F2243.174 +G1 X133.812 Y101.272 E.00415 +G1 F1818.51 +G1 X133.979 Y101.359 E.00637 +G1 F1803.066 +G1 X134.775 Y101.359 E.02694 +G1 X134.775 Y102.164 E.02725 +G1 F2351 +G1 X134.775 Y107.923 E.19494 +G1 F1796.558 +G1 X134.775 Y108.73 E.02732 +G1 X133.979 Y108.73 E.02694 +G1 F1812.165 +G1 X133.833 Y108.791 E.00536 +G1 F2164.281 +G1 X133.775 Y108.934 E.00522 +G1 F2351 +G1 X133.775 Y110.981 E.06929 +G1 X133.822 Y111.111 E.00468 +G1 X133.979 Y111.184 E.00586 +G1 X134.775 Y111.184 E.02694 +G1 X134.775 Y114.775 E.12155 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y106.1 E.29364 +G1 F1200 +G1 X115.225 Y105.2 E.03046 +G1 F900 +G1 X115.225 Y105 E.00677 +G1 X115.225 Y104.8 E.00677 +G1 F1200 +G1 X115.225 Y103.9 E.03046 +G1 F2351 +G1 X115.225 Y95.225 E.29364 +G1 X119.41 Y95.225 E.14166 +G1 X119.41 Y96.021 E.02694 +G1 X119.493 Y96.185 E.00622 +G1 X119.614 Y96.225 E.00431 +G1 X121.705 Y96.225 E.07078 +G1 F2188.473 +G1 X121.839 Y96.175 E.00484 +G1 F1751.816 +G1 X121.908 Y96.021 E.00571 +G1 F1734.452 +G1 X121.908 Y95.225 E.02694 +G1 X122.725 Y95.225 E.02765 +G1 F2351 +G1 X127.682 Y95.225 E.16779 +G1 F1669.3 +G1 X128.508 Y95.225 E.02796 +G1 X128.508 Y96.021 E.02694 +G1 F1688.983 +G1 X128.578 Y96.175 E.00573 +G1 F2186.334 +G1 X128.712 Y96.225 E.00484 +G1 F2351 +G1 X130.626 Y96.225 E.06479 +G1 X130.76 Y96.174 E.00485 +G1 X130.829 Y96.021 E.00568 +G1 X130.829 Y95.225 E.02694 +G1 X134.715 Y95.225 E.13154 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X129.965 Y96.998 Z16.687 F12000 +G1 Z16.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2351 +G1 X130.637 Y96.998 E.02275 +G2 X131.555 Y96.203 I.004 J-.922 E.04487 +G1 X133.521 Y98.169 E.09411 +G1 X134.002 Y98.042 E.01684 +G1 X134.002 Y95.998 E.06919 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X117.084 Y105.24 E.44241 +G1 X117.084 Y104.76 E.01625 +G1 X126.326 Y114.002 E.44241 +G1 X131.35 Y114.002 E.17006 +G1 X133.521 Y111.831 E.10392 +G1 X134.002 Y111.958 E.01684 +G1 X134.002 Y114.002 E.06919 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.637 Y96.011 E.12633 +G1 X118.827 Y96.607 E.02117 +G2 X120.078 Y96.998 I.966 J-.894 E.04644 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X118.827 Y96.607 I-.285 J-1.285 E-.28511 +G1 X118.637 Y96.011 E-.13 +G1 X117.872 Y96.776 E-.22489 +;WIPE_END +G1 X116.039 Y106.456 Z16.772 F12000 +G1 Z16.6 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.531415 +M106 S127.5 +G1 X116.039 Y103.733 E.1106 +G1 X116.344 Y103.812 E.0128 +G1 X116.528 Y103.966 E.00975 +G1 X116.528 Y106.281 E.09403 +M106 S124.95 +;LAYER_CHANGE +;Z:16.8 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;16.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.528 Y103.966 E-.48109 +G1 X116.344 Y103.812 E-.04986 +G1 X116.039 Y103.733 E-.06547 +G1 X116.039 Y103.943 E-.04358 +;WIPE_END +G1 X116.039 Y103.943 Z16.6 F12000 +G1 X134.368 Y95.632 Z16.951 +;AFTER_LAYER_CHANGE +;16.8 +G1 X134.368 Y95.632 +G1 Z16.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X134.368 Y99.041 E.11539 +G1 F900 +G1 X134.368 Y99.241 E.00677 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X134.368 Y100.934 E.05731 +M106 S124.95 +;TYPE:Perimeter +G1 X134.368 Y101.134 E.00677 +G1 F1200 +G1 X134.368 Y105 E.13086 +G1 X134.368 Y108.955 E.13387 +G1 F900 +G1 X134.368 Y109.155 E.00677 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X134.368 Y110.759 E.05429 +M106 S124.95 +;TYPE:Perimeter +G1 X134.368 Y110.959 E.00677 +G1 F1200 +G1 X134.368 Y114.368 E.11539 +G1 F4200 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X118.735 Y95.632 E.10503 +G1 F1200 +G1 X119.635 Y95.632 E.03046 +G1 F900 +G1 X119.835 Y95.632 E.00677 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X121.483 Y95.632 E.05578 +M106 S124.95 +;TYPE:Perimeter +G1 X122.583 Y95.632 E.03723 +G1 F4145.416 +M73 Q76 S4 +G1 X127.833 Y95.632 E.17771 +G1 F900 +G1 X128.933 Y95.632 E.03723 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X130.404 Y95.632 E.04979 +M106 S124.95 +;TYPE:Perimeter +G1 X130.604 Y95.632 E.00677 +G1 F1200 +G1 X134.308 Y95.632 E.12538 +G1 X134.775 Y95.225 F12000 +M73 P76 R4 +M204 P800 +;TYPE:External perimeter +G1 F1200 +G1 X134.775 Y99.041 E.12917 +G1 F900 +G1 X134.775 Y99.241 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X134.775 Y100.934 E.05731 +M106 S124.95 +M204 P800 +;TYPE:External perimeter +G1 X134.775 Y101.134 E.00677 +G1 F1200 +G1 X134.775 Y105 E.13086 +G1 X134.775 Y108.955 E.13387 +G1 F900 +M73 Q77 S4 +G1 X134.775 Y109.155 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X134.775 Y110.759 E.05429 +M106 S124.95 +M204 P800 +;TYPE:External perimeter +G1 X134.775 Y110.959 E.00677 +G1 F1200 +G1 X134.775 Y114.775 E.12917 +G1 F2393.158 +M73 P77 R4 +G1 X134.1 Y114.775 E.02285 +G1 F2399.997 +G1 X115.225 Y114.775 E.6389 +G1 X115.225 Y95.225 E.66174 +G1 X118.735 Y95.225 E.11881 +G1 F1200 +G1 X119.635 Y95.225 E.03046 +G1 F900 +G1 X119.835 Y95.225 E.00677 +M204 P1000 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X121.483 Y95.225 E.05578 +M106 S124.95 +M204 P800 +;TYPE:External perimeter +G1 X122.583 Y95.225 E.03723 +G1 F2387.822 +G1 X127.833 Y95.225 E.17771 +G1 F900 +G1 X128.933 Y95.225 E.03723 +M204 P1000 +;TYPE:Overhang perimeter +M106 S127.5 +G1 X130.404 Y95.225 E.04979 +M106 S124.95 +M204 P800 +;TYPE:External perimeter +G1 X130.604 Y95.225 E.00677 +G1 F1200 +G1 X131.504 Y95.225 E.03046 +G1 F2306.853 +G1 X134.1 Y95.225 E.08787 +G1 F2393.997 +G1 X134.715 Y95.225 E.02082 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.961 Y98.185 Z16.814 F12000 +G1 Z16.8 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.531415 +M106 S127.5 +G1 X133.961 Y102.06 E.1574 +G1 X133.607 Y101.952 E.01503 +G1 X133.472 Y101.831 E.00736 +G1 X133.472 Y98.353 E.14127 +M106 S124.95 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.472 Y101.432 E-.64 +;WIPE_END +G1 X130.026 Y97.084 Z16.897 F12000 +G1 Z16.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F5492 +G1 X130.628 Y97.084 E.02038 +G2 X131.623 Y96.271 I.019 J-.993 E.0473 +G1 X133.454 Y98.102 E.08765 +G1 X134.002 Y97.957 E.01919 +G1 X134.002 Y95.998 E.06631 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X131.35 Y114.002 E.17006 +G1 X133.454 Y111.898 E.10072 +G1 X134.002 Y112.043 E.01919 +G1 X134.002 Y114.002 E.06631 +G1 X115.998 Y95.998 E.86184 +G1 X118.551 Y95.998 E.08642 +G1 X118.561 Y96.087 E.00303 +G1 X115.998 Y98.65 E.12269 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X122.823 Y96.039 Z16.911 F12000 +G1 Z16.8 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.531415 +M106 S127.5 +G1 X118.973 Y96.039 E.15638 +G1 X119.106 Y96.428 E.0167 +G1 X119.205 Y96.528 E.00572 +G1 X122.647 Y96.528 E.13981 +M106 S124.95 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.567 Y96.528 E-.64 +;WIPE_END +G1 X127.574 Y96.039 Z16.94 F12000 +G1 Z16.8 F720 +G1 E.8 F1500 +M106 S127.5 +G1 X131.269 Y96.039 E.15009 +G1 X131.16 Y96.396 E.01516 +G1 X131.036 Y96.528 E.00736 +G1 X127.75 Y96.528 E.13347 +M106 S124.95 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.83 Y96.528 E-.64 +;WIPE_END +G1 X133.961 Y111.815 Z17.072 F12000 +G1 Z16.8 F720 +G1 E.8 F1500 +M106 S127.5 +G1 X133.961 Y108.027 E.15386 +G1 X133.607 Y108.135 E.01503 +G1 X133.472 Y108.256 E.00736 +G1 X133.472 Y111.639 E.13741 +M106 S124.95 +;LAYER_CHANGE +;Z:17 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;17 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.472 Y108.559 E-.64 +;WIPE_END +G1 X133.472 Y108.559 Z16.8 F12000 +G1 X134.368 Y95.632 Z17.026 +;AFTER_LAYER_CHANGE +;17 +G1 X134.368 Y95.632 +G1 Z17 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2452 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.026 Y97.084 Z17.085 F12000 +G1 Z17 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2452 +G1 X130.628 Y97.084 E.02038 +G2 X131.623 Y96.271 I.019 J-.993 E.0473 +G1 X133.454 Y98.102 E.08765 +G1 X134.002 Y97.957 E.01919 +G1 X134.002 Y95.998 E.06631 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +M73 Q78 S4 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +M73 P78 R4 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X131.35 Y114.002 E.17006 +G1 X133.454 Y111.898 E.10072 +G1 X134.002 Y112.043 E.01919 +G1 X134.002 Y114.002 E.06631 +G1 X115.998 Y95.998 E.86184 +G1 X118.551 Y95.998 E.08642 +G1 X118.561 Y96.087 E.00303 +G1 X115.998 Y98.65 E.12269 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X119.058 Y96.066 Z17.051 F12000 +G1 Z17 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.58567 +G1 F2452 +G1 X122.419 Y96.06 E.15168 +;WIDTH:0.573018 +G1 X122.302 Y96.388 E.01535 +G1 X122.064 Y96.556 E.01284 +;WIDTH:0.58567 +G1 X121.682 Y96.609 E.0174 +G1 X119.663 Y96.609 E.09112 +;WIDTH:0.585453 +G3 X119.19 Y96.409 I.007 J-.676 E.02377 +G1 X119.132 Y96.256 E.00738 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.19 Y96.409 E-.034 +G2 X119.663 Y96.609 I.48 J-.475 E-.10948 +G1 X121.682 Y96.609 E-.41957 +G1 X122.049 Y96.558 E-.07695 +;WIPE_END +G1 X127.997 Y96.066 Z17.104 F12000 +G1 Z17 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585672 +G1 F2452 +G1 X131.195 Y96.061 E.14433 +;WIDTH:0.574806 +G1 X131.094 Y96.366 E.01421 +G1 X130.849 Y96.563 E.0139 +;WIDTH:0.585672 +G1 X130.463 Y96.609 E.01754 +G1 X128.605 Y96.609 E.08385 +;WIDTH:0.58549 +G3 X128.088 Y96.35 I.06 J-.767 E.02675 +G1 X128.059 Y96.26 E.00427 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.088 Y96.35 E-.01965 +G2 X128.605 Y96.609 I.578 J-.507 E-.1232 +G1 X130.463 Y96.609 E-.38612 +G1 X130.849 Y96.563 E-.08078 +G1 X130.963 Y96.472 E-.03025 +;WIPE_END +G1 X133.391 Y101.156 Z17.092 F12000 +G1 Z17 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585515 +G1 F2452 +G1 X133.391 Y99.067 E.09425 +G3 X133.607 Y98.57 I.91 J.1 E.02482 +G1 X133.934 Y98.458 E.01559 +G1 X133.942 Y101.857 E.15335 +;WIDTH:0.570017 +G1 X133.611 Y101.745 E.01532 +G1 X133.446 Y101.522 E.01216 +;WIDTH:0.576988 +G1 X133.421 Y101.358 E.00737 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.446 Y101.522 E-.03447 +G1 X133.611 Y101.745 E-.05765 +G1 X133.942 Y101.857 E-.07262 +G1 X133.936 Y99.57 E-.47526 +;WIPE_END +G1 X133.392 Y108.859 Z17.162 F12000 +G1 Z17 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585279 +G1 F2452 +G1 X133.467 Y108.521 E.01561 +G1 X133.616 Y108.356 E.01003 +G1 X133.934 Y108.243 E.01522 +G1 X133.934 Y108.503 E.01173 +;WIDTH:0.585668 +G1 X133.935 Y111.009 E.11309 +;WIDTH:0.582397 +G1 X133.935 Y111.545 E.02404 +G3 X133.562 Y111.382 I.156 J-.864 E.01843 +G1 X133.422 Y111.124 E.01317 +;WIDTH:0.585668 +G1 X133.391 Y110.963 E.0074 +G1 X133.392 Y109.062 E.08579 +M204 P1000 +;LAYER_CHANGE +;Z:17.2 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;17.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.391 Y110.963 E-.39505 +G1 X133.422 Y111.124 E-.03407 +G1 X133.562 Y111.382 E-.061 +G2 X133.935 Y111.545 I.528 J-.701 E-.08537 +G1 X133.935 Y111.234 E-.06451 +;WIPE_END +G1 X133.935 Y111.234 Z17 F12000 +G1 X134.368 Y95.632 Z17.272 +;AFTER_LAYER_CHANGE +;17.2 +G1 X134.368 Y95.632 +G1 Z17.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2463 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X133.934 Y98.458 Z17.215 F12000 +G1 Z17.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.585488 +G1 F2463 +G1 X133.939 Y101.728 E.14753 +;WIDTH:0.575283 +G1 X133.672 Y101.651 E.0123 +G1 X133.467 Y101.435 E.01318 +;WIDTH:0.58554 +G1 X133.391 Y101.147 E.01344 +G1 X133.391 Y99.067 E.09385 +G3 X133.607 Y98.57 I.91 J.1 E.02482 +G1 X133.741 Y98.524 E.00639 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.607 Y98.57 E-.02944 +G2 X133.391 Y99.067 I.694 J.597 E-.11433 +G1 X133.391 Y101.147 E-.43225 +G1 X133.467 Y101.435 E-.0619 +G1 X133.474 Y101.442 E-.00208 +;WIPE_END +G1 X130.026 Y97.084 Z17.297 F12000 +G1 Z17.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2463 +G1 X130.628 Y97.084 E.02038 +G2 X131.623 Y96.271 I.019 J-.993 E.0473 +G1 X133.454 Y98.102 E.08765 +G1 X134.002 Y97.957 E.01919 +G1 X134.002 Y95.998 E.06631 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X131.35 Y114.002 E.17006 +M73 P79 R4 +M73 Q79 S4 +G1 X133.454 Y111.898 E.10072 +G1 X134.002 Y112.043 E.01919 +G1 X134.002 Y114.002 E.06631 +G1 X115.998 Y95.998 E.86184 +G1 X118.551 Y95.998 E.08642 +G1 X118.561 Y96.087 E.00303 +G1 X115.998 Y98.65 E.12269 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X121.655 Y96.066 Z17.291 F12000 +G1 Z17.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.585669 +G1 F2463 +G1 X122.277 Y96.06 E.02807 +;WIDTH:0.572129 +G1 X122.146 Y96.393 E.01575 +G1 X121.998 Y96.534 E.009 +;WIDTH:0.585669 +G1 X121.677 Y96.609 E.01488 +G1 X119.663 Y96.609 E.09089 +;WIDTH:0.585453 +G3 X119.19 Y96.409 I.007 J-.676 E.02377 +G1 X119.058 Y96.066 E.01658 +;WIDTH:0.585652 +G1 X121.451 Y96.066 E.10799 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.058 Y96.066 E-.4973 +G1 X119.19 Y96.409 E-.07638 +G2 X119.459 Y96.575 I.479 J-.476 E-.06632 +;WIPE_END +G1 X130.585 Y96.066 Z17.394 F12000 +G1 Z17.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585665 +G1 F2463 +G1 X131.193 Y96.062 E.02744 +;WIDTH:0.577432 +G1 X131.054 Y96.4 E.01624 +G1 X130.891 Y96.545 E.0097 +;WIDTH:0.585665 +G1 X130.603 Y96.609 E.01331 +G1 X128.77 Y96.608 E.08272 +;WIDTH:0.585273 +G1 X128.429 Y96.531 E.01577 +G1 X128.249 Y96.363 E.0111 +G1 X128.15 Y96.066 E.01412 +G1 X128.481 Y96.066 E.01493 +;WIDTH:0.585627 +G1 X130.382 Y96.066 E.08579 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.481 Y96.066 E-.39505 +G1 X128.15 Y96.066 E-.06879 +G1 X128.249 Y96.363 E-.06506 +G1 X128.429 Y96.531 E-.05117 +G1 X128.71 Y96.595 E-.05993 +;WIPE_END +G1 X133.934 Y108.368 Z17.425 F12000 +G1 Z17.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.585348 +G1 F2463 +G1 X133.934 Y108.585 E.00979 +;WIDTH:0.585669 +G1 X133.935 Y111.009 E.10939 +;WIDTH:0.582397 +G1 X133.935 Y111.545 E.02404 +G3 X133.562 Y111.382 I.156 J-.864 E.01843 +G1 X133.422 Y111.124 E.01317 +;WIDTH:0.585669 +G1 X133.391 Y110.963 E.0074 +G1 X133.391 Y108.982 E.0894 +;WIDTH:0.585348 +G1 X133.472 Y108.664 E.0148 +G1 X133.668 Y108.448 E.01316 +G1 X133.739 Y108.427 E.00334 +M204 P1000 +;LAYER_CHANGE +;Z:17.4 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;17.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.668 Y108.448 E-.01539 +G1 X133.472 Y108.664 E-.06061 +G1 X133.391 Y108.982 E-.06819 +G1 X133.391 Y110.963 E-.41168 +G1 X133.422 Y111.124 E-.03407 +G1 X133.537 Y111.336 E-.05006 +;WIPE_END +G1 X133.537 Y111.336 Z17.2 F12000 +G1 X134.368 Y95.632 Z17.474 +;AFTER_LAYER_CHANGE +;17.4 +G1 X134.368 Y95.632 +G1 Z17.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2158 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2158 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z17.444 F12000 +G1 Z17.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2158 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z17.562 F12000 +G1 Z17.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2158 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M73 Q80 S4 +M204 P1000 +;LAYER_CHANGE +;Z:17.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;17.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z17.4 F12000 +M73 P80 R4 +G1 X134.368 Y95.632 Z17.688 +;AFTER_LAYER_CHANGE +;17.6 +G1 X134.368 Y95.632 +G1 Z17.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z17.644 F12000 +G1 Z17.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z17.762 F12000 +G1 Z17.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:17.8 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;17.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z17.6 F12000 +G1 X134.368 Y95.632 Z17.888 +;AFTER_LAYER_CHANGE +;17.8 +G1 X134.368 Y95.632 +G1 Z17.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2160 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2160 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +M73 P81 R4 +M73 Q81 S4 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z17.844 F12000 +G1 Z17.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2160 +G1 X134.002 Y98.65 E.0677 +G1 X131.35 Y95.998 E.12695 +G1 X134.002 Y95.998 E.08977 +G1 X115.998 Y114.002 E.86184 +G1 X115.998 Y111.35 E.08977 +G1 X118.65 Y114.002 E.12695 +G1 X123.674 Y114.002 E.17006 +G1 X134.002 Y103.674 E.4944 +G1 X134.002 Y106.326 E.08977 +G1 X123.674 Y95.998 E.4944 +G1 X126.326 Y95.998 E.08977 +G1 X115.998 Y106.326 E.4944 +G1 X115.998 Y103.674 E.08977 +G1 X126.326 Y114.002 E.4944 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X126.326 Y114.002 E-.41562 +G1 X125.563 Y113.239 E-.22438 +;WIPE_END +G1 X134.002 Y109.35 Z17.962 F12000 +G1 Z17.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F2160 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X115.998 Y98.65 E.08977 +G1 X118.65 Y95.998 E.12695 +M73 Q81 S3 +G1 X120.65 Y95.998 E.0677 +M204 P1000 +;LAYER_CHANGE +;Z:18 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;18 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.65 Y95.998 E-.41563 +G1 X117.887 Y96.761 E-.22437 +;WIPE_END +G1 X117.887 Y96.761 Z17.8 F12000 +G1 X134.368 Y95.632 Z18.088 +;AFTER_LAYER_CHANGE +;18 +G1 X134.368 Y95.632 +M73 P81 R3 +G1 Z18 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2451 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.002 Y100.65 Z18.044 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2451 +G1 X134.002 Y98.65 E.0677 +G1 X131.757 Y96.406 E.10744 +G1 X132.375 Y96.406 E.02092 +G1 X132.442 Y95.998 E.014 +G1 X134.002 Y95.998 E.0528 +G1 X115.998 Y114.002 E.86184 +G1 X117.637 Y114.002 E.05548 +G1 X117.678 Y113.594 E.01388 +G1 X118.243 Y113.594 E.01912 +G1 X115.998 Y111.35 E.10744 +G1 X115.998 Y109.35 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y111.35 E-.41563 +G1 X116.761 Y112.113 E-.22437 +;WIPE_END +G1 X118.047 Y114.002 Z18.04 F12000 +M73 Q82 S3 +G1 Z18 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.45003 +G1 F2451 +G1 X122.129 Y114.002 E.13818 +M204 P1000 +G1 E-.16 F2100 +M73 P82 R3 +;WIPE_START +G1 F9600 +G1 X119.049 Y114.002 E-.64 +;WIPE_END +G1 X125.696 Y114.002 Z18.116 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P1500 +G1 F2451 +G1 X127.538 Y114.002 E.06235 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.696 Y114.002 E-.38279 +;WIPE_END +G1 E-.25721 F2100 +G1 X134.002 Y109.35 Z18.166 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2451 +G1 X134.002 Y111.35 E.0677 +G1 X131.35 Y114.002 E.12695 +G1 X134.002 Y114.002 E.08977 +G1 X115.998 Y95.998 E.86184 +G1 X118.65 Y95.998 E.08977 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y98.65 E-.41563 +G1 X116.761 Y97.887 E-.22437 +;WIPE_END +G1 X115.998 Y102.889 Z18.088 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.450028 +G1 F2451 +G1 X115.998 Y104.008 E.03788 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X115.998 Y102.889 E-.23254 +;WIPE_END +G1 E-.40746 F2100 +G1 X122.082 Y96.406 Z18.155 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2451 +G1 X124.082 Y96.406 E.0677 +G1 X133.594 Y105.918 E.45533 +G1 X133.594 Y105.336 E.0197 +G1 X134.002 Y105.285 E.01392 +G1 X134.002 Y103.674 E.05453 +G1 X123.674 Y114.002 E.4944 +G1 X125.285 Y114.002 E.05453 +G1 X125.336 Y113.594 E.01392 +G1 X125.918 Y113.594 E.0197 +G1 X116.406 Y104.082 E.45533 +G1 X116.406 Y104.935 E.02887 +G1 X115.998 Y104.528 E.01951 +G1 X115.998 Y106.326 E.06086 +G1 X126.326 Y95.998 E.4944 +G1 X127.404 Y95.998 E.03649 +G1 X126.997 Y96.406 E.01951 +G1 X127.343 Y96.406 E.01171 +M204 P1000 +G1 X127.343 Y96.406 F12000 +G1 X127.98 Y95.998 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.45003 +G1 F2451 +G1 X132.029 Y95.998 E.13706 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.949 Y95.998 E-.64 +;WIPE_END +G1 X122.252 Y95.998 Z18.117 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.450032 +G1 F2451 +G1 X124.008 Y95.998 E.05944 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.252 Y95.998 E-.36492 +;WIPE_END +G1 E-.27508 F2100 +G1 X134.002 Y105.696 Z18.266 F12000 +G1 Z18 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.45003 +G1 F2451 +G1 X134.002 Y106.584 E.03006 +M204 P1000 +M106 S76.5 +;LAYER_CHANGE +;Z:18.2 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;18.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y105.696 E-.18454 +;WIPE_END +G1 E-.45546 F2100 +G1 X134.002 Y105.696 Z18 F12000 +G1 X134.368 Y95.632 Z18.2 +;AFTER_LAYER_CHANGE +;18.2 +G1 X134.368 Y95.632 +G1 Z18.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X132.573 Y96.368 Z18.251 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F6600 +G3 X132.442 Y95.998 I.118 J-.251 E.01477 +G1 X134.002 Y95.998 E.0528 +G1 X133.103 Y96.897 E.04303 +G1 X133.592 Y97.387 E.02343 +G1 X133.165 Y97.813 E.02042 +G1 X134.002 Y98.65 E.04007 +G1 X134.002 Y103.674 E.17006 +G1 X133.103 Y104.573 E.04303 +G1 X131.689 Y103.159 E.06769 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.103 Y104.573 E-.41556 +G1 X133.866 Y103.81 E-.22444 +;WIPE_END +G1 X131.517 Y109.644 Z18.31 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X131.007 Y110.154 E.02441 +G1 X131.485 Y110.631 E.02286 +G1 X131.058 Y111.058 E.02044 +G1 X134.002 Y114.002 E.14093 +G1 X134.002 Y111.35 E.08977 +G1 X131.35 Y114.002 E.12695 +G1 X129.35 Y114.002 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.35 Y114.002 E-.41563 +G1 X132.113 Y113.239 E-.22437 +;WIPE_END +G1 X127.322 Y114.183 Z18.285 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.406081 +;HEIGHT:0.4 +M106 S127.5 +G1 X130.876 Y110.629 E.27063 +G1 X130.554 Y110.306 E.02456 +G1 X126.88 Y113.98 E.27977 +G1 X126.235 Y113.98 E.03473 +G1 X133.98 Y106.235 E.58977 +G1 X133.98 Y105.72 E.02773 +G1 X133.832 Y105.738 E.00803 +G1 X125.738 Y113.832 E.61635 +G1 X125.757 Y113.68 E.00825 +G1 X125.501 Y113.424 E.01949 +G1 X133.424 Y105.501 E.60333 +G1 X133.102 Y105.178 E.02456 +G1 X125.178 Y113.102 E.6034 +G1 X124.856 Y112.779 E.02456 +M73 Q83 S3 +G1 X132.779 Y104.856 E.60333 +G1 X132.457 Y104.533 E.02456 +M73 P83 R3 +G1 X124.533 Y112.457 E.6034 +G1 X124.211 Y112.134 E.02456 +G1 X132.134 Y104.211 E.60333 +G1 X131.812 Y103.888 E.02456 +G1 X121.72 Y113.98 E.7685 +G1 X121.075 Y113.98 E.03473 +G1 X131.489 Y103.566 E.79302 +G1 X131.167 Y103.243 E.02456 +G1 X120.43 Y113.98 E.81761 +G1 X119.785 Y113.98 E.03473 +G1 X130.844 Y102.921 E.84213 +G1 X130.522 Y102.598 E.02456 +G1 X119.14 Y113.98 E.86673 +G1 X118.495 Y113.98 E.03473 +G1 X130.199 Y102.276 E.89125 +G1 X129.877 Y101.953 E.02456 +G1 X118.094 Y113.736 E.89726 +G1 X118.098 Y113.698 E.00206 +G1 X117.793 Y113.392 E.02326 +G1 X129.554 Y101.631 E.89559 +G1 X129.232 Y101.308 E.02456 +G1 X117.47 Y113.07 E.89566 +G1 X117.387 Y112.986 E.00636 +G1 X121.225 Y109.148 E.29226 +G1 X120.986 Y108.909 E.0182 +G1 X132.747 Y97.148 E.89559 +G1 X132.425 Y96.825 E.02456 +G1 X120.663 Y108.587 E.89566 +G1 X120.341 Y108.264 E.02456 +G1 X132.102 Y96.503 E.89559 +G1 X131.95 Y96.35 E.01161 +G1 X132.004 Y96.02 E.01801 +G1 X131.94 Y96.02 E.00345 +G1 X120.018 Y107.942 E.90785 +G1 X119.696 Y107.619 E.02456 +G1 X131.295 Y96.02 E.88325 +G1 X130.65 Y96.02 E.03473 +G1 X119.373 Y107.297 E.85873 +G1 X119.051 Y106.974 E.02456 +G1 X130.005 Y96.02 E.83414 +G1 X129.36 Y96.02 E.03473 +G1 X118.728 Y106.652 E.80962 +G1 X118.406 Y106.329 E.02456 +M73 P84 R3 +M73 Q84 S3 +G1 X128.715 Y96.02 E.78502 +G1 X128.07 Y96.02 E.03473 +G1 X118.083 Y106.007 E.7605 +G1 X117.761 Y105.684 E.02456 +G1 X125.684 Y97.761 E.60333 +G1 X125.362 Y97.438 E.02456 +G1 X117.438 Y105.362 E.6034 +G1 X117.116 Y105.039 E.02456 +G1 X125.039 Y97.116 E.60333 +G1 X124.717 Y96.793 E.02456 +G1 X116.793 Y104.717 E.6034 +G1 X116.471 Y104.394 E.02456 +G1 X124.394 Y96.471 E.60333 +G1 X124.072 Y96.148 E.02456 +G1 X116.148 Y104.072 E.6034 +G2 X116.02 Y103.993 I-.173 J.137 E.00826 +G1 X116.02 Y103.555 E.02358 +G1 X123.555 Y96.02 E.57378 +G1 X122.91 Y96.02 E.03473 +G1 X116.02 Y102.91 E.52467 +G1 X119.335 Y99.582 E.25293 +G1 X119.019 Y99.266 E.02406 +G1 X122.468 Y95.817 E.26264 +M106 S76.5 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.29 Y97.995 E-.64 +;WIPE_END +G1 X120.65 Y95.998 Z18.235 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +;HEIGHT:0.2 +G1 F6600 +G1 X118.65 Y95.998 E.0677 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y95.998 E.08977 +G1 X118.837 Y98.837 E.1359 +G1 X118.411 Y99.264 E.02042 +G1 X118.729 Y99.582 E.01522 +G1 X118.06 Y100.252 E.03205 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X118.729 Y99.582 E-.19676 +G1 X118.411 Y99.264 E-.09346 +G1 X118.837 Y98.837 E-.12534 +G1 X118.073 Y98.073 E-.22444 +;WIPE_END +G1 X118.311 Y106.841 Z18.353 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X116.897 Y105.427 E.06769 +G1 X115.998 Y106.326 E.04303 +G1 X115.998 Y111.35 E.17006 +G1 X117.207 Y112.559 E.05787 +G1 X116.78 Y112.986 E.02044 +G1 X116.897 Y113.103 E.0056 +G1 X115.998 Y114.002 E.04303 +G1 X117.637 Y114.002 E.05548 +G2 X117.499 Y113.705 I-.24 J-.069 E.01209 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X117.637 Y114.002 I-.102 J.228 E-.0742 +G1 X115.998 Y114.002 E-.3406 +G1 X116.764 Y113.236 E-.2252 +;WIPE_END +G1 X123.674 Y114.002 Z18.321 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X124.573 Y113.103 E.04303 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.674 Y114.002 E-.26421 +;WIPE_END +G1 E-.37579 F2100 +G1 X125.427 Y96.897 Z18.5 F12000 +G1 Z18.2 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X126.326 Y95.998 E.04303 +M204 P1000 +M106 S124.95 +;LAYER_CHANGE +;Z:18.4 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;18.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.427 Y96.897 E-.26421 +;WIPE_END +G1 E-.37579 F2100 +G1 X125.427 Y96.897 Z18.2 F12000 +G1 X134.368 Y95.632 Z18.4 +;AFTER_LAYER_CHANGE +;18.4 +G1 X134.368 Y95.632 +G1 Z18.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.12 Y97.416 Z18.483 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F4857 +G3 X131.221 Y98.779 I-.01 J1.134 E.06777 +G1 X134.002 Y95.998 E.13312 +G1 X131.35 Y95.998 E.08977 +G1 X134.002 Y98.65 E.12695 +G1 X134.002 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y98.65 E-.41563 +G1 X133.239 Y97.887 E-.22437 +;WIPE_END +G1 X128.326 Y95.998 Z18.492 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4857 +G1 X126.326 Y95.998 E.0677 +G1 X124.908 Y97.416 E.06788 +G1 X125.092 Y97.416 E.00623 +G1 X123.674 Y95.998 E.06788 +G1 X118.65 Y95.998 E.17006 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y95.998 E.08977 +G1 X118.777 Y98.777 E.13303 +G2 X118.855 Y100.497 I5.459 J.616 E.05852 +M73 Q85 S3 +G2 X120.142 Y102.182 I9.926 J-6.252 E.07187 +G1 X115.998 Y106.326 E.19837 +G1 X115.998 Y103.674 E.08977 +G1 X121.206 Y108.882 E.2493 +M73 P85 R3 +G1 X121.118 Y108.882 E.00298 +G1 X123.555 Y106.445 E.11666 +G1 X124.805 Y108.007 E.06772 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.555 Y106.445 E-.41575 +G1 X122.792 Y107.208 E-.22425 +;WIPE_END +G1 X125.014 Y102.173 Z18.496 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4857 +G1 X126.272 Y103.728 E.0677 +G1 X128.882 Y101.118 E.12494 +G1 X128.794 Y101.118 E.00298 +G1 X134.002 Y106.326 E.2493 +G1 X134.002 Y103.674 E.08977 +G1 X129.706 Y107.97 E.20565 +G3 X130.931 Y109.561 I-7.705 J7.202 E.06807 +G3 X131.002 Y111.002 I-4.011 J.921 E.04909 +G1 X134.002 Y114.002 E.14361 +G1 X131.35 Y114.002 E.08977 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y109.35 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y111.35 E-.41563 +G1 X133.239 Y112.113 E-.22437 +;WIPE_END +G1 X128.326 Y114.002 Z18.492 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4857 +G1 X126.326 Y114.002 E.0677 +G1 X124.908 Y112.584 E.06788 +G1 X125.092 Y112.584 E.00623 +G1 X123.674 Y114.002 E.06788 +G1 X118.65 Y114.002 E.17006 +G1 X115.998 Y111.35 E.12695 +G1 X115.998 Y114.002 E.08977 +G1 X119.531 Y110.469 E.16912 +G1 X119.531 Y111.523 E.03568 +G2 X119.942 Y112.338 I1.019 J-.003 E.03204 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.531 Y111.523 I.608 J-.818 E-.19669 +G1 X119.531 Y110.469 E-.21903 +G1 X118.768 Y111.232 E-.22428 +;WIPE_END +G1 X119.291 Y97.963 Z18.632 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.38292 +G1 F4857 +G1 X119.523 Y97.766 E.0086 +;WIDTH:0.41646 +G1 X119.692 Y97.794 E.00532 +;WIDTH:0.449999 +G1 X119.861 Y97.823 E.0058 +G1 X119.184 Y98.5 E.03241 +G1 X119.184 Y99.076 E.0195 +G1 X120.437 Y97.823 E.05998 +G1 X121.012 Y97.823 E.01946 +G1 X119.184 Y99.651 E.08751 +G1 X119.205 Y100.207 E.01883 +G1 X121.588 Y97.823 E.1141 +G1 X122.164 Y97.823 E.0195 +G1 X119.393 Y100.594 E.13265 +G1 X119.649 Y100.914 E.01387 +G1 X122.74 Y97.823 E.14796 +G1 X123.315 Y97.823 E.01946 +G1 X119.905 Y101.234 E.16326 +G1 X120.161 Y101.553 E.01384 +G1 X123.891 Y97.823 E.17855 +G1 X124.467 Y97.823 E.0195 +G1 X120.417 Y101.873 E.19387 +G1 X120.673 Y102.193 E.01387 +G1 X125.042 Y97.823 E.20917 +G1 X125.618 Y97.823 E.0195 +G1 X120.929 Y102.513 E.22448 +G1 X121.184 Y102.832 E.01382 +G1 X126.194 Y97.823 E.2398 +G1 X126.769 Y97.823 E.01946 +G1 X121.44 Y103.152 E.2551 +G1 X121.696 Y103.472 E.01387 +G1 X123.822 Y101.347 E.10175 +G1 X128.199 Y106.756 E.23552 +G1 X125.968 Y108.987 E.1068 +G1 X125.933 Y108.764 E.00764 +G1 X121.951 Y103.791 E.21564 +G1 X123.935 Y101.809 E.09492 +G1 X124.047 Y102.272 E.01612 +G1 X127.652 Y106.727 E.19398 +G1 X126.081 Y108.298 E.0752 +G1 X122.497 Y103.821 E.19412 +G1 X123.903 Y102.416 E.06728 +G1 X124.015 Y102.88 E.01616 +G1 X127.105 Y106.698 E.16626 +G1 X126.115 Y107.689 E.04741 +G1 X123.043 Y103.851 E.1664 +G1 X123.871 Y103.024 E.03961 +G1 X123.98 Y103.543 E.01795 +;WIDTH:0.524412 +G1 X124.148 Y103.751 E.0107 +;WIDTH:0.533806 +G1 X126.502 Y106.667 E.15297 +G1 X126.152 Y107.017 E.0202 +G1 X123.773 Y104.052 E.15516 +;WIDTH:0.524412 +G1 X123.638 Y103.884 E.00863 +G1 X123.836 Y103.687 E.01118 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.638 Y103.884 E-.05804 +G1 X123.773 Y104.052 E-.04479 +G1 X125.391 Y106.068 E-.53717 +;WIPE_END +G1 X124.457 Y100.711 Z18.495 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4857 +G1 X127.345 Y97.823 E.13825 +G1 X127.921 Y97.823 E.0195 +G1 X125.033 Y100.711 E.13825 +G1 X125.609 Y100.711 E.0195 +G1 X128.497 Y97.823 E.13825 +G1 X129.072 Y97.823 E.01946 +G1 X126.184 Y100.711 E.13825 +G1 X126.76 Y100.711 E.0195 +G1 X129.648 Y97.823 E.13825 +G1 X130.209 Y97.838 E.019 +G1 X127.336 Y100.711 E.13753 +G1 X127.912 Y100.711 E.0195 +G1 X130.608 Y98.015 E.12906 +G1 X130.797 Y98.401 E.01455 +G1 X128.487 Y100.711 E.11058 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.665 Y98.533 E-.64 +;WIPE_END +G1 X130.667 Y99.107 Z18.41 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F4857 +G1 X129.063 Y100.711 E.07678 +G1 X130.137 Y100.711 E.03635 +G2 X130.606 Y100.518 I-.067 J-.828 E.01745 +G2 X130.8 Y100.172 I-.977 J-.776 E.01348 +G1 X130.811 Y98.963 E.04092 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.8 Y100.172 E-.25126 +G3 X130.606 Y100.518 I-1.171 J-.429 E-.08279 +G3 X130.137 Y100.711 I-.535 J-.635 E-.1071 +G1 X129.18 Y100.711 E-.19885 +;WIPE_END +G1 X120.46 Y109.314 Z18.614 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F4857 +G1 X119.958 Y109.816 E.02403 +G1 X119.938 Y110.411 E.02015 +G1 X121.061 Y109.289 E.05373 +G1 X121.636 Y109.289 E.01946 +G1 X119.938 Y110.987 E.08128 +G1 X119.943 Y111.558 E.01933 +G1 X122.212 Y109.289 E.10862 +G1 X122.788 Y109.289 E.0195 +G1 X120.125 Y111.952 E.12748 +G1 X120.505 Y112.148 E.01447 +G1 X123.363 Y109.289 E.13683 +G1 X123.939 Y109.289 E.0195 +G1 X121.051 Y112.177 E.13825 +G1 X121.627 Y112.177 E.0195 +G1 X124.515 Y109.289 E.13825 +G1 X125.09 Y109.289 E.01946 +G1 X122.203 Y112.177 E.13822 +G1 X122.778 Y112.177 E.01946 +G1 X125.666 Y109.289 E.13825 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.489 Y111.467 E-.64 +;WIPE_END +G1 X128.457 Y107.074 Z18.516 F12000 +G1 Z18.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F4857 +G1 X123.354 Y112.177 E.24428 +G1 X123.93 Y112.177 E.0195 +G1 X128.714 Y107.392 E.22903 +G1 X128.972 Y107.711 E.01389 +G1 X124.505 Y112.177 E.21381 +G1 X125.081 Y112.177 E.0195 +G1 X129.229 Y108.029 E.19856 +G1 X129.487 Y108.347 E.01386 +G1 X125.657 Y112.177 E.18334 +G1 X126.232 Y112.177 E.01946 +G1 X129.744 Y108.665 E.16812 +G1 X130.002 Y108.983 E.01386 +G1 X126.808 Y112.177 E.15289 +G1 X127.384 Y112.177 E.0195 +G1 X130.259 Y109.302 E.13762 +G1 X130.504 Y109.633 E.01394 +G1 X127.96 Y112.177 E.12178 +G1 X128.535 Y112.177 E.01946 +G1 X130.602 Y110.11 E.09895 +G1 X130.588 Y111.56 E.04908 +G3 X130.222 Y112.108 I-.791 J-.131 E.02299 +G1 X129.922 Y112.177 E.01042 +G1 X129.111 Y112.177 E.02745 +G1 X130.452 Y110.835 E.06422 +M204 P1000 +;LAYER_CHANGE +;Z:18.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;18.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.111 Y112.177 E-.39426 +G1 X129.922 Y112.177 E-.16854 +G1 X130.222 Y112.108 E-.06397 +G2 X130.275 Y112.072 I-.425 J-.68 E-.01323 +;WIPE_END +G1 X130.275 Y112.072 Z18.4 F12000 +G1 X134.368 Y95.632 Z18.696 +;AFTER_LAYER_CHANGE +;18.6 +G1 X134.368 Y95.632 +G1 Z18.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +M73 P86 R3 +M73 Q86 S3 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.12 Y97.416 Z18.683 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F4673 +G3 X131.221 Y98.779 I-.01 J1.134 E.06777 +G1 X134.002 Y95.998 E.13312 +G1 X131.35 Y95.998 E.08977 +G1 X134.002 Y98.65 E.12695 +G1 X134.002 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y98.65 E-.41563 +G1 X133.239 Y97.887 E-.22437 +M73 Q86 S2 +;WIPE_END +G1 X128.326 Y95.998 Z18.692 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4673 +G1 X126.326 Y95.998 E.0677 +G1 X124.908 Y97.416 E.06788 +G1 X125.092 Y97.416 E.00623 +G1 X123.674 Y95.998 E.06788 +M73 P86 R2 +G1 X118.65 Y95.998 E.17006 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y95.998 E.08977 +G1 X118.777 Y98.777 E.13303 +G2 X118.855 Y100.497 I5.459 J.616 E.05852 +G2 X120.142 Y102.182 I9.926 J-6.252 E.07187 +G1 X115.998 Y106.326 E.19837 +G1 X115.998 Y103.674 E.08977 +G1 X121.206 Y108.882 E.2493 +G1 X121.118 Y108.882 E.00298 +G1 X123.555 Y106.445 E.11666 +G1 X124.805 Y108.007 E.06772 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.555 Y106.445 E-.41575 +G1 X122.792 Y107.208 E-.22425 +;WIPE_END +G1 X125.014 Y102.173 Z18.696 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4673 +G1 X126.272 Y103.728 E.0677 +G1 X128.882 Y101.118 E.12494 +G1 X128.794 Y101.118 E.00298 +G1 X134.002 Y106.326 E.2493 +G1 X134.002 Y103.674 E.08977 +G1 X129.706 Y107.97 E.20565 +G3 X130.931 Y109.561 I-7.705 J7.202 E.06807 +G3 X131.002 Y111.002 I-4.011 J.921 E.04909 +G1 X134.002 Y114.002 E.14361 +G1 X131.35 Y114.002 E.08977 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y109.35 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y111.35 E-.41563 +G1 X133.239 Y112.113 E-.22437 +;WIPE_END +G1 X128.326 Y114.002 Z18.692 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4673 +G1 X126.326 Y114.002 E.0677 +G1 X124.908 Y112.584 E.06788 +G1 X125.092 Y112.584 E.00623 +G1 X123.674 Y114.002 E.06788 +G1 X118.65 Y114.002 E.17006 +G1 X115.998 Y111.35 E.12695 +G1 X115.998 Y114.002 E.08977 +G1 X119.531 Y110.469 E.16912 +G1 X119.531 Y111.523 E.03568 +G2 X119.942 Y112.338 I1.019 J-.003 E.03204 +M204 P1000 +G1 X119.942 Y112.338 F12000 +G1 X119.938 Y111.484 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F4673 +G1 X120.631 Y112.177 E.03317 +G1 X121.207 Y112.177 E.0195 +G1 X119.938 Y110.908 E.06075 +G1 X119.938 Y110.332 E.0195 +G1 X121.783 Y112.177 E.08832 +G1 X122.358 Y112.177 E.01946 +G1 X119.963 Y109.781 E.11467 +G1 X120.191 Y109.434 E.01405 +G1 X122.934 Y112.177 E.13131 +G1 X123.51 Y112.177 E.0195 +G1 X120.622 Y109.289 E.13825 +G1 X121.198 Y109.289 E.0195 +G1 X124.086 Y112.177 E.13825 +G1 X124.661 Y112.177 E.01946 +G1 X121.773 Y109.289 E.13825 +G1 X122.349 Y109.289 E.0195 +G1 X125.237 Y112.177 E.13825 +G1 X125.813 Y112.177 E.0195 +G1 X122.925 Y109.289 E.13825 +G1 X123.5 Y109.289 E.01946 +G1 X126.388 Y112.177 E.13825 +G1 X126.964 Y112.177 E.0195 +G1 X124.076 Y109.289 E.13825 +G1 X124.652 Y109.289 E.0195 +G1 X127.54 Y112.177 E.13825 +G1 X128.115 Y112.177 E.01946 +G1 X125.228 Y109.289 E.13822 +G1 X125.803 Y109.289 E.01946 +G1 X128.691 Y112.177 E.13825 +G1 X129.267 Y112.177 E.0195 +G1 X125.994 Y108.904 E.15668 +G1 X125.878 Y108.76 E.00626 +;WIDTH:0.41646 +G1 X125.763 Y108.616 E.00572 +;WIDTH:0.38292 +G1 X124.396 Y107.089 E.05794 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.763 Y108.616 E-.42591 +G1 X125.878 Y108.76 E-.0383 +G1 X125.994 Y108.904 E-.03843 +G1 X126.461 Y109.371 E-.13736 +;WIPE_END +G1 X122.086 Y104.204 Z18.718 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.407366 +G1 F4673 +G1 X123.68 Y105.985 E.07243 +;WIDTH:0.449999 +G1 X123.938 Y106.272 E.01306 +G1 X129.843 Y112.177 E.28267 +G1 X130.298 Y112.057 E.01593 +G1 X121.629 Y103.387 E.415 +G1 X121.371 Y103.099 E.01309 +;WIDTH:0.407366 +G1 X119.777 Y101.319 E.07241 +M204 P1000 +G1 X119.057 Y100.108 F12000 +M204 P1500 +;WIDTH:0.38292 +G1 F4673 +G1 X119.113 Y100.347 E.00694 +;WIDTH:0.41646 +G1 X119.222 Y100.431 E.00427 +;WIDTH:0.449999 +G1 X119.331 Y100.514 E.00464 +G1 X130.561 Y111.744 E.53757 +G1 X130.594 Y111.201 E.01841 +G1 X119.184 Y99.792 E.54617 +G1 X119.184 Y99.216 E.0195 +G1 X130.598 Y110.63 E.54638 +G1 X130.602 Y110.058 E.01936 +G1 X119.184 Y98.64 E.54657 +G1 X119.28 Y98.16 E.01657 +G1 X129.571 Y108.451 E.49262 +G1 X129.83 Y108.739 E.01311 +;WIDTH:0.409082 +G1 X130.514 Y109.5 E.03115 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.83 Y108.739 E-.21264 +G1 X129.571 Y108.451 E-.08049 +G1 X128.391 Y107.271 E-.34687 +;WIPE_END +G1 X129.08 Y107.601 Z18.613 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.40932 +G1 F4673 +G1 X127.388 Y105.721 E.07706 +;WIDTH:0.449999 +G1 X127.128 Y105.433 E.01313 +G1 X119.577 Y97.882 E.36146 +G1 X120.094 Y97.823 E.01761 +G1 X124.686 Y102.415 E.21982 +G1 X124.945 Y102.703 E.01311 +;WIDTH:0.40932 +G1 X126.638 Y104.583 E.07708 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X124.945 Y102.703 E-.52576 +G1 X124.686 Y102.415 E-.08049 +G1 X124.571 Y102.3 E-.03375 +;WIPE_END +G1 X124.195 Y101.565 Z18.614 F12000 +G1 Z18.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F4673 +G1 X123.867 Y101.201 E.01385 +;WIDTH:0.41646 +G1 X123.813 Y101.057 E.00478 +;WIDTH:0.449999 +G1 X123.76 Y100.913 E.00519 +G1 X120.67 Y97.823 E.14792 +G1 X121.246 Y97.823 E.0195 +G1 X124.134 Y100.711 E.13825 +G1 X124.709 Y100.711 E.01946 +G1 X121.821 Y97.823 E.13825 +G1 X122.397 Y97.823 E.0195 +G1 X125.285 Y100.711 E.13825 +G1 X125.861 Y100.711 E.0195 +G1 X122.973 Y97.823 E.13825 +G1 X123.549 Y97.823 E.0195 +G1 X126.436 Y100.711 E.13822 +G1 X127.012 Y100.711 E.0195 +G1 X124.124 Y97.823 E.13825 +G1 X124.7 Y97.823 E.0195 +G1 X127.588 Y100.711 E.13825 +G1 X128.163 Y100.711 E.01946 +G1 X125.276 Y97.823 E.13822 +G1 X125.851 Y97.823 E.01946 +G1 X128.739 Y100.711 E.13825 +G1 X129.315 Y100.711 E.0195 +G1 X126.427 Y97.823 E.13825 +G1 X127.003 Y97.823 E.0195 +G1 X129.891 Y100.711 E.13825 +G1 X130.412 Y100.656 E.01773 +G1 X127.578 Y97.823 E.13564 +G1 X128.154 Y97.823 E.0195 +G1 X130.715 Y100.384 E.12259 +G1 X130.806 Y99.899 E.0167 +G1 X128.73 Y97.823 E.09938 +G1 X130.111 Y97.826 E.04675 +G3 X130.597 Y98.013 I-.057 J.872 E.0179 +G3 X130.815 Y98.504 I-.627 J.573 E.0185 +G1 X130.81 Y99.327 E.02786 +G1 X129.45 Y97.968 E.06508 +M204 P1000 +;LAYER_CHANGE +;Z:18.8 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;18.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.81 Y99.327 E-.39955 +G1 X130.815 Y98.504 E-.17103 +G2 X130.719 Y98.186 I-.846 J.082 E-.06942 +;WIPE_END +G1 X130.719 Y98.186 Z18.6 F12000 +G1 X134.368 Y95.632 Z18.8 +;AFTER_LAYER_CHANGE +;18.8 +G1 X134.368 Y95.632 +G1 Z18.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +M73 P87 R2 +M73 Q87 S2 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.12 Y97.416 Z18.883 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F4808 +G3 X131.221 Y98.779 I-.01 J1.134 E.06777 +G1 X134.002 Y95.998 E.13312 +G1 X131.35 Y95.998 E.08977 +G1 X134.002 Y98.65 E.12695 +G1 X134.002 Y100.65 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y98.65 E-.41563 +G1 X133.239 Y97.887 E-.22437 +;WIPE_END +G1 X128.326 Y95.998 Z18.892 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4808 +G1 X126.326 Y95.998 E.0677 +G1 X124.908 Y97.416 E.06788 +G1 X125.092 Y97.416 E.00623 +G1 X123.674 Y95.998 E.06788 +G1 X118.65 Y95.998 E.17006 +G1 X115.998 Y98.65 E.12695 +G1 X115.998 Y95.998 E.08977 +G1 X118.777 Y98.777 E.13303 +G2 X118.855 Y100.497 I5.459 J.616 E.05852 +G2 X120.142 Y102.182 I9.926 J-6.252 E.07187 +G1 X115.998 Y106.326 E.19837 +G1 X115.998 Y103.674 E.08977 +G1 X121.206 Y108.882 E.2493 +G1 X121.118 Y108.882 E.00298 +G1 X123.555 Y106.445 E.11666 +G1 X124.805 Y108.007 E.06772 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.555 Y106.445 E-.41575 +G1 X122.792 Y107.208 E-.22425 +;WIPE_END +G1 X125.014 Y102.173 Z18.896 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4808 +G1 X126.272 Y103.728 E.0677 +G1 X128.882 Y101.118 E.12494 +G1 X128.794 Y101.118 E.00298 +G1 X134.002 Y106.326 E.2493 +G1 X134.002 Y103.674 E.08977 +G1 X129.706 Y107.97 E.20565 +G3 X130.931 Y109.561 I-7.705 J7.202 E.06807 +G3 X131.002 Y111.002 I-4.011 J.921 E.04909 +G1 X134.002 Y114.002 E.14361 +G1 X131.35 Y114.002 E.08977 +G1 X134.002 Y111.35 E.12695 +G1 X134.002 Y109.35 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y111.35 E-.41563 +G1 X133.239 Y112.113 E-.22437 +;WIPE_END +G1 X128.326 Y114.002 Z18.892 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P2000 +G1 F4808 +G1 X126.326 Y114.002 E.0677 +G1 X124.908 Y112.584 E.06788 +G1 X125.092 Y112.584 E.00623 +G1 X123.674 Y114.002 E.06788 +G1 X118.65 Y114.002 E.17006 +G1 X115.998 Y111.35 E.12695 +G1 X115.998 Y114.002 E.08977 +G1 X119.531 Y110.469 E.16912 +G1 X119.531 Y111.523 E.03568 +G2 X119.942 Y112.338 I1.019 J-.003 E.03204 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X119.531 Y111.523 I.608 J-.818 E-.19669 +G1 X119.531 Y110.469 E-.21903 +G1 X118.768 Y111.232 E-.22428 +;WIPE_END +G1 X119.291 Y97.963 Z19.032 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.38292 +G1 F4808 +G1 X119.523 Y97.766 E.0086 +;WIDTH:0.41646 +G1 X119.692 Y97.794 E.00532 +;WIDTH:0.449999 +G1 X119.861 Y97.823 E.0058 +G1 X119.184 Y98.5 E.03241 +G1 X119.184 Y99.076 E.0195 +G1 X120.437 Y97.823 E.05998 +G1 X121.012 Y97.823 E.01946 +G1 X119.184 Y99.651 E.08751 +G1 X119.205 Y100.207 E.01883 +G1 X121.588 Y97.823 E.1141 +G1 X122.164 Y97.823 E.0195 +G1 X119.393 Y100.594 E.13265 +G1 X119.649 Y100.914 E.01387 +G1 X122.74 Y97.823 E.14796 +G1 X123.315 Y97.823 E.01946 +G1 X119.905 Y101.234 E.16326 +G1 X120.161 Y101.553 E.01384 +G1 X123.891 Y97.823 E.17855 +G1 X124.467 Y97.823 E.0195 +G1 X120.417 Y101.873 E.19387 +G1 X120.673 Y102.193 E.01387 +G1 X125.042 Y97.823 E.20917 +G1 X125.618 Y97.823 E.0195 +G1 X120.929 Y102.513 E.22448 +G1 X121.184 Y102.832 E.01382 +G1 X126.194 Y97.823 E.2398 +G1 X126.769 Y97.823 E.01946 +G1 X121.44 Y103.152 E.2551 +G1 X121.696 Y103.472 E.01387 +G1 X123.822 Y101.347 E.10175 +G1 X128.199 Y106.756 E.23552 +G1 X125.968 Y108.987 E.1068 +G1 X125.933 Y108.764 E.00764 +G1 X121.951 Y103.791 E.21564 +G1 X123.935 Y101.809 E.09492 +G1 X124.047 Y102.272 E.01612 +G1 X127.652 Y106.727 E.19398 +G1 X126.081 Y108.298 E.0752 +G1 X122.497 Y103.821 E.19412 +G1 X123.903 Y102.416 E.06728 +G1 X124.015 Y102.88 E.01616 +G1 X127.105 Y106.698 E.16626 +G1 X126.115 Y107.689 E.04741 +G1 X123.043 Y103.851 E.1664 +G1 X123.871 Y103.024 E.03961 +G1 X123.98 Y103.543 E.01795 +;WIDTH:0.524412 +G1 X124.148 Y103.751 E.0107 +;WIDTH:0.533806 +G1 X126.502 Y106.667 E.15297 +G1 X126.152 Y107.017 E.0202 +G1 X123.773 Y104.052 E.15516 +;WIDTH:0.524412 +G1 X123.638 Y103.884 E.00863 +G1 X123.836 Y103.687 E.01118 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.638 Y103.884 E-.05804 +G1 X123.773 Y104.052 E-.04479 +G1 X125.391 Y106.068 E-.53717 +;WIPE_END +G1 X124.457 Y100.711 Z18.895 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F4808 +G1 X127.345 Y97.823 E.13825 +G1 X127.921 Y97.823 E.0195 +G1 X125.033 Y100.711 E.13825 +G1 X125.609 Y100.711 E.0195 +G1 X128.497 Y97.823 E.13825 +G1 X129.072 Y97.823 E.01946 +G1 X126.184 Y100.711 E.13825 +G1 X126.76 Y100.711 E.0195 +G1 X129.648 Y97.823 E.13825 +G1 X130.209 Y97.838 E.019 +G1 X127.336 Y100.711 E.13753 +G1 X127.912 Y100.711 E.0195 +G1 X130.608 Y98.015 E.12906 +G1 X130.797 Y98.401 E.01455 +G1 X128.487 Y100.711 E.11058 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.665 Y98.533 E-.64 +;WIPE_END +G1 X130.667 Y99.107 Z18.81 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F4808 +G1 X129.063 Y100.711 E.07678 +G1 X130.137 Y100.711 E.03635 +G2 X130.606 Y100.518 I-.067 J-.828 E.01745 +G2 X130.8 Y100.172 I-.977 J-.776 E.01348 +G1 X130.811 Y98.963 E.04092 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X130.8 Y100.172 E-.25126 +G3 X130.606 Y100.518 I-1.171 J-.429 E-.08279 +G3 X130.137 Y100.711 I-.535 J-.635 E-.1071 +G1 X129.18 Y100.711 E-.19885 +;WIPE_END +G1 X120.46 Y109.314 Z19.014 F12000 +G1 Z18.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F4808 +G1 X119.958 Y109.816 E.02403 +G1 X119.938 Y110.411 E.02015 +G1 X121.061 Y109.289 E.05373 +G1 X121.636 Y109.289 E.01946 +G1 X119.938 Y110.987 E.08128 +G1 X119.943 Y111.558 E.01933 +G1 X122.212 Y109.289 E.10862 +G1 X122.788 Y109.289 E.0195 +G1 X120.125 Y111.952 E.12748 +G1 X120.505 Y112.148 E.01447 +G1 X123.363 Y109.289 E.13683 +G1 X123.939 Y109.289 E.0195 +G1 X121.051 Y112.177 E.13825 +G1 X121.627 Y112.177 E.0195 +G1 X124.515 Y109.289 E.13825 +G1 X125.09 Y109.289 E.01946 +G1 X122.203 Y112.177 E.13822 +G1 X122.778 Y112.177 E.01946 +G1 X125.666 Y109.289 E.13825 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.489 Y111.467 E-.64 +;WIPE_END +G1 X128.457 Y107.074 Z18.916 F12000 +M73 Q88 S2 +G1 Z18.8 F720 +G1 E.8 F1500 +M73 P88 R2 +M204 P1500 +G1 F4808 +G1 X123.354 Y112.177 E.24428 +G1 X123.93 Y112.177 E.0195 +G1 X128.714 Y107.392 E.22903 +G1 X128.972 Y107.711 E.01389 +G1 X124.505 Y112.177 E.21381 +G1 X125.081 Y112.177 E.0195 +G1 X129.229 Y108.029 E.19856 +G1 X129.487 Y108.347 E.01386 +G1 X125.657 Y112.177 E.18334 +G1 X126.232 Y112.177 E.01946 +G1 X129.744 Y108.665 E.16812 +G1 X130.002 Y108.983 E.01386 +G1 X126.808 Y112.177 E.15289 +G1 X127.384 Y112.177 E.0195 +G1 X130.259 Y109.302 E.13762 +G1 X130.504 Y109.633 E.01394 +G1 X127.96 Y112.177 E.12178 +G1 X128.535 Y112.177 E.01946 +G1 X130.602 Y110.11 E.09895 +G1 X130.588 Y111.56 E.04908 +G3 X130.222 Y112.108 I-.791 J-.131 E.02299 +G1 X129.922 Y112.177 E.01042 +G1 X129.111 Y112.177 E.02745 +G1 X130.452 Y110.835 E.06422 +M204 P1000 +M106 S107.1 +;LAYER_CHANGE +;Z:19 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;19 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X129.111 Y112.177 E-.39426 +G1 X129.922 Y112.177 E-.16854 +G1 X130.222 Y112.108 E-.06397 +G2 X130.275 Y112.072 I-.425 J-.68 E-.01323 +;WIPE_END +G1 X130.275 Y112.072 Z18.8 F12000 +G1 X134.368 Y95.632 Z19.096 +;AFTER_LAYER_CHANGE +;19 +G1 X134.368 Y95.632 +G1 Z19 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X130.977 Y98.357 Z19.066 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.427412 +G1 F2400 +G1 X130.285 Y97.666 E.03127 +M204 P1000 +G1 X130.285 Y97.666 F12000 +G1 X129.714 Y97.638 +M204 P800 +G1 F2400 +G1 X130.997 Y98.921 E.05801 +M204 P1000 +G1 X130.997 Y98.921 F12000 +G1 X130.993 Y99.461 +M204 P800 +G1 F2400 +G1 X129.17 Y97.638 E.08242 +M204 P1000 +G1 X129.17 Y97.638 F12000 +G1 X128.627 Y97.638 +M204 P800 +G1 F2400 +G1 X130.99 Y100.001 E.10684 +M204 P1000 +G1 X130.99 Y100.001 F12000 +G1 X130.892 Y100.447 +M204 P800 +G1 F2400 +G1 X128.083 Y97.638 E.127 +M204 P1000 +G1 X128.083 Y97.638 F12000 +G1 X127.539 Y97.638 +M204 P800 +G1 F2400 +G1 X130.642 Y100.742 E.14032 +M204 P1000 +G1 X130.642 Y100.742 F12000 +G1 X130.239 Y100.882 +M204 P800 +G1 F2400 +G1 X126.995 Y97.638 E.14667 +M204 P1000 +G1 X126.995 Y97.638 F12000 +G1 X126.452 Y97.638 +M204 P800 +G1 F2400 +G1 X129.709 Y100.896 E.14728 +M204 P1000 +G1 X129.709 Y100.896 F12000 +G1 X129.165 Y100.896 +M204 P800 +G1 F2400 +G1 X125.908 Y97.638 E.14728 +M204 P1000 +G1 X125.908 Y97.638 F12000 +G1 X125.364 Y97.638 +M204 P800 +G1 F2400 +G1 X128.621 Y100.896 E.14728 +M204 P1000 +G1 X128.621 Y100.896 F12000 +G1 X128.078 Y100.896 +M204 P800 +G1 F2400 +G1 X124.82 Y97.638 E.1473 +M204 P1000 +G1 X124.82 Y97.638 F12000 +G1 X124.277 Y97.638 +M204 P800 +G1 F2400 +G1 X127.534 Y100.896 E.14728 +M204 P1000 +G1 X127.534 Y100.896 F12000 +G1 X126.99 Y100.896 +M204 P800 +G1 F2400 +G1 X123.733 Y97.638 E.14728 +M204 P1000 +G1 X123.733 Y97.638 F12000 +G1 X123.189 Y97.638 +M204 P800 +G1 F2400 +G1 X126.446 Y100.896 E.14728 +M204 P1000 +G1 X126.446 Y100.896 F12000 +G1 X125.903 Y100.896 +M204 P800 +G1 F2400 +G1 X122.645 Y97.638 E.1473 +M204 P1000 +G1 X122.645 Y97.638 F12000 +G1 X122.102 Y97.638 +M204 P800 +G1 F2400 +G1 X125.359 Y100.896 E.14728 +M204 P1000 +G1 X125.359 Y100.896 F12000 +G1 X124.815 Y100.896 +M204 P800 +G1 F2400 +G1 X121.558 Y97.638 E.14728 +M204 P1000 +G1 X121.558 Y97.638 F12000 +G1 X121.014 Y97.638 +M204 P800 +G1 F2400 +G1 X124.271 Y100.896 E.14728 +M204 P1000 +G1 X124.271 Y100.896 F12000 +G1 X123.893 Y101.061 +M204 P800 +G1 F2400 +G1 X120.47 Y97.638 E.15477 +M204 P1000 +G1 X120.47 Y97.638 F12000 +G1 X119.927 Y97.638 +M204 P800 +G1 F2400 +G1 X125.86 Y103.571 E.26825 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.682 Y101.393 E-.64 +;WIPE_END +G1 X128.166 Y106.422 Z19.118 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X119.471 Y97.727 E.39313 +M204 P1000 +G1 X119.471 Y97.727 F12000 +G1 X119.177 Y97.976 +M204 P800 +G1 F2400 +G1 X130.473 Y109.273 E.51075 +M204 P1000 +G1 X130.473 Y109.273 F12000 +G1 X130.786 Y110.129 +M204 P800 +G1 F2400 +G1 X119.022 Y98.365 E.53189 +M204 P1000 +G1 X119.022 Y98.365 F12000 +G1 X119 Y98.886 +M204 P800 +G1 F2400 +G1 X130.782 Y110.669 E.53273 +M204 P1000 +G1 X130.782 Y110.669 F12000 +G1 X130.779 Y111.209 +M204 P800 +G1 F2400 +G1 X119 Y99.43 E.53257 +M204 P1000 +G1 X119 Y99.43 F12000 +G1 X119 Y99.974 +M204 P800 +G1 F2400 +G1 X130.758 Y111.732 E.53162 +M204 P1000 +G1 X130.758 Y111.732 F12000 +G1 X130.564 Y112.082 +M204 P800 +G1 F2400 +G1 X119.478 Y100.996 E.50123 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.656 Y103.174 E-.64 +;WIPE_END +G1 X121.659 Y103.721 Z19.01 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X130.239 Y112.301 E.38793 +M204 P1000 +G1 X130.239 Y112.301 F12000 +G1 X129.756 Y112.362 +M204 P800 +G1 F2400 +G1 X123.841 Y106.446 E.26746 +M204 P1000 +G1 E-.16 F2100 +M73 Q89 S2 +;WIPE_START +G1 F9600 +G1 X126.018 Y108.624 E-.64 +;WIPE_END +G1 X125.778 Y108.927 Z19.007 F12000 +M73 P89 R2 +G1 Z19 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X129.212 Y112.362 E.15529 +M204 P1000 +G1 X129.212 Y112.362 F12000 +G1 X128.668 Y112.362 +M204 P800 +G1 F2400 +G1 X125.411 Y109.104 E.14728 +M204 P1000 +G1 X125.411 Y109.104 F12000 +G1 X124.867 Y109.104 +M204 P800 +G1 F2400 +G1 X128.125 Y112.362 E.1473 +M204 P1000 +G1 X128.125 Y112.362 F12000 +G1 X127.581 Y112.362 +M204 P800 +G1 F2400 +G1 X124.324 Y109.104 E.14728 +M204 P1000 +G1 X124.324 Y109.104 F12000 +G1 X123.78 Y109.104 +M204 P800 +G1 F2400 +G1 X127.037 Y112.362 E.14728 +M204 P1000 +G1 X127.037 Y112.362 F12000 +G1 X126.493 Y112.362 +M204 P800 +G1 F2400 +G1 X123.236 Y109.104 E.14728 +M204 P1000 +G1 X123.236 Y109.104 F12000 +G1 X122.692 Y109.104 +M204 P800 +G1 F2400 +G1 X125.95 Y112.362 E.1473 +M204 P1000 +G1 X125.95 Y112.362 F12000 +G1 X125.406 Y112.362 +M204 P800 +G1 F2400 +G1 X122.149 Y109.104 E.14728 +M204 P1000 +G1 X122.149 Y109.104 F12000 +G1 X121.605 Y109.104 +M204 P800 +G1 F2400 +G1 X124.862 Y112.362 E.14728 +M204 P1000 +G1 X124.862 Y112.362 F12000 +G1 X124.318 Y112.362 +M204 P800 +G1 F2400 +G1 X121.061 Y109.104 E.14728 +M204 P1000 +G1 X121.061 Y109.104 F12000 +G1 X120.528 Y109.115 +M204 P800 +G1 F2400 +G1 X123.775 Y112.362 E.14681 +M204 P1000 +G1 X123.775 Y112.362 F12000 +G1 X123.231 Y112.362 +M204 P800 +G1 F2400 +G1 X120.123 Y109.253 E.14055 +M204 P1000 +G1 X120.123 Y109.253 F12000 +G1 X119.863 Y109.537 +M204 P800 +G1 F2400 +G1 X122.687 Y112.362 E.12771 +M204 P1000 +G1 X122.687 Y112.362 F12000 +G1 X122.143 Y112.362 +M204 P800 +G1 F2400 +G1 X119.753 Y109.972 E.10806 +M204 P1000 +G1 X119.753 Y109.972 F12000 +G1 X119.753 Y110.515 +M204 P800 +G1 F2400 +G1 X121.6 Y112.362 E.08351 +M204 P1000 +G1 X121.6 Y112.362 F12000 +G1 X121.056 Y112.362 +M204 P800 +G1 F2400 +G1 X119.753 Y111.059 E.05891 +M204 P1000 +G1 X119.753 Y111.059 F12000 +G1 X119.764 Y111.613 +M204 P800 +G1 F2400 +G1 X120.484 Y112.333 E.03255 +M204 P1000 +G1 X120.484 Y112.333 F12000 +G1 X119.942 Y112.338 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F6600 +G3 X119.531 Y111.523 I.608 J-.818 E.03204 +G1 X119.531 Y110.469 E.03568 +G1 X116.406 Y113.594 E.14959 +G1 X116.406 Y111.757 E.06218 +G1 X118.243 Y113.594 E.08794 +G1 X122.964 Y113.594 E.1598 +G1 X123.371 Y114.002 E.01951 +G1 X123.674 Y114.002 E.01026 +G1 X125.092 Y112.584 E.06788 +G1 X124.908 Y112.584 E.00623 +G1 X126.326 Y114.002 E.06788 +G1 X128.326 Y114.002 E.0677 +M204 P1000 +G1 X128.326 Y114.002 F12000 +G1 X129.735 Y114.002 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.450028 +G1 F8842.862 +G1 X133.798 Y114.002 E.13754 +;WIDTH:0.484952 +G1 F8144.206 +G1 X133.882 Y113.984 E.00316 +;WIDTH:0.519876 +G1 F7547.866 +G2 X133.984 Y113.882 I.017 J-.085 E.00674 +;WIDTH:0.484952 +G1 F8144.206 +G1 X134.002 Y113.798 E.00316 +;WIDTH:0.450028 +G1 F8842.862 +G1 X134.002 Y96.202 E.59565 +;WIDTH:0.484954 +G1 F8144.17 +G1 X133.984 Y96.118 E.00316 +;WIDTH:0.51988 +G1 F7547.803 +G2 X133.882 Y96.016 I-.085 J-.017 E.00674 +;WIDTH:0.484954 +G1 F8144.17 +G1 X133.798 Y95.998 E.00316 +;WIDTH:0.45003 +G1 F8842.818 +G1 X127.642 Y95.998 E.20839 +M204 P1000 +G1 X127.642 Y95.998 F12000 +G1 X128.157 Y96.406 +M204 P2000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F6600 +G1 X127.473 Y96.406 E.02315 +G1 X127.066 Y95.998 E.01951 +G1 X126.326 Y95.998 E.02505 +G1 X124.908 Y97.416 E.06788 +G1 X125.092 Y97.416 E.00623 +G1 X123.674 Y95.998 E.06788 +G1 X121.674 Y95.998 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.674 Y95.998 E-.41563 +G1 X124.437 Y96.761 E-.22437 +;WIPE_END +G1 X116.406 Y100.243 Z19.153 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X116.406 Y98.243 E.0677 +G1 X118.243 Y96.406 E.08794 +G1 X116.406 Y96.406 E.06218 +G1 X118.777 Y98.777 E.1135 +G2 X118.855 Y100.497 I5.459 J.616 E.05852 +G2 X120.142 Y102.182 I9.927 J-6.252 E.07187 +G1 X116.406 Y105.918 E.17884 +G1 X116.406 Y104.082 E.06215 +G1 X121.206 Y108.882 E.22977 +G1 X121.118 Y108.882 E.00298 +G1 X123.555 Y106.445 E.11666 +G1 X124.805 Y108.007 E.06772 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.555 Y106.445 E-.41575 +G1 X122.792 Y107.208 E-.22425 +;WIPE_END +G1 X125.014 Y102.173 Z19.096 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X126.272 Y103.728 E.0677 +G1 X128.882 Y101.118 E.12494 +G1 X128.794 Y101.118 E.00298 +G1 X133.594 Y105.918 E.22977 +G1 X133.594 Y104.082 E.06215 +G1 X129.706 Y107.97 E.18612 +G3 X130.931 Y109.561 I-7.705 J7.202 E.06807 +G3 X131.002 Y111.002 I-4.011 J.921 E.04909 +G1 X133.594 Y113.594 E.12408 +G1 X131.757 Y113.594 E.06218 +G1 X133.594 Y111.757 E.08794 +G1 X133.594 Y109.757 E.0677 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.594 Y111.757 E-.41563 +G1 X132.831 Y112.521 E-.22437 +;WIPE_END +G1 X133.594 Y100.243 Z19.215 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P2000 +G1 F6600 +G1 X133.594 Y98.243 E.0677 +G1 X131.757 Y96.406 E.08794 +G1 X133.594 Y96.406 E.06218 +G1 X131.221 Y98.779 E.11359 +G2 X130.12 Y97.416 I-1.111 J-.229 E.06777 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X131.221 Y98.779 I-.01 J1.134 E-.41608 +G1 X131.983 Y98.017 E-.22392 +;WIPE_END +G1 X122.795 Y114.002 Z19.322 F12000 +G1 Z19 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.45003 +G1 F8842.818 +G1 X116.202 Y114.002 E.22318 +;WIDTH:0.484953 +G1 F8144.188 +G1 X116.118 Y113.984 E.00316 +;WIDTH:0.519878 +G1 F7547.834 +G3 X116.016 Y113.882 I-.017 J-.085 E.00674 +;WIDTH:0.484953 +G1 F8144.188 +G1 X115.998 Y113.798 E.00316 +;WIDTH:0.450028 +G1 F8842.862 +G1 X115.998 Y96.202 E.59565 +;WIDTH:0.484952 +G1 F8144.206 +G1 X116.016 Y96.118 E.00316 +;WIDTH:0.519876 +G1 F7547.866 +G3 X116.118 Y96.016 I.085 J-.017 E.00674 +;WIDTH:0.484952 +G1 F8144.206 +G1 X116.202 Y95.998 E.00316 +;WIDTH:0.45003 +G1 F8842.818 +G1 X120.065 Y95.998 E.13077 +M204 P1000 +M106 S76.5 +;LAYER_CHANGE +;Z:19.2 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;19.2 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X116.985 Y95.998 E-.64 +;WIPE_END +G1 X116.985 Y95.998 Z19 F12000 +G1 X120.173 Y111.943 Z19.284 +;AFTER_LAYER_CHANGE +;19.2 +G1 X120.173 Y111.943 +G1 Z19.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X120.088 Y111.864 E.00393 +G1 X119.992 Y111.627 E.00866 +G1 X119.983 Y111.521 E.0036 +G1 X119.983 Y109.944 E.05338 +G1 X120.073 Y109.624 E.01125 +G1 X120.279 Y109.421 E.00979 +G1 X120.485 Y109.343 E.00746 +G1 X120.593 Y109.334 E.00367 +G1 X126.456 Y109.334 E.19846 +G1 X126.035 Y108.827 E.02231 +G1 X126.027 Y108.818 E.00041 +G1 X119.363 Y100.485 E.36116 +G1 X119.238 Y100.209 E.01026 +G1 X119.229 Y100.104 E.00357 +G1 X119.229 Y98.479 E.055 +G1 X119.275 Y98.246 E.00804 +G1 X119.385 Y98.071 E.007 +G1 X119.58 Y97.926 E.00823 +G1 X119.84 Y97.868 E.00902 +G1 X130.16 Y97.868 E.34932 +G1 X130.42 Y97.926 E.00902 +G1 X130.615 Y98.071 E.00823 +G1 X130.725 Y98.246 E.007 +G1 X130.771 Y98.479 E.00804 +G1 X130.771 Y100.056 E.05338 +G1 X130.735 Y100.261 E.00705 +G1 X130.639 Y100.434 E.0067 +G1 X130.374 Y100.628 E.01112 +G1 X130.16 Y100.666 E.00736 +G1 X123.213 Y100.666 E.23515 +G1 X130.422 Y109.574 E.38789 +G1 X130.521 Y109.749 E.00681 +G1 X130.558 Y109.958 E.00718 +G1 X130.558 Y111.521 E.05291 +G1 X130.523 Y111.726 E.00704 +G1 X130.418 Y111.911 E.0072 +G1 X130.22 Y112.068 E.00855 +G1 X129.947 Y112.132 E.00949 +G1 X120.593 Y112.132 E.31662 +G1 X120.291 Y112.052 E.01057 +G1 X120.217 Y111.983 E.00342 +G1 X120.217 Y111.983 F12000 +G1 X120.463 Y111.656 +M204 P800 +;TYPE:External perimeter +G1 F2400 +G1 X120.425 Y111.636 E.00145 +G1 X120.39 Y111.521 E.00407 +G1 X120.39 Y109.944 E.05338 +G1 X120.452 Y109.798 E.00537 +G1 X120.593 Y109.741 E.00515 +G1 X127.357 Y109.741 E.22895 +G1 X127.01 Y109.362 E.01739 +G1 X127.004 Y109.355 E.00031 +G1 X126.348 Y108.567 E.03471 +G1 X126.345 Y108.564 E.00014 +G1 X119.681 Y100.231 E.36116 +G1 X119.636 Y100.104 E.00456 +G1 X119.636 Y98.479 E.055 +G1 X119.688 Y98.343 E.00493 +G1 X119.84 Y98.275 E.00564 +G1 X130.16 Y98.275 E.34932 +G1 X130.312 Y98.343 E.00564 +G1 X130.364 Y98.479 E.00493 +G1 X130.364 Y100.056 E.05338 +G1 X130.32 Y100.182 E.00452 +G1 X130.189 Y100.257 E.00511 +G1 X130.16 Y100.259 E.00098 +G1 X122.33 Y100.259 E.26504 +G1 X122.798 Y100.801 E.02424 +G1 X122.803 Y100.806 E.00024 +G1 X130.106 Y109.83 E.39295 +G1 X130.151 Y109.958 E.00459 +G1 X130.151 Y111.521 E.05291 +G1 X130.104 Y111.651 E.00468 +G1 X129.947 Y111.725 E.00587 +G1 X120.593 Y111.725 E.31662 +G1 X120.516 Y111.684 E.00295 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.463 Y111.656 E-.01246 +G1 X120.425 Y111.636 E-.00892 +G1 X120.39 Y111.521 E-.02498 +G1 X120.39 Y109.944 E-.32772 +G1 X120.452 Y109.798 E-.03296 +G1 X120.593 Y109.741 E-.03161 +G1 X121.562 Y109.741 E-.20135 +;WIPE_END +G1 X134.368 Y95.632 Z19.533 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M73 P90 R2 +M73 Q90 S2 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X126.862 Y95.998 Z19.344 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +G1 F8843.491 +G1 X120.641 Y95.998 E.21057 +G1 X122.144 Y97.502 E.07197 +G1 X128.569 Y97.502 E.21748 +G1 X127.066 Y95.998 E.07197 +G1 X126.868 Y96.476 E.01751 +;WIDTH:0.590941 +G1 F6569.091 +G1 X127.416 Y97.024 E.03531 +G1 X122.342 Y97.024 E.23121 +G1 X121.794 Y96.476 E.03531 +G1 X126.664 Y96.476 E.22192 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.584 Y96.476 E-.64 +;WIPE_END +G1 X121.868 Y112.498 Z19.481 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X127.656 Y112.498 E.19592 +G1 X129.159 Y114.002 E.07197 +G1 X123.371 Y114.002 E.19592 +G1 X122.012 Y112.642 E.06508 +M204 P1000 +G1 X122.012 Y112.642 F12000 +G1 X123.021 Y112.976 +M204 P1500 +;WIDTH:0.590941 +G1 F6569.091 +G1 X127.458 Y112.976 E.20219 +G1 X128.006 Y113.524 E.03531 +G1 X123.569 Y113.524 E.20219 +G1 X123.165 Y113.12 E.02604 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.569 Y113.524 E-.11873 +G1 X126.077 Y113.524 E-.52127 +;WIPE_END +G1 X129.947 Y114.183 Z19.269 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +;TYPE:Bridge infill +;WIDTH:0.401073 +;HEIGHT:0.4 +M106 S127.5 +G1 X128.284 Y112.52 E.12353 +G1 X128.922 Y112.52 E.03351 +G1 X130.382 Y113.98 E.10845 +G1 X131.02 Y113.98 E.03351 +G1 X129.56 Y112.52 E.10845 +G1 X129.955 Y112.52 E.02075 +G1 X130.169 Y112.491 E.01134 +G1 X131.658 Y113.98 E.11061 +G1 X132.296 Y113.98 E.03351 +G1 X130.584 Y112.268 E.12717 +G2 X130.862 Y111.908 I-.441 J-.627 E.02426 +G1 X132.934 Y113.98 E.15391 +G1 X133.572 Y113.98 E.03351 +G1 X130.936 Y111.344 E.19581 +G1 X130.94 Y110.711 E.03325 +G1 X133.98 Y113.75 E.22578 +G1 X133.98 Y113.112 E.03351 +G1 X130.945 Y110.077 E.22545 +G2 X130.733 Y109.342 I-1.147 J-.067 E.04096 +G1 X130.248 Y108.742 E.04052 +G1 X133.98 Y112.475 E.27726 +G1 X133.98 Y111.837 E.03351 +G1 X127.542 Y105.398 E.47827 +G1 X124.835 Y102.054 E.22598 +G1 X133.98 Y111.199 E.67931 +G1 X133.98 Y110.561 E.03351 +G1 X124.473 Y101.054 E.7062 +G1 X125.111 Y101.054 E.03351 +G1 X133.98 Y109.923 E.65881 +G1 X133.98 Y109.285 E.03351 +G1 X125.749 Y101.054 E.61142 +G1 X126.387 Y101.054 E.03351 +G1 X133.98 Y108.647 E.56403 +G1 X133.98 Y108.009 E.03351 +G1 X127.025 Y101.054 E.51663 +G1 X127.663 Y101.054 E.03351 +G1 X133.98 Y107.371 E.46924 +G1 X133.98 Y106.733 E.03351 +M73 Q90 S1 +G1 X128.301 Y101.054 E.42185 +G1 X128.939 Y101.054 E.03351 +M73 P90 R1 +G1 X133.98 Y106.095 E.37446 +G1 X133.98 Y105.458 E.03346 +G1 X129.577 Y101.054 E.3271 +G1 X130.209 Y101.048 E.0332 +G1 X133.98 Y104.82 E.28016 +G1 X133.98 Y104.182 E.03351 +G1 X130.694 Y100.896 E.24409 +G2 X131.005 Y100.569 I-.43 J-.721 E.024 +G1 X133.98 Y103.544 E.22099 +G1 X133.98 Y102.906 E.03351 +G1 X131.147 Y100.073 E.21044 +G1 X131.152 Y99.44 E.03325 +G1 X133.98 Y102.268 E.21007 +G1 X133.98 Y101.63 E.03351 +G1 X130.955 Y98.605 E.2247 +M106 S76.5 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.133 Y100.783 E-.64 +;WIPE_END +G1 X129.395 Y97.683 Z19.285 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +M106 S127.5 +G1 X127.732 Y96.02 E.12353 +G1 X128.37 Y96.02 E.03351 +M73 Q91 S1 +G1 X130.033 Y97.683 E.12353 +M106 S76.5 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.37 Y96.02 E-.48874 +G1 X127.732 Y96.02 E-.13258 +M73 P91 R1 +G1 X127.796 Y96.084 E-.01868 +;WIPE_END +G1 X128.805 Y95.817 Z19.218 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +M106 S127.5 +G1 X133.98 Y100.992 E.38441 +G1 X133.98 Y100.354 E.03351 +G1 X129.646 Y96.02 E.32194 +G1 X130.284 Y96.02 E.03351 +G1 X133.98 Y99.716 E.27455 +G1 X133.98 Y99.078 E.03351 +G1 X130.922 Y96.02 E.22716 +G1 X131.56 Y96.02 E.03351 +G1 X133.98 Y98.44 E.17976 +G1 X133.98 Y97.803 E.03346 +G1 X132.197 Y96.02 E.13245 +G1 X132.835 Y96.02 E.03351 +G1 X133.98 Y97.165 E.08505 +G1 X133.98 Y96.527 E.03351 +G1 X133.271 Y95.817 E.0527 +M106 S76.5 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.98 Y96.527 E-.20852 +G1 X133.98 Y97.165 E-.13258 +G1 X132.963 Y96.148 E-.2989 +;WIPE_END +G1 X115.817 Y113.252 Z19.623 F12000 +G1 Z19.2 F720 +G1 E.8 F1500 +;WIDTH:0.409924 +M106 S127.5 +G1 X116.546 Y113.98 E.05653 +G1 X117.196 Y113.98 E.03567 +G1 X116.02 Y112.804 E.09125 +G1 X116.02 Y112.154 E.03567 +G1 X117.846 Y113.98 E.14169 +G1 X118.497 Y113.98 E.03572 +G1 X116.02 Y111.503 E.19221 +G1 X116.02 Y110.853 E.03567 +G1 X119.147 Y113.98 E.24265 +G1 X119.798 Y113.98 E.03572 +G1 X116.02 Y110.202 E.29316 +G1 X116.02 Y109.552 E.03567 +G1 X120.448 Y113.98 E.3436 +G1 X121.099 Y113.98 E.03572 +G1 X116.02 Y108.901 E.39412 +G1 X116.02 Y108.251 E.03567 +G1 X121.749 Y113.98 E.44455 +G1 X122.399 Y113.98 E.03567 +G1 X120.737 Y112.317 E.12901 +M106 S76.5 +G1 X120.737 Y112.317 F12000 +G1 X119.798 Y111.378 +M106 S127.5 +G1 F1500 +G1 X116.02 Y107.601 E.29312 +G1 X116.02 Y106.95 E.03572 +G1 X119.595 Y110.525 E.27741 +G1 X119.602 Y109.882 E.03528 +G1 X116.02 Y106.3 E.27795 +G1 X116.02 Y105.649 E.03572 +G1 X119.764 Y109.393 E.29052 +G3 X120.099 Y109.078 I.863 J.583 E.02544 +G1 X116.02 Y104.999 E.31652 +G1 X116.02 Y104.348 E.03572 +G1 X120.618 Y108.946 E.35679 +G1 X121.268 Y108.946 E.03567 +G1 X116.02 Y103.698 E.40723 +G1 X116.02 Y103.047 E.03572 +G1 X121.918 Y108.946 E.45771 +G1 X122.569 Y108.946 E.03572 +G1 X116.02 Y102.397 E.50818 +G1 X116.02 Y101.747 E.03567 +G1 X123.219 Y108.946 E.55862 +G1 X123.87 Y108.946 E.03572 +G1 X116.02 Y101.096 E.60914 +G1 X116.02 Y100.446 E.03567 +G1 X124.52 Y108.946 E.65958 +G1 X125.171 Y108.946 E.03572 +G1 X116.02 Y99.795 E.71009 +G1 X116.02 Y99.145 E.03567 +G1 X124.908 Y108.033 E.68968 +G1 X122.298 Y104.773 E.22914 +G1 X116.02 Y98.494 E.48719 +G1 X116.02 Y97.844 E.03567 +G1 X119.689 Y101.513 E.2847 +G1 X119.051 Y100.716 E.05602 +G3 X118.841 Y100.015 I.93 J-.66 E.04087 +G1 X116.02 Y97.194 E.2189 +G1 X116.02 Y96.543 E.03572 +G1 X118.841 Y99.365 E.21894 +G1 X118.841 Y98.714 E.03572 +G1 X116.147 Y96.02 E.20905 +G1 X116.797 Y96.02 E.03567 +G1 X118.907 Y98.129 E.16369 +G1 X118.913 Y98.099 E.00168 +G3 X119.176 Y97.748 I.887 J.391 E.02428 +G1 X117.448 Y96.02 E.13409 +G1 X118.098 Y96.02 E.03567 +G1 X119.595 Y97.516 E.11612 +G3 X120.209 Y97.48 I.429 J2.052 E.03387 +G1 X118.749 Y96.02 E.11329 +G1 X119.399 Y96.02 E.03567 +G1 X120.859 Y97.48 E.11329 +G1 X121.51 Y97.48 E.03572 +G1 X119.847 Y95.817 E.12904 +M106 S76.5 +M106 S117.3 +;LAYER_CHANGE +;Z:19.4 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;19.4 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.51 Y97.48 E-.48874 +G1 X120.859 Y97.48 E-.13529 +G1 X120.805 Y97.426 E-.01597 +;WIPE_END +G1 X120.805 Y97.426 Z19.2 F12000 +G1 X120.173 Y111.943 Z19.454 +;AFTER_LAYER_CHANGE +;19.4 +G1 X120.173 Y111.943 +G1 Z19.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X120.088 Y111.864 E.00393 +G1 X119.992 Y111.627 E.00866 +G1 X119.983 Y109.944 E.05697 +G1 X120.073 Y109.624 E.01125 +G1 X120.279 Y109.421 E.00979 +G1 X120.485 Y109.343 E.00746 +G1 X120.593 Y109.334 E.00367 +G1 X126.456 Y109.334 E.19846 +G1 X126.035 Y108.827 E.02231 +G1 X119.363 Y100.485 E.36157 +G3 X119.229 Y100.104 I.758 J-.48 E.01379 +G1 X119.229 Y98.479 E.055 +G1 X119.275 Y98.246 E.00804 +G1 X119.385 Y98.071 E.007 +G1 X119.58 Y97.926 E.00823 +G1 X119.84 Y97.868 E.00902 +G1 X130.16 Y97.868 E.34932 +G1 X130.42 Y97.926 E.00902 +M73 P92 R1 +M73 Q92 S1 +G1 X130.615 Y98.071 E.00823 +G1 X130.725 Y98.246 E.007 +G1 X130.771 Y98.479 E.00804 +G1 X130.771 Y100.056 E.05338 +G1 X130.735 Y100.261 E.00705 +G3 X130.374 Y100.628 I-.693 J-.322 E.01777 +G1 X130.16 Y100.666 E.00736 +G1 X123.213 Y100.666 E.23515 +G1 X130.422 Y109.574 E.38789 +G3 X130.558 Y109.958 I-.599 J.428 E.01397 +G1 X130.558 Y111.521 E.05291 +G1 X130.523 Y111.726 E.00704 +G1 X130.418 Y111.911 E.0072 +G1 X130.22 Y112.068 E.00855 +G1 X129.947 Y112.132 E.00949 +G1 X120.593 Y112.132 E.31662 +G1 X120.291 Y112.052 E.01057 +G1 X120.217 Y111.983 E.00342 +G1 X120.217 Y111.983 F12000 +G1 X120.463 Y111.656 +M204 P800 +;TYPE:External perimeter +G1 F2400 +G1 X120.425 Y111.636 E.00145 +G1 X120.39 Y111.521 E.00407 +G1 X120.39 Y109.944 E.05338 +G3 X120.593 Y109.741 I.235 J.032 E.01044 +G1 F2399.997 +G1 X126.677 Y109.741 E.20594 +G1 F2379.19 +G1 X127.357 Y109.741 E.02302 +G1 X127.01 Y109.362 E.01739 +G1 F2399.901 +G1 X126.345 Y108.564 E.03516 +G1 X119.681 Y100.231 E.36116 +G1 X119.636 Y100.104 E.00456 +G1 X119.636 Y98.479 E.055 +G1 F2400 +G3 X119.84 Y98.275 I.236 J.032 E.01049 +G1 F2399.997 +G1 X130.16 Y98.275 E.34932 +G1 F2400 +G3 X130.364 Y98.479 I-.032 J.236 E.01049 +G1 X130.364 Y100.056 E.05338 +G1 X130.32 Y100.182 E.00452 +G1 X130.189 Y100.257 E.00511 +G1 X130.16 Y100.259 E.00098 +G1 X122.33 Y100.259 E.26504 +G1 X122.803 Y100.806 E.02448 +G1 X130.106 Y109.83 E.39295 +G1 X130.151 Y109.958 E.00459 +G1 X130.151 Y111.521 E.05291 +G3 X129.947 Y111.725 I-.237 J-.033 E.01048 +G1 F2399.997 +G1 X120.593 Y111.725 E.31662 +G1 F2400 +G1 X120.516 Y111.684 E.00295 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.463 Y111.656 E-.01246 +G1 X120.425 Y111.636 E-.00892 +G1 X120.39 Y111.521 E-.02498 +G1 X120.39 Y109.944 E-.32772 +G3 X120.593 Y109.741 I.235 J.032 E-.06407 +G1 X121.564 Y109.741 E-.20185 +;WIPE_END +G1 X134.368 Y95.632 Z19.733 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X116.031 Y113.891 Z19.826 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F7116.79 +G2 X116.093 Y113.907 I.018 J.058 E.01318 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y113.257 E.02224 +G1 X116.815 Y114.068 E.03897 +;WIDTH:0.41646 +G1 F9637.536 +G1 X116.888 Y114.134 E.00306 +;WIDTH:0.38292 +G1 F10588.235 +G1 X116.869 Y114.205 E.00208 +G2 X116.929 Y114.145 I.02 J-.04 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X117.318 Y114.002 E.01403 +G1 X115.998 Y112.682 E.06319 +G1 X115.998 Y112.106 E.0195 +G1 X117.894 Y114.002 E.09076 +G1 X118.47 Y114.002 E.0195 +G1 X115.998 Y111.53 E.11833 +G1 X115.998 Y110.954 E.0195 +G1 X119.046 Y114.002 E.14591 +G1 X119.621 Y114.002 E.01946 +G1 X115.998 Y110.379 E.17343 +G1 X115.998 Y109.803 E.0195 +G1 X120.197 Y114.002 E.201 +G1 X120.773 Y114.002 E.0195 +G1 X115.998 Y109.227 E.22858 +G1 X115.998 Y108.652 E.01946 +G1 X121.348 Y114.002 E.2561 +G1 X121.487 Y114.041 E.00489 +;WIDTH:0.41646 +G1 F9637.536 +G1 X121.625 Y114.08 E.00445 +;WIDTH:0.38292 +G1 F10588.235 +G1 X121.606 Y114.151 E.00208 +G2 X121.666 Y114.091 I.02 J-.04 E.00315 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X121.606 Y114.151 I-.04 J.02 E-.02315 +G1 X121.625 Y114.08 E-.01527 +G1 X121.487 Y114.041 E-.0298 +G1 X121.348 Y114.002 E-.03 +G1 X119.505 Y112.158 E-.54178 +;WIPE_END +G1 X120.227 Y112.249 Z19.413 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F10588.235 +G1 X120.078 Y112.253 E.00421 +G1 X119.828 Y112.003 E.01 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.733 Y111.859 E.00536 +;WIDTH:0.449999 +G1 F8843.491 +G1 X119.637 Y111.715 E.00586 +G1 X115.998 Y108.076 E.1742 +G1 X115.998 Y107.5 E.0195 +G1 X119.616 Y111.118 E.17319 +G1 X119.616 Y110.543 E.01946 +G1 X115.998 Y106.925 E.17319 +G1 X115.998 Y106.349 E.0195 +G1 X119.616 Y109.967 E.17319 +G1 X119.728 Y109.503 E.01616 +G1 X115.998 Y105.773 E.17855 +G1 X115.998 Y105.197 E.0195 +G1 X119.986 Y109.185 E.1909 +G1 X120.379 Y109.002 E.01467 +G1 X115.998 Y104.622 E.20969 +G1 X115.998 Y104.046 E.0195 +G1 X120.987 Y109.034 E.2388 +;WIDTH:0.41646 +G1 F9637.536 +G1 X121.055 Y109.1 E.00294 +;WIDTH:0.38292 +G1 F10588.235 +G1 X121.036 Y109.171 E.00208 +G2 X121.096 Y109.111 I.02 J-.04 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X121.496 Y108.967 E.01439 +G1 X115.998 Y103.47 E.26316 +G1 X115.998 Y102.895 E.01946 +G1 X122.071 Y108.967 E.29069 +G1 X122.647 Y108.967 E.0195 +G1 X115.998 Y102.319 E.31826 +G1 X115.998 Y101.743 E.0195 +G1 X123.223 Y108.967 E.34583 +G1 X123.798 Y108.967 E.01946 +G1 X115.998 Y101.168 E.37336 +G1 X115.998 Y100.592 E.0195 +G1 X124.374 Y108.967 E.40093 +G1 X124.95 Y108.967 E.0195 +G1 X115.998 Y100.016 E.4285 +G1 X115.998 Y99.441 E.01946 +G1 X125.525 Y108.967 E.45603 +G1 X125.701 Y109.014 E.00617 +;WIDTH:0.41646 +G1 F9637.536 +G1 X125.877 Y109.06 E.00565 +;WIDTH:0.38292 +G1 F10588.235 +G1 X125.849 Y108.931 E.00373 +;WIDTH:0.407366 +G1 F9878.007 +G1 X124.266 Y107.162 E.07194 +;WIDTH:0.449999 +G1 F8843.491 +G1 X124.008 Y106.874 E.01309 +G1 X115.998 Y98.865 E.38341 +G1 X115.998 Y98.289 E.0195 +G1 X121.698 Y103.989 E.27286 +G1 X121.956 Y104.277 E.01309 +;WIDTH:0.407366 +G1 F9878.007 +G1 X123.55 Y106.057 E.07241 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.956 Y104.277 E-.49655 +G1 X121.698 Y103.989 E-.08035 +G1 X121.483 Y103.774 E-.0631 +;WIPE_END +G1 X121.24 Y103.172 Z19.411 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F9878.007 +G1 X119.647 Y101.392 E.07239 +;WIDTH:0.449999 +G1 F8843.491 +G1 X119.389 Y101.104 E.01309 +G1 X115.998 Y97.713 E.16233 +G1 X115.998 Y97.138 E.01946 +G1 X118.863 Y100.002 E.13712 +G1 X118.927 Y100.124 E.00466 +;WIDTH:0.41646 +G1 F9637.536 +G1 X118.99 Y100.247 E.00429 +;WIDTH:0.38292 +G1 F10588.235 +G1 X118.971 Y100.318 E.00208 +G2 X119.031 Y100.258 I.02 J-.04 E.00315 +M204 P1000 +G1 X119.031 Y100.258 F12000 +G1 X118.863 Y99.426 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y96.562 E.13712 +G1 X116.011 Y95.998 E.0191 +G1 X118.863 Y98.851 E.13655 +G1 X118.944 Y98.902 E.00324 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.025 Y98.954 E.00299 +;WIDTH:0.38292 +G1 F10588.235 +G1 X119.006 Y99.025 E.00208 +G2 X119.067 Y98.965 I.02 J-.04 E.00321 +;WIDTH:0.449999 +G1 F8843.491 +G1 X118.894 Y98.306 E.02306 +G1 X116.586 Y95.998 E.11048 +G1 X117.162 Y95.998 E.0195 +G1 X119.063 Y97.9 E.09102 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.162 Y95.998 E-.55883 +G1 X116.772 Y95.998 E-.08117 +;WIPE_END +G1 X117.882 Y96.142 Z19.42 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X119.37 Y97.631 E.07125 +G1 X119.841 Y97.502 E.01653 +G1 X130.182 Y97.504 E.35003 +G1 X128.82 Y96.142 E.0652 +G1 X128.82 Y95.998 E.00487 +G1 X117.738 Y95.998 E.37511 +M204 P1000 +G1 X117.738 Y95.998 F12000 +G1 X118.91 Y96.484 +M204 P1500 +;WIDTH:0.606955 +G1 F6382.583 +G1 X119.841 Y96.476 E.04367 +;WIDTH:0.590931 +G1 F6569.211 +M73 Q93 S1 +G1 X128.482 Y96.476 E.39375 +G1 X129.026 Y97.024 E.03519 +G1 X119.841 Y97.024 E.41854 +;WIDTH:0.606955 +G1 F6382.583 +M73 P93 R1 +G1 X119.501 Y97.075 E.01612 +G1 X119.054 Y96.628 E.02965 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.501 Y97.075 E-.13137 +G1 X119.841 Y97.024 E-.07145 +G1 X121.945 Y97.024 E-.43718 +;WIPE_END +G1 X120.366 Y112.444 Z19.671 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X121.924 Y114.002 E.07458 +G1 X122.5 Y114.002 E.0195 +G1 X120.997 Y112.498 E.07197 +G1 X129.946 Y112.498 E.30291 +G1 X130.338 Y112.412 E.01358 +G1 X130.67 Y112.173 E.01385 +G1 X130.893 Y111.742 E.01643 +G1 X130.914 Y111.477 E.009 +G1 X133.438 Y114.002 E.12085 +G1 X122.932 Y114.002 E.35562 +G1 X122.932 Y113.858 E.00487 +G1 X121.716 Y112.642 E.05821 +M204 P1000 +G1 X121.716 Y112.642 F12000 +G1 X122.725 Y112.976 +M204 P1500 +;WIDTH:0.590931 +G1 F6569.211 +G1 X129.946 Y112.976 E.32904 +G1 X130.1 Y112.951 E.00711 +;WIDTH:0.577623 +G1 F6732.709 +G1 X130.29 Y112.865 E.00927 +;WIDTH:0.535082 +G1 F7314.669 +G1 X130.48 Y112.778 E.00855 +;WIDTH:0.492541 +G1 F8006.755 +G1 X130.671 Y112.692 E.00783 +;WIDTH:0.449999 +G1 F8843.491 +G2 X131.108 Y112.247 I-.838 J-1.261 E.02126 +G1 X132.455 Y113.594 E.06448 +G1 X130.522 Y113.594 E.06543 +;WIDTH:0.496977 +G1 F7928.531 +G1 X130.33 Y113.571 E.0073 +;WIDTH:0.543954 +G1 F7185.146 +G1 X130.138 Y113.547 E.00806 +;WIDTH:0.590931 +G1 F6569.211 +G1 X129.946 Y113.524 E.00881 +G1 X123.269 Y113.52 E.30425 +G1 X122.869 Y113.12 E.02578 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.269 Y113.52 E-.11756 +G1 X125.783 Y113.522 E-.52244 +;WIPE_END +G1 X130.348 Y113.216 Z19.48 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.392446 +G1 F10299.664 +G2 X131.133 Y112.808 I-.381 J-1.692 E.026 +G1 X131.542 Y113.216 E.01679 +G1 X130.552 Y113.216 E.02877 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.542 Y113.216 E-.20573 +G1 X131.133 Y112.808 E-.12005 +G3 X130.348 Y113.216 I-1.166 J-1.284 E-.1859 +;WIPE_END +G1 E-.12832 F2100 +G1 X130.918 Y110.906 Z19.442 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X134.002 Y113.989 E.14761 +G1 X134.002 Y113.414 E.01946 +G1 X130.922 Y110.334 E.14744 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.099 Y112.511 E-.64 +;WIPE_END +G1 X129.603 Y108.223 Z19.497 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G1 X130.687 Y109.434 E.04595 +;WIDTH:0.41646 +G1 F9637.536 +G1 X130.786 Y109.578 E.00543 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.885 Y109.722 E.00592 +G1 X134.002 Y112.838 E.14918 +G1 X134.002 Y112.262 E.0195 +G1 X129.112 Y107.373 E.23406 +G1 X128.853 Y107.085 E.01311 +;WIDTH:0.409324 +G1 F9825.221 +G1 X127.16 Y105.205 E.07708 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.853 Y107.085 E-.52576 +G1 X129.112 Y107.373 E-.08049 +G1 X129.227 Y107.488 E-.03375 +;WIPE_END +G1 X124.718 Y102.187 Z19.521 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F9825.221 +G1 X126.41 Y104.067 E.07706 +;WIDTH:0.449999 +G1 F8843.491 +G1 X126.67 Y104.355 E.01313 +G1 X134.002 Y111.687 E.35098 +G1 X134.002 Y111.111 E.0195 +G1 X124.227 Y101.337 E.4679 +G1 X124.499 Y101.033 E.01381 +G1 X134.002 Y110.535 E.45488 +G1 X134.002 Y109.96 E.01946 +G1 X125.075 Y101.033 E.42733 +G1 X125.65 Y101.033 E.01946 +G1 X134.002 Y109.384 E.39978 +G1 X134.002 Y108.808 E.0195 +G1 X126.226 Y101.033 E.37221 +G1 X126.802 Y101.033 E.0195 +G1 X134.002 Y108.232 E.34464 +G1 X134.002 Y107.657 E.01946 +G1 X127.377 Y101.033 E.31711 +G1 X127.953 Y101.033 E.0195 +G1 X134.002 Y107.081 E.28954 +G1 X134.002 Y106.505 E.0195 +G1 X128.529 Y101.033 E.26197 +G1 X129.104 Y101.033 E.01946 +G1 X134.002 Y105.93 E.23444 +G1 X134.002 Y105.354 E.0195 +G1 X129.68 Y101.033 E.20687 +G1 X129.57 Y100.824 E.00799 +;WIDTH:0.433298 +G1 F9221.832 +G1 X129.582 Y100.871 E.00157 +G1 X129.501 Y100.893 E.00272 +G1 X129.523 Y100.812 E.00272 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.243 Y101.02 E.02537 +G1 X134.002 Y104.778 E.17992 +G1 X134.002 Y104.203 E.01946 +G1 X130.679 Y100.88 E.15907 +G1 X130.967 Y100.593 E.01376 +G1 X134.002 Y103.627 E.14526 +G1 X134.002 Y103.051 E.0195 +G1 X131.125 Y100.175 E.1377 +G1 X131.129 Y99.603 E.01936 +G1 X134.002 Y102.476 E.13753 +G1 X134.002 Y101.9 E.0195 +G1 X131.133 Y99.032 E.13731 +M204 P1000 +G1 X131.133 Y99.032 F12000 +G1 X130.543 Y97.707 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G3 X130.983 Y98.171 I-1.224 J1.598 E.01815 +;WIDTH:0.41646 +G1 F9637.536 +G1 X131.06 Y98.315 E.00507 +;WIDTH:0.449999 +G1 F8843.491 +G1 X131.137 Y98.459 E.00553 +G1 X134.002 Y101.324 E.13715 +G1 X134.002 Y100.748 E.0195 +G1 X129.252 Y95.998 E.22738 +G1 X129.827 Y95.998 E.01946 +G1 X134.002 Y100.173 E.19985 +G1 X134.002 Y99.597 E.0195 +G1 X130.403 Y95.998 E.17228 +G1 X130.979 Y95.998 E.0195 +G1 X134.002 Y99.021 E.14471 +G1 X134.002 Y98.446 E.01946 +G1 X131.554 Y95.998 E.11718 +G1 X132.13 Y95.998 E.0195 +G1 X134.002 Y97.87 E.08961 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.13 Y95.998 E-.55016 +G1 X131.698 Y95.998 E-.08984 +;WIPE_END +G1 X132.85 Y96.142 Z19.42 F12000 +G1 Z19.4 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X134.002 Y97.294 E.05515 +G1 X134.002 Y95.998 E.04387 +G1 X132.706 Y95.998 E.04387 +M204 P1000 +M106 S114.75 +;LAYER_CHANGE +;Z:19.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;19.6 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y95.998 E-.26933 +G1 X134.002 Y97.294 E-.26932 +G1 X133.657 Y96.949 E-.10135 +;WIPE_END +G1 X133.657 Y96.949 Z19.4 F12000 +G1 X120.173 Y111.943 Z19.752 +;AFTER_LAYER_CHANGE +;19.6 +G1 X120.173 Y111.943 +G1 Z19.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X120.088 Y111.864 E.00393 +G1 X119.992 Y111.627 E.00866 +G1 X119.983 Y109.944 E.05697 +G1 X120.073 Y109.624 E.01125 +G1 X120.279 Y109.421 E.00979 +G1 X120.485 Y109.343 E.00746 +G1 X120.593 Y109.334 E.00367 +G1 X126.456 Y109.334 E.19846 +G1 X126.035 Y108.827 E.02231 +G1 X119.363 Y100.485 E.36157 +G3 X119.229 Y100.104 I.758 J-.48 E.01379 +G1 X119.229 Y98.479 E.055 +G1 X119.275 Y98.246 E.00804 +G1 X119.385 Y98.071 E.007 +G1 X119.58 Y97.926 E.00823 +G1 X119.84 Y97.868 E.00902 +G1 X130.16 Y97.868 E.34932 +G1 X130.42 Y97.926 E.00902 +G1 X130.615 Y98.071 E.00823 +G1 X130.725 Y98.246 E.007 +G1 X130.771 Y98.479 E.00804 +G1 X130.771 Y100.056 E.05338 +G1 X130.735 Y100.261 E.00705 +G3 X130.374 Y100.628 I-.693 J-.322 E.01777 +G1 X130.16 Y100.666 E.00736 +G1 X123.213 Y100.666 E.23515 +G1 X130.422 Y109.574 E.38789 +G3 X130.558 Y109.958 I-.599 J.428 E.01397 +G1 X130.558 Y111.521 E.05291 +G1 X130.523 Y111.726 E.00704 +G1 X130.418 Y111.911 E.0072 +G1 X130.22 Y112.068 E.00855 +G1 X129.947 Y112.132 E.00949 +G1 X120.593 Y112.132 E.31662 +G1 X120.291 Y112.052 E.01057 +G1 X120.217 Y111.983 E.00342 +G1 X120.217 Y111.983 F12000 +G1 X120.463 Y111.656 +M204 P800 +;TYPE:External perimeter +G1 F2400 +G1 X120.425 Y111.636 E.00145 +G1 X120.39 Y111.521 E.00407 +G1 X120.39 Y109.944 E.05338 +G3 X120.593 Y109.741 I.235 J.032 E.01044 +G1 F2399.997 +G1 X126.677 Y109.741 E.20594 +G1 F2379.19 +G1 X127.357 Y109.741 E.02302 +G1 X127.01 Y109.362 E.01739 +G1 F2399.903 +G1 X126.345 Y108.564 E.03516 +G1 X119.681 Y100.231 E.36116 +G1 X119.636 Y100.104 E.00456 +G1 X119.636 Y98.479 E.055 +G1 F2400 +G3 X119.84 Y98.275 I.236 J.032 E.01049 +G1 F2399.997 +G1 X130.16 Y98.275 E.34932 +G1 F2400 +G3 X130.364 Y98.479 I-.032 J.236 E.01049 +G1 X130.364 Y100.056 E.05338 +G1 X130.32 Y100.182 E.00452 +G1 X130.189 Y100.257 E.00511 +G1 X130.16 Y100.259 E.00098 +G1 X122.33 Y100.259 E.26504 +G1 X122.803 Y100.806 E.02448 +G1 X130.106 Y109.83 E.39295 +G1 X130.151 Y109.958 E.00459 +G1 X130.151 Y111.521 E.05291 +G3 X129.947 Y111.725 I-.237 J-.033 E.01048 +G1 F2399.997 +G1 X120.593 Y111.725 E.31662 +G1 F2400 +G1 X120.516 Y111.684 E.00295 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.463 Y111.656 E-.01246 +G1 X120.425 Y111.636 E-.00892 +G1 X120.39 Y111.521 E-.02498 +G1 X120.39 Y109.944 E-.32772 +G3 X120.593 Y109.741 I.235 J.032 E-.06407 +G1 X121.564 Y109.741 E-.20185 +;WIPE_END +G1 X134.368 Y95.632 Z19.933 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M73 P94 R1 +M73 Q94 S1 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X116.109 Y96.031 Z19.928 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F7116.79 +G2 X116.093 Y96.093 I-.058 J.018 E.01318 +;WIDTH:0.449999 +G1 F8843.491 +G1 X116.743 Y95.998 E.02224 +G1 X115.932 Y96.815 E.03897 +;WIDTH:0.41646 +G1 F9637.536 +G1 X115.866 Y96.888 E.00306 +;WIDTH:0.38292 +G1 F10588.235 +G1 X115.795 Y96.869 E.00208 +G2 X115.855 Y96.929 I.04 J.02 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y97.318 E.01403 +G1 X117.318 Y95.998 E.06319 +G1 X117.894 Y95.998 E.0195 +G1 X115.998 Y97.894 E.09076 +G1 X115.998 Y98.47 E.0195 +G1 X118.47 Y95.998 E.11833 +G1 X119.046 Y95.998 E.0195 +G1 X115.998 Y99.046 E.14591 +G1 X115.998 Y99.621 E.01946 +G1 X119.621 Y95.998 E.17343 +G1 X120.197 Y95.998 E.0195 +G1 X115.998 Y100.197 E.201 +M204 P1000 +G1 X115.998 Y100.197 F12000 +G1 X115.909 Y101.091 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G3 X115.849 Y101.03 I-.02 J-.041 E.00321 +G1 X115.92 Y101.049 E.00208 +;WIDTH:0.41646 +G1 F9637.536 +G1 X115.959 Y100.911 E.00445 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y100.773 E.00485 +G1 X120.773 Y95.998 E.22858 +G1 X121.348 Y95.998 E.01946 +G1 X119.845 Y97.502 E.07197 +G1 X119.701 Y97.577 E.0055 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.557 Y97.652 E.00504 +;WIDTH:0.38292 +G1 F10588.235 +G2 X119.014 Y98.196 I1.069 J1.612 E.02188 +;WIDTH:0.449999 +G1 F8843.491 +G1 X118.863 Y98.484 E.01101 +G1 X115.998 Y101.348 E.13712 +G1 X115.998 Y101.924 E.0195 +G1 X118.863 Y99.06 E.13712 +G1 X118.863 Y99.635 E.01946 +G1 X115.998 Y102.5 E.13715 +G1 X115.998 Y103.075 E.01946 +G1 X118.877 Y100.197 E.13779 +G1 X119.024 Y100.625 E.01532 +G1 X115.998 Y103.651 E.14485 +G1 X115.998 Y104.227 E.0195 +G1 X119.27 Y100.955 E.15663 +G1 X119.526 Y101.275 E.01387 +G1 X115.998 Y104.803 E.16888 +G1 X115.998 Y105.378 E.01946 +G1 X119.782 Y101.595 E.18111 +G1 X120.038 Y101.915 E.01387 +G1 X115.998 Y105.954 E.19337 +G1 X115.998 Y106.53 E.0195 +G1 X120.294 Y102.234 E.20565 +G1 X120.55 Y102.554 E.01387 +G1 X115.998 Y107.105 E.21788 +G1 X115.998 Y107.681 E.0195 +G1 X120.806 Y102.874 E.23013 +G1 X121.062 Y103.194 E.01387 +G1 X115.998 Y108.257 E.24239 +G1 X115.998 Y108.832 E.01946 +G1 X121.318 Y103.513 E.25464 +G1 X121.573 Y103.833 E.01385 +G1 X115.998 Y109.408 E.26687 +G1 X115.998 Y109.984 E.0195 +G1 X121.829 Y104.153 E.27913 +G1 X122.085 Y104.473 E.01387 +G1 X115.998 Y110.559 E.29136 +G1 X115.998 Y111.135 E.0195 +G1 X122.341 Y104.792 E.30364 +G1 X122.597 Y105.112 E.01387 +G1 X115.998 Y111.711 E.31589 +G1 X115.998 Y112.287 E.0195 +G1 X122.853 Y105.432 E.32814 +G1 X123.109 Y105.752 E.01387 +G1 X115.998 Y112.862 E.34038 +G1 X115.959 Y113.001 E.00489 +;WIDTH:0.41646 +G1 F9637.536 +G1 X115.92 Y113.139 E.00445 +;WIDTH:0.38292 +G1 F10588.235 +G1 X115.849 Y113.12 E.00208 +G2 X115.909 Y113.18 I.04 J.02 E.00315 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X115.849 Y113.12 I-.02 J-.04 E-.02315 +G1 X115.92 Y113.139 E-.01527 +G1 X115.959 Y113.001 E-.0298 +G1 X115.998 Y112.862 E-.03 +G1 X117.842 Y111.019 E-.54178 +;WIPE_END +G1 X119.811 Y109.513 Z19.643 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F10588.235 +G1 X120.158 Y109.173 E.01373 +;WIDTH:0.41646 +G1 F9637.536 +G1 X120.302 Y109.082 E.00529 +;WIDTH:0.449999 +G1 F8843.491 +G1 X120.446 Y108.99 E.00578 +G1 X123.365 Y106.071 E.13973 +G1 X123.621 Y106.391 E.01387 +G1 X121.045 Y108.967 E.12331 +G1 X121.62 Y108.967 E.01946 +G1 X123.877 Y106.711 E.10802 +G1 X125.683 Y108.967 E.09782 +G1 X122.196 Y108.967 E.11803 +G1 X123.989 Y107.174 E.08583 +G1 X124.099 Y107.64 E.01621 +G1 X124.836 Y108.56 E.0399 +G1 X123.179 Y108.56 E.05609 +G1 X123.955 Y107.784 E.03715 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.179 Y108.56 E-.22806 +G1 X124.836 Y108.56 E-.34435 +G1 X124.633 Y108.306 E-.06759 +;WIPE_END +G1 X128.776 Y101.033 Z19.746 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X123.981 Y101.033 E.1623 +G1 X126.217 Y103.795 E.12029 +G1 X128.98 Y101.033 E.13224 +G1 X129.753 Y100.848 E.0269 +;WIDTH:0.38292 +G1 F10588.235 +G1 X129.681 Y100.829 E.00211 +G2 X129.742 Y100.889 I.041 J.02 E.00321 +;WIDTH:0.41646 +G1 F9637.536 +G1 X129.648 Y100.961 E.00368 +;WIDTH:0.449999 +G1 F8843.491 +G1 X129.555 Y101.033 E.00398 +G1 X126.474 Y104.113 E.14746 +G1 X126.732 Y104.432 E.01389 +G1 X130.131 Y101.033 E.16271 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X127.953 Y103.21 E-.64 +;WIPE_END +G1 X127.997 Y101.44 Z19.631 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X126.249 Y103.188 E.08368 +G1 X124.834 Y101.44 E.07612 +G1 X127.793 Y101.44 E.10016 +M204 P1000 +G1 X127.793 Y101.44 F12000 +G1 X126.905 Y101.892 +M204 P1500 +;WIDTH:0.540618 +G1 F7233.302 +G1 X126.284 Y102.512 E.03632 +G1 X125.782 Y101.892 E.03301 +G1 X126.701 Y101.892 E.03803 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.782 Y101.892 E-.19098 +G1 X126.284 Y102.512 E-.16578 +G1 X126.905 Y101.892 E-.18236 +;WIPE_END +G1 E-.10088 F2100 +G1 X121.924 Y95.998 Z19.735 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X120.421 Y97.502 E.07197 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.924 Y95.998 E-.44187 +;WIPE_END +G1 E-.19813 F2100 +G1 X122.314 Y96.185 Z19.608 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X120.997 Y97.502 E.06304 +G1 X130.156 Y97.502 E.31002 +G1 X130.611 Y97.616 E.01588 +G3 X131.074 Y98.144 I-.598 J.993 E.02415 +G1 X131.11 Y98.326 E.00628 +G1 X133.438 Y95.998 E.11144 +G1 X122.356 Y95.998 E.37511 +G1 X122.694 Y96.476 E.01982 +;WIDTH:0.590931 +G1 F6569.211 +G1 X130.156 Y96.476 E.34002 +;WIDTH:0.602537 +G1 F6432.971 +G2 X130.48 Y96.457 I.112 J-.865 E.01519 +;WIDTH:0.553066 +G1 F7056.808 +G1 X130.594 Y96.432 E.00495 +;WIDTH:0.503595 +G1 F7814.631 +G1 X130.708 Y96.408 E.00446 +;WIDTH:0.454123 +G1 F8754.8 +G1 X131.146 Y96.406 E.01498 +;WIDTH:0.449999 +G1 F8843.491 +G1 X132.455 Y96.406 E.04431 +G1 X131.238 Y97.622 E.05823 +G1 X130.854 Y97.29 E.01718 +;WIDTH:0.488134 +G1 F8086.008 +G1 X130.716 Y97.226 E.00563 +;WIDTH:0.526268 +G1 F7448.05 +G1 X130.579 Y97.162 E.00608 +;WIDTH:0.564403 +G1 F6903.395 +G1 X130.442 Y97.098 E.00656 +;WIDTH:0.602537 +G1 F6432.971 +G2 X130.156 Y97.024 I-.263 J.426 E.01395 +;WIDTH:0.590931 +G1 F6569.211 +G1 X122.15 Y97.024 E.36481 +G1 X122.55 Y96.62 E.02591 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X122.15 Y97.024 E-.11815 +G1 X124.661 Y97.024 E-.52185 +;WIPE_END +G1 X130.456 Y96.772 Z19.701 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.359567 +G1 F11369.127 +G1 X131.59 Y96.764 E.02986 +;WIDTH:0.352816 +G1 F11616.801 +G1 X131.231 Y97.123 E.01308 +G1 X131.123 Y97.043 E.00346 +;WIDTH:0.359567 +G1 F11369.127 +G1 X130.655 Y96.816 E.0137 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.123 Y97.043 E-.10809 +G1 X131.231 Y97.123 E-.02793 +G1 X131.59 Y96.764 E-.10551 +G1 X130.456 Y96.772 E-.23567 +;WIPE_END +G1 E-.1628 F2100 +G1 X131.134 Y98.878 Z19.639 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X134.002 Y96.011 E.13727 +G1 X134.002 Y96.586 E.01946 +G1 X131.13 Y99.458 E.13748 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.307 Y97.281 E-.64 +;WIPE_END +G1 X130.419 Y100.887 Z19.681 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G2 X130.977 Y100.325 I-.985 J-1.537 E.02257 +;WIDTH:0.41646 +G1 F9637.536 +G1 X131.051 Y100.181 E.00503 +;WIDTH:0.449999 +G1 F8843.491 +G1 X131.126 Y100.037 E.0055 +G1 X134.002 Y97.162 E.13765 +G1 X134.002 Y97.738 E.0195 +G1 X126.989 Y104.75 E.33568 +G1 X127.247 Y105.068 E.01386 +G1 X134.002 Y98.313 E.32336 +G1 X134.002 Y98.889 E.0195 +G1 X127.504 Y105.386 E.31103 +G1 X127.762 Y105.704 E.01386 +G1 X134.002 Y99.465 E.29868 +G1 X134.002 Y100.04 E.01946 +G1 X128.019 Y106.023 E.2864 +G1 X128.277 Y106.341 E.01386 +G1 X134.002 Y100.616 E.27405 +G1 X134.002 Y101.192 E.0195 +G1 X128.534 Y106.659 E.26173 +G1 X128.792 Y106.977 E.01386 +M73 Q95 S1 +G1 X134.002 Y101.768 E.24938 +G1 X134.002 Y102.343 E.01946 +M73 P95 R1 +G1 X129.049 Y107.295 E.23707 +G1 X129.307 Y107.614 E.01389 +G1 X134.002 Y102.919 E.22475 +G1 X134.002 Y103.495 E.0195 +G1 X129.564 Y107.932 E.21242 +G1 X129.822 Y108.25 E.01386 +G1 X134.002 Y104.07 E.20009 +G1 X134.002 Y104.646 E.0195 +G1 X130.079 Y108.568 E.18777 +G1 X130.337 Y108.886 E.01386 +G1 X134.002 Y105.222 E.17542 +G1 X134.002 Y105.797 E.01946 +G1 X130.594 Y109.205 E.16314 +G1 X130.826 Y109.549 E.01404 +G1 X134.002 Y106.373 E.15203 +G1 X134.002 Y106.949 E.0195 +G1 X130.924 Y110.026 E.14732 +G1 X130.92 Y110.606 E.01963 +G1 X134.002 Y107.525 E.14751 +G1 X134.002 Y108.1 E.01946 +G1 X130.916 Y111.186 E.14772 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.093 Y109.008 E-.64 +;WIPE_END +G1 X119.635 Y109.801 Z19.835 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X115.998 Y113.438 E.1741 +G1 X116.011 Y114.002 E.0191 +G1 X119.616 Y110.396 E.17259 +G1 X119.616 Y110.971 E.01946 +G1 X116.586 Y114.002 E.14507 +G1 X117.162 Y114.002 E.0195 +G1 X119.618 Y111.545 E.11759 +G1 X119.748 Y111.992 E.01576 +G1 X117.738 Y114.002 E.09622 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.748 Y111.992 E-.59072 +G1 X119.682 Y111.764 E-.04928 +;WIPE_END +G1 X129.742 Y112.498 Z19.776 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X120.599 Y112.498 E.30948 +G1 X120.158 Y112.389 E.01538 +G1 X120.021 Y112.294 E.00564 +G1 X118.313 Y114.002 E.08176 +G1 X128.82 Y114.002 E.35565 +G1 X128.82 Y113.858 E.00487 +G1 X130.195 Y112.498 E.06546 +;WIDTH:0.421947 +G1 F9498.016 +G1 X130.33 Y112.38 E.00565 +G1 X130.138 Y112.439 E.00633 +;WIDTH:0.449999 +G1 F8843.491 +G1 X129.946 Y112.498 E.0068 +G1 X130.385 Y112.348 E.0157 +;WIDTH:0.38292 +G1 F10588.235 +G1 X130.674 Y112.082 E.0111 +;WIDTH:0.41646 +G1 F9637.536 +G1 X130.778 Y111.938 E.00552 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.883 Y111.795 E.00601 +G1 X134.002 Y108.676 E.1493 +G1 X134.002 Y109.252 E.0195 +G1 X129.252 Y114.002 E.22738 +G1 X129.827 Y114.002 E.01946 +G1 X134.002 Y109.827 E.19985 +G1 X134.002 Y110.403 E.0195 +G1 X130.403 Y114.002 E.17228 +G1 X130.979 Y114.002 E.0195 +G1 X134.002 Y110.979 E.14471 +G1 X134.002 Y111.554 E.01946 +G1 X131.554 Y114.002 E.11718 +G1 X132.13 Y114.002 E.0195 +G1 X134.002 Y112.13 E.08961 +G1 X134.002 Y114.002 E.06336 +G1 X132.706 Y114.002 E.04387 +G1 X133.858 Y112.85 E.05515 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.706 Y114.002 E-.33856 +G1 X134.002 Y114.002 E-.26933 +G1 X134.002 Y113.847 E-.03211 +;WIPE_END +G1 X129.026 Y112.976 Z19.688 F12000 +G1 Z19.6 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.59093 +G1 F6569.223 +G1 X128.482 Y113.524 E.03519 +G1 X120.599 Y113.524 E.35921 +;WIDTH:0.625285 +G1 F6181.69 +G1 X119.508 Y113.507 E.05284 +G1 X120.123 Y112.891 E.04215 +G1 X120.599 Y112.976 E.02341 +;WIDTH:0.590931 +G1 F6569.211 +G1 X128.823 Y112.976 E.37475 +M204 P1000 +M106 S117.3 +;LAYER_CHANGE +;Z:19.8 +;HEIGHT:0.199999 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;19.8 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X125.743 Y112.976 E-.64 +;WIPE_END +G1 X125.743 Y112.976 Z19.6 F12000 +G1 X120.173 Y111.943 Z19.8 +;AFTER_LAYER_CHANGE +;19.8 +G1 X120.173 Y111.943 +M73 Q95 S0 +G1 Z19.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F4200 +G1 X120.088 Y111.864 E.00393 +G1 X119.992 Y111.627 E.00866 +G1 X119.983 Y109.944 E.05697 +G1 X120.073 Y109.624 E.01125 +G1 X120.279 Y109.421 E.00979 +G1 X120.485 Y109.343 E.00746 +G1 X120.593 Y109.334 E.00367 +G1 X126.456 Y109.334 E.19846 +G1 X126.035 Y108.827 E.02231 +M73 P95 R0 +G1 X119.363 Y100.485 E.36157 +G3 X119.229 Y100.104 I.758 J-.48 E.01379 +G1 X119.229 Y98.479 E.055 +G1 X119.275 Y98.246 E.00804 +G1 X119.385 Y98.071 E.007 +G1 X119.58 Y97.926 E.00823 +G1 X119.84 Y97.868 E.00902 +G1 X130.16 Y97.868 E.34932 +G1 X130.42 Y97.926 E.00902 +G1 X130.615 Y98.071 E.00823 +G1 X130.725 Y98.246 E.007 +G1 X130.771 Y98.479 E.00804 +G1 X130.771 Y100.056 E.05338 +G1 X130.735 Y100.261 E.00705 +G3 X130.374 Y100.628 I-.693 J-.322 E.01777 +G1 X130.16 Y100.666 E.00736 +G1 X123.213 Y100.666 E.23515 +G1 X130.422 Y109.574 E.38789 +G3 X130.558 Y109.958 I-.599 J.428 E.01397 +G1 X130.558 Y111.521 E.05291 +G1 X130.523 Y111.726 E.00704 +G1 X130.418 Y111.911 E.0072 +G1 X130.22 Y112.068 E.00855 +G1 X129.947 Y112.132 E.00949 +G1 X120.593 Y112.132 E.31662 +G1 X120.291 Y112.052 E.01057 +G1 X120.217 Y111.983 E.00342 +G1 X120.217 Y111.983 F12000 +G1 X120.463 Y111.656 +M204 P800 +;TYPE:External perimeter +G1 F2400 +G1 X120.425 Y111.636 E.00145 +G1 X120.39 Y111.521 E.00407 +G1 X120.39 Y109.944 E.05338 +G3 X120.593 Y109.741 I.235 J.032 E.01044 +G1 F2399.997 +G1 X126.677 Y109.741 E.20594 +G1 F2379.191 +G1 X127.357 Y109.741 E.02302 +G1 X127.01 Y109.362 E.01739 +G1 F2399.903 +G1 X126.345 Y108.564 E.03516 +G1 X119.681 Y100.231 E.36116 +G1 X119.636 Y100.104 E.00456 +G1 X119.636 Y98.479 E.055 +G1 F2400 +G3 X119.84 Y98.275 I.236 J.032 E.01049 +G1 F2399.997 +G1 X130.16 Y98.275 E.34932 +G1 F2400 +G3 X130.364 Y98.479 I-.032 J.236 E.01049 +G1 X130.364 Y100.056 E.05338 +G1 X130.32 Y100.182 E.00452 +G1 X130.189 Y100.257 E.00511 +G1 X130.16 Y100.259 E.00098 +G1 X122.33 Y100.259 E.26504 +G1 X122.803 Y100.806 E.02448 +G1 X130.106 Y109.83 E.39295 +G1 X130.151 Y109.958 E.00459 +G1 X130.151 Y111.521 E.05291 +G3 X129.947 Y111.725 I-.237 J-.033 E.01048 +G1 F2399.997 +G1 X120.593 Y111.725 E.31662 +G1 F2400 +G1 X120.516 Y111.684 E.00295 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.463 Y111.656 E-.01246 +G1 X120.425 Y111.636 E-.00892 +G1 X120.39 Y111.521 E-.02498 +G1 X120.39 Y109.944 E-.32772 +G3 X120.593 Y109.741 I.235 J.032 E-.06407 +G1 X121.564 Y109.741 E-.20185 +;WIPE_END +G1 X134.368 Y95.632 Z20.133 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X116.031 Y113.891 Z20.226 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F7116.79 +G2 X116.093 Y113.907 I.018 J.058 E.01318 +M73 Q96 S0 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y113.257 E.02224 +G1 X116.815 Y114.068 E.03897 +;WIDTH:0.41646 +G1 F9637.536 +G1 X116.888 Y114.134 E.00306 +;WIDTH:0.38292 +G1 F10588.235 +G1 X116.869 Y114.205 E.00208 +G2 X116.929 Y114.145 I.02 J-.04 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X117.318 Y114.002 E.01403 +G1 X115.998 Y112.682 E.06319 +G1 X115.998 Y112.106 E.0195 +M73 P96 R0 +G1 X117.894 Y114.002 E.09076 +G1 X118.47 Y114.002 E.0195 +G1 X115.998 Y111.53 E.11833 +G1 X115.998 Y110.954 E.0195 +G1 X119.046 Y114.002 E.14591 +G1 X119.621 Y114.002 E.01946 +G1 X115.998 Y110.379 E.17343 +G1 X115.998 Y109.803 E.0195 +G1 X120.197 Y114.002 E.201 +G1 X120.773 Y114.002 E.0195 +G1 X115.998 Y109.227 E.22858 +G1 X115.998 Y108.652 E.01946 +G1 X121.348 Y114.002 E.2561 +G1 X121.487 Y114.041 E.00489 +;WIDTH:0.41646 +G1 F9637.536 +G1 X121.625 Y114.08 E.00445 +;WIDTH:0.38292 +G1 F10588.235 +G1 X121.606 Y114.151 E.00208 +G2 X121.666 Y114.091 I.02 J-.04 E.00315 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G3 X121.606 Y114.151 I-.04 J.02 E-.02315 +G1 X121.625 Y114.08 E-.01527 +G1 X121.487 Y114.041 E-.0298 +G1 X121.348 Y114.002 E-.03 +G1 X119.505 Y112.158 E-.54178 +;WIPE_END +G1 X120.227 Y112.249 Z19.813 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F10588.235 +G1 X120.078 Y112.253 E.00421 +G1 X119.828 Y112.003 E.01 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.733 Y111.859 E.00536 +;WIDTH:0.449999 +G1 F8843.491 +G1 X119.637 Y111.715 E.00586 +G1 X115.998 Y108.076 E.1742 +G1 X115.998 Y107.5 E.0195 +G1 X119.616 Y111.118 E.17319 +G1 X119.616 Y110.543 E.01946 +G1 X115.998 Y106.925 E.17319 +G1 X115.998 Y106.349 E.0195 +G1 X119.616 Y109.967 E.17319 +G1 X119.728 Y109.503 E.01616 +G1 X115.998 Y105.773 E.17855 +G1 X115.998 Y105.197 E.0195 +G1 X119.986 Y109.185 E.1909 +G1 X120.379 Y109.002 E.01467 +G1 X115.998 Y104.622 E.20969 +G1 X115.998 Y104.046 E.0195 +G1 X120.987 Y109.034 E.2388 +;WIDTH:0.41646 +G1 F9637.536 +G1 X121.055 Y109.1 E.00294 +;WIDTH:0.38292 +G1 F10588.235 +G1 X121.036 Y109.171 E.00208 +G2 X121.096 Y109.111 I.02 J-.04 E.00315 +;WIDTH:0.449999 +G1 F8843.491 +G1 X121.496 Y108.967 E.01439 +G1 X115.998 Y103.47 E.26316 +G1 X115.998 Y102.895 E.01946 +G1 X122.071 Y108.967 E.29069 +G1 X122.647 Y108.967 E.0195 +G1 X115.998 Y102.319 E.31826 +G1 X115.998 Y101.743 E.0195 +G1 X123.223 Y108.967 E.34583 +G1 X123.798 Y108.967 E.01946 +G1 X115.998 Y101.168 E.37336 +G1 X115.998 Y100.592 E.0195 +G1 X124.374 Y108.967 E.40093 +G1 X124.95 Y108.967 E.0195 +G1 X115.998 Y100.016 E.4285 +G1 X115.998 Y99.441 E.01946 +G1 X125.525 Y108.967 E.45603 +G1 X125.701 Y109.014 E.00617 +;WIDTH:0.41646 +G1 F9637.536 +G1 X125.877 Y109.06 E.00565 +;WIDTH:0.38292 +G1 F10588.235 +G1 X125.849 Y108.931 E.00373 +;WIDTH:0.407366 +G1 F9878.007 +G1 X124.266 Y107.162 E.07194 +;WIDTH:0.449999 +G1 F8843.491 +G1 X124.008 Y106.874 E.01309 +G1 X115.998 Y98.865 E.38341 +G1 X115.998 Y98.289 E.0195 +G1 X121.698 Y103.989 E.27286 +G1 X121.956 Y104.277 E.01309 +;WIDTH:0.407366 +G1 F9878.007 +G1 X123.55 Y106.057 E.07241 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.956 Y104.277 E-.49655 +G1 X121.698 Y103.989 E-.08035 +G1 X121.483 Y103.774 E-.0631 +;WIPE_END +G1 X121.24 Y103.172 Z19.811 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F9878.007 +G1 X119.647 Y101.392 E.07239 +;WIDTH:0.449999 +G1 F8843.491 +G1 X119.389 Y101.104 E.01309 +G1 X115.998 Y97.713 E.16233 +G1 X115.998 Y97.138 E.01946 +G1 X118.863 Y100.002 E.13712 +G1 X118.927 Y100.124 E.00466 +;WIDTH:0.41646 +G1 F9637.536 +G1 X118.99 Y100.247 E.00429 +;WIDTH:0.38292 +G1 F10588.235 +G1 X118.971 Y100.318 E.00208 +G2 X119.031 Y100.258 I.02 J-.04 E.00315 +M204 P1000 +G1 X119.031 Y100.258 F12000 +G1 X118.863 Y99.426 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X115.998 Y96.562 E.13712 +G1 X116.011 Y95.998 E.0191 +G1 X118.863 Y98.851 E.13655 +G1 X118.944 Y98.902 E.00324 +;WIDTH:0.41646 +G1 F9637.536 +G1 X119.025 Y98.954 E.00299 +;WIDTH:0.38292 +G1 F10588.235 +G1 X119.006 Y99.025 E.00208 +G2 X119.067 Y98.965 I.02 J-.04 E.00321 +;WIDTH:0.449999 +G1 F8843.491 +G1 X118.894 Y98.306 E.02306 +G1 X116.586 Y95.998 E.11048 +G1 X117.162 Y95.998 E.0195 +G1 X119.063 Y97.9 E.09102 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X117.162 Y95.998 E-.55883 +G1 X116.772 Y95.998 E-.08117 +;WIPE_END +G1 X117.882 Y96.142 Z19.82 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X119.37 Y97.631 E.07125 +G1 X119.841 Y97.502 E.01653 +G1 X130.182 Y97.504 E.35003 +G1 X128.82 Y96.142 E.0652 +G1 X128.82 Y95.998 E.00487 +G1 X117.738 Y95.998 E.37511 +M204 P1000 +G1 X117.738 Y95.998 F12000 +G1 X118.91 Y96.484 +M204 P1500 +;WIDTH:0.606955 +G1 F6382.583 +G1 X119.841 Y96.476 E.04367 +;WIDTH:0.590931 +G1 F6569.211 +G1 X128.482 Y96.476 E.39375 +G1 X129.026 Y97.024 E.03519 +G1 X119.841 Y97.024 E.41854 +;WIDTH:0.606955 +G1 F6382.583 +G1 X119.501 Y97.075 E.01612 +G1 X119.054 Y96.628 E.02965 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X119.501 Y97.075 E-.13137 +G1 X119.841 Y97.024 E-.07145 +G1 X121.945 Y97.024 E-.43718 +;WIPE_END +G1 X120.366 Y112.444 Z20.071 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X121.924 Y114.002 E.07458 +G1 X122.5 Y114.002 E.0195 +G1 X120.997 Y112.498 E.07197 +G1 X129.946 Y112.498 E.30291 +G1 X130.338 Y112.412 E.01358 +G1 X130.67 Y112.173 E.01385 +G1 X130.893 Y111.742 E.01643 +G1 X130.914 Y111.477 E.009 +G1 X133.438 Y114.002 E.12085 +G1 X122.932 Y114.002 E.35562 +G1 X122.932 Y113.858 E.00487 +G1 X121.716 Y112.642 E.05821 +M204 P1000 +G1 X121.716 Y112.642 F12000 +G1 X122.725 Y112.976 +M204 P1500 +;WIDTH:0.590931 +G1 F6569.211 +G1 X129.946 Y112.976 E.32904 +G1 X130.1 Y112.951 E.00711 +;WIDTH:0.577623 +G1 F6732.709 +G1 X130.29 Y112.865 E.00927 +;WIDTH:0.535082 +G1 F7314.669 +G1 X130.48 Y112.778 E.00855 +;WIDTH:0.492541 +G1 F8006.755 +G1 X130.671 Y112.692 E.00783 +;WIDTH:0.449999 +G1 F8843.491 +G2 X131.108 Y112.247 I-.838 J-1.261 E.02126 +G1 X132.455 Y113.594 E.06448 +G1 X130.522 Y113.594 E.06543 +;WIDTH:0.496977 +G1 F7928.531 +G1 X130.33 Y113.571 E.0073 +;WIDTH:0.543954 +G1 F7185.146 +G1 X130.138 Y113.547 E.00806 +;WIDTH:0.590931 +G1 F6569.211 +G1 X129.946 Y113.524 E.00881 +G1 X123.269 Y113.52 E.30425 +G1 X122.869 Y113.12 E.02578 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X123.269 Y113.52 E-.11756 +G1 X125.783 Y113.522 E-.52244 +;WIPE_END +G1 X130.348 Y113.216 Z19.88 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.392446 +G1 F10299.664 +G2 X131.133 Y112.808 I-.381 J-1.692 E.026 +G1 X131.542 Y113.216 E.01679 +G1 X130.552 Y113.216 E.02877 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X131.542 Y113.216 E-.20573 +G1 X131.133 Y112.808 E-.12005 +G3 X130.348 Y113.216 I-1.166 J-1.284 E-.1859 +;WIPE_END +G1 E-.12832 F2100 +G1 X130.918 Y110.906 Z19.842 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.449999 +G1 F8843.491 +G1 X134.002 Y113.989 E.14761 +G1 X134.002 Y113.414 E.01946 +G1 X130.922 Y110.334 E.14744 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X133.099 Y112.511 E-.64 +;WIPE_END +G1 X129.603 Y108.223 Z19.897 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G1 X130.687 Y109.434 E.04595 +;WIDTH:0.41646 +G1 F9637.536 +G1 X130.786 Y109.578 E.00543 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.885 Y109.722 E.00592 +G1 X134.002 Y112.838 E.14918 +G1 X134.002 Y112.262 E.0195 +G1 X129.112 Y107.373 E.23406 +G1 X128.853 Y107.085 E.01311 +;WIDTH:0.409324 +G1 F9825.221 +G1 X127.16 Y105.205 E.07708 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X128.853 Y107.085 E-.52576 +G1 X129.112 Y107.373 E-.08049 +G1 X129.227 Y107.488 E-.03375 +;WIPE_END +G1 X124.718 Y102.187 Z19.921 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F9825.221 +G1 X126.41 Y104.067 E.07706 +;WIDTH:0.449999 +G1 F8843.491 +G1 X126.67 Y104.355 E.01313 +G1 X134.002 Y111.687 E.35098 +G1 X134.002 Y111.111 E.0195 +G1 X124.227 Y101.337 E.4679 +G1 X124.499 Y101.033 E.01381 +G1 X134.002 Y110.535 E.45488 +G1 X134.002 Y109.96 E.01946 +G1 X125.075 Y101.033 E.42733 +G1 X125.65 Y101.033 E.01946 +G1 X134.002 Y109.384 E.39978 +G1 X134.002 Y108.808 E.0195 +G1 X126.226 Y101.033 E.37221 +G1 X126.802 Y101.033 E.0195 +G1 X134.002 Y108.232 E.34464 +G1 X134.002 Y107.657 E.01946 +G1 X127.377 Y101.033 E.31711 +G1 X127.953 Y101.033 E.0195 +G1 X134.002 Y107.081 E.28954 +G1 X134.002 Y106.505 E.0195 +G1 X128.529 Y101.033 E.26197 +G1 X129.104 Y101.033 E.01946 +G1 X134.002 Y105.93 E.23444 +G1 X134.002 Y105.354 E.0195 +G1 X129.68 Y101.033 E.20687 +G1 X129.57 Y100.824 E.00799 +;WIDTH:0.433298 +G1 F9221.832 +G1 X129.582 Y100.871 E.00157 +G1 X129.501 Y100.893 E.00272 +G1 X129.523 Y100.812 E.00272 +;WIDTH:0.449999 +G1 F8843.491 +G1 X130.243 Y101.02 E.02537 +G1 X134.002 Y104.778 E.17992 +G1 X134.002 Y104.203 E.01946 +G1 X130.679 Y100.88 E.15907 +G1 X130.967 Y100.593 E.01376 +G1 X134.002 Y103.627 E.14526 +G1 X134.002 Y103.051 E.0195 +G1 X131.125 Y100.175 E.1377 +G1 X131.129 Y99.603 E.01936 +G1 X134.002 Y102.476 E.13753 +G1 X134.002 Y101.9 E.0195 +G1 X131.133 Y99.032 E.13731 +M204 P1000 +G1 X131.133 Y99.032 F12000 +G1 X130.543 Y97.707 +M204 P1500 +;WIDTH:0.38292 +G1 F10588.235 +G3 X130.983 Y98.171 I-1.224 J1.598 E.01815 +;WIDTH:0.41646 +G1 F9637.536 +G1 X131.06 Y98.315 E.00507 +;WIDTH:0.449999 +G1 F8843.491 +G1 X131.137 Y98.459 E.00553 +G1 X134.002 Y101.324 E.13715 +G1 X134.002 Y100.748 E.0195 +G1 X129.252 Y95.998 E.22738 +G1 X129.827 Y95.998 E.01946 +G1 X134.002 Y100.173 E.19985 +G1 X134.002 Y99.597 E.0195 +G1 X130.403 Y95.998 E.17228 +G1 X130.979 Y95.998 E.0195 +G1 X134.002 Y99.021 E.14471 +G1 X134.002 Y98.446 E.01946 +M73 Q97 S0 +G1 X131.554 Y95.998 E.11718 +G1 X132.13 Y95.998 E.0195 +M73 P97 R0 +G1 X134.002 Y97.87 E.08961 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X132.13 Y95.998 E-.55016 +G1 X131.698 Y95.998 E-.08984 +;WIPE_END +G1 X132.85 Y96.142 Z19.82 F12000 +G1 Z19.8 F720 +G1 E.8 F1500 +M204 P1500 +G1 F8843.491 +G1 X134.002 Y97.294 E.05515 +G1 X134.002 Y95.998 E.04387 +G1 X132.706 Y95.998 E.04387 +M204 P1000 +M106 S76.5 +;LAYER_CHANGE +;Z:20 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;20 + + +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.002 Y95.998 E-.26933 +G1 X134.002 Y97.294 E-.26932 +G1 X133.657 Y96.949 E-.10135 +;WIPE_END +G1 X133.657 Y96.949 Z19.8 F12000 +G1 X120.173 Y111.943 Z20.152 +;AFTER_LAYER_CHANGE +;20 +G1 X120.173 Y111.943 +G1 Z20 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X120.088 Y111.864 E.00393 +G1 X119.992 Y111.627 E.00866 +G1 X119.983 Y109.944 E.05697 +G1 X120.073 Y109.624 E.01125 +G1 X120.279 Y109.421 E.00979 +G1 X120.485 Y109.343 E.00746 +G1 X120.593 Y109.334 E.00367 +G1 X126.456 Y109.334 E.19846 +G1 X126.035 Y108.827 E.02231 +G1 X119.363 Y100.485 E.36157 +G3 X119.229 Y100.104 I.758 J-.48 E.01379 +G1 X119.229 Y98.479 E.055 +G1 X119.275 Y98.246 E.00804 +G1 X119.385 Y98.071 E.007 +G1 X119.58 Y97.926 E.00823 +G1 X119.84 Y97.868 E.00902 +G1 X130.16 Y97.868 E.34932 +G1 X130.42 Y97.926 E.00902 +G1 X130.615 Y98.071 E.00823 +G1 X130.725 Y98.246 E.007 +G1 X130.771 Y98.479 E.00804 +G1 X130.771 Y100.056 E.05338 +G1 X130.735 Y100.261 E.00705 +G3 X130.374 Y100.628 I-.693 J-.322 E.01777 +G1 X130.16 Y100.666 E.00736 +G1 X123.213 Y100.666 E.23515 +G1 X130.422 Y109.574 E.38789 +G3 X130.558 Y109.958 I-.599 J.428 E.01397 +G1 X130.558 Y111.521 E.05291 +G1 X130.523 Y111.726 E.00704 +G1 X130.418 Y111.911 E.0072 +G1 X130.22 Y112.068 E.00855 +G1 X129.947 Y112.132 E.00949 +G1 X120.593 Y112.132 E.31662 +G1 X120.291 Y112.052 E.01057 +G1 X120.217 Y111.983 E.00342 +G1 X120.217 Y111.983 F12000 +G1 X120.463 Y111.656 +M204 P800 +;TYPE:External perimeter +G1 F2400 +G1 X120.425 Y111.636 E.00145 +G1 X120.39 Y111.521 E.00407 +G1 X120.39 Y109.944 E.05338 +G3 X120.593 Y109.741 I.235 J.032 E.01044 +G1 F2399.997 +G1 X126.677 Y109.741 E.20594 +G1 F2379.19 +G1 X127.357 Y109.741 E.02302 +G1 X127.01 Y109.362 E.01739 +G1 F2399.901 +G1 X126.345 Y108.564 E.03516 +G1 X119.681 Y100.231 E.36116 +G1 X119.636 Y100.104 E.00456 +G1 X119.636 Y98.479 E.055 +G1 F2400 +G3 X119.84 Y98.275 I.236 J.032 E.01049 +G1 F2399.997 +G1 X130.16 Y98.275 E.34932 +G1 F2400 +G3 X130.364 Y98.479 I-.032 J.236 E.01049 +G1 X130.364 Y100.056 E.05338 +G1 X130.32 Y100.182 E.00452 +G1 X130.189 Y100.257 E.00511 +G1 X130.16 Y100.259 E.00098 +G1 X122.33 Y100.259 E.26504 +G1 X122.803 Y100.806 E.02448 +G1 X130.106 Y109.83 E.39295 +G1 X130.151 Y109.958 E.00459 +G1 X130.151 Y111.521 E.05291 +G3 X129.947 Y111.725 I-.237 J-.033 E.01048 +G1 F2399.997 +G1 X120.593 Y111.725 E.31662 +G1 F2400 +G1 X120.516 Y111.684 E.00295 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X120.463 Y111.656 E-.01246 +G1 X120.425 Y111.636 E-.00892 +G1 X120.39 Y111.521 E-.02498 +G1 X120.39 Y109.944 E-.32772 +G3 X120.593 Y109.741 I.235 J.032 E-.06407 +G1 X121.564 Y109.741 E-.20185 +;WIPE_END +G1 X134.368 Y95.632 Z20.333 F12000 +G1 Z20 F720 +G1 E.8 F1500 +;TYPE:Perimeter +G1 F4200 +G1 X134.368 Y114.368 E.63419 +G1 X115.632 Y114.368 E.63419 +G1 X115.632 Y95.632 E.63419 +G1 X134.308 Y95.632 E.63216 +G1 X134.775 Y95.225 F12000 +M204 P800 +;TYPE:External perimeter +G1 F2399.997 +G1 X134.775 Y114.775 E.66174 +G1 X115.225 Y114.775 E.66174 +G1 X115.225 Y95.225 E.66174 +G1 X134.715 Y95.225 E.65971 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X134.775 Y95.225 E-.01247 +G1 X134.775 Y98.245 E-.62753 +;WIPE_END +G1 X134.186 Y113.423 Z20.265 F12000 +G1 Z20 F720 +G1 E.8 F1500 +M204 P800 +;TYPE:Top solid infill +;WIDTH:0.420252 +G1 F2400 +G1 X133.423 Y114.186 E.03386 +M204 P1000 +G1 X133.423 Y114.186 F12000 +G1 X132.89 Y114.186 +M204 P800 +G1 F2400 +G1 X134.186 Y112.89 E.05751 +M204 P1000 +G1 X134.186 Y112.89 F12000 +G1 X134.186 Y112.356 +M204 P800 +G1 F2400 +G1 X132.356 Y114.186 E.0812 +M204 P1000 +G1 X132.356 Y114.186 F12000 +G1 X131.823 Y114.186 +M204 P800 +G1 F2400 +G1 X134.186 Y111.823 E.10485 +M204 P1000 +G1 X134.186 Y111.823 F12000 +G1 X134.186 Y111.289 +M204 P800 +G1 F2400 +G1 X131.289 Y114.186 E.12854 +M204 P1000 +G1 X131.289 Y114.186 F12000 +G1 X130.755 Y114.186 +M204 P800 +G1 F2400 +G1 X134.186 Y110.755 E.15224 +M204 P1000 +G1 X134.186 Y110.755 F12000 +G1 X134.186 Y110.222 +M204 P800 +G1 F2400 +G1 X130.222 Y114.186 E.17589 +M204 P1000 +G1 X130.222 Y114.186 F12000 +G1 X129.688 Y114.186 +M204 P800 +G1 F2400 +G1 X134.186 Y109.688 E.19958 +M204 P1000 +G1 X134.186 Y109.688 F12000 +G1 X134.186 Y109.154 +M204 P800 +G1 F2400 +G1 X129.154 Y114.186 E.22328 +M204 P1000 +G1 X129.154 Y114.186 F12000 +G1 X128.621 Y114.186 +M204 P800 +G1 F2400 +G1 X134.186 Y108.621 E.24693 +M204 P1000 +G1 X134.186 Y108.621 F12000 +G1 X134.186 Y108.087 +M204 P800 +G1 F2400 +G1 X130.728 Y111.545 E.15344 +M204 P1000 +G1 X130.728 Y111.545 F12000 +G1 X130.732 Y111.008 +M204 P800 +G1 F2400 +G1 X134.186 Y107.554 E.15326 +M204 P1000 +G1 X134.186 Y107.554 F12000 +G1 X134.186 Y107.02 +M204 P800 +G1 F2400 +G1 X130.736 Y110.47 E.15308 +M204 P1000 +G1 X130.736 Y109.937 F12000 +M204 P800 +G1 F2400 +G1 X134.186 Y106.486 E.1531 +M204 P1000 +G1 X134.186 Y106.486 F12000 +G1 X134.186 Y105.953 +M204 P800 +G1 F2400 +G1 X130.605 Y109.534 E.15889 +M204 P1000 +G1 X130.376 Y109.229 F12000 +M204 P800 +G1 F2400 +G1 X134.186 Y105.419 E.16905 +M204 P1000 +G1 X134.186 Y105.419 F12000 +G1 X134.186 Y104.885 +M204 P800 +G1 F2400 +G1 X130.138 Y108.934 E.17964 +M204 P1000 +G1 X130.138 Y108.934 F12000 +G1 X129.899 Y108.639 +M204 P800 +G1 F2400 +G1 X134.186 Y104.352 E.19022 +M204 P1000 +G1 X134.186 Y104.352 F12000 +G1 X134.186 Y103.818 +M204 P800 +G1 F2400 +G1 X129.66 Y108.344 E.20082 +M204 P1000 +G1 X129.66 Y108.344 F12000 +G1 X129.422 Y108.049 +M204 P800 +G1 F2400 +G1 X134.186 Y103.285 E.21139 +M204 P1000 +G1 X134.186 Y103.285 F12000 +G1 X134.186 Y102.751 +M204 P800 +G1 F2400 +G1 X129.183 Y107.754 E.22199 +M204 P1000 +G1 X129.183 Y107.754 F12000 +G1 X128.944 Y107.459 +M204 P800 +G1 F2400 +G1 X134.186 Y102.217 E.23259 +M204 P1000 +G1 X134.186 Y102.217 F12000 +G1 X134.186 Y101.684 +M204 P800 +G1 F2400 +G1 X128.706 Y107.164 E.24316 +M204 P1000 +G1 X128.706 Y107.164 F12000 +G1 X128.467 Y106.869 +M204 P800 +G1 F2400 +G1 X134.186 Y101.15 E.25376 +M204 P1000 +G1 X134.186 Y101.15 F12000 +M73 Q98 S0 +G1 X134.186 Y100.616 +M204 P800 +G1 F2400 +M73 P98 R0 +G1 X128.228 Y106.574 E.26436 +M204 P1000 +G1 X128.228 Y106.574 F12000 +G1 X127.99 Y106.279 +M204 P800 +G1 F2400 +G1 X134.186 Y100.083 E.27492 +M204 P1000 +G1 X134.186 Y100.083 F12000 +G1 X134.186 Y99.549 +M204 P800 +G1 F2400 +G1 X127.751 Y105.984 E.28553 +M204 P1000 +G1 X127.751 Y105.984 F12000 +G1 X127.512 Y105.69 +M204 P800 +G1 F2400 +G1 X134.186 Y99.015 E.29616 +M204 P1000 +G1 X134.186 Y99.015 F12000 +G1 X134.186 Y98.482 +M204 P800 +G1 F2400 +G1 X127.274 Y105.395 E.30672 +M204 P1000 +G1 X127.274 Y105.395 F12000 +G1 X127.035 Y105.1 +M204 P800 +G1 F2400 +G1 X134.186 Y97.948 E.31732 +M204 P1000 +G1 X134.186 Y97.948 F12000 +G1 X134.186 Y97.415 +M204 P800 +G1 F2400 +G1 X126.796 Y104.805 E.3279 +M204 P1000 +G1 X126.796 Y104.805 F12000 +G1 X126.557 Y104.51 +M204 P800 +G1 F2400 +G1 X130.233 Y100.834 E.16311 +M204 P1000 +G1 X130.233 Y100.834 F12000 +G1 X129.686 Y100.848 +M204 P800 +G1 F2400 +G1 X126.319 Y104.215 E.1494 +M204 P1000 +G1 X126.319 Y104.215 F12000 +G1 X126.08 Y103.92 +M204 P800 +G1 F2400 +G1 X129.152 Y100.848 E.13631 +M204 P1000 +G1 X129.152 Y100.848 F12000 +G1 X128.618 Y100.848 +M204 P800 +G1 F2400 +G1 X125.841 Y103.625 E.12322 +M204 P1000 +G1 X125.841 Y103.625 F12000 +G1 X125.603 Y103.33 +M204 P800 +G1 F2400 +G1 X128.085 Y100.848 E.11013 +M204 P1000 +G1 X128.085 Y100.848 F12000 +G1 X127.551 Y100.848 +M204 P800 +G1 F2400 +G1 X125.364 Y103.035 E.09704 +M204 P1000 +G1 X125.125 Y102.74 F12000 +M204 P800 +G1 F2400 +G1 X127.018 Y100.848 E.08397 +M204 P1000 +G1 X127.018 Y100.848 F12000 +G1 X126.484 Y100.848 +M204 P800 +G1 F2400 +G1 X124.887 Y102.445 E.07086 +M204 P1000 +G1 X124.887 Y102.445 F12000 +G1 X124.648 Y102.15 +M204 P800 +G1 F2400 +G1 X125.95 Y100.848 E.05777 +M204 P1000 +G1 X125.95 Y100.848 F12000 +G1 X125.417 Y100.848 +M204 P800 +G1 F2400 +G1 X124.409 Y101.855 E.0447 +M204 P1000 +G1 X124.409 Y101.855 F12000 +G1 X124.171 Y101.56 +M204 P800 +G1 F2400 +G1 X124.883 Y100.848 E.03159 +M204 P1000 +G1 X124.883 Y100.848 F12000 +G1 X124.349 Y100.848 +M204 P800 +G1 F2400 +G1 X123.932 Y101.265 E.0185 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X124.349 Y100.848 E-.12255 +;WIPE_END +G1 E-.51745 F2100 +G1 X130.941 Y100.126 Z20.116 F12000 +G1 Z20 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X134.186 Y96.881 E.14399 +M204 P1000 +G1 X134.186 Y96.881 F12000 +G1 X134.186 Y96.347 +M204 P800 +G1 F2400 +G1 X130.945 Y99.589 E.14383 +M204 P1000 +G1 X130.945 Y99.589 F12000 +G1 X130.948 Y99.051 +M204 P800 +G1 F2400 +G1 X134.186 Y95.814 E.14365 +M204 P1000 +G1 X134.186 Y95.814 F12000 +G1 X133.653 Y95.814 +M204 P800 +G1 F2400 +G1 X130.952 Y98.514 E.11982 +M204 P1000 +G1 X130.952 Y98.514 F12000 +G1 X130.84 Y98.093 +M204 P800 +G1 F2400 +G1 X133.119 Y95.814 E.10112 +M204 P1000 +G1 X133.119 Y95.814 F12000 +G1 X132.585 Y95.814 +M204 P800 +G1 F2400 +G1 X130.577 Y97.822 E.0891 +M204 P1000 +G1 X130.577 Y97.822 F12000 +G1 X130.175 Y97.69 +M204 P800 +G1 F2400 +G1 X132.052 Y95.814 E.08326 +M204 P1000 +G1 X132.052 Y95.814 F12000 +G1 X131.518 Y95.814 +M204 P800 +G1 F2400 +G1 X129.646 Y97.686 E.08306 +M204 P1000 +G1 X129.646 Y97.686 F12000 +G1 X129.112 Y97.686 +M204 P800 +G1 F2400 +G1 X130.984 Y95.814 E.08306 +M204 P1000 +G1 X130.984 Y95.814 F12000 +G1 X130.451 Y95.814 +M204 P800 +G1 F2400 +G1 X128.578 Y97.686 E.08309 +M204 P1000 +G1 X128.578 Y97.686 F12000 +G1 X128.045 Y97.686 +M204 P800 +G1 F2400 +G1 X129.917 Y95.814 E.08306 +M204 P1000 +G1 X129.917 Y95.814 F12000 +G1 X129.384 Y95.814 +M204 P800 +G1 F2400 +G1 X127.511 Y97.686 E.08309 +M204 P1000 +G1 X127.511 Y97.686 F12000 +G1 X126.977 Y97.686 +M204 P800 +G1 F2400 +G1 X128.85 Y95.814 E.08309 +M204 P1000 +G1 X128.85 Y95.814 F12000 +G1 X128.316 Y95.814 +M204 P800 +G1 F2400 +G1 X126.444 Y97.686 E.08306 +M204 P1000 +G1 X126.444 Y97.686 F12000 +G1 X125.91 Y97.686 +M204 P800 +G1 F2400 +G1 X127.783 Y95.814 E.08309 +M204 P1000 +G1 X127.783 Y95.814 F12000 +G1 X127.249 Y95.814 +M204 P800 +G1 F2400 +G1 X125.377 Y97.686 E.08306 +M204 P1000 +G1 X125.377 Y97.686 F12000 +G1 X124.843 Y97.686 +M204 P800 +G1 F2400 +G1 X126.715 Y95.814 E.08306 +M204 P1000 +G1 X126.715 Y95.814 F12000 +G1 X126.182 Y95.814 +M204 P800 +G1 F2400 +G1 X124.309 Y97.686 E.08309 +M204 P1000 +G1 X124.309 Y97.686 F12000 +G1 X123.776 Y97.686 +M204 P800 +G1 F2400 +G1 X125.648 Y95.814 E.08306 +M204 P1000 +G1 X125.648 Y95.814 F12000 +G1 X125.115 Y95.814 +M204 P800 +G1 F2400 +G1 X123.242 Y97.686 E.08309 +M204 P1000 +G1 X123.242 Y97.686 F12000 +G1 X122.708 Y97.686 +M204 P800 +G1 F2400 +G1 X124.581 Y95.814 E.08309 +M204 P1000 +G1 X124.581 Y95.814 F12000 +G1 X124.047 Y95.814 +M204 P800 +G1 F2400 +G1 X122.175 Y97.686 E.08306 +M204 P1000 +G1 X122.175 Y97.686 F12000 +G1 X121.641 Y97.686 +M204 P800 +G1 F2400 +G1 X123.514 Y95.814 E.08309 +M204 P1000 +G1 X123.514 Y95.814 F12000 +G1 X122.98 Y95.814 +M204 P800 +G1 F2400 +G1 X121.108 Y97.686 E.08306 +M204 P1000 +G1 X121.108 Y97.686 F12000 +G1 X120.574 Y97.686 +M204 P800 +G1 F2400 +G1 X122.446 Y95.814 E.08306 +M204 P1000 +G1 X122.446 Y95.814 F12000 +G1 X121.913 Y95.814 +M204 P800 +G1 F2400 +G1 X120.04 Y97.686 E.08309 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.913 Y95.814 E-.55031 +;WIPE_END +G1 E-.08969 F2100 +G1 X125.645 Y108.624 Z20.233 F12000 +G1 Z20 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X125.117 Y109.152 E.02343 +M204 P1000 +G1 X125.117 Y109.152 F12000 +G1 X124.583 Y109.152 +M204 P800 +G1 F2400 +G1 X125.408 Y108.328 E.03658 +M204 P1000 +G1 X125.408 Y108.328 F12000 +G1 X125.171 Y108.031 +M204 P800 +G1 F2400 +G1 X124.05 Y109.152 E.04974 +M204 P1000 +G1 X124.05 Y109.152 F12000 +G1 X123.516 Y109.152 +M204 P800 +G1 F2400 +G1 X124.933 Y107.735 E.06287 +M204 P1000 +G1 X124.933 Y107.735 F12000 +G1 X124.696 Y107.438 +M204 P800 +G1 F2400 +G1 X122.982 Y109.152 E.07605 +M204 P1000 +G1 X122.982 Y109.152 F12000 +G1 X122.449 Y109.152 +M204 P800 +G1 F2400 +G1 X124.459 Y107.142 E.08919 +M204 P1000 +G1 X124.459 Y107.142 F12000 +G1 X124.222 Y106.846 +M204 P800 +G1 F2400 +G1 X121.915 Y109.152 E.10234 +M204 P1000 +G1 X121.915 Y109.152 F12000 +G1 X121.381 Y109.152 +M204 P800 +G1 F2400 +G1 X123.984 Y106.549 E.1155 +M204 P1000 +G1 X123.984 Y106.549 F12000 +G1 X123.747 Y106.253 +M204 P800 +G1 F2400 +G1 X120.848 Y109.152 E.12863 +M204 P1000 +G1 X120.848 Y109.152 F12000 +G1 X120.215 Y109.252 +M204 P800 +G1 F2400 +G1 X123.51 Y105.957 E.1462 +M204 P1000 +G1 E-.16 F2100 +;WIPE_START +G1 F9600 +G1 X121.332 Y108.134 E-.64 +;WIPE_END +G1 X129.961 Y112.313 Z20.167 F12000 +G1 Z20 F720 +G1 E.8 F1500 +M204 P800 +G1 F2400 +G1 X128.087 Y114.186 E.08313 +M204 P1000 +G1 X128.087 Y114.186 F12000 +G1 X127.554 Y114.186 +M204 P800 +G1 F2400 +G1 X129.426 Y112.314 E.08306 +M204 P1000 +G1 X129.426 Y112.314 F12000 +G1 X128.892 Y112.314 +M204 P800 +G1 F2400 +G1 X127.02 Y114.186 E.08306 +M204 P1000 +G1 X127.02 Y114.186 F12000 +G1 X126.486 Y114.186 +M204 P800 +G1 F2400 +G1 X128.359 Y112.314 E.08309 +M204 P1000 +G1 X128.359 Y112.314 F12000 +G1 X127.825 Y112.314 +M204 P800 +G1 F2400 +G1 X125.953 Y114.186 E.08306 +M204 P1000 +G1 X125.953 Y114.186 F12000 +G1 X125.419 Y114.186 +M204 P800 +G1 F2400 +G1 X127.291 Y112.314 E.08306 +M204 P1000 +G1 X127.291 Y112.314 F12000 +G1 X126.758 Y112.314 +M204 P800 +G1 F2400 +G1 X124.885 Y114.186 E.08309 +M204 P1000 +G1 X124.885 Y114.186 F12000 +G1 X124.352 Y114.186 +M204 P800 +G1 F2400 +G1 X126.224 Y112.314 E.08306 +M204 P1000 +G1 X126.224 Y112.314 F12000 +G1 X125.691 Y112.314 +M204 P800 +G1 F2400 +G1 X123.818 Y114.186 E.08309 +M204 P1000 +G1 X123.818 Y114.186 F12000 +G1 X123.284 Y114.186 +M204 P800 +G1 F2400 +G1 X125.157 Y112.314 E.08309 +M204 P1000 +G1 X125.157 Y112.314 F12000 +G1 X124.623 Y112.314 +M204 P800 +G1 F2400 +G1 X122.751 Y114.186 E.08306 +M204 P1000 +G1 X122.751 Y114.186 F12000 +G1 X122.217 Y114.186 +M204 P800 +G1 F2400 +G1 X124.09 Y112.314 E.08309 +M204 P1000 +G1 X124.09 Y112.314 F12000 +G1 X123.556 Y112.314 +M204 P800 +G1 F2400 +G1 X121.684 Y114.186 E.08306 +M204 P1000 +G1 X121.684 Y114.186 F12000 +G1 X121.15 Y114.186 +M204 P800 +G1 F2400 +G1 X123.022 Y112.314 E.08306 +M204 P1000 +G1 X123.022 Y112.314 F12000 +G1 X122.489 Y112.314 +M204 P800 +G1 F2400 +G1 X120.616 Y114.186 E.08309 +M204 P1000 +G1 X120.616 Y114.186 F12000 +G1 X120.083 Y114.186 +M204 P800 +G1 F2400 +G1 X121.955 Y112.314 E.08306 +M204 P1000 +G1 X121.955 Y112.314 F12000 +G1 X121.422 Y112.314 +M204 P800 +G1 F2400 +G1 X119.549 Y114.186 E.08309 +M204 P1000 +G1 X119.549 Y114.186 F12000 +G1 X119.015 Y114.186 +M204 P800 +G1 F2400 +G1 X120.888 Y112.314 E.08309 +M204 P1000 +G1 X120.888 Y112.314 F12000 +G1 X120.405 Y112.263 +M204 P800 +G1 F2400 +G1 X118.482 Y114.186 E.08533 +M204 P1000 +G1 X118.482 Y114.186 F12000 +G1 X117.948 Y114.186 +M204 P800 +G1 F2400 +G1 X120.058 Y112.077 E.0936 +M204 P1000 +G1 X120.058 Y112.077 F12000 +G1 X119.846 Y111.755 +M204 P800 +G1 F2400 +G1 X117.415 Y114.186 E.10787 +M204 P1000 +G1 X117.415 Y114.186 F12000 +G1 X116.881 Y114.186 +M204 P800 +G1 F2400 +G1 X119.801 Y111.266 E.12956 +M204 P1000 +G1 X119.801 Y111.266 F12000 +G1 X119.801 Y110.732 +M204 P800 +G1 F2400 +G1 X116.347 Y114.186 E.15326 +M204 P1000 +G1 X116.347 Y114.186 F12000 +G1 X115.814 Y114.186 +M204 P800 +G1 F2400 +G1 X119.801 Y110.199 E.17691 +M204 P1000 +G1 X119.801 Y110.199 F12000 +G1 X119.904 Y109.562 +M204 P800 +G1 F2400 +G1 X115.814 Y113.653 E.1815 +M204 P1000 +G1 X115.814 Y113.653 F12000 +G1 X115.814 Y113.119 +M204 P800 +G1 F2400 +G1 X123.273 Y105.66 E.33097 +M204 P1000 +G1 X123.273 Y105.66 F12000 +G1 X123.035 Y105.364 +M204 P800 +G1 F2400 +G1 X115.814 Y112.585 E.32041 +M204 P1000 +G1 X115.814 Y112.585 F12000 +G1 X115.814 Y112.052 +M204 P800 +G1 F2400 +G1 X122.798 Y105.067 E.30991 +M204 P1000 +G1 X122.798 Y105.067 F12000 +M73 P99 R0 +M73 Q99 S0 +G1 X122.561 Y104.771 +M204 P800 +G1 F2400 +G1 X115.814 Y111.518 E.29937 +M204 P1000 +G1 X115.814 Y111.518 F12000 +G1 X115.814 Y110.984 +M204 P800 +G1 F2400 +G1 X122.324 Y104.475 E.28884 +M204 P1000 +G1 X122.324 Y104.475 F12000 +G1 X122.086 Y104.178 +M204 P800 +G1 F2400 +G1 X115.814 Y110.451 E.27832 +M204 P1000 +G1 X115.814 Y110.451 F12000 +G1 X115.814 Y109.917 +M204 P800 +G1 F2400 +G1 X121.849 Y103.882 E.26778 +M204 P1000 +G1 X121.849 Y103.882 F12000 +G1 X121.612 Y103.585 +M204 P800 +G1 F2400 +G1 X115.814 Y109.384 E.25729 +M204 P1000 +G1 X115.814 Y109.384 F12000 +G1 X115.814 Y108.85 +M204 P800 +G1 F2400 +G1 X121.375 Y103.289 E.24675 +M204 P1000 +G1 X121.375 Y103.289 F12000 +G1 X121.137 Y102.993 +M204 P800 +G1 F2400 +G1 X115.814 Y108.316 E.23619 +M204 P1000 +G1 X115.814 Y108.316 F12000 +G1 X115.814 Y107.783 +M204 P800 +G1 F2400 +G1 X120.9 Y102.696 E.22569 +M204 P1000 +G1 X120.9 Y102.696 F12000 +G1 X120.663 Y102.4 +M204 P800 +G1 F2400 +G1 X115.814 Y107.249 E.21516 +M204 P1000 +G1 X115.814 Y107.249 F12000 +G1 X115.814 Y106.715 +M204 P800 +G1 F2400 +G1 X120.426 Y102.104 E.20462 +M204 P1000 +G1 X120.426 Y102.104 F12000 +G1 X120.188 Y101.807 +M204 P800 +G1 F2400 +G1 X115.814 Y106.182 E.1941 +M204 P1000 +G1 X115.814 Y106.182 F12000 +G1 X115.814 Y105.648 +M204 P800 +G1 F2400 +G1 X119.951 Y101.511 E.18356 +M204 P1000 +G1 X119.951 Y101.511 F12000 +G1 X119.714 Y101.214 +M204 P800 +G1 F2400 +G1 X115.814 Y105.115 E.17307 +M204 P1000 +G1 X115.814 Y105.115 F12000 +G1 X115.814 Y104.581 +M204 P800 +G1 F2400 +G1 X119.477 Y100.918 E.16253 +M204 P1000 +G1 X119.477 Y100.918 F12000 +G1 X119.239 Y100.622 +M204 P800 +G1 F2400 +G1 X115.814 Y104.047 E.15197 +M204 P1000 +G1 X115.814 Y104.047 F12000 +G1 X115.814 Y103.514 +M204 P800 +G1 F2400 +G1 X119.074 Y100.253 E.14467 +M204 P1000 +G1 X119.074 Y100.253 F12000 +G1 X119.047 Y99.746 +M204 P800 +G1 F2400 +G1 X115.814 Y102.98 E.14347 +M204 P1000 +G1 X115.814 Y102.98 F12000 +G1 X115.814 Y102.446 +M204 P800 +G1 F2400 +G1 X119.047 Y99.213 E.14345 +M204 P1000 +G1 X119.047 Y99.213 F12000 +G1 X119.047 Y98.679 +M204 P800 +G1 F2400 +G1 X115.814 Y101.913 E.14347 +M204 P1000 +G1 X115.814 Y101.913 F12000 +G1 X115.814 Y101.379 +M204 P800 +G1 F2400 +G1 X121.379 Y95.814 E.24693 +M204 P1000 +G1 X121.379 Y95.814 F12000 +G1 X120.845 Y95.814 +M204 P800 +G1 F2400 +G1 X115.814 Y100.845 E.22323 +M204 P1000 +G1 X115.814 Y100.845 F12000 +G1 X115.814 Y100.312 +M204 P800 +G1 F2400 +G1 X120.312 Y95.814 E.19958 +M204 P1000 +G1 X120.312 Y95.814 F12000 +G1 X119.778 Y95.814 +M204 P800 +G1 F2400 +G1 X115.814 Y99.778 E.17589 +M204 P1000 +G1 X115.814 Y99.778 F12000 +G1 X115.814 Y99.245 +M204 P800 +G1 F2400 +G1 X119.245 Y95.814 E.15224 +M204 P1000 +G1 X119.245 Y95.814 F12000 +G1 X118.711 Y95.814 +M204 P800 +G1 F2400 +G1 X115.814 Y98.711 E.12854 +M204 P1000 +G1 X115.814 Y98.711 F12000 +G1 X115.814 Y98.177 +M204 P800 +G1 F2400 +G1 X118.177 Y95.814 E.10485 +M204 P1000 +G1 X118.177 Y95.814 F12000 +G1 X117.644 Y95.814 +M204 P800 +G1 F2400 +G1 X115.814 Y97.644 E.0812 +M204 P1000 +G1 X115.814 Y97.644 F12000 +G1 X115.814 Y97.11 +M204 P800 +G1 F2400 +G1 X117.11 Y95.814 E.05751 +M204 P1000 +G1 X117.11 Y95.814 F12000 +G1 X116.576 Y95.814 +M204 P800 +G1 F2400 +G1 X115.814 Y96.576 E.03381 +M204 P1000 +M486 S-1 +G1 E-.16 F2100 +;WIPE_START +G1 F9600;_WIPE +G1 X116.576 Y95.814 E-.22395 +;WIPE_END +G1 E-.41605 F2100 +M107 +;TYPE:Custom +; Filament-specific end gcode +G1 Z21 F720 ; Move print head up +M104 S0 ; turn off temperature +M140 S0 ; turn off heatbed +M107 ; turn off fan +G1 X241 Y170 F3600 ; park +G1 Z43 F300 ; Move print head up +G4 ; wait +M900 K0 ; reset LA +M142 S36 ; reset heatbreak target temp +M84 X Y E ; disable motors +; max_layer_z = 20 +M73 P100 R0 +M73 Q100 S0 +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl","polygon":[[135.000,115.000],[115.000,115.000],[115.000,95.000],[135.000,95.000]]}]} +; filament used [mm] = 1340.83 +; filament used [cm3] = 3.23 +; filament used [g] = 4.10 +; filament cost = 0.11 +; total filament used [g] = 4.10 +; total filament cost = 0.11 +; total filament used for wipe tower [g] = 0.00 +; estimated printing time (normal mode) = 21m 37s +; estimated printing time (silent mode) = 21m 36s +; estimated first layer printing time (normal mode) = 53s +; estimated first layer printing time (silent mode) = 53s + +; prusaslicer_config = begin +; arc_fitting = emit_center +; autoemit_temperature_commands = 1 +; avoid_crossing_curled_overhangs = 0 +; avoid_crossing_perimeters = 0 +; avoid_crossing_perimeters_max_detour = 0 +; bed_custom_model = +; bed_custom_texture = +; bed_shape = 0x0,250x0,250x210,0x210 +; bed_temperature = 90 +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n +; between_objects_gcode = +; binary_gcode = 0 +; bottom_fill_pattern = monotonic +; bottom_solid_layers = 4 +; bottom_solid_min_thickness = 0.5 +; bridge_acceleration = 1000 +; bridge_angle = 0 +; bridge_fan_speed = 50 +; bridge_flow_ratio = 1 +; bridge_speed = 25 +; brim_separation = 0.1 +; brim_type = outer_only +; brim_width = 0 +; chamber_minimal_temperature = 0 +; chamber_temperature = 0 +; color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change +; colorprint_heights = +; compatible_printers_condition_cummulative = "printer_model==\"MK4\" and nozzle_diameter[0]==0.4";"printer_model==\"MK4\" and printer_notes!~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6" +; complete_objects = 0 +; cooling = 1 +; cooling_tube_length = 5 +; cooling_tube_retraction = 91.5 +; default_acceleration = 1000 +; default_filament_profile = "Prusament PLA @PG" +; default_print_profile = 0.20mm QUALITY @MK4 0.4 +; deretract_speed = 25 +; disable_fan_first_layers = 3 +; dont_support_bridges = 0 +; draft_shield = disabled +; duplicate_distance = 6 +; elefant_foot_compensation = 0.2 +; enable_dynamic_fan_speeds = 0 +; enable_dynamic_overhang_speeds = 1 +; end_filament_gcode = "; Filament-specific end gcode" +; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM900 K0 ; reset LA\nM142 S36 ; reset heatbreak target temp\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +; external_perimeter_acceleration = 800 +; external_perimeter_extrusion_width = 0.45 +; external_perimeter_speed = 40 +; external_perimeters_first = 0 +; extra_loading_move = -2 +; extra_perimeters = 0 +; extra_perimeters_on_overhangs = 0 +; extruder_clearance_height = 13 +; extruder_clearance_radius = 45 +; extruder_colour = "" +; extruder_offset = 0x0 +; extrusion_axis = E +; extrusion_multiplier = 1 +; extrusion_width = 0.45 +; fan_always_on = 1 +; fan_below_layer_time = 20 +; filament_abrasive = 0 +; filament_colour = #FF8000 +; filament_cooling_final_speed = 2.5 +; filament_cooling_initial_speed = 5 +; filament_cooling_moves = 3 +; filament_cost = 27.82 +; filament_density = 1.27 +; filament_deretract_speed = nil +; filament_diameter = 1.75 +; filament_infill_max_crossing_speed = 110 +; filament_infill_max_speed = 0 +; filament_load_time = 10.5 +; filament_loading_speed = 10 +; filament_loading_speed_start = 50 +; filament_max_volumetric_speed = 12 +; filament_minimal_purge_on_wipe_tower = 35 +; filament_multitool_ramming = 0 +; filament_multitool_ramming_flow = 10 +; filament_multitool_ramming_volume = 10 +; filament_notes = "" +; filament_purge_multiplier = 100% +; filament_ramming_parameters = "250 100 42.4194 42.4194 42.4194 42.4194 42.4194| 0.05 42.4387 0.45 42.4387 0.95 42.4387 1.45 42.4387 1.95 42.4387 2.45 42.4387 2.95 42.4387 3.45 42.4387 3.95 42.4387 4.45 42.4387 4.95 42.4387" +; filament_retract_before_travel = nil +; filament_retract_before_wipe = 20% +; filament_retract_layer_change = nil +; filament_retract_length = 0.8 +; filament_retract_length_toolchange = nil +; filament_retract_lift = 0.15 +; filament_retract_lift_above = nil +; filament_retract_lift_below = nil +; filament_retract_restart_extra = nil +; filament_retract_restart_extra_toolchange = nil +; filament_retract_speed = nil +; filament_settings_id = "Generic PETG @PG" +; filament_shrinkage_compensation_xy = 0% +; filament_shrinkage_compensation_z = 0% +; filament_soluble = 0 +; filament_spool_weight = 0 +; filament_stamping_distance = 45 +; filament_stamping_loading_speed = 26.5 +; filament_toolchange_delay = 0 +; filament_travel_lift_before_obstacle = nil +; filament_travel_max_lift = 1.5 +; filament_travel_ramping_lift = 1 +; filament_travel_slope = 1 +; filament_type = PETG +; filament_unload_time = 8.5 +; filament_unloading_speed = 100 +; filament_unloading_speed_start = 100 +; filament_vendor = Generic +; filament_wipe = 1 +; fill_angle = 45 +; fill_density = 15% +; fill_pattern = grid +; first_layer_acceleration = 600 +; first_layer_acceleration_over_raft = 0 +; first_layer_bed_temperature = 85 +; first_layer_extrusion_width = 0.5 +; first_layer_height = 0.2 +; first_layer_speed = 20 +; first_layer_speed_over_raft = 30 +; first_layer_temperature = 230 +; full_fan_speed_layer = 5 +; fuzzy_skin = none +; fuzzy_skin_point_dist = 0.8 +; fuzzy_skin_thickness = 0.3 +; gap_fill_enabled = 1 +; gap_fill_speed = 45 +; gcode_comments = 0 +; gcode_flavor = marlin2 +; gcode_label_objects = firmware +; gcode_resolution = 0.0125 +; gcode_substitutions = +; high_current_on_filament_swap = 0 +; host_type = prusalink +; idle_temperature = 70 +; infill_acceleration = 2000 +; infill_anchor = 2 +; infill_anchor_max = 12 +; infill_every_layers = 1 +; infill_extruder = 1 +; infill_extrusion_width = 0.45 +; infill_first = 0 +; infill_overlap = 10% +; infill_speed = 200 +; inherits_cummulative = ;;"Original Prusa MK4 0.4 nozzle" +; interface_shells = 0 +; ironing = 0 +; ironing_flowrate = 15% +; ironing_spacing = 0.1 +; ironing_speed = 15 +; ironing_type = top +; layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] +; layer_height = 0.2 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 2500,2500 +; machine_max_acceleration_extruding = 2000,2000 +; machine_max_acceleration_retracting = 1200,1200 +; machine_max_acceleration_travel = 2000,2000 +; machine_max_acceleration_x = 2500,2500 +; machine_max_acceleration_y = 2500,2500 +; machine_max_acceleration_z = 200,200 +; machine_max_feedrate_e = 100,100 +; machine_max_feedrate_x = 200,160 +; machine_max_feedrate_y = 200,160 +; machine_max_feedrate_z = 40,40 +; machine_max_jerk_e = 10,10 +; machine_max_jerk_x = 8,8 +; machine_max_jerk_y = 8,8 +; machine_max_jerk_z = 2,2 +; machine_min_extruding_rate = 0 +; machine_min_travel_rate = 0 +; max_fan_speed = 50 +; max_layer_height = 0.3 +; max_print_height = 220 +; max_print_speed = 200 +; max_volumetric_extrusion_rate_slope_negative = 0 +; max_volumetric_extrusion_rate_slope_positive = 0 +; max_volumetric_speed = 0 +; min_bead_width = 85% +; min_fan_speed = 30 +; min_feature_size = 25% +; min_layer_height = 0.07 +; min_print_speed = 15 +; min_skirt_length = 4 +; mmu_segmented_region_interlocking_depth = 0 +; mmu_segmented_region_max_width = 0 +; multimaterial_purging = 140 +; notes = +; nozzle_diameter = 0.4 +; nozzle_high_flow = 0 +; only_one_perimeter_first_layer = 0 +; only_retract_when_crossing_perimeters = 0 +; ooze_prevention = 0 +; output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +; overhang_fan_speed_0 = 0 +; overhang_fan_speed_1 = 0 +; overhang_fan_speed_2 = 0 +; overhang_fan_speed_3 = 0 +; overhang_speed_0 = 15 +; overhang_speed_1 = 15 +; overhang_speed_2 = 20 +; overhang_speed_3 = 80% +; overhangs = 1 +; parking_pos_retraction = 92 +; pause_print_gcode = M601 +; perimeter_acceleration = 1000 +; perimeter_extruder = 1 +; perimeter_extrusion_width = 0.45 +; perimeter_generator = arachne +; perimeter_speed = 70 +; perimeters = 2 +; physical_printer_settings_id = +; post_process = +; prefer_clockwise_movements = 0 +; print_settings_id = 0.20mm SPEED @MK4 0.4 +; printer_model = MK4 +; printer_notes = Do not remove the keywords below.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK4\nPG +; printer_settings_id = School Prusa MK4 0.4 nozzle +; printer_technology = FFF +; printer_variant = 0.4 +; printer_vendor = +; raft_contact_distance = 0.2 +; raft_expansion = 1.5 +; raft_first_layer_density = 80% +; raft_first_layer_expansion = 3 +; raft_layers = 0 +; remaining_times = 1 +; resolution = 0 +; retract_before_travel = 1.5 +; retract_before_wipe = 80% +; retract_layer_change = 1 +; retract_length = 0.8 +; retract_length_toolchange = 0 +; retract_lift = 0.2 +; retract_lift_above = 0 +; retract_lift_below = 219 +; retract_restart_extra = 0 +; retract_restart_extra_toolchange = 0 +; retract_speed = 35 +; seam_position = aligned +; silent_mode = 1 +; single_extruder_multi_material = 0 +; single_extruder_multi_material_priming = 0 +; skirt_distance = 2 +; skirt_height = 3 +; skirts = 0 +; slice_closing_radius = 0.049 +; slicing_mode = regular +; slowdown_below_layer_time = 9 +; small_perimeter_speed = 35 +; solid_infill_acceleration = 1500 +; solid_infill_below_area = 0 +; solid_infill_every_layers = 0 +; solid_infill_extruder = 1 +; solid_infill_extrusion_width = 0.45 +; solid_infill_speed = 200 +; spiral_vase = 0 +; staggered_inner_seams = 0 +; standby_temperature_delta = -5 +; start_filament_gcode = "M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" +; start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] A{(filament_abrasive[0] ? 1 : 0)} F{(nozzle_high_flow[0] ? 1 : 0)} ; nozzle check\nM115 U6.1.2+7894\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; set extruder temp for bed leveling\nM109 T0 R{((filament_notes[0]=~/.*HT_MBL10.*/) ? (first_layer_temperature[0] - 10) : (filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX") ? 210 : (filament_type[0]=~/.*PET.*/) ? 175 : 170)} ; wait for temp\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; support_material = 0 +; support_material_angle = 0 +; support_material_auto = 1 +; support_material_bottom_contact_distance = 0 +; support_material_bottom_interface_layers = 0 +; support_material_buildplate_only = 0 +; support_material_closing_radius = 2 +; support_material_contact_distance = 0.2 +; support_material_enforce_layers = 0 +; support_material_extruder = 0 +; support_material_extrusion_width = 0.37 +; support_material_interface_contact_loops = 0 +; support_material_interface_extruder = 0 +; support_material_interface_layers = 5 +; support_material_interface_pattern = rectilinear +; support_material_interface_spacing = 0.2 +; support_material_interface_speed = 70% +; support_material_pattern = rectilinear +; support_material_spacing = 2 +; support_material_speed = 50 +; support_material_style = snug +; support_material_synchronize_layers = 0 +; support_material_threshold = 45 +; support_material_with_sheath = 0 +; support_material_xy_spacing = 80% +; support_tree_angle = 40 +; support_tree_angle_slow = 30 +; support_tree_branch_diameter = 2 +; support_tree_branch_diameter_angle = 3 +; support_tree_branch_diameter_double_wall = 3 +; support_tree_branch_distance = 1 +; support_tree_tip_diameter = 0.6 +; support_tree_top_rate = 30% +; temperature = 240 +; template_custom_gcode = +; thick_bridges = 0 +; thin_walls = 0 +; thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 480x240/QOI, 640x480/PNG +; thumbnails_format = PNG +; toolchange_gcode = +; top_fill_pattern = monotoniclines +; top_infill_extrusion_width = 0.42 +; top_one_perimeter_type = none +; top_solid_infill_acceleration = 800 +; top_solid_infill_speed = 40 +; top_solid_layers = 5 +; top_solid_min_thickness = 0.7 +; travel_acceleration = 0 +; travel_lift_before_obstacle = 0 +; travel_max_lift = 1.5 +; travel_ramping_lift = 1 +; travel_slope = 1 +; travel_speed = 200 +; travel_speed_z = 12 +; use_firmware_retraction = 0 +; use_relative_e_distances = 1 +; use_volumetric_e = 0 +; variable_layer_height = 1 +; wall_distribution_count = 1 +; wall_transition_angle = 10 +; wall_transition_filter_deviation = 25% +; wall_transition_length = 100% +; wipe = 1 +; wipe_into_infill = 0 +; wipe_into_objects = 0 +; wipe_tower = 1 +; wipe_tower_acceleration = 0 +; wipe_tower_bridging = 10 +; wipe_tower_brim_width = 2 +; wipe_tower_cone_angle = 0 +; wipe_tower_extra_flow = 100% +; wipe_tower_extra_spacing = 100% +; wipe_tower_extruder = 0 +; wipe_tower_no_sparse_layers = 0 +; wipe_tower_rotation_angle = 0 +; wipe_tower_width = 60 +; wipe_tower_x = 170 +; wipe_tower_y = 140 +; wiping_volumes_matrix = 0 +; wiping_volumes_use_custom_matrix = 0 +; xy_size_compensation = 0 +; z_offset = 0 +; prusaslicer_config = end From ed77991a0f3d6e42f00e8bf713c5f190429a282e Mon Sep 17 00:00:00 2001 From: iron768 Date: Wed, 30 Oct 2024 16:52:50 -0400 Subject: [PATCH 029/194] feat: printer can now emulate gcode commands --- printeremu/cmd/test_printer.go | 2 +- printeremu/src/emulator.go | 28 +++++-- printeremu/src/extruder.go | 16 ++-- printeremu/src/gcode_commands.go | 140 +++++++++++++++++++++++++++++++ printeremu/src/printer.go | 70 ++++++++++++++-- 5 files changed, 231 insertions(+), 25 deletions(-) create mode 100644 printeremu/src/gcode_commands.go diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 71d95d32..bdde17e2 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -7,7 +7,7 @@ import ( ) func main() { - extruder, printer, err := src.Init(1, "Ender3", "Creality", "hwid:032uhb3293n2", "Testing Printer", "Init") + extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "hwid:032uhb3293n2", "Testing Printer 1", "Init") if err != nil { fmt.Println(err) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 302eb9f3..d3994049 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -1,7 +1,11 @@ -package src +package src import ( + "bufio" "fmt" + "log" + "os" + "strings" "time" ) @@ -24,15 +28,25 @@ func Init(id int, device string, description string, hwid string, name string, s } func Run(extruder *Extruder, printer *Printer) { - fmt.Println("Running...") + scanner := bufio.NewScanner(os.Stdin) - extruder.SetExtruderTemp(200) + fmt.Println("Enter G-code commands (type 'exit' to quit):") - printer.GetExtruder().SetBedTemp(50) + for { + fmt.Print("> ") + scanner.Scan() + command := scanner.Text() - printer.GetExtruder().SetFanSpeed(100) + if strings.ToLower(command) == "exit" { + fmt.Println("Exiting printer emulator...") + break + } - fmt.Println(printer.String()) + response := CommandHandler(command, printer) + fmt.Println(response) + } - fmt.Println("Done!") + if err := scanner.Err(); err != nil { + log.Println("Error reading input:", err) + } } \ No newline at end of file diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go index a0157c79..3315696a 100644 --- a/printeremu/src/extruder.go +++ b/printeremu/src/extruder.go @@ -26,18 +26,18 @@ func NewExtruder(position Vector3, extruderTemp, bedTemp, fanSpeed float64) *Ext } } -func (e *Extruder) SetExtruderTemp(temp float64) { - e.ExtruderTemp = temp +func (extruder *Extruder) SetExtruderTemp(temp float64) { + extruder.ExtruderTemp = temp } -func (e *Extruder) SetBedTemp(temp float64) { - e.BedTemp = temp +func (extruder *Extruder) SetBedTemp(temp float64) { + extruder.BedTemp = temp } -func (e *Extruder) SetFanSpeed(speed float64) { - e.FanSpeed = speed +func (extruder *Extruder) SetFanSpeed(speed float64) { + extruder.FanSpeed = speed } -func (e *Extruder) String() string { - return fmt.Sprintf("{Position: %v, ExtruderTemp: %vc, BedTemp: %vc, FanSpeed: %v}", e.Position, e.ExtruderTemp, e.BedTemp, e.FanSpeed) +func (extruder *Extruder) String() string { + return fmt.Sprintf("{Position: %v, ExtruderTemp: %vc, BedTemp: %vc, FanSpeed: %v}", extruder.Position, extruder.ExtruderTemp, extruder.BedTemp, extruder.FanSpeed) } \ No newline at end of file diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go new file mode 100644 index 00000000..bf7467ba --- /dev/null +++ b/printeremu/src/gcode_commands.go @@ -0,0 +1,140 @@ +package src + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +type Command interface { + Execute(printer *Printer) string +} + +type G28Command struct{} + +func (cmd *G28Command) Execute(printer *Printer) string { + printer.extruder.Position = Vector3{X: 0, Y: 0, Z: 0} + return "ok\n" +} + +type G0G1Command struct { + target Vector3 + feedRate float64 +} + +func (cmd *G0G1Command) Execute(printer *Printer) string { + if (printer.paused) { + return "Printer is paused\n" + } + + printer.extruder.moveExtruder(cmd.target, cmd.feedRate) + return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) +} + +type CancelCommand struct{} + +func (cmd *CancelCommand) Execute(printer *Printer) string { + // todo: logic to cancel the operation + return "Canceled\n" +} + +type KeepAliveCommand struct { + state bool +} + +func (cmd *KeepAliveCommand) Execute(printer *Printer) string { + if cmd.state { + // todo: Logic to enable keep-alive + return "Keep-Alive Enabled\n" + } + // todo: Logic to disable keep-alive + return "Keep-Alive Disabled\n" +} + +type M114Command struct{} + +func (cmd *M114Command) Execute(printer *Printer) string { + position := printer.extruder.Position + return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) +} + +type M997Command struct {} + +func (cmd *M997Command) Execute(printer *Printer) string { + return fmt.Sprintf("Machine name: %s", printer.device) +} + +type M601Command struct {} + +func (cmd *M601Command) Execute(printer *Printer) string { + if (printer.paused) { + return "Printer is already paused\n" + } + + printer.paused = true + return "Machine is paused\n" +} + +type M602Command struct {} + +func (cmd *M602Command) Execute(printer *Printer) string { + if (printer.paused) { + return "Printer is not paused\n" + } + + printer.paused = false + return "Machine is no longer paused\n" +} + +var commandRegistry = map[string]func() Command{ + "G28": func() Command { return &G28Command{} }, + "G0": func() Command { return &G0G1Command{} }, + "G1": func() Command { return &G0G1Command{} }, + "M112": func() Command { return &CancelCommand{} }, + "M113": func() Command { return &KeepAliveCommand{state: true} }, + "M114": func() Command { return &M114Command{} }, + "M997": func() Command { return &M997Command{} }, + "M601": func() Command { return &M601Command{} }, + "M602": func() Command { return &M602Command{} }, +} + +func NewCommand(command string, printer *Printer) Command { + for key, factory := range commandRegistry { + if strings.HasPrefix(command, key) { + if key == "G0" || key == "G1" { + target, feedRate := parseMoveCommand(command, printer.extruder.Position) + return &G0G1Command{target: target, feedRate: feedRate} + } + return factory() + } + } + return nil +} + +func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { + // Regular expression patterns for parsing + reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) + reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) + reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) + reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) + + // Default target and feed rate + target := currentPos + feedRate := 3600.0 // Default feed rate + + if xMatch := reX.FindStringSubmatch(command); xMatch != nil { + target.X, _ = strconv.ParseFloat(xMatch[1], 64) + } + if yMatch := reY.FindStringSubmatch(command); yMatch != nil { + target.Y, _ = strconv.ParseFloat(yMatch[1], 64) + } + if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { + target.Z, _ = strconv.ParseFloat(zMatch[1], 64) + } + if fMatch := reF.FindStringSubmatch(command); fMatch != nil { + feedRate, _ = strconv.ParseFloat(fMatch[1], 64) + } + + return target, feedRate +} diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 91e7af68..13fdfc42 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -1,6 +1,11 @@ package src -import "fmt" +import ( + "fmt" + "math" + "strings" + "time" +) type Printer struct { id int @@ -11,6 +16,7 @@ type Printer struct { status string date string extruder *Extruder // Keep this as a pointer + paused bool } func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder) *Printer { @@ -23,22 +29,68 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex status: status, date: date, extruder: extruder, + paused: false, } } -func (p *Printer) SetExtruderTemp(temp float64) { - p.extruder.SetExtruderTemp(temp) +func (printer *Printer) SetExtruderTemp(temp float64) { + printer.extruder.SetExtruderTemp(temp) } -func (p *Printer) GetExtruderTemp() float64 { - return p.extruder.ExtruderTemp +func (printer *Printer) GetExtruderTemp() float64 { + return printer.extruder.ExtruderTemp } -func (p *Printer) GetExtruder() *Extruder { - return p.extruder +func (printer *Printer) GetExtruder() *Extruder { + return printer.extruder } -func (p *Printer) String() string { +func (printer *Printer) SetBedTemp(temp float64) { + printer.extruder.SetBedTemp(temp) +} + +func (printer *Printer) GetBedTemp() float64 { + return printer.extruder.BedTemp +} + +func (printer *Printer) SetFanSpeed(speed float64) { + printer.extruder.SetFanSpeed(speed) +} + +func (printer *Printer) GetFanSpeed() float64 { + return printer.extruder.FanSpeed +} + +func (printer *Printer) Pause() { + printer.paused = true +} + +func (printer *Printer) Resume() { + printer.paused = false +} + +func (printer *Printer) String() string { return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v}", - p.id, p.device, p.description, p.hwid, p.name, p.status, p.date, p.extruder.String()) + printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String()) +} + +func (extruder *Extruder) moveExtruder(target Vector3, feedRate float64) { + // distance to move + distance := math.Sqrt(math.Pow(target.X-extruder.Position.X, 2) + math.Pow(target.Y-extruder.Position.Y, 2) + math.Pow(target.Z-extruder.Position.Z, 2)) + + // time taken for movement based on feed rate + moveTime := distance / feedRate + time.Sleep(time.Duration(moveTime*1000) * time.Millisecond) // movement delay + + extruder.Position = target +} + +func CommandHandler(command string, printer *Printer) string { + command = strings.TrimSpace(command) + cmd := NewCommand(command, printer) + + if cmd == nil { + return "Unknown command\n" + } + return cmd.Execute(printer) } From 6be03095e584337ca0eb2b97b0793485a2dd948f Mon Sep 17 00:00:00 2001 From: iron768 Date: Wed, 30 Oct 2024 16:55:43 -0400 Subject: [PATCH 030/194] fix: M602 command --- printeremu/src/gcode_commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index bf7467ba..d4c3e803 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -79,7 +79,7 @@ func (cmd *M601Command) Execute(printer *Printer) string { type M602Command struct {} func (cmd *M602Command) Execute(printer *Printer) string { - if (printer.paused) { + if (!printer.paused) { return "Printer is not paused\n" } From c852e8cee6a2191258532544cc9f97b6856f29fe Mon Sep 17 00:00:00 2001 From: iron768 Date: Wed, 30 Oct 2024 17:07:57 -0400 Subject: [PATCH 031/194] feat: add second option to quit command handler --- printeremu/src/emulator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index d3994049..08ef085a 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -30,14 +30,14 @@ func Init(id int, device string, description string, hwid string, name string, s func Run(extruder *Extruder, printer *Printer) { scanner := bufio.NewScanner(os.Stdin) - fmt.Println("Enter G-code commands (type 'exit' to quit):") + fmt.Println("Enter G-code commands (type 'exit' or 'quit' to quit):") for { fmt.Print("> ") scanner.Scan() command := scanner.Text() - if strings.ToLower(command) == "exit" { + if strings.ToLower(command) == "exit" || strings.ToLower(command) == "quit" { fmt.Println("Exiting printer emulator...") break } From 5f14f51153d5d04bd5ae2ba7b07e031f142d675f Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:35:03 -0400 Subject: [PATCH 032/194] expanded codes + reworked a bit --- printeremu/src/emulator.go | 22 ++- printeremu/src/extruder.go | 69 ++++--- printeremu/src/gcode_commands.go | 300 +++++++++++++++++++++++++------ printeremu/src/printer.go | 43 ++--- 4 files changed, 327 insertions(+), 107 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 08ef085a..5e9bc0a9 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -9,24 +9,36 @@ import ( "time" ) +// Main function to initialize and run the printer emulator func main() { - + extruder, printer, err := Init(1, "Device1", "Test Printer", "HWID1234", "Printer1", "Active") + if err != nil { + log.Fatalf("Failed to initialize printer: %v", err) + } + Run(extruder, printer) } +// Init function initializes the Extruder and Printer func Init(id int, device string, description string, hwid string, name string, status string) (*Extruder, *Printer, error) { - // TODO: add! - fmt.Println("Reading up...") + fmt.Println("Initializing printer...") position := Vector3{X: 100, Y: 27, Z: 76} extruder := NewExtruder(position, 250, 100, 125) - printer := NewPrinter(id, device, description, hwid, name, status, time.Now().String(), extruder) + // Create a new heatbed instance + heatbed := &Heatbed{ + Temp: 60.0, // Default initial temperature + TargetTemp: 100.0, // Target temperature for the heatbed + } + + printer := NewPrinter(id, device, description, hwid, name, status, time.Now().String(), extruder, heatbed) fmt.Println(printer.String()) return extruder, printer, nil } +// Run function for G-code command input and processing func Run(extruder *Extruder, printer *Printer) { scanner := bufio.NewScanner(os.Stdin) @@ -49,4 +61,4 @@ func Run(extruder *Extruder, printer *Printer) { if err := scanner.Err(); err != nil { log.Println("Error reading input:", err) } -} \ No newline at end of file +} diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go index 3315696a..196e4c1a 100644 --- a/printeremu/src/extruder.go +++ b/printeremu/src/extruder.go @@ -1,43 +1,70 @@ package src import ( - "fmt" + "fmt" ) type Vector3 struct { - X float64 - Y float64 - Z float64 + X float64 + Y float64 + Z float64 } +// Extruder struct to handle extruder settings, including temperature and fan speed. type Extruder struct { - Position Vector3 - ExtruderTemp float64 - BedTemp float64 - FanSpeed float64 + Position Vector3 + FanSpeed float64 + ExtruderTemp float64 + TargetTemp float64 // Desired temperature for the extruder. + AbsolutePositioning bool } -func NewExtruder(position Vector3, extruderTemp, bedTemp, fanSpeed float64) *Extruder { - return &Extruder{ - Position: position, - ExtruderTemp: extruderTemp, - BedTemp: bedTemp, - FanSpeed: fanSpeed, - } +// Heatbed struct to handle heatbed temperature. +type Heatbed struct { + Temp float64 + TargetTemp float64 // Desired temperature for the heatbed. } +// NewExtruder creates a new Extruder with specified initial values. +func NewExtruder(position Vector3, extruderTemp, targetTemp, fanSpeed float64) *Extruder { + return &Extruder{ + Position: position, + ExtruderTemp: extruderTemp, + TargetTemp: targetTemp, + FanSpeed: fanSpeed, + AbsolutePositioning: true, // Default positioning mode + } +} + +// NewHeatbed creates a new Heatbed with specified initial values. +func NewHeatbed(temp, targetTemp float64) *Heatbed { + return &Heatbed{ + Temp: temp, + TargetTemp: targetTemp, + } +} + +// Extruder methods to set temperature and fan speed func (extruder *Extruder) SetExtruderTemp(temp float64) { - extruder.ExtruderTemp = temp + extruder.ExtruderTemp = temp } -func (extruder *Extruder) SetBedTemp(temp float64) { - extruder.BedTemp = temp +// Heatbed method to set bed temperature +func (heatbed *Heatbed) SetBedTemp(temp float64) { + heatbed.Temp = temp } +// Set fan speed for Extruder func (extruder *Extruder) SetFanSpeed(speed float64) { - extruder.FanSpeed = speed + extruder.FanSpeed = speed } +// String method to display Extruder details in a formatted way func (extruder *Extruder) String() string { - return fmt.Sprintf("{Position: %v, ExtruderTemp: %vc, BedTemp: %vc, FanSpeed: %v}", extruder.Position, extruder.ExtruderTemp, extruder.BedTemp, extruder.FanSpeed) -} \ No newline at end of file + return fmt.Sprintf("{Position: %v, ExtruderTemp: %.2f°C, TargetTemp: %.2f°C, FanSpeed: %.2f}", extruder.Position, extruder.ExtruderTemp, extruder.TargetTemp, extruder.FanSpeed) +} + +// String method to display Heatbed details in a formatted way +func (heatbed *Heatbed) String() string { + return fmt.Sprintf("{Temp: %.2f°C, TargetTemp: %.2f°C}", heatbed.Temp, heatbed.TargetTemp) +} diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index d4c3e803..62ce6ebe 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -2,6 +2,7 @@ package src import ( "fmt" + "math" "regexp" "strconv" "strings" @@ -11,11 +12,34 @@ type Command interface { Execute(printer *Printer) string } +// CommandHandler parses and executes commands +func CommandHandler(command string, printer *Printer) string { + command = strings.TrimSpace(command) + cmd := NewCommand(command, printer) + + if cmd == nil { + return "Unknown command\n" + } + return cmd.Execute(printer) +} + +// NewCommand parses a command string and returns the appropriate Command +func NewCommand(command string, printer *Printer) Command { + for key, factory := range commandRegistry { + if strings.HasPrefix(command, key) { + return factory(command, printer) // Pass command and printer + } + } + return nil +} + +// ==================== Movement and Positioning Commands ==================== + type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { printer.extruder.Position = Vector3{X: 0, Y: 0, Z: 0} - return "ok\n" + return "Homing completed\n" } type G0G1Command struct { @@ -23,33 +47,167 @@ type G0G1Command struct { feedRate float64 } +func NewG0G1Command(command string, printer *Printer) *G0G1Command { + target, feedRate := parseMoveCommand(command, printer.extruder.Position) + return &G0G1Command{target: target, feedRate: feedRate} +} + func (cmd *G0G1Command) Execute(printer *Printer) string { - if (printer.paused) { + if printer.paused { return "Printer is paused\n" } - - printer.extruder.moveExtruder(cmd.target, cmd.feedRate) + printer.extruder.Position = cmd.target return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } -type CancelCommand struct{} - -func (cmd *CancelCommand) Execute(printer *Printer) string { - // todo: logic to cancel the operation - return "Canceled\n" +type G2G3Command struct { + target Vector3 + radius float64 + clockwise bool } -type KeepAliveCommand struct { - state bool +func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Command { + target, radius := parseArcCommand(command, printer.extruder.Position) + return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } -func (cmd *KeepAliveCommand) Execute(printer *Printer) string { - if cmd.state { - // todo: Logic to enable keep-alive - return "Keep-Alive Enabled\n" +func (cmd *G2G3Command) Execute(printer *Printer) string { + if printer.paused { + return "Printer is paused\n" + } + currentPos := printer.extruder.Position + arcAngle := 180.0 + angleRad := arcAngle * math.Pi / 180.0 + + if cmd.clockwise { + cmd.target.X = currentPos.X + cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y - cmd.radius*math.Sin(angleRad) + } else { + cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) } - // todo: Logic to disable keep-alive - return "Keep-Alive Disabled\n" + printer.extruder.Position = cmd.target + + return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) +} + +type G4Command struct { + duration int +} + +func NewG4Command(command string) *G4Command { + duration := parseDuration(command) + return &G4Command{duration: duration} +} + +func (cmd *G4Command) Execute(printer *Printer) string { + return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) +} + +// ==================== Positioning Commands ==================== + +type G90Command struct{} + +func (cmd *G90Command) Execute(printer *Printer) string { + printer.extruder.AbsolutePositioning = true + return "Set to Absolute Positioning\n" +} + +type G91Command struct{} + +func (cmd *G91Command) Execute(printer *Printer) string { + printer.extruder.AbsolutePositioning = false + return "Set to Relative Positioning\n" +} + +type G92Command struct { + position Vector3 +} + +func NewG92Command(command string) *G92Command { + position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) + return &G92Command{position: position} +} + +func (cmd *G92Command) Execute(printer *Printer) string { + printer.extruder.Position = cmd.position + return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) +} + +// ==================== Unit Conversion Commands ==================== + +type G20Command struct{} + +func (cmd *G20Command) Execute(printer *Printer) string { + printer.units = "inches" + return "Units set to inches\n" +} + +type G21Command struct{} + +func (cmd *G21Command) Execute(printer *Printer) string { + printer.units = "mm" + return "Units set to millimeters\n" +} + +// ==================== Temperature and Fan Control Commands ==================== + +type M104Command struct { + temperature float64 +} + +func NewM104Command(command string) *M104Command { + temperature := parseTemperature(command) + return &M104Command{temperature: temperature} +} + +func (cmd *M104Command) Execute(printer *Printer) string { + printer.extruder.TargetTemp = cmd.temperature + return fmt.Sprintf("Extruder temperature set to %.2f\n", cmd.temperature) +} + +type M106Command struct { + fanSpeed float64 +} + +func NewM106Command(command string) *M106Command { + fanSpeed := parseFanSpeed(command) + return &M106Command{fanSpeed: fanSpeed} +} + +func (cmd *M106Command) Execute(printer *Printer) string { + printer.extruder.FanSpeed = cmd.fanSpeed + return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) +} + +type M107Command struct{} + +func (cmd *M107Command) Execute(printer *Printer) string { + printer.extruder.FanSpeed = 0.0 + return "Fan turned off\n" +} + +type M140Command struct { + temperature float64 +} + +func NewM140Command(command string) *M140Command { + temperature := parseTemperature(command) + return &M140Command{temperature: temperature} +} + +func (cmd *M140Command) Execute(printer *Printer) string { + printer.heatbed.TargetTemp = cmd.temperature + return fmt.Sprintf("Bed temperature set to %.2f\n", cmd.temperature) +} + +// ==================== Control and Status Commands ==================== + +type CancelCommand struct{} + +func (cmd *CancelCommand) Execute(printer *Printer) string { + printer.paused = true + return "Emergency stop activated, printer paused\n" } type M114Command struct{} @@ -59,69 +217,42 @@ func (cmd *M114Command) Execute(printer *Printer) string { return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) } -type M997Command struct {} +type M997Command struct{} func (cmd *M997Command) Execute(printer *Printer) string { return fmt.Sprintf("Machine name: %s", printer.device) } -type M601Command struct {} +type M601Command struct{} func (cmd *M601Command) Execute(printer *Printer) string { - if (printer.paused) { + if printer.paused { return "Printer is already paused\n" } - printer.paused = true return "Machine is paused\n" } -type M602Command struct {} +type M602Command struct{} func (cmd *M602Command) Execute(printer *Printer) string { - if (!printer.paused) { + if !printer.paused { return "Printer is not paused\n" } - printer.paused = false return "Machine is no longer paused\n" } -var commandRegistry = map[string]func() Command{ - "G28": func() Command { return &G28Command{} }, - "G0": func() Command { return &G0G1Command{} }, - "G1": func() Command { return &G0G1Command{} }, - "M112": func() Command { return &CancelCommand{} }, - "M113": func() Command { return &KeepAliveCommand{state: true} }, - "M114": func() Command { return &M114Command{} }, - "M997": func() Command { return &M997Command{} }, - "M601": func() Command { return &M601Command{} }, - "M602": func() Command { return &M602Command{} }, -} - -func NewCommand(command string, printer *Printer) Command { - for key, factory := range commandRegistry { - if strings.HasPrefix(command, key) { - if key == "G0" || key == "G1" { - target, feedRate := parseMoveCommand(command, printer.extruder.Position) - return &G0G1Command{target: target, feedRate: feedRate} - } - return factory() - } - } - return nil -} +// ==================== Parsing Helpers ==================== func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { - // Regular expression patterns for parsing reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) - // Default target and feed rate target := currentPos - feedRate := 3600.0 // Default feed rate + feedRate := 3600.0 if xMatch := reX.FindStringSubmatch(command); xMatch != nil { target.X, _ = strconv.ParseFloat(xMatch[1], 64) @@ -138,3 +269,70 @@ func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { return target, feedRate } + +func parseArcCommand(command string, currentPos Vector3) (Vector3, float64) { + reR := regexp.MustCompile(`R([-+]?[0-9]*\.?[0-9]+)`) + radius := 0.0 + target := currentPos + + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + radius, _ = strconv.ParseFloat(rMatch[1], 64) + } + return target, radius +} + +func parseDuration(command string) int { + reP := regexp.MustCompile(`P([0-9]+)`) + duration := 0 + + if pMatch := reP.FindStringSubmatch(command); pMatch != nil { + duration, _ = strconv.Atoi(pMatch[1]) + } + return duration +} + +func parseTemperature(command string) float64 { + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + temperature := 0.0 + + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + temperature, _ = strconv.ParseFloat(sMatch[1], 64) + } + return temperature +} + +func parseFanSpeed(command string) float64 { + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + fanSpeed := 0.0 + + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) + } + return fanSpeed +} + +// ==================== Command Registry ==================== + +var commandRegistry = map[string]func(string, *Printer) Command{ + "G28": func(cmd string, p *Printer) Command { return &G28Command{} }, + "G0": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G1": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G2": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, true, p) }, + "G3": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, false, p) }, + "G4": func(cmd string, p *Printer) Command { return NewG4Command(cmd) }, + "G20": func(cmd string, p *Printer) Command { return &G20Command{} }, + "G21": func(cmd string, p *Printer) Command { return &G21Command{} }, + "G90": func(cmd string, p *Printer) Command { return &G90Command{} }, + "G91": func(cmd string, p *Printer) Command { return &G91Command{} }, + "G92": func(cmd string, p *Printer) Command { return NewG92Command(cmd) }, + "M104": func(cmd string, p *Printer) Command { return NewM104Command(cmd) }, + "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, + "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, + "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, + "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, + "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, + "M190": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, + "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, + "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, + "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, +} diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 13fdfc42..118e00eb 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -2,11 +2,9 @@ package src import ( "fmt" - "math" - "strings" - "time" ) +// Printer struct with associated components as pointers for easy modification type Printer struct { id int device string @@ -15,11 +13,14 @@ type Printer struct { name string status string date string - extruder *Extruder // Keep this as a pointer + extruder *Extruder // Pointer to Extruder + heatbed *Heatbed // Pointer to Heatbed paused bool + units string // Track units (e.g., "mm" or "inches"). } -func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder) *Printer { +// NewPrinter initializes a Printer with given specifications +func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) *Printer { return &Printer{ id: id, device: device, @@ -29,10 +30,13 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex status: status, date: date, extruder: extruder, + heatbed: heatbed, paused: false, + units: "mm", // Default units } } +// Printer methods for managing extruder and heatbed states func (printer *Printer) SetExtruderTemp(temp float64) { printer.extruder.SetExtruderTemp(temp) } @@ -46,11 +50,11 @@ func (printer *Printer) GetExtruder() *Extruder { } func (printer *Printer) SetBedTemp(temp float64) { - printer.extruder.SetBedTemp(temp) + printer.heatbed.SetBedTemp(temp) } func (printer *Printer) GetBedTemp() float64 { - return printer.extruder.BedTemp + return printer.heatbed.Temp } func (printer *Printer) SetFanSpeed(speed float64) { @@ -70,27 +74,6 @@ func (printer *Printer) Resume() { } func (printer *Printer) String() string { - return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v}", - printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String()) -} - -func (extruder *Extruder) moveExtruder(target Vector3, feedRate float64) { - // distance to move - distance := math.Sqrt(math.Pow(target.X-extruder.Position.X, 2) + math.Pow(target.Y-extruder.Position.Y, 2) + math.Pow(target.Z-extruder.Position.Z, 2)) - - // time taken for movement based on feed rate - moveTime := distance / feedRate - time.Sleep(time.Duration(moveTime*1000) * time.Millisecond) // movement delay - - extruder.Position = target -} - -func CommandHandler(command string, printer *Printer) string { - command = strings.TrimSpace(command) - cmd := NewCommand(command, printer) - - if cmd == nil { - return "Unknown command\n" - } - return cmd.Execute(printer) + return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v}", + printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String(), printer.heatbed) } From d75ffac191258abad37f9ab38b237dae3c861d50 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:22:52 -0400 Subject: [PATCH 033/194] more codes + added back what i got rid of --- printeremu/src/extruder.go | 66 ++++++++++---------- printeremu/src/gcode_commands.go | 100 +++++++++++++++++++++++++++++-- printeremu/src/printer.go | 77 +++++++++++++++--------- 3 files changed, 178 insertions(+), 65 deletions(-) diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go index 196e4c1a..9796758b 100644 --- a/printeremu/src/extruder.go +++ b/printeremu/src/extruder.go @@ -1,70 +1,74 @@ package src import ( - "fmt" + "fmt" ) +// Vector3 struct to represent a 3D position type Vector3 struct { - X float64 - Y float64 - Z float64 + X float64 + Y float64 + Z float64 } // Extruder struct to handle extruder settings, including temperature and fan speed. type Extruder struct { - Position Vector3 - FanSpeed float64 - ExtruderTemp float64 - TargetTemp float64 // Desired temperature for the extruder. - AbsolutePositioning bool + Position Vector3 + FanSpeed float64 + ExtruderTemp float64 + TargetTemp float64 // Desired temperature for the extruder. + AbsolutePositioning bool } // Heatbed struct to handle heatbed temperature. type Heatbed struct { - Temp float64 - TargetTemp float64 // Desired temperature for the heatbed. + Temp float64 + TargetTemp float64 // Desired temperature for the heatbed. + Heating bool // Indicates if the bed is currently heating. } // NewExtruder creates a new Extruder with specified initial values. func NewExtruder(position Vector3, extruderTemp, targetTemp, fanSpeed float64) *Extruder { - return &Extruder{ - Position: position, - ExtruderTemp: extruderTemp, - TargetTemp: targetTemp, - FanSpeed: fanSpeed, - AbsolutePositioning: true, // Default positioning mode - } + return &Extruder{ + Position: position, + ExtruderTemp: extruderTemp, + TargetTemp: targetTemp, + FanSpeed: fanSpeed, + AbsolutePositioning: true, // Default positioning mode + } } // NewHeatbed creates a new Heatbed with specified initial values. func NewHeatbed(temp, targetTemp float64) *Heatbed { - return &Heatbed{ - Temp: temp, - TargetTemp: targetTemp, - } + return &Heatbed{ + Temp: temp, + TargetTemp: targetTemp, + Heating: false, // Default heating state + } } // Extruder methods to set temperature and fan speed func (extruder *Extruder) SetExtruderTemp(temp float64) { - extruder.ExtruderTemp = temp + extruder.ExtruderTemp = temp } -// Heatbed method to set bed temperature -func (heatbed *Heatbed) SetBedTemp(temp float64) { - heatbed.Temp = temp +// SetFanSpeed sets the fan speed for Extruder +func (extruder *Extruder) SetFanSpeed(speed float64) { + extruder.FanSpeed = speed } -// Set fan speed for Extruder -func (extruder *Extruder) SetFanSpeed(speed float64) { - extruder.FanSpeed = speed +// SetBedTemp sets the target temperature for the heatbed and starts heating +func (heatbed *Heatbed) SetBedTemp(temp float64) { + heatbed.TargetTemp = temp + heatbed.Heating = true // Start heating when a target temp is set } // String method to display Extruder details in a formatted way func (extruder *Extruder) String() string { - return fmt.Sprintf("{Position: %v, ExtruderTemp: %.2f°C, TargetTemp: %.2f°C, FanSpeed: %.2f}", extruder.Position, extruder.ExtruderTemp, extruder.TargetTemp, extruder.FanSpeed) + return fmt.Sprintf("{Position: %v, ExtruderTemp: %.2f°C, TargetTemp: %.2f°C, FanSpeed: %.2f}", extruder.Position, extruder.ExtruderTemp, extruder.TargetTemp, extruder.FanSpeed) } // String method to display Heatbed details in a formatted way func (heatbed *Heatbed) String() string { - return fmt.Sprintf("{Temp: %.2f°C, TargetTemp: %.2f°C}", heatbed.Temp, heatbed.TargetTemp) + return fmt.Sprintf("{Temp: %.2f°C, TargetTemp: %.2f°C, Heating: %t}", heatbed.Temp, heatbed.TargetTemp, heatbed.Heating) } diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 62ce6ebe..8d4918c9 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -6,6 +6,7 @@ import ( "regexp" "strconv" "strings" + "time" ) type Command interface { @@ -27,7 +28,7 @@ func CommandHandler(command string, printer *Printer) string { func NewCommand(command string, printer *Printer) Command { for key, factory := range commandRegistry { if strings.HasPrefix(command, key) { - return factory(command, printer) // Pass command and printer + return factory(command, printer) } } return nil @@ -104,8 +105,6 @@ func (cmd *G4Command) Execute(printer *Printer) string { return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) } -// ==================== Positioning Commands ==================== - type G90Command struct{} func (cmd *G90Command) Execute(printer *Printer) string { @@ -197,8 +196,74 @@ func NewM140Command(command string) *M140Command { } func (cmd *M140Command) Execute(printer *Printer) string { + // Set the target temperature of the bed, but do not block printer.heatbed.TargetTemp = cmd.temperature - return fmt.Sprintf("Bed temperature set to %.2f\n", cmd.temperature) + printer.heatbed.Heating = true // Start heating + return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) +} + +// M190Command waits until the bed reaches the target temperature before allowing further commands +type M190Command struct { + temperature float64 +} + +func NewM190Command(command string) *M190Command { + temperature := parseTemperature(command) + return &M190Command{temperature: temperature} +} + +func (cmd *M190Command) Execute(printer *Printer) string { + // Set the target temperature and check if the bed is heated + printer.heatbed.TargetTemp = cmd.temperature + + // Check if the current temperature is below the target + if printer.heatbed.Temp < printer.heatbed.TargetTemp { + return "Waiting for bed to reach target temperature...\n" + } + + // If target temperature reached, continue + printer.heatbed.Heating = false + return fmt.Sprintf("Bed temperature reached %.2f\n", cmd.temperature) +} + + +type M113Command struct{} + +func NewM113Command(command string) *M113Command { + return &M113Command{} +} + +func (cmd *M113Command) Execute(printer *Printer) string { + printer.keepAliveTime = time.Now() + return "Keepalive signal sent\n" +} + +type M204Command struct { + acceleration float64 +} + +func NewM204Command(command string) *M204Command { + acceleration := parseAcceleration(command) + return &M204Command{acceleration: acceleration} +} + +func (cmd *M204Command) Execute(printer *Printer) string { + printer.acceleration = cmd.acceleration + return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) +} + +type M73Command struct { + progress int +} + +func NewM73Command(command string) *M73Command { + progress := parseProgress(command) + return &M73Command{progress: progress} +} + +func (cmd *M73Command) Execute(printer *Printer) string { + printer.progress = cmd.progress + return fmt.Sprintf("Progress set to %d%%\n", cmd.progress) } // ==================== Control and Status Commands ==================== @@ -311,6 +376,26 @@ func parseFanSpeed(command string) float64 { return fanSpeed } +func parseAcceleration(command string) float64 { + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + acceleration := 0.0 + + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + acceleration, _ = strconv.ParseFloat(sMatch[1], 64) + } + return acceleration +} + +func parseProgress(command string) int { + reS := regexp.MustCompile(`S([0-9]+)`) + progress := 0 + + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + progress, _ = strconv.Atoi(sMatch[1]) + } + return progress +} + // ==================== Command Registry ==================== var commandRegistry = map[string]func(string, *Printer) Command{ @@ -329,10 +414,15 @@ var commandRegistry = map[string]func(string, *Printer) Command{ "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, + "M113": func(cmd string, p *Printer) Command { return &M113Command{} }, "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, - "M190": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, + "M190": func(cmd string, p *Printer) Command { return NewM190Command(cmd) }, + "M204": func(cmd string, p *Printer) Command { return NewM204Command(cmd) }, + "M73": func(cmd string, p *Printer) Command { return NewM73Command(cmd) }, "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, } + + diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 118e00eb..43f31153 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -2,78 +2,97 @@ package src import ( "fmt" + "time" ) // Printer struct with associated components as pointers for easy modification type Printer struct { - id int - device string - description string - hwid string - name string - status string - date string - extruder *Extruder // Pointer to Extruder - heatbed *Heatbed // Pointer to Heatbed - paused bool - units string // Track units (e.g., "mm" or "inches"). + id int + device string + description string + hwid string + name string + status string + date string + extruder *Extruder // Pointer to Extruder + heatbed *Heatbed // Pointer to Heatbed + paused bool + units string // Track units (e.g., "mm" or "inches") + keepAliveTime time.Time // Timestamp to track keepalive signals + acceleration float64 // Acceleration setting for movement + progress int // Progress indicator (percentage) for print jobs } // NewPrinter initializes a Printer with given specifications func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) *Printer { return &Printer{ - id: id, - device: device, - description: description, - hwid: hwid, - name: name, - status: status, - date: date, - extruder: extruder, - heatbed: heatbed, - paused: false, - units: "mm", // Default units + id: id, + device: device, + description: description, + hwid: hwid, + name: name, + status: status, + date: date, + extruder: extruder, + heatbed: heatbed, + paused: false, + units: "mm", // Default units + keepAliveTime: time.Now(), // Initialize with current time + acceleration: 0.0, // Default acceleration + progress: 0, // Default progress percentage } } -// Printer methods for managing extruder and heatbed states +// SetExtruderTemp sets the extruder temperature func (printer *Printer) SetExtruderTemp(temp float64) { printer.extruder.SetExtruderTemp(temp) } +// GetExtruderTemp gets the extruder temperature func (printer *Printer) GetExtruderTemp() float64 { return printer.extruder.ExtruderTemp } -func (printer *Printer) GetExtruder() *Extruder { - return printer.extruder -} - +// SetBedTemp sets the target bed temperature and begins heating func (printer *Printer) SetBedTemp(temp float64) { printer.heatbed.SetBedTemp(temp) } +// GetBedTemp gets the current bed temperature func (printer *Printer) GetBedTemp() float64 { return printer.heatbed.Temp } +// UpdateBedTemperature updates the current temperature of the bed +func (printer *Printer) UpdateBedTemperature(currentTemp float64) { + printer.heatbed.Temp = currentTemp + if printer.heatbed.Temp >= printer.heatbed.TargetTemp { + printer.heatbed.Heating = false + } +} + +// SetFanSpeed sets the fan speed on the extruder func (printer *Printer) SetFanSpeed(speed float64) { printer.extruder.SetFanSpeed(speed) } +// GetFanSpeed gets the current fan speed on the extruder func (printer *Printer) GetFanSpeed() float64 { return printer.extruder.FanSpeed } +// Pause pauses the printer func (printer *Printer) Pause() { printer.paused = true } +// Resume resumes the printer operation func (printer *Printer) Resume() { printer.paused = false } +// String provides a formatted string representation of the Printer state func (printer *Printer) String() string { - return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v}", - printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String(), printer.heatbed) + return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, Progress: %d%%, KeepAliveTime: %v}", + printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String(), printer.heatbed.String(), printer.acceleration, printer.progress, printer.keepAliveTime) } From 547e2233d187f475132b80b4d4465d0103c8359c Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:39:22 -0400 Subject: [PATCH 034/194] more codes added --- printeremu/src/gcode_commands.go | 61 ++++++++++++++++++++++++++++++++ printeremu/src/printer.go | 39 ++++++++++++-------- 2 files changed, 86 insertions(+), 14 deletions(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 8d4918c9..5739430d 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -308,6 +308,50 @@ func (cmd *M602Command) Execute(printer *Printer) string { return "Machine is no longer paused\n" } +type M900Command struct { + kFactor float64 +} + +func NewM900Command(command string) *M900Command { + kFactor := parseKFactor(command) // Extracts the K value from the command + return &M900Command{kFactor: kFactor} +} + +func (cmd *M900Command) Execute(printer *Printer) string { + printer.linearAdvanceFactor = cmd.kFactor + return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) +} + +type M142Command struct { + temperature float64 +} + +func NewM142Command(command string) *M142Command { + temperature := parseTemperature(command) // Uses parseTemperature to get S value + return &M142Command{temperature: temperature} +} + +func (cmd *M142Command) Execute(printer *Printer) string { + printer.heatbreakTemp = cmd.temperature + return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) +} + +type M84Command struct { + axes []string +} + +func NewM84Command(command string) *M84Command { + axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command + return &M84Command{axes: axes} +} + +func (cmd *M84Command) Execute(printer *Printer) string { + for _, axis := range cmd.axes { + printer.DisableMotor(axis) + } + return fmt.Sprintf("Motors %v disabled\n", cmd.axes) +} + // ==================== Parsing Helpers ==================== func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { @@ -396,6 +440,20 @@ func parseProgress(command string) int { return progress } +func parseKFactor(command string) float64 { + reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) + kFactor := 0.0 + if kMatch := reK.FindStringSubmatch(command); kMatch != nil { + kFactor, _ = strconv.ParseFloat(kMatch[1], 64) + } + return kFactor +} + +func parseAxes(command string) []string { + reAxes := regexp.MustCompile(`[XYZE]`) + return reAxes.FindAllString(command, -1) +} + // ==================== Command Registry ==================== var commandRegistry = map[string]func(string, *Printer) Command{ @@ -423,6 +481,9 @@ var commandRegistry = map[string]func(string, *Printer) Command{ "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, + "M900": func(cmd string, p *Printer) Command { return NewM900Command(cmd) }, + "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, + "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 43f31153..bd0b1e4c 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -7,22 +7,33 @@ import ( // Printer struct with associated components as pointers for easy modification type Printer struct { - id int - device string - description string - hwid string - name string - status string - date string - extruder *Extruder // Pointer to Extruder - heatbed *Heatbed // Pointer to Heatbed - paused bool - units string // Track units (e.g., "mm" or "inches") - keepAliveTime time.Time // Timestamp to track keepalive signals - acceleration float64 // Acceleration setting for movement - progress int // Progress indicator (percentage) for print jobs + id int + device string + description string + hwid string + name string + status string + date string + extruder *Extruder + heatbed *Heatbed + paused bool + units string + keepAliveTime time.Time + acceleration float64 + progress int + linearAdvanceFactor float64 + heatbreakTemp float64 + enabledMotors map[string]bool } +func (printer *Printer) DisableMotor(axis string) { + if printer.enabledMotors == nil { + printer.enabledMotors = make(map[string]bool) + } + printer.enabledMotors[axis] = false +} + + // NewPrinter initializes a Printer with given specifications func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) *Printer { return &Printer{ From 3f377223e6606c528ca7173b3b5ee07808ad3a7d Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 31 Oct 2024 13:39:03 -0400 Subject: [PATCH 035/194] fix: ignore gcode comments --- printeremu/src/gcode_commands.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 5739430d..ca6c0bfa 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -15,6 +15,14 @@ type Command interface { // CommandHandler parses and executes commands func CommandHandler(command string, printer *Printer) string { + if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { + command = command[:semicolonIndex] + } + + if command == "" { + return "" + } + command = strings.TrimSpace(command) cmd := NewCommand(command, printer) From 621b9609b31c40cd832273893e982dc9a4b66868 Mon Sep 17 00:00:00 2001 From: CJ Date: Fri, 1 Nov 2024 22:15:39 -0400 Subject: [PATCH 036/194] feat: begin basic emulator and server communication --- client/package-lock.json | 10557 ++++++++++++------------ printeremu/cmd/test_printer.go | 8 +- printeremu/go.mod | 8 +- printeremu/go.sum | 71 + printeremu/src/emulator.go | 90 +- server/app.py | 19 +- server/models/PrinterStatusService.py | 9 +- server/models/printers.py | 9 + 8 files changed, 5584 insertions(+), 5187 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 8816256c..b5aa54d3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -19,7 +19,6 @@ "bootstrap-daterangepicker": "^3.1.0", "cors": "^2.8.5", "electron-squirrel-startup": "^1.0.1", - "electron-store": "^10.0.0", "file-saver": "^2.0.5", "gcode-preview": "^2.13.0", "node": "^21.6.2", @@ -54,7 +53,6 @@ "@vue/test-utils": "^2.4.3", "@vue/tsconfig": "^0.5.0", "electron": "^32.1.1", - "electron-wix-msi": "^5.1.3", "eslint": "^8.49.0", "eslint-plugin-vue": "^9.17.0", "jsdom": "^23.0.1", @@ -69,6 +67,8 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -81,6 +81,8 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz", + "integrity": "sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -90,10 +92,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -101,7 +107,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "license": "MIT", "engines": { @@ -109,20 +117,22 @@ } }, "node_modules/@babel/core": { - "version": "7.24.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -139,6 +149,8 @@ }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -146,38 +158,45 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -185,40 +204,29 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -230,80 +238,52 @@ }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -313,18 +293,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "license": "MIT", "engines": { @@ -332,13 +316,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -347,58 +333,42 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "dev": true, + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -406,33 +376,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.7", - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -441,11 +405,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -455,11 +421,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -469,14 +437,17 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -486,7 +457,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -496,31 +469,32 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -529,27 +503,28 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "dev": true, + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@changesets/apply-release-plan": { - "version": "7.0.3", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.5.tgz", + "integrity": "sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/config": "^3.0.1", + "@changesets/config": "^3.0.3", "@changesets/get-version-range-type": "^0.4.0", - "@changesets/git": "^3.0.0", - "@changesets/should-skip-package": "^0.1.0", + "@changesets/git": "^3.0.1", + "@changesets/should-skip-package": "^0.1.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", @@ -561,8 +536,33 @@ "semver": "^7.5.3" } }, + "node_modules/@changesets/apply-release-plan/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/apply-release-plan/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/@changesets/apply-release-plan/node_modules/prettier": { "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "license": "MIT", "bin": { "prettier": "bin-prettier.js" @@ -574,14 +574,33 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@changesets/apply-release-plan/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@changesets/apply-release-plan/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/assemble-release-plan": { - "version": "6.0.2", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.4.tgz", + "integrity": "sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.0", - "@changesets/should-skip-package": "^0.1.0", + "@changesets/get-dependents-graph": "^2.1.2", + "@changesets/should-skip-package": "^0.1.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" @@ -589,108 +608,203 @@ }, "node_modules/@changesets/changelog-git": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.0.tgz", + "integrity": "sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==", "license": "MIT", "dependencies": { "@changesets/types": "^6.0.0" } }, "node_modules/@changesets/cli": { - "version": "2.27.5", + "version": "2.27.9", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.27.9.tgz", + "integrity": "sha512-q42a/ZbDnxPpCb5Wkm6tMVIxgeI9C/bexntzTeCFBrQEdpisQqk8kCHllYZMDjYtEc1ZzumbMJAG8H0Z4rdvjg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/apply-release-plan": "^7.0.3", - "@changesets/assemble-release-plan": "^6.0.2", + "@changesets/apply-release-plan": "^7.0.5", + "@changesets/assemble-release-plan": "^6.0.4", "@changesets/changelog-git": "^0.2.0", - "@changesets/config": "^3.0.1", + "@changesets/config": "^3.0.3", "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.0", - "@changesets/get-release-plan": "^4.0.2", - "@changesets/git": "^3.0.0", - "@changesets/logger": "^0.1.0", - "@changesets/pre": "^2.0.0", - "@changesets/read": "^0.6.0", - "@changesets/should-skip-package": "^0.1.0", + "@changesets/get-dependents-graph": "^2.1.2", + "@changesets/get-release-plan": "^4.0.4", + "@changesets/git": "^3.0.1", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.1", + "@changesets/read": "^0.6.1", + "@changesets/should-skip-package": "^0.1.1", "@changesets/types": "^6.0.0", - "@changesets/write": "^0.3.1", + "@changesets/write": "^0.3.2", "@manypkg/get-packages": "^1.1.3", - "@types/semver": "^7.5.0", "ansi-colors": "^4.1.3", - "chalk": "^2.1.0", "ci-info": "^3.7.0", "enquirer": "^2.3.0", "external-editor": "^3.1.0", "fs-extra": "^7.0.1", - "human-id": "^1.0.2", - "meow": "^6.0.0", - "outdent": "^0.5.0", + "mri": "^1.2.0", "p-limit": "^2.2.0", - "preferred-pm": "^3.0.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^2.0.0", - "term-size": "^2.1.0", - "tty-table": "^4.1.5" + "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, + "node_modules/@changesets/cli/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/cli/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/cli/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@changesets/cli/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@changesets/cli/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/config": { - "version": "3.0.1", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.0.3.tgz", + "integrity": "sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==", "license": "MIT", "dependencies": { "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.0", - "@changesets/logger": "^0.1.0", + "@changesets/get-dependents-graph": "^2.1.2", + "@changesets/logger": "^0.1.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.2" } }, + "node_modules/@changesets/config/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/config/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/config/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/errors": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", "license": "MIT", "dependencies": { "extendable-error": "^0.1.5" } }, "node_modules/@changesets/get-dependents-graph": { - "version": "2.1.0", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.2.tgz", + "integrity": "sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==", "license": "MIT", "dependencies": { "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", - "chalk": "^2.1.0", - "fs-extra": "^7.0.1", + "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "node_modules/@changesets/get-release-plan": { - "version": "4.0.2", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.4.tgz", + "integrity": "sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/assemble-release-plan": "^6.0.2", - "@changesets/config": "^3.0.1", - "@changesets/pre": "^2.0.0", - "@changesets/read": "^0.6.0", + "@changesets/assemble-release-plan": "^6.0.4", + "@changesets/config": "^3.0.3", + "@changesets/pre": "^2.0.1", + "@changesets/read": "^0.6.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3" } }, "node_modules/@changesets/get-version-range-type": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", "license": "MIT" }, "node_modules/@changesets/git": { - "version": "3.0.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.1.tgz", + "integrity": "sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", "@changesets/errors": "^0.2.0", - "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.2", @@ -698,82 +812,218 @@ } }, "node_modules/@changesets/logger": { - "version": "0.1.0", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", "license": "MIT", "dependencies": { - "chalk": "^2.1.0" + "picocolors": "^1.1.0" } }, "node_modules/@changesets/parse": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.0.tgz", + "integrity": "sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==", "license": "MIT", "dependencies": { "@changesets/types": "^6.0.0", "js-yaml": "^3.13.1" } }, + "node_modules/@changesets/parse/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@changesets/parse/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@changesets/parse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/@changesets/pre": { - "version": "2.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.1.tgz", + "integrity": "sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", "@changesets/errors": "^0.2.0", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, + "node_modules/@changesets/pre/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/pre/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/pre/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/read": { - "version": "0.6.0", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.1.tgz", + "integrity": "sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", - "@changesets/git": "^3.0.0", - "@changesets/logger": "^0.1.0", + "@changesets/git": "^3.0.1", + "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.0", "@changesets/types": "^6.0.0", - "chalk": "^2.1.0", "fs-extra": "^7.0.1", - "p-filter": "^2.1.0" + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/read/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/read/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/read/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" } }, "node_modules/@changesets/should-skip-package": { - "version": "0.1.0", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.1.tgz", + "integrity": "sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3" } }, "node_modules/@changesets/types": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.0.0.tgz", + "integrity": "sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==", "license": "MIT" }, "node_modules/@changesets/write": { - "version": "0.3.1", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.3.2.tgz", + "integrity": "sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.1", "@changesets/types": "^6.0.0", "fs-extra": "^7.0.1", "human-id": "^1.0.2", "prettier": "^2.7.1" } }, - "node_modules/@changesets/write/node_modules/prettier": { - "version": "2.8.8", + "node_modules/@changesets/write/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/write/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/write/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { "node": ">=10.13.0" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@changesets/write/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@cyhnkckali/vue3-color-picker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@cyhnkckali/vue3-color-picker/-/vue3-color-picker-2.0.2.tgz", @@ -784,6 +1034,8 @@ }, "node_modules/@electron-forge/cli": { "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.5.0.tgz", + "integrity": "sha512-dlxr4ac5ONWs1Wmbgh18rclxcp9Fe5SzCF9ookp08Z1w4YP9FbQd1SHs0oLKWLF6qH9qdX8H2EWB9Nt6tOzC5g==", "dev": true, "funding": [ { @@ -816,112 +1068,10 @@ "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@electron-forge/cli/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@electron-forge/cli/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@electron-forge/cli/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron-forge/cli/node_modules/commander": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@electron-forge/cli/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/cli/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron-forge/cli/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/cli/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron-forge/cli/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron-forge/core": { "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.5.0.tgz", + "integrity": "sha512-Hg/fXabRZtMbyrtnpzpb3i49qNai+juCg+6bgyjYfWgJGr5VGH947lWd7skujH5qJ+Y7FgvANDGnenZuQwxZNw==", "dev": true, "funding": [ { @@ -977,6 +1127,8 @@ }, "node_modules/@electron-forge/core-utils": { "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.5.0.tgz", + "integrity": "sha512-PtyQT6qeOEJsi4ltoB7Jb6YUUCwK3gDt5gVyAF2aJ8eZi9rJ0hasHc5vjcmBaz9FwDMjYZrBD8oRBhNgbhEakQ==", "dev": true, "license": "MIT", "dependencies": { @@ -995,1317 +1147,492 @@ "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/@electron-forge/maker-base": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.5.0.tgz", + "integrity": "sha512-+jluKW2UPxaI1+qQQ8fqaUVVbZohRjOSF0Iti7STRFbgJKJitzPB24Cjji9qJCKIx5klMeEiwp0YPAE/d9Xt8g==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@electron-forge/shared-types": "7.5.0", + "fs-extra": "^10.0.0", + "which": "^2.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/chalk": { - "version": "4.1.2", + "node_modules/@electron-forge/maker-deb": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.5.0.tgz", + "integrity": "sha512-rMXYJzu2LuyDQ5TX4VzQ2OSWDFC7Y1nJpBoyJvXfkA5fF5NNScOFYPv1YzEYAXMcG5hCOVLorsG9HDTEC8uh9g==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@electron-forge/maker-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0" }, "engines": { - "node": ">=10" + "node": ">= 16.4.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "optionalDependencies": { + "electron-installer-debian": "^3.2.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/@electron-forge/maker-rpm": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.5.0.tgz", + "integrity": "sha512-NzrtEqa61D++GoaURpicj9oxwOA/z0wcZaeipsrzUEBXE53UAo47tdIZjh396MhC0z9Mr6bxgDwpPUZF9QgfDA==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@electron-forge/maker-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-installer-redhat": "^3.2.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron-forge/core-utils/node_modules/find-up": { - "version": "5.0.0", + "node_modules/@electron-forge/maker-squirrel": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.5.0.tgz", + "integrity": "sha512-fz3vbp1BnbQWeZVVM3lKOGhrCVKLjAXKDTntBL2+8Rz02a63eozGjOtC5KZYXax6nM4TF6LvwjagY/qTs5jFag==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@electron-forge/maker-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0", + "fs-extra": "^10.0.0" }, "engines": { - "node": ">=10" + "node": ">= 16.4.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "electron-winstaller": "^5.3.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/fs-extra": { - "version": "10.1.0", + "node_modules/@electron-forge/maker-zip": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.5.0.tgz", + "integrity": "sha512-gIO3bEbubOJqWV6kd0b9nBwTrFfFQv/K8PAqg6e0uSZiy7QuSCFZVAZse02gO3AzxVDSVjjTQ4nmXBXC4Glh1A==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@electron-forge/maker-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0", + "cross-zip": "^4.0.0", + "fs-extra": "^10.0.0", + "got": "^11.8.5" }, "engines": { - "node": ">=12" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/@electron-forge/plugin-auto-unpack-natives": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.5.0.tgz", + "integrity": "sha512-cT/yTAr5Zd81HANi5bdyCoSG9nzIrB1WIEkFA7sWuyATiOjk9SGKn1aM543XWJ/TFkTg0800JpyocsSC9wOzEg==", "dev": true, "license": "MIT", + "dependencies": { + "@electron-forge/plugin-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0" + }, "engines": { - "node": ">=8" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@electron-forge/plugin-base": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.5.0.tgz", + "integrity": "sha512-44AbXSb5lDY8uHIo0mJ91atOSWgxv3iuECk07/gDBiuMPX62dwHnLteEjQF4GBXJZTpnV7SxhD+d2AUBQmoojw==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@electron-forge/shared-types": "7.5.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/locate-path": { - "version": "6.0.0", + "node_modules/@electron-forge/plugin-fuses": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-fuses/-/plugin-fuses-7.5.0.tgz", + "integrity": "sha512-nF5C4TN/rzN95F5HwAkyYTgy44Mu3EAys8O5zShzg7rye8d5BTnx2peAGtROZcChSaHRql09EeoOswNtuJNMcw==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "@electron-forge/plugin-base": "7.5.0", + "@electron-forge/shared-types": "7.5.0" }, "engines": { - "node": ">=10" + "node": ">= 16.4.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@electron/fuses": ">=1.0.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/p-limit": { - "version": "3.1.0", + "node_modules/@electron-forge/publisher-base": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.5.0.tgz", + "integrity": "sha512-PcF3jWA+oXRwNVWjKW6GxHJywJ62QXpYF/8SMs7kgKzBDzLqrbUnWuaXoCP5kCP+AxM495ZU5L2dyJek1eM+VA==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "@electron-forge/shared-types": "7.5.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/p-locate": { - "version": "5.0.0", + "node_modules/@electron-forge/shared-types": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.5.0.tgz", + "integrity": "sha512-VXuLVGYa3ZulBlmjA40ZEpk+iPH5ebN0v7t27wDt3rm23bph2aQrL7uSTLXhobenXYBVKggXnQt6rJ9A7FCDNQ==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "@electron-forge/tracer": "7.5.0", + "@electron/packager": "^18.3.5", + "@electron/rebuild": "^3.2.10", + "listr2": "^7.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/@electron-forge/template-base": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.5.0.tgz", + "integrity": "sha512-wEz4FI90jje4FdwJ4FzqUejodfioNcJjlgG2Ci1FiRn4Qv0jX4MP8SEgKmnD44181/44HgMa429zxRv/fDYzOw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@electron-forge/shared-types": "7.5.0", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "fs-extra": "^10.0.0", + "username": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core-utils/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@electron-forge/template-vite": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.5.0.tgz", + "integrity": "sha512-AkMO5nW5jC8ijCYeoAK7hu+K5o7NMoHNsn71eepJ/kjOnSxXjJeBVGmP4DgzF2zc6AgeRz2TCKx6P8GUtFG5cw==", "dev": true, "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.5.0", + "@electron-forge/template-base": "7.5.0", + "fs-extra": "^10.0.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/@electron-forge/template-vite-typescript": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.5.0.tgz", + "integrity": "sha512-bD9QQ6uEsDHp6/V7odCkoK53egy0A4LEh++F1VYFt7SWJ5+InkcSLww7ELz2hrNmpmXb+euRrNagL1gorPToSA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@electron-forge/shared-types": "7.5.0", + "@electron-forge/template-base": "7.5.0", + "fs-extra": "^10.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core/node_modules/chalk": { - "version": "4.1.2", + "node_modules/@electron-forge/template-webpack": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.5.0.tgz", + "integrity": "sha512-Il9dO4VMhxibsYTsKRkccWUN3WFg55PEQFL93oarFcEtAT3sjMx/1bZDj/2AIHqbwIf7IrCylPKiPP2uUNOM9Q==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@electron-forge/shared-types": "7.5.0", + "@electron-forge/template-base": "7.5.0", + "fs-extra": "^10.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/@electron-forge/template-webpack-typescript": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.5.0.tgz", + "integrity": "sha512-Q11xAzFxWtES0bwykMd8MAzrVRtmSruXQxQIvqM7Qf3VmU8joq8v5njmn13LeCDkcRGjALiJqO8EsgsW3bttNw==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@electron-forge/shared-types": "7.5.0", + "@electron-forge/template-base": "7.5.0", + "fs-extra": "^10.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 16.4.0" } }, - "node_modules/@electron-forge/core/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron-forge/core/node_modules/find-up": { - "version": "5.0.0", + "node_modules/@electron-forge/tracer": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.5.0.tgz", + "integrity": "sha512-1dE0wKCmv/K3BXCH70o2jp/y2kXgZQm73gIvzyadySXYwu2L4BWxhAO+Q+JsnbUk+nclHEup5ph4D0JoPIWLcQ==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "chrome-trace-event": "^1.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 14.17.5" } }, - "node_modules/@electron-forge/core/node_modules/fs-extra": { - "version": "10.1.0", + "node_modules/@electron/asar": { + "version": "3.2.15", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.15.tgz", + "integrity": "sha512-AerUbRZpkDVRs58WP32t4U2bx85sfwRkQI8RMIEi6s2NBE++sgjsgAAMtXvnfTISKUkXo386pxFW7sa7WtMCrw==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" }, "engines": { - "node": ">=12" + "node": ">=10.12.0" } }, - "node_modules/@electron-forge/core/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/@electron-forge/core/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "bin": { + "electron-fuses": "dist/bin.js" } }, - "node_modules/@electron-forge/core/node_modules/locate-path": { - "version": "6.0.0", + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@electron-forge/core/node_modules/p-limit": { + "node_modules/@electron/get": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" }, "engines": { - "node": ">=10" + "node": ">=14" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "global-agent": "^3.0.0" } }, - "node_modules/@electron-forge/core/node_modules/p-locate": { - "version": "5.0.0", + "node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6 <7 || >=8" } }, - "node_modules/@electron-forge/core/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/@electron/get/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/@electron-forge/core/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@electron-forge/maker-base": { - "version": "7.5.0", + "node_modules/@electron/get/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, "engines": { - "node": ">= 16.4.0" + "node": ">= 4.0.0" } }, - "node_modules/@electron-forge/maker-base/node_modules/fs-extra": { - "version": "10.1.0", + "node_modules/@electron/node-gyp": { + "version": "10.2.0-electron.1", + "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "integrity": "sha512-lBSgDMQqt7QWMuIjS8zNAq5FI5o5RVBAcJUGWGI6GgoQITJt3msAkUrHp8YHj3RTVE+h70ndqMGqURjp3IfRyQ==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^8.1.0", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.2.1", + "nopt": "^6.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">=12" + "node": ">=12.13.0" } }, - "node_modules/@electron-forge/maker-base/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@electron/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "balanced-match": "^1.0.0" } }, - "node_modules/@electron-forge/maker-base/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@electron/node-gyp/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@electron-forge/maker-base/node_modules/which": { - "version": "2.0.2", + "node_modules/@electron/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/@electron-forge/maker-deb": { - "version": "7.5.0", + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, "license": "MIT", "dependencies": { - "@electron-forge/maker-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0" + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" }, "engines": { - "node": ">= 16.4.0" - }, - "optionalDependencies": { - "electron-installer-debian": "^3.2.0" + "node": ">= 10.0.0" } }, - "node_modules/@electron-forge/maker-deb/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "optional": true, + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/@electron-forge/maker-deb/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", "dev": true, - "optional": true, + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "optional": true - }, - "node_modules/@electron-forge/maker-deb/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "optional": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/electron-installer-debian": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", - "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", - "dev": true, - "optional": true, - "os": [ - "darwin", - "linux" - ], - "dependencies": { - "@malept/cross-spawn-promise": "^1.0.0", - "debug": "^4.1.1", - "electron-installer-common": "^0.10.2", - "fs-extra": "^9.0.0", - "get-folder-size": "^2.0.1", - "lodash": "^4.17.4", - "word-wrap": "^1.2.3", - "yargs": "^16.0.2" - }, - "bin": { - "electron-installer-debian": "src/cli.js" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "optional": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "optional": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "optional": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "optional": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron-forge/maker-rpm": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/maker-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0" - }, - "engines": { - "node": ">= 16.4.0" - }, - "optionalDependencies": { - "electron-installer-redhat": "^3.2.0" - } - }, - "node_modules/@electron-forge/maker-squirrel": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/maker-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 16.4.0" - }, - "optionalDependencies": { - "electron-winstaller": "^5.3.0" - } - }, - "node_modules/@electron-forge/maker-squirrel/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/maker-squirrel/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/maker-squirrel/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/maker-zip": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/maker-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0", - "cross-zip": "^4.0.0", - "fs-extra": "^10.0.0", - "got": "^11.8.5" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/maker-zip/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/maker-zip/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/maker-zip/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/plugin-auto-unpack-natives": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/plugin-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/plugin-base": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/plugin-fuses": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/plugin-base": "7.5.0", - "@electron-forge/shared-types": "7.5.0" - }, - "engines": { - "node": ">= 16.4.0" - }, - "peerDependencies": { - "@electron/fuses": ">=1.0.0" - } - }, - "node_modules/@electron-forge/publisher-base": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/shared-types": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/tracer": "7.5.0", - "@electron/packager": "^18.3.5", - "@electron/rebuild": "^3.2.10", - "listr2": "^7.0.2" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-base": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "fs-extra": "^10.0.0", - "username": "^5.1.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-base/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/template-base/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/template-base/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/template-vite": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "@electron-forge/template-base": "7.5.0", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-vite-typescript": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "@electron-forge/template-base": "7.5.0", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-vite-typescript/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/template-vite-typescript/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/template-vite-typescript/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/template-vite/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/template-vite/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/template-vite/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/template-webpack": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "@electron-forge/template-base": "7.5.0", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-webpack-typescript": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron-forge/shared-types": "7.5.0", - "@electron-forge/template-base": "7.5.0", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 16.4.0" - } - }, - "node_modules/@electron-forge/template-webpack-typescript/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/template-webpack-typescript/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/template-webpack-typescript/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/template-webpack/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron-forge/template-webpack/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron-forge/template-webpack/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron-forge/tracer": { - "version": "7.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chrome-trace-event": "^1.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron/asar": { - "version": "3.2.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/glob": "^7.1.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@electron/asar/node_modules/commander": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@electron/asar/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@electron/asar/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@electron/fuses": { - "version": "1.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.1", - "fs-extra": "^9.0.1", - "minimist": "^1.2.5" - }, - "bin": { - "electron-fuses": "dist/bin.js" - } - }, - "node_modules/@electron/fuses/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@electron/fuses/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@electron/fuses/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@electron/fuses/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@electron/fuses/node_modules/fs-extra": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/fuses/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/fuses/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/fuses/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/fuses/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/get": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@electron/get/node_modules/fs-extra": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@electron/get/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@electron/notarize": { - "version": "2.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/osx-sign": { - "version": "1.3.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", @@ -2315,40 +1642,10 @@ "node": ">=12.0.0" } }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/packager": { "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.5.tgz", + "integrity": "sha512-ClgTxXTt3MesWAcjIxIkgxELjTcllw1FRoVsihP7uT48kpDMqI71p4XvnMWbq8PvU57TcrKICAaLkxRhbc+/wQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2384,6 +1681,8 @@ }, "node_modules/@electron/packager/node_modules/fs-extra": { "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "license": "MIT", "dependencies": { @@ -2395,38 +1694,14 @@ "node": ">=14.14" } }, - "node_modules/@electron/packager/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/packager/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/packager/node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/@electron/rebuild": { - "version": "3.6.0", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", + "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", "dev": true, "license": "MIT", "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", @@ -2435,7 +1710,6 @@ "got": "^11.7.0", "node-abi": "^3.45.0", "node-api-version": "^0.2.0", - "node-gyp": "^9.0.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", @@ -2449,53 +1723,90 @@ "node": ">=12.13.0" } }, - "node_modules/@electron/rebuild/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=16.4" } }, - "node_modules/@electron/rebuild/node_modules/chalk": { - "version": "4.1.2", + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=14.14" } }, - "node_modules/@electron/rebuild/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@electron/rebuild/node_modules/color-name": { - "version": "1.1.4", + "node_modules/@electron/windows-sign": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.1.3.tgz", + "integrity": "sha512-OqVSdAe+/88fIjvTDWiy+5Ho1nXsiBhE5RTsIQ6M/zcxcDAEP2TlQCkOyusItnmzXRN+XTFaK9gKhiZ6KGyXQw==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } }, - "node_modules/@electron/rebuild/node_modules/fs-extra": { - "version": "10.1.0", + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "license": "MIT", "dependencies": { @@ -2503,149 +1814,388 @@ "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { "node": ">=12" } }, - "node_modules/@electron/rebuild/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@electron/rebuild/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@electron/rebuild/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@electron/rebuild/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/@electron/universal": { - "version": "2.0.1", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@electron/asar": "^3.2.7", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "dir-compare": "^4.2.0", - "fs-extra": "^11.1.1", - "minimatch": "^9.0.3", - "plist": "^3.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16.4" + "node": ">=12" } }, - "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "11.2.0", + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.14" + "node": ">=12" } }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/@electron/windows-sign": { - "version": "1.1.3", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "cross-dirname": "^0.1.0", - "debug": "^4.3.4", - "fs-extra": "^11.1.1", - "minimist": "^1.2.8", - "postject": "^1.0.0-alpha.6" - }, - "bin": { - "electron-windows-sign": "bin/electron-windows-sign.js" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=14.14" + "node": ">=12" } }, - "node_modules/@electron/windows-sign/node_modules/fs-extra": { - "version": "11.2.0", + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=14.14" + "node": ">=12" } }, - "node_modules/@electron/windows-sign/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@electron/windows-sign/node_modules/universalify": { - "version": "2.0.1", + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -2660,21 +2210,28 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -2683,6 +2240,8 @@ }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2703,22 +2262,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2731,30 +2278,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -2765,7 +2292,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -2773,8 +2302,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.5.2", - "hasInstallScript": true, + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", "engines": { "node": ">=6" @@ -2782,15 +2312,20 @@ }, "node_modules/@gar/promisify": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true, "license": "MIT" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -2798,28 +2333,10 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2832,11 +2349,16 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, "license": "BSD-3-Clause" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", "dependencies": { @@ -2852,7 +2374,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -2862,70 +2386,26 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@jest/schemas": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", "dependencies": { @@ -2937,6 +2417,8 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "license": "MIT", "dependencies": { @@ -2950,6 +2432,8 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -2958,6 +2442,8 @@ }, "node_modules/@jridgewell/set-array": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", "engines": { @@ -2965,11 +2451,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2979,6 +2469,8 @@ }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, "funding": [ { @@ -2998,80 +2490,112 @@ "node": ">= 12.13.0" } }, - "node_modules/@malept/cross-spawn-promise/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" } }, - "node_modules/@malept/cross-spawn-promise/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@malept/cross-spawn-promise/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, "engines": { - "node": ">=8" + "node": ">=6 <7 || >=8" } }, - "node_modules/@malept/cross-spawn-promise/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", + "node_modules/@manypkg/find-root/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@manypkg/find-root/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@manypkg/find-root": { - "version": "1.1.0", + "node_modules/@manypkg/find-root/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "@types/node": "^12.7.1", - "find-up": "^4.1.0", - "fs-extra": "^8.1.0" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@manypkg/find-root/node_modules/@types/node": { - "version": "12.20.55", - "license": "MIT" - }, - "node_modules/@manypkg/find-root/node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/@manypkg/find-root/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=8" + } + }, + "node_modules/@manypkg/find-root/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" } }, "node_modules/@manypkg/get-packages": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.5.5", @@ -3084,10 +2608,14 @@ }, "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", "license": "MIT" }, "node_modules/@manypkg/get-packages/node_modules/fs-extra": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -3098,95 +2626,372 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", + "node_modules/@manypkg/get-packages/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@manypkg/get-packages/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz", + "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz", + "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz", + "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz", + "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz", + "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz", + "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz", + "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz", + "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz", + "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz", + "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz", + "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz", + "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz", + "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz", + "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz", + "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "engines": { - "node": ">=14" - } + "os": [ + "linux" + ] }, - "node_modules/@pkgr/core": { - "version": "0.1.1", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz", + "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@popperjs/core": { - "version": "2.11.8", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz", + "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz", + "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==", "cpu": [ "x64" ], @@ -3198,17 +3003,23 @@ ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.3", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", "dev": true, "license": "MIT" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, "license": "MIT" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", "engines": { @@ -3220,10 +3031,14 @@ }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, "license": "MIT", "dependencies": { @@ -3235,6 +3050,8 @@ }, "node_modules/@toastdotdev/lib": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@toastdotdev/lib/-/lib-0.0.1.tgz", + "integrity": "sha512-aNCJ7WzEK3im7mNdDMRo1RsnPiHkzNzFY5rBSL3nN9bTndVIoVCRp9gWM23g32h/GgKeo+jHV5TErTFiVJ2FNA==", "license": "MIT", "engines": { "node": ">= 10" @@ -3246,8 +3063,58 @@ "@toastdotdev/lib-win32-x64-msvc": "0.0.1" } }, + "node_modules/@toastdotdev/lib-darwin-arm64": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@toastdotdev/lib-darwin-arm64/-/lib-darwin-arm64-0.0.1.tgz", + "integrity": "sha512-6UbPdjDFeZsH/2gdNpc3YKVI9FF2VYGLaVUYSUfywpfw4EbAfw/xS+4VZR1ThdowEkoP1Bgy360z3/Iwmv1svg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@toastdotdev/lib-darwin-x64": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@toastdotdev/lib-darwin-x64/-/lib-darwin-x64-0.0.1.tgz", + "integrity": "sha512-nesyX960NPgXm8if2c0UUZwCVkhAi/gUGDnd8kAh5cKhb2ioxE37qgGTOHdyyM7IA73zZzDAv1tD9yZuPlHM+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@toastdotdev/lib-linux-x64-gnu": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@toastdotdev/lib-linux-x64-gnu/-/lib-linux-x64-gnu-0.0.1.tgz", + "integrity": "sha512-ThaNF545J8lFOlYmFuuXHFj+PpNZSQg58hjgRM69uf5wNZB3AuiHLGyV0yHtenCUfFKd9OeG2jTtSxaTgOLPbA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@toastdotdev/lib-win32-x64-msvc": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@toastdotdev/lib-win32-x64-msvc/-/lib-win32-x64-msvc-0.0.1.tgz", + "integrity": "sha512-xPL3bqnRwEXUzO6bxErvcNiQ+kH4K3E78cpjYu3q+94np9tb0VpCj38WrD8xyQFhvbvnvS+SP8MbZcBFTPgrRA==", "cpu": [ "x64" ], @@ -3262,6 +3129,8 @@ }, "node_modules/@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "license": "MIT", "engines": { @@ -3270,11 +3139,15 @@ }, "node_modules/@tsconfig/node18": { "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.4.tgz", + "integrity": "sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==", "dev": true, "license": "MIT" }, "node_modules/@types/cacheable-request": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, "license": "MIT", "dependencies": { @@ -3285,17 +3158,23 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, "license": "MIT" }, "node_modules/@types/file-saver": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", "dev": true, "license": "MIT" }, "node_modules/@types/fs-extra": { "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, "license": "MIT", "optional": true, @@ -3305,8 +3184,11 @@ }, "node_modules/@types/glob": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "@types/minimatch": "*", "@types/node": "*" @@ -3314,11 +3196,15 @@ }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true, "license": "MIT" }, "node_modules/@types/jsdom": { "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", "dev": true, "license": "MIT", "dependencies": { @@ -3329,11 +3215,15 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "license": "MIT", "dependencies": { @@ -3342,27 +3232,26 @@ }, "node_modules/@types/minimatch": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/@types/node": { - "version": "18.19.34", + "version": "18.19.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.63.tgz", + "integrity": "sha512-hcUB7THvrGmaEcPcvUZCZtQ2Z3C+UR/aOcraBLCvTsFMh916Gc1kCCYcfcMuB76HM2pSerxl1PoP3KnmHzd9Lw==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "license": "MIT" - }, "node_modules/@types/responselike": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "license": "MIT", "dependencies": { @@ -3371,24 +3260,34 @@ }, "node_modules/@types/semver": { "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, "license": "MIT" }, "node_modules/@types/socket.io-client": { "version": "1.4.36", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.36.tgz", + "integrity": "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag==", "license": "MIT" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true, "license": "MIT" }, "node_modules/@types/web-bluetooth": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "license": "MIT", "optional": true, @@ -3398,6 +3297,8 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "license": "MIT", "dependencies": { @@ -3432,6 +3333,8 @@ }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3459,6 +3362,8 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "license": "MIT", "dependencies": { @@ -3475,6 +3380,8 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "license": "MIT", "dependencies": { @@ -3501,6 +3408,8 @@ }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "license": "MIT", "engines": { @@ -3513,6 +3422,8 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3538,8 +3449,36 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3564,6 +3503,8 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "license": "MIT", "dependencies": { @@ -3580,11 +3521,15 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true, "license": "ISC" }, "node_modules/@vitejs/plugin-vue": { "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz", + "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==", "dev": true, "license": "MIT", "engines": { @@ -3597,6 +3542,8 @@ }, "node_modules/@vitejs/plugin-vue-jsx": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz", + "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==", "dev": true, "license": "MIT", "dependencies": { @@ -3614,6 +3561,8 @@ }, "node_modules/@vitest/expect": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3627,6 +3576,8 @@ }, "node_modules/@vitest/runner": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "license": "MIT", "dependencies": { @@ -3640,6 +3591,8 @@ }, "node_modules/@vitest/runner/node_modules/p-limit": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3653,7 +3606,9 @@ } }, "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "license": "MIT", "engines": { @@ -3665,6 +3620,8 @@ }, "node_modules/@vitest/snapshot": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3678,6 +3635,8 @@ }, "node_modules/@vitest/snapshot/node_modules/ansi-styles": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -3689,6 +3648,8 @@ }, "node_modules/@vitest/snapshot/node_modules/pretty-format": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3702,11 +3663,15 @@ }, "node_modules/@vitest/snapshot/node_modules/react-is": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, "node_modules/@vitest/spy": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, "license": "MIT", "dependencies": { @@ -3718,6 +3683,8 @@ }, "node_modules/@vitest/utils": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "license": "MIT", "dependencies": { @@ -3732,6 +3699,8 @@ }, "node_modules/@vitest/utils/node_modules/ansi-styles": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -3743,6 +3712,8 @@ }, "node_modules/@vitest/utils/node_modules/estree-walker": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { @@ -3751,6 +3722,8 @@ }, "node_modules/@vitest/utils/node_modules/pretty-format": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3764,11 +3737,15 @@ }, "node_modules/@vitest/utils/node_modules/react-is": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, "node_modules/@volar/language-core": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", + "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", "dev": true, "license": "MIT", "dependencies": { @@ -3777,6 +3754,8 @@ }, "node_modules/@volar/source-map": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", + "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", "dev": true, "license": "MIT", "dependencies": { @@ -3785,6 +3764,8 @@ }, "node_modules/@volar/typescript": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", + "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3793,24 +3774,27 @@ } }, "node_modules/@vue/babel-helper-vue-transform-on": { - "version": "1.2.2", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.2.5.tgz", + "integrity": "sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==", "dev": true, "license": "MIT" }, "node_modules/@vue/babel-plugin-jsx": { - "version": "1.2.2", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.2.5.tgz", + "integrity": "sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "~7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "@vue/babel-helper-vue-transform-on": "1.2.2", - "@vue/babel-plugin-resolve-type": "1.2.2", - "camelcase": "^6.3.0", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.6", + "@babel/types": "^7.25.6", + "@vue/babel-helper-vue-transform-on": "1.2.5", + "@vue/babel-plugin-resolve-type": "1.2.5", "html-tags": "^3.3.1", "svg-tags": "^1.0.0" }, @@ -3823,102 +3807,83 @@ } } }, - "node_modules/@vue/babel-plugin-jsx/node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@vue/babel-plugin-jsx/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@vue/babel-plugin-resolve-type": { - "version": "1.2.2", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.2.5.tgz", + "integrity": "sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/helper-module-imports": "~7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/parser": "^7.23.9", - "@vue/compiler-sfc": "^3.4.15" + "@babel/code-frame": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/parser": "^7.25.6", + "@vue/compiler-sfc": "^3.5.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@vue/babel-plugin-resolve-type/node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@vue/compiler-core": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/shared": "3.4.27", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/compiler-core": "3.4.27", - "@vue/compiler-dom": "3.4.27", - "@vue/compiler-ssr": "3.4.27", - "@vue/shared": "3.4.27", + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", "estree-walker": "^2.0.2", - "magic-string": "^0.30.10", - "postcss": "^8.4.38", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/devtools-api": { - "version": "6.6.3", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, "node_modules/@vue/eslint-config-prettier": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", + "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==", "dev": true, "license": "MIT", "dependencies": { @@ -3932,6 +3897,8 @@ }, "node_modules/@vue/eslint-config-typescript": { "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz", + "integrity": "sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==", "dev": true, "license": "MIT", "dependencies": { @@ -3955,6 +3922,8 @@ }, "node_modules/@vue/language-core": { "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", "dev": true, "license": "MIT", "dependencies": { @@ -3977,47 +3946,86 @@ } } }, + "node_modules/@vue/language-core/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@vue/reactivity": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.4.27" + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", "license": "MIT", "dependencies": { - "@vue/runtime-core": "3.4.27", - "@vue/shared": "3.4.27", + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { - "vue": "3.4.27" + "vue": "3.5.12" } }, "node_modules/@vue/shared": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", "license": "MIT" }, "node_modules/@vue/test-utils": { "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", "dev": true, "license": "MIT", "dependencies": { @@ -4027,29 +4035,31 @@ }, "node_modules/@vue/tsconfig": { "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", "dev": true, "license": "MIT" }, "node_modules/@vuepic/vue-datepicker": { - "version": "8.7.0", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-8.8.1.tgz", + "integrity": "sha512-8ehfUz1m69Vuc16Pm4ukgb3Mg1VT14x4EsG1ag4O/qbSNRWztTo+pUV4JnFt0FGLl5gGb6NXlxIvR7EjLgD7Gg==", "license": "MIT", "dependencies": { "date-fns": "^3.6.0" }, - "engines": { - "node": ">=18.12.0" - }, "peerDependencies": { "vue": ">=3.2.0" } }, "node_modules/@vueuse/components": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/components/-/components-11.1.0.tgz", - "integrity": "sha512-8CTbvH1rHfAlqsMdMg6G2OiIvHiHn8HRx0yyJ+Ri/C8J6cLylXAd7VESawoCAkRdAXT4ucPnj/uYHZqEb0JbBQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/components/-/components-11.2.0.tgz", + "integrity": "sha512-L9uDsTcaMvz3x1tX2RepdmvDJGIHBiSeYVXNFfHceiM3mmPY6vfRlS/XqZTpip7FdXxu0s/zSmtZCffZGTNRXQ==", + "license": "MIT", "dependencies": { - "@vueuse/core": "11.1.0", - "@vueuse/shared": "11.1.0", + "@vueuse/core": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" } }, @@ -4058,6 +4068,7 @@ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "hasInstallScript": true, + "license": "MIT", "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" @@ -4079,13 +4090,14 @@ } }, "node_modules/@vueuse/core": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.1.0.tgz", - "integrity": "sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.2.0.tgz", + "integrity": "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==", + "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "11.1.0", - "@vueuse/shared": "11.1.0", + "@vueuse/metadata": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -4097,6 +4109,7 @@ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "hasInstallScript": true, + "license": "MIT", "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" @@ -4118,17 +4131,19 @@ } }, "node_modules/@vueuse/metadata": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.1.0.tgz", - "integrity": "sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.2.0.tgz", + "integrity": "sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.1.0.tgz", - "integrity": "sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.2.0.tgz", + "integrity": "sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==", + "license": "MIT", "dependencies": { "vue-demi": ">=0.14.10" }, @@ -4141,6 +4156,7 @@ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "hasInstallScript": true, + "license": "MIT", "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" @@ -4163,6 +4179,8 @@ }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true, "license": "MIT", "engines": { @@ -4170,15 +4188,16 @@ } }, "node_modules/abbrev": { - "version": "2.0.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "ISC" }, "node_modules/acorn": { - "version": "8.11.3", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -4190,6 +4209,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4197,15 +4218,22 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.2", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } }, "node_modules/agent-base": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "license": "MIT", "dependencies": { @@ -4217,6 +4245,8 @@ }, "node_modules/agentkeepalive": { "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dev": true, "license": "MIT", "dependencies": { @@ -4228,6 +4258,8 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "license": "MIT", "dependencies": { @@ -4240,6 +4272,8 @@ }, "node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -4253,41 +4287,10 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "license": "MIT", "engines": { "node": ">=6" @@ -4295,6 +4298,8 @@ }, "node_modules/ansi-escapes": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "license": "MIT", "dependencies": { @@ -4307,60 +4312,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "1.4.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "3.2.1", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "node": ">=8" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { - "version": "1.0.10", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -4375,29 +4363,18 @@ }, "node_modules/array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -4416,15 +4393,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arrify": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/asar": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", + "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", + "deprecated": "Please use @electron/asar moving forward. There is no API change, just a package name change", "dev": true, "license": "MIT", "optional": true, @@ -4444,18 +4417,10 @@ "@types/glob": "^7.1.1" } }, - "node_modules/asar/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/asar/node_modules/commander": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", "optional": true, @@ -4463,40 +4428,10 @@ "node": ">= 6" } }, - "node_modules/asar/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/asar/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/assertion-error": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "license": "MIT", "engines": { @@ -4505,25 +4440,24 @@ }, "node_modules/asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, "license": "ISC", "engines": { "node": ">= 4.0.0" } }, - "node_modules/atomically": { - "version": "2.0.3", - "dependencies": { - "stubborn-fs": "^1.2.5", - "when-exit": "^2.1.1" - } - }, "node_modules/author-regex": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", + "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", "dev": true, "license": "MIT", "engines": { @@ -4532,6 +4466,9 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -4544,7 +4481,9 @@ } }, "node_modules/axios": { - "version": "1.7.2", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -4554,10 +4493,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -4577,6 +4520,8 @@ }, "node_modules/better-path-resolve": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", "license": "MIT", "dependencies": { "is-windows": "^1.0.0" @@ -4587,6 +4532,8 @@ }, "node_modules/bidi-js": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "dev": true, "license": "MIT", "dependencies": { @@ -4595,6 +4542,8 @@ }, "node_modules/bl": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "license": "MIT", "dependencies": { @@ -4605,22 +4554,31 @@ }, "node_modules/bluebird": { "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, "license": "MIT" }, "node_modules/boolbase": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, "license": "ISC" }, "node_modules/boolean": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT", "optional": true }, "node_modules/bootstrap": { "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", "funding": [ { "type": "github", @@ -4638,6 +4596,8 @@ }, "node_modules/bootstrap-daterangepicker": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bootstrap-daterangepicker/-/bootstrap-daterangepicker-3.1.0.tgz", + "integrity": "sha512-oaQZx6ZBDo/dZNyXGVi2rx5GmFXThyQLAxdtIqjtLlYVaQUfQALl5JZMJJZzyDIX7blfy4ppZPAJ10g8Ma4d/g==", "license": "MIT", "dependencies": { "jquery": ">=1.10", @@ -4645,15 +4605,19 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -4662,15 +4626,10 @@ "node": ">=8" } }, - "node_modules/breakword": { - "version": "1.0.6", - "license": "MIT", - "dependencies": { - "wcwidth": "^1.0.1" - } - }, "node_modules/browserslist": { - "version": "4.23.0", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -4688,10 +4647,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4702,6 +4661,8 @@ }, "node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -4725,6 +4686,8 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", "engines": { @@ -4733,11 +4696,15 @@ }, "node_modules/buffer-from": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "node_modules/bytes": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4745,6 +4712,8 @@ }, "node_modules/cac": { "version": "6.7.12", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.12.tgz", + "integrity": "sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==", "license": "MIT", "engines": { "node": ">=8" @@ -4752,6 +4721,8 @@ }, "node_modules/cacache": { "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4778,8 +4749,21 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/cacache/node_modules/glob": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -4798,6 +4782,8 @@ }, "node_modules/cacache/node_modules/lru-cache": { "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -4806,6 +4792,8 @@ }, "node_modules/cacache/node_modules/minimatch": { "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -4815,38 +4803,10 @@ "node": ">=10" } }, - "node_modules/cacache/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacache/node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacache/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/cacheable-lookup": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, "license": "MIT", "engines": { @@ -4855,6 +4815,8 @@ }, "node_modules/cacheable-request": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "license": "MIT", "dependencies": { @@ -4870,22 +4832,11 @@ "node": ">=8" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -4903,36 +4854,18 @@ }, "node_modules/callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "license": "MIT", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001628", + "version": "1.0.30001676", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", + "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", "dev": true, "funding": [ { @@ -4951,7 +4884,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "4.4.1", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "license": "MIT", "dependencies": { @@ -4961,30 +4896,39 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" }, "engines": { "node": ">=4" } }, "node_modules/chalk": { - "version": "2.4.2", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chardet": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "license": "MIT" }, "node_modules/check-error": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "license": "MIT", "dependencies": { @@ -4996,6 +4940,8 @@ }, "node_modules/chownr": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, "license": "ISC", "engines": { @@ -5004,6 +4950,8 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", "engines": { @@ -5012,12 +4960,16 @@ }, "node_modules/chromium-pickle-js": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", "dev": true, "license": "MIT", "optional": true }, "node_modules/ci-info": { "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "funding": [ { "type": "github", @@ -5031,6 +4983,8 @@ }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "license": "MIT", "engines": { @@ -5039,6 +4993,8 @@ }, "node_modules/cli-cursor": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "license": "MIT", "dependencies": { @@ -5053,6 +5009,8 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", "engines": { @@ -5064,6 +5022,8 @@ }, "node_modules/cli-truncate": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, "license": "MIT", "dependencies": { @@ -5077,63 +5037,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.1.0", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8" @@ -5141,6 +5114,8 @@ }, "node_modules/clone-response": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "license": "MIT", "dependencies": { @@ -5151,31 +5126,36 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -5185,15 +5165,19 @@ } }, "node_modules/commander": { - "version": "10.0.1", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">= 6" } }, "node_modules/compare-version": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, "license": "MIT", "engines": { @@ -5202,69 +5186,28 @@ }, "node_modules/computeds": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "license": "MIT" - }, - "node_modules/conf": { - "version": "13.0.1", - "license": "MIT", - "dependencies": { - "ajv": "^8.16.0", - "ajv-formats": "^3.0.1", - "atomically": "^2.0.3", - "debounce-fn": "^6.0.0", - "dot-prop": "^9.0.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.6.2", - "uint8array-extras": "^1.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/ajv": { - "version": "8.17.1", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/conf/node_modules/env-paths": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/json-schema-traverse": { - "version": "1.0.0", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, "node_modules/confbox": { - "version": "0.1.7", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "dev": true, "license": "MIT" }, "node_modules/config-chain": { "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5272,13 +5215,10 @@ "proto-list": "~1.2.1" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "dev": true, - "license": "ISC" - }, "node_modules/content-disposition": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -5286,11 +5226,15 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -5302,64 +5246,15 @@ }, "node_modules/cross-dirname": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { - "version": "5.1.0", - "license": "MIT", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/cross-spawn-windows-exe": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "@malept/cross-spawn-promise": "^1.1.0", - "is-wsl": "^2.2.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/cross-spawn": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "license": "MIT", "dependencies": { @@ -5371,41 +5266,10 @@ "node": ">= 8" } }, - "node_modules/cross-spawn-windows-exe/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/cross-zip": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-4.0.1.tgz", + "integrity": "sha512-n63i0lZ0rvQ6FXiGQ+/JFCKAUyPFhLQYJIqKaa+tSJtfKeULF/IDNDAbdnSIxgS4NTuw2b0+lj8LzfITuq+ZxQ==", "dev": true, "funding": [ { @@ -5428,6 +5292,8 @@ }, "node_modules/css-tree": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "license": "MIT", "dependencies": { @@ -5440,6 +5306,8 @@ }, "node_modules/cssesc": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", "bin": { @@ -5450,47 +5318,35 @@ } }, "node_modules/cssstyle": { - "version": "4.0.1", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", "dev": true, "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.6.0" + "rrweb-cssom": "^0.7.1" }, "engines": { "node": ">=18" } }, - "node_modules/csstype": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/csv": { - "version": "5.5.3", - "license": "MIT", - "dependencies": { - "csv-generate": "^3.4.3", - "csv-parse": "^4.16.3", - "csv-stringify": "^5.6.5", - "stream-transform": "^2.1.3" - }, - "engines": { - "node": ">= 0.1.90" - } - }, - "node_modules/csv-generate": { - "version": "3.4.3", - "license": "MIT" - }, - "node_modules/csv-parse": { - "version": "4.16.3", + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, "license": "MIT" }, - "node_modules/csv-stringify": { - "version": "5.6.5", + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, "node_modules/data-urls": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", "dependencies": { @@ -5503,6 +5359,9 @@ }, "node_modules/data-view-buffer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -5518,6 +5377,9 @@ }, "node_modules/data-view-byte-length": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -5533,6 +5395,9 @@ }, "node_modules/data-view-byte-offset": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -5548,6 +5413,8 @@ }, "node_modules/date-fns": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "license": "MIT", "funding": { "type": "github", @@ -5556,27 +5423,18 @@ }, "node_modules/de-indent": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "dev": true, "license": "MIT" }, - "node_modules/debounce-fn": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/debug": { - "version": "4.3.5", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5587,41 +5445,17 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/decimal.js": { "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true, "license": "MIT" }, "node_modules/decompress-response": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5636,6 +5470,8 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, "license": "MIT", "engines": { @@ -5647,6 +5483,8 @@ }, "node_modules/deep-eql": { "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "license": "MIT", "dependencies": { @@ -5658,11 +5496,16 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/defaults": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, "license": "MIT", "dependencies": { "clone": "^1.0.2" @@ -5673,6 +5516,8 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, "license": "MIT", "engines": { @@ -5681,6 +5526,9 @@ }, "node_modules/define-data-property": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -5696,6 +5544,9 @@ }, "node_modules/define-properties": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", @@ -5711,18 +5562,17 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/detect-indent": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "license": "MIT", "engines": { "node": ">=8" @@ -5730,6 +5580,8 @@ }, "node_modules/detect-libc": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5738,12 +5590,16 @@ }, "node_modules/detect-node": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, "license": "MIT", "optional": true }, "node_modules/diff-sequences": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", "engines": { @@ -5752,6 +5608,8 @@ }, "node_modules/dir-compare": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5759,165 +5617,379 @@ "p-limit": "^3.1.0 " } }, - "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "esutils": "^2.0.2" }, "engines": { - "node": "*" + "node": ">=6.0.0" } }, - "node_modules/dir-compare/node_modules/p-limit": { - "version": "3.1.0", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" }, "engines": { - "node": ">=10" + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/dir-glob": { - "version": "3.0.1", + "node_modules/electron": { + "version": "32.2.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-32.2.2.tgz", + "integrity": "sha512-c7TRE42JcgEmJ4elJyCdKk/2os0UX7YMkRDeXBkxFEoM34iX1/2x+c5T9PgeroKz8FEG7omRU5TvjulqVtXvdw==", + "dev": true, + "hasInstallScript": true, "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" }, "engines": { - "node": ">=8" + "node": ">= 12.20.55" } }, - "node_modules/doctrine": { - "version": "3.0.0", + "node_modules/electron-installer-common": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.3.tgz", + "integrity": "sha512-mYbP+6i+nHMIm0WZHXgGdmmXMe+KXncl6jZYQNcCF9C1WsNA9C5SZ2VP4TLQMSIoFO+X4ugkMEA5uld1bmyEvA==", "dev": true, "license": "Apache-2.0", + "optional": true, "dependencies": { - "esutils": "^2.0.2" + "@malept/cross-spawn-promise": "^1.0.0", + "asar": "^3.0.0", + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "glob": "^7.1.4", + "lodash": "^4.17.15", + "parse-author": "^2.0.0", + "semver": "^7.1.1", + "tmp-promise": "^3.0.2" }, "engines": { - "node": ">=6.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1" + }, + "optionalDependencies": { + "@types/fs-extra": "^9.0.1" } }, - "node_modules/dot-prop": { - "version": "9.0.0", - "license": "MIT", + "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, "dependencies": { - "type-fest": "^4.18.2" + "cross-spawn": "^7.0.1" }, "engines": { - "node": ">=18" + "node": ">= 10" + } + }, + "node_modules/electron-installer-common/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=10" } }, - "node_modules/dot-prop/node_modules/type-fest": { - "version": "4.26.1", - "license": "(MIT OR CC0-1.0)", + "node_modules/electron-installer-debian": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", + "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux" + ], + "dependencies": { + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "electron-installer-common": "^0.10.2", + "fs-extra": "^9.0.0", + "get-folder-size": "^2.0.1", + "lodash": "^4.17.4", + "word-wrap": "^1.2.3", + "yargs": "^16.0.2" + }, + "bin": { + "electron-installer-debian": "src/cli.js" + }, "engines": { - "node": ">=16" + "node": ">= 10.0.0" + } + }, + "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 10" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", + "node_modules/electron-installer-debian/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "MIT" + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } }, - "node_modules/editorconfig": { - "version": "1.0.4", + "node_modules/electron-installer-debian/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/electron-installer-debian/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "9.0.1", - "semver": "^7.5.3" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "bin": { - "editorconfig": "bin/editorconfig" + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-debian/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=14" + "node": ">=8" } }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", + "node_modules/electron-installer-debian/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, "dependencies": { - "brace-expansion": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/electron": { - "version": "32.1.2", + "node_modules/electron-installer-debian/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "hasInstallScript": true, "license": "MIT", + "optional": true, "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">= 12.20.55" + "node": ">=10" } }, - "node_modules/electron-installer-common": { - "version": "0.10.3", + "node_modules/electron-installer-debian/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-redhat": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz", + "integrity": "sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw==", + "dev": true, + "license": "MIT", "optional": true, + "os": [ + "darwin", + "linux" + ], "dependencies": { "@malept/cross-spawn-promise": "^1.0.0", - "asar": "^3.0.0", "debug": "^4.1.1", + "electron-installer-common": "^0.10.2", "fs-extra": "^9.0.0", - "glob": "^7.1.4", "lodash": "^4.17.15", - "parse-author": "^2.0.0", - "semver": "^7.1.1", - "tmp-promise": "^3.0.2" + "word-wrap": "^1.2.3", + "yargs": "^16.0.2" + }, + "bin": { + "electron-installer-redhat": "src/cli.js" }, "engines": { "node": ">= 10.0.0" - }, - "funding": { - "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1" - }, - "optionalDependencies": { - "@types/fs-extra": "^9.0.1" } }, - "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { + "node_modules/electron-installer-redhat/node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -5938,32 +6010,31 @@ "node": ">= 10" } }, - "node_modules/electron-installer-common/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/electron-installer-redhat/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "MIT", + "license": "ISC", "optional": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/electron-installer-common/node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/electron-installer-redhat/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "optional": true }, - "node_modules/electron-installer-common/node_modules/fs-extra": { + "node_modules/electron-installer-redhat/node_modules/fs-extra": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "optional": true, @@ -5977,97 +6048,87 @@ "node": ">=10" } }, - "node_modules/electron-installer-common/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/electron-installer-common/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/electron-installer-redhat/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-installer-common/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/electron-installer-common/node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/electron-installer-redhat/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "shebang-regex": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/electron-installer-common/node_modules/shebang-regex": { - "version": "3.0.0", + "node_modules/electron-installer-redhat/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/electron-installer-common/node_modules/universalify": { - "version": "2.0.1", + "node_modules/electron-installer-redhat/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "optional": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=10" } }, - "node_modules/electron-installer-common/node_modules/which": { - "version": "2.0.2", + "node_modules/electron-installer-redhat/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, "engines": { - "node": ">= 8" + "node": ">=10" } }, "node_modules/electron-squirrel-startup": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz", + "integrity": "sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==", "license": "Apache-2.0", "dependencies": { "debug": "^2.2.0" @@ -6075,6 +6136,8 @@ }, "node_modules/electron-squirrel-startup/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -6082,39 +6145,21 @@ }, "node_modules/electron-squirrel-startup/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/electron-store": { - "version": "10.0.0", - "license": "MIT", - "dependencies": { - "conf": "^13.0.0", - "type-fest": "^4.20.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-store/node_modules/type-fest": { - "version": "4.26.1", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/electron-to-chromium": { - "version": "1.4.790", + "version": "1.5.50", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", + "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", "dev": true, "license": "ISC" }, "node_modules/electron-winstaller": { "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6133,61 +6178,48 @@ "@electron/windows-sign": "^1.1.2" } }, - "node_modules/electron-wix-msi": { - "version": "5.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/windows-sign": "^1.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.1.0", - "klaw": "^4.1.0", - "lodash": "^4.17.21", - "rcedit": "^4.0.1", - "rcinfo": "^0.1.3", - "semver": "^7.6.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@bitdisaster/exe-icon-extractor": "^1.0.10" - } - }, - "node_modules/electron-wix-msi/node_modules/fs-extra": { - "version": "10.1.0", + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=6 <7 || >=8" } }, - "node_modules/electron-wix-msi/node_modules/jsonfile": { - "version": "6.1.0", + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, + "optional": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "node_modules/electron-wix-msi/node_modules/universalify": { - "version": "2.0.1", + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", + "optional": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/electron/node_modules/@electron/get": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6207,7 +6239,9 @@ } }, "node_modules/electron/node_modules/@types/node": { - "version": "20.16.6", + "version": "20.17.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.5.tgz", + "integrity": "sha512-n8FYY/pRxu496441gIcAQFZPKXbhsd6VZygcq+PTSZ75eMh/Ke0hCAROdUa21qiFqKNsPPYic46yXDO1JGiPBQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6216,6 +6250,8 @@ }, "node_modules/electron/node_modules/fs-extra": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { @@ -6227,8 +6263,20 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/electron/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/electron/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -6237,15 +6285,32 @@ }, "node_modules/electron/node_modules/undici-types": { "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, "license": "MIT" }, + "node_modules/electron/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/emoji-regex": { - "version": "8.0.0", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, "license": "MIT" }, "node_modules/encoding": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, "license": "MIT", "optional": true, @@ -6253,20 +6318,10 @@ "iconv-lite": "^0.6.2" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6274,18 +6329,43 @@ } }, "node_modules/engine.io-client": { - "version": "6.5.3", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz", + "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/engine.io-parser": { - "version": "5.2.2", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6293,6 +6373,8 @@ }, "node_modules/enquirer": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1", @@ -6304,6 +6386,8 @@ }, "node_modules/entities": { "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -6314,6 +6398,8 @@ }, "node_modules/env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { @@ -6322,11 +6408,16 @@ }, "node_modules/err-code": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true, "license": "MIT" }, "node_modules/error-ex": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -6334,6 +6425,9 @@ }, "node_modules/es-abstract": { "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -6392,6 +6486,9 @@ }, "node_modules/es-define-property": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" @@ -6402,6 +6499,9 @@ }, "node_modules/es-errors": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6409,6 +6509,9 @@ }, "node_modules/es-object-atoms": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -6419,6 +6522,9 @@ }, "node_modules/es-set-tostringtag": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4", @@ -6429,15 +6535,11 @@ "node": ">= 0.4" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, "node_modules/es-to-primitive": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.4", @@ -6453,12 +6555,16 @@ }, "node_modules/es6-error": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/esbuild": { - "version": "0.20.2", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6469,55 +6575,67 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { - "version": "3.1.2", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "8.57.0", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -6564,6 +6682,8 @@ }, "node_modules/eslint-config-prettier": { "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, "license": "MIT", "bin": { @@ -6574,12 +6694,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -6603,265 +6725,50 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.26.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "globals": "^13.24.0", - "natural-compare": "^1.4.0", - "nth-check": "^2.1.1", - "postcss-selector-parser": "^6.0.15", - "semver": "^7.6.0", - "vue-eslint-parser": "^9.4.2", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-vue/node_modules/globals": { - "version": "13.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-vue/node_modules/type-fest": { - "version": "0.20.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", + "version": "9.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz", + "integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" }, "engines": { - "node": "*" + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", + "node_modules/eslint-plugin-vue/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", + "node_modules/eslint-plugin-vue/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -6869,38 +6776,56 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "shebang-regex": "^3.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -6910,22 +6835,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/espree": { "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -6942,6 +6855,8 @@ }, "node_modules/esprima": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -6952,7 +6867,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6964,6 +6881,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -6975,6 +6894,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -6983,10 +6904,14 @@ }, "node_modules/estree-walker": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -6995,90 +6920,120 @@ }, "node_modules/eventemitter3": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true, "license": "MIT" }, "node_modules/execa": { - "version": "8.0.1", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=6" } }, "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">= 8" + "node": ">=4.8" } }, - "node_modules/execa/node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/execa/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "pump": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "3.0.0", + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/execa/node_modules/signal-exit": { - "version": "4.1.0", + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", - "engines": { - "node": ">=14" + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, "node_modules/execa/node_modules/which": { - "version": "2.0.2", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "which": "bin/which" } }, "node_modules/expand-tilde": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "license": "MIT", "dependencies": { @@ -7090,15 +7045,21 @@ }, "node_modules/exponential-backoff": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", "dev": true, "license": "Apache-2.0" }, "node_modules/extendable-error": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", "license": "MIT" }, "node_modules/external-editor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "license": "MIT", "dependencies": { "chardet": "^0.7.0", @@ -7109,8 +7070,34 @@ "node": ">=4" } }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7128,31 +7115,24 @@ "@types/yauzl": "^2.9.1" } }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, "license": "Apache-2.0" }, "node_modules/fast-glob": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -7165,33 +7145,36 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.1", - "license": "MIT" - }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "license": "MIT", - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fast-url-parser/node_modules/punycode": { - "version": "1.4.1", - "license": "MIT" - }, "node_modules/fastq": { "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -7199,6 +7182,8 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", "dependencies": { @@ -7207,6 +7192,8 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", "dependencies": { @@ -7218,10 +7205,14 @@ }, "node_modules/file-saver": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", "license": "MIT" }, "node_modules/filename-reserved-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true, "license": "MIT", "engines": { @@ -7230,6 +7221,8 @@ }, "node_modules/filenamify": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", "dev": true, "license": "MIT", "dependencies": { @@ -7246,6 +7239,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -7255,26 +7250,26 @@ } }, "node_modules/find-up": { - "version": "4.1.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/find-yarn-workspace-root2": { - "version": "1.2.16", - "license": "Apache-2.0", - "dependencies": { - "micromatch": "^4.0.2", - "pkg-dir": "^4.2.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", "dependencies": { @@ -7288,55 +7283,29 @@ }, "node_modules/flatted": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true, "license": "ISC" }, "node_modules/flora-colossus": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/flora-colossus/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/flora-colossus/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/flora-colossus/node_modules/universalify": { - "version": "2.0.1", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", + "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", "dev": true, "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 12" } }, "node_modules/follow-redirects": { - "version": "1.15.6", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -7355,13 +7324,18 @@ }, "node_modules/for-each": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } }, "node_modules/foreground-child": { - "version": "3.1.1", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { @@ -7375,40 +7349,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/foreground-child/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", "engines": { @@ -7418,22 +7362,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/form-data": { - "version": "4.0.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -7445,19 +7377,24 @@ } }, "node_modules/fs-extra": { - "version": "7.0.1", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=12" } }, "node_modules/fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "license": "ISC", "dependencies": { @@ -7467,29 +7404,33 @@ "node": ">= 8" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7497,6 +7438,9 @@ }, "node_modules/function.prototype.name": { "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -7513,6 +7457,9 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7520,6 +7467,8 @@ }, "node_modules/galactus": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", + "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7531,73 +7480,29 @@ "node": ">= 12" } }, - "node_modules/galactus/node_modules/fs-extra": { - "version": "10.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/galactus/node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/galactus/node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/gar": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, + "license": "MIT", "optional": true }, - "node_modules/gauge": { - "version": "4.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/gcode-preview": { - "version": "2.17.0", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/gcode-preview/-/gcode-preview-2.18.0.tgz", + "integrity": "sha512-uc9QYciG6ES/A6BWJpUZk4fHxCPvt5EnvDhHIHDbNdR/m3f9VkGvpSMh9HDygXjAXX0x1Lbz/e9ZGlIrYNB29A==", "license": "MIT", "dependencies": { - "three": "^0.155.0" + "lil-gui": "^0.19.2", + "three": "^0.159.0" } }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -7606,6 +7511,9 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -7616,6 +7524,7 @@ "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "gar": "^1.0.4", @@ -7627,6 +7536,8 @@ }, "node_modules/get-func-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "license": "MIT", "engines": { @@ -7635,6 +7546,8 @@ }, "node_modules/get-installed-path": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/get-installed-path/-/get-installed-path-2.1.1.tgz", + "integrity": "sha512-Qkn9eq6tW5/q9BDVdMpB8tOHljX9OSP0jRC5TRNVA4qRc839t4g8KQaR8t0Uv0EFVL0MlyG7m/ofjEgAROtYsA==", "dev": true, "license": "MIT", "dependencies": { @@ -7643,6 +7556,9 @@ }, "node_modules/get-intrinsic": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7660,167 +7576,48 @@ }, "node_modules/get-package-info": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", + "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", "dev": true, "license": "MIT", "dependencies": { "bluebird": "^3.1.1", "debug": "^2.2.0", "lodash.get": "^4.0.0", - "read-pkg-up": "^2.0.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/get-package-info/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/get-package-info/node_modules/find-up": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/load-json-file": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/locate-path": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/get-package-info/node_modules/p-limit": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/p-locate": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/p-try": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/parse-json": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-package-info/node_modules/path-exists": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/path-type": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-package-info/node_modules/pify": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-package-info/node_modules/read-pkg": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "read-pkg-up": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">= 4.0" } }, - "node_modules/get-package-info/node_modules/read-pkg-up": { - "version": "2.0.0", + "node_modules/get-package-info/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" + "ms": "2.0.0" } }, + "node_modules/get-package-info/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/get-stream": { - "version": "8.0.1", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, "engines": { - "node": ">=16" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7828,6 +7625,9 @@ }, "node_modules/get-symbol-description": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -7842,52 +7642,44 @@ } }, "node_modules/glob": { - "version": "10.4.1", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.4", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10.13.0" } }, "node_modules/global-agent": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -7905,6 +7697,8 @@ }, "node_modules/global-modules": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "license": "MIT", "dependencies": { @@ -7918,6 +7712,8 @@ }, "node_modules/global-prefix": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "license": "MIT", "dependencies": { @@ -7931,8 +7727,23 @@ "node": ">=0.10.0" } }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "license": "MIT", "engines": { @@ -7941,6 +7752,9 @@ }, "node_modules/globalthis": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, "license": "MIT", "dependencies": { "define-properties": "^1.2.1", @@ -7955,6 +7769,8 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "license": "MIT", "dependencies": { "array-union": "^2.1.0", @@ -7973,6 +7789,9 @@ }, "node_modules/gopd": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" @@ -7983,6 +7802,8 @@ }, "node_modules/got": { "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "license": "MIT", "dependencies": { @@ -8007,40 +7828,42 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "license": "MIT" - }, "node_modules/graphemer": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/has-bigints": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { - "version": "3.0.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -8051,6 +7874,9 @@ }, "node_modules/has-proto": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8061,6 +7887,9 @@ }, "node_modules/has-symbols": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8071,6 +7900,9 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -8082,13 +7914,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/hasown": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -8099,6 +7929,8 @@ }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", "bin": { @@ -8107,6 +7939,8 @@ }, "node_modules/homedir-polyfill": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "license": "MIT", "dependencies": { @@ -8118,10 +7952,15 @@ }, "node_modules/hosted-git-info": { "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, "license": "ISC" }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8133,6 +7972,8 @@ }, "node_modules/html-tags": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "license": "MIT", "engines": { @@ -8144,11 +7985,15 @@ }, "node_modules/http-cache-semantics": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { @@ -8161,6 +8006,8 @@ }, "node_modules/http2-wrapper": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "license": "MIT", "dependencies": { @@ -8171,19 +8018,10 @@ "node": ">=10.19.0" } }, - "node_modules/http2-wrapper/node_modules/quick-lru": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/https-proxy-agent": { - "version": "7.0.4", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "license": "MIT", "dependencies": { @@ -8196,10 +8034,14 @@ }, "node_modules/human-id": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-1.0.2.tgz", + "integrity": "sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==", "license": "MIT" }, "node_modules/human-signals": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -8208,6 +8050,8 @@ }, "node_modules/humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8215,10 +8059,13 @@ } }, "node_modules/iconv-lite": { - "version": "0.4.24", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -8226,6 +8073,8 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -8244,7 +8093,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.1", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -8252,6 +8103,8 @@ }, "node_modules/import-fresh": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8265,16 +8118,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -8283,6 +8130,9 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8290,11 +8140,16 @@ }, "node_modules/infer-owner": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "dev": true, "license": "ISC" }, "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", "dependencies": { @@ -8304,16 +8159,23 @@ }, "node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8326,6 +8188,8 @@ }, "node_modules/interpret": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { @@ -8334,6 +8198,8 @@ }, "node_modules/ip-address": { "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "license": "MIT", "dependencies": { @@ -8344,13 +8210,11 @@ "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/is-array-buffer": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -8365,10 +8229,16 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, "node_modules/is-bigint": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" @@ -8379,6 +8249,9 @@ }, "node_modules/is-boolean-object": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -8393,6 +8266,9 @@ }, "node_modules/is-callable": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8402,10 +8278,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8413,6 +8295,9 @@ }, "node_modules/is-data-view": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, "license": "MIT", "dependencies": { "is-typed-array": "^1.1.13" @@ -8426,6 +8311,9 @@ }, "node_modules/is-date-object": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -8437,36 +8325,32 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -8477,6 +8361,8 @@ }, "node_modules/is-interactive": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, "license": "MIT", "engines": { @@ -8485,11 +8371,16 @@ }, "node_modules/is-lambda": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true, "license": "MIT" }, "node_modules/is-negative-zero": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8500,6 +8391,8 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -8507,6 +8400,9 @@ }, "node_modules/is-number-object": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -8520,26 +8416,26 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, "node_modules/is-regex": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -8554,6 +8450,9 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7" @@ -8566,18 +8465,20 @@ } }, "node_modules/is-stream": { - "version": "3.0.0", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/is-string": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -8591,6 +8492,8 @@ }, "node_modules/is-subdir": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", "license": "MIT", "dependencies": { "better-path-resolve": "1.0.0" @@ -8601,6 +8504,9 @@ }, "node_modules/is-symbol": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" @@ -8614,6 +8520,9 @@ }, "node_modules/is-typed-array": { "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" @@ -8627,6 +8536,8 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", "engines": { @@ -8638,6 +8549,9 @@ }, "node_modules/is-weakref": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2" @@ -8648,28 +8562,24 @@ }, "node_modules/is-windows": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, "license": "MIT" }, "node_modules/isbinaryfile": { "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "license": "MIT", "engines": { @@ -8681,51 +8591,141 @@ }, "node_modules/isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.0", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=14" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jquery": { - "version": "3.7.1", - "license": "MIT" + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "node_modules/js-beautify": { - "version": "1.15.1", + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.3.3", - "js-cookie": "^3.0.5", - "nopt": "^7.2.0" + "abbrev": "^2.0.0" }, "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=14" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/js-cookie": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", "dev": true, "license": "MIT", "engines": { @@ -8734,14 +8734,18 @@ }, "node_modules/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -8749,11 +8753,15 @@ }, "node_modules/jsbn": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true, "license": "MIT" }, "node_modules/jsdom": { "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz", + "integrity": "sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==", "dev": true, "license": "MIT", "dependencies": { @@ -8791,28 +8799,10 @@ } } }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.17.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsdom/node_modules/xml-name-validator": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -8820,52 +8810,68 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "license": "MIT" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, - "node_modules/json-schema-typed": { - "version": "8.0.1", - "license": "BSD-2-Clause" - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, "license": "ISC", "optional": true }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -8876,14 +8882,22 @@ } }, "node_modules/jsonfile": { - "version": "4.0.0", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/junk": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", "dev": true, "license": "MIT", "engines": { @@ -8892,36 +8906,18 @@ }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klaw": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8932,12 +8928,16 @@ "node": ">= 0.8.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", + "node_modules/lil-gui": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.19.2.tgz", + "integrity": "sha512-nU8j4ND702ouGfQZoaTN4dfXxacvGOAVK0DtmZBVcUYUAeYQXLQAjAN50igMHiba3T5jZyKEjXZU+Ntm1Qs6ZQ==", "license": "MIT" }, "node_modules/listr2": { "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, "license": "MIT", "dependencies": { @@ -8952,81 +8952,10 @@ "node": ">=16.0.0" } }, - "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/listr2/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/load-json-file": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "license": "MIT", "dependencies": { @@ -9036,167 +8965,90 @@ "strip-bom": "^3.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/load-yaml-file": { - "version": "0.2.0", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.13.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/local-pkg": { - "version": "0.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } + "license": "MIT" }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true, "license": "MIT" }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "license": "MIT", "dependencies": { @@ -9215,6 +9067,8 @@ }, "node_modules/log-update/node_modules/ansi-regex": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -9224,40 +9078,10 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update/node_modules/strip-ansi": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9270,24 +9094,10 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/loose-envify": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -9298,6 +9108,8 @@ }, "node_modules/loupe": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -9306,6 +9118,8 @@ }, "node_modules/lowercase-keys": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "license": "MIT", "engines": { @@ -9313,22 +9127,28 @@ } }, "node_modules/lru-cache": { - "version": "4.1.5", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, "node_modules/magic-string": { - "version": "0.30.10", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-fetch-happen": { "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "license": "ISC", "dependencies": { @@ -9355,6 +9175,8 @@ }, "node_modules/make-fetch-happen/node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9366,6 +9188,8 @@ }, "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "license": "MIT", "dependencies": { @@ -9379,6 +9203,8 @@ }, "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "license": "MIT", "dependencies": { @@ -9391,30 +9217,18 @@ }, "node_modules/make-fetch-happen/node_modules/lru-cache": { "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { "node": ">=12" } }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/map-age-cleaner": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "license": "MIT", "dependencies": { @@ -9424,18 +9238,10 @@ "node": ">=6" } }, - "node_modules/map-obj": { - "version": "4.3.0", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/matcher": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, "license": "MIT", "optional": true, @@ -9446,25 +9252,17 @@ "node": ">=10" } }, - "node_modules/matcher/node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mdn-data": { "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true, "license": "CC0-1.0" }, "node_modules/mem": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "license": "MIT", "dependencies": { @@ -9476,58 +9274,35 @@ "node": ">=6" } }, - "node_modules/mem/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/memorystream": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "engines": { "node": ">= 0.10.0" } }, - "node_modules/meow": { - "version": "6.1.1", - "license": "MIT", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "^4.0.2", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.7", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -9539,6 +9314,8 @@ }, "node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -9546,6 +9323,8 @@ }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -9555,128 +9334,51 @@ } }, "node_modules/mimic-fn": { - "version": "4.0.0", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/mimic-response": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/minimatch": { - "version": "9.0.3", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass-fetch/node_modules/minipass": { + "node_modules/minipass": { "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "license": "ISC", "dependencies": { @@ -9686,13 +9388,10 @@ "node": ">=8" } }, - "node_modules/minipass-fetch/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-flush": { - "version": "1.0.5", + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, "license": "ISC", "dependencies": { @@ -9702,51 +9401,54 @@ "node": ">= 8" } }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/minipass-sized": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, "license": "ISC", "dependencies": { @@ -9756,24 +9458,17 @@ "node": ">=8" } }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/yallist": { + "node_modules/minipass/node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "license": "MIT", "dependencies": { @@ -9784,31 +9479,17 @@ "node": ">= 8" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "license": "ISC" }, - "node_modules/mixme": { - "version": "0.5.10", - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", "bin": { @@ -9819,38 +9500,59 @@ } }, "node_modules/mlly": { - "version": "1.7.1", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.11.3", + "acorn": "^8.12.1", "pathe": "^1.1.2", - "pkg-types": "^1.1.1", - "ufo": "^1.5.3" + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" } }, "node_modules/module-alias": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", "license": "MIT" }, "node_modules/moment": { "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "license": "MIT", "engines": { "node": "*" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/muggle-string": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -9867,11 +9569,15 @@ }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, "license": "MIT", "engines": { @@ -9880,25 +9586,31 @@ }, "node_modules/nice-try": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true, "license": "MIT" }, "node_modules/node": { "version": "21.7.3", + "resolved": "https://registry.npmjs.org/node/-/node-21.7.3.tgz", + "integrity": "sha512-ouAXROl1nmh9mAZvejCfOnBp7CnRk14CCuBjZSIyDjv3da78m0nImWYlDRrPcgikBl2LHvvYq5BvDWfpfHoMtQ==", "hasInstallScript": true, "license": "ISC", "dependencies": { "node-bin-setup": "^1.0.0" }, "bin": { - "node": "bin/node.exe" + "node": "bin/node" }, "engines": { "npm": ">=5.0.0" } }, "node_modules/node-abi": { - "version": "3.68.0", + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", "dev": true, "license": "MIT", "dependencies": { @@ -9910,6 +9622,8 @@ }, "node_modules/node-api-version": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz", + "integrity": "sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==", "dev": true, "license": "MIT", "dependencies": { @@ -9918,10 +9632,14 @@ }, "node_modules/node-bin-setup": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.1.3.tgz", + "integrity": "sha512-opgw9iSCAzT2+6wJOETCpeRYAQxSopqQ2z+N6BXwIMsQQ7Zj5M8MaafQY8JMlolRR6R1UXg2WmhKp0p9lSOivg==", "license": "ISC" }, "node_modules/node-fetch": { "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", "dependencies": { @@ -9941,16 +9659,22 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "license": "MIT", "dependencies": { @@ -9958,123 +9682,34 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/node-gyp": { - "version": "9.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.13 || ^14.13 || >=16" - } - }, - "node_modules/node-gyp/node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/node-releases": { - "version": "2.0.14", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true, "license": "MIT" }, "node_modules/nopt": { - "version": "7.2.1", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^2.0.0" + "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/normalize-package-data": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", @@ -10085,6 +9720,9 @@ }, "node_modules/normalize-package-data/node_modules/semver": { "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" @@ -10092,6 +9730,8 @@ }, "node_modules/normalize-url": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, "license": "MIT", "engines": { @@ -10103,6 +9743,8 @@ }, "node_modules/npm-normalize-package-bin": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, "license": "ISC", "engines": { @@ -10111,6 +9753,8 @@ }, "node_modules/npm-run-all": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10133,17 +9777,55 @@ "node": ">= 4" } }, - "node_modules/npm-run-all/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" } }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, "node_modules/npm-run-all/node_modules/cross-spawn": { "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10157,78 +9839,99 @@ "node": ">=4.8" } }, - "node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">=0.8.0" } }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/npm-run-all/node_modules/path-type": { - "version": "3.0.0", + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, "engines": { "node": ">=4" } }, - "node_modules/npm-run-all/node_modules/pidtree": { - "version": "0.3.1", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", + "license": "ISC", "bin": { - "pidtree": "bin/pidtree.js" + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/pify": { - "version": "3.0.0", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/read-pkg": { - "version": "3.0.0", + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, "bin": { - "semver": "bin/semver" + "which": "bin/which" } }, "node_modules/npm-run-all2": { - "version": "6.2.0", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.6.tgz", + "integrity": "sha512-tkyb4pc0Zb0oOswCb5tORPk9MvVL6gcDq1cMItQHmsbVk1skk7YF6cH+UU2GxeNLHMuk6wFEOSmEmJ2cnAK1jg==", "dev": true, "license": "MIT", "dependencies": { @@ -10238,7 +9941,8 @@ "minimatch": "^9.0.0", "pidtree": "^0.6.0", "read-package-json-fast": "^3.0.2", - "shell-quote": "^1.7.3" + "shell-quote": "^1.7.3", + "which": "^3.0.1" }, "bin": { "npm-run-all": "bin/npm-run-all/index.js", @@ -10247,12 +9951,14 @@ "run-s": "bin/run-s/index.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0", + "node": "^14.18.0 || ^16.13.0 || >=18.0.0", "npm": ">= 8" } }, "node_modules/npm-run-all2/node_modules/ansi-styles": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -10262,93 +9968,88 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm-run-all2/node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/npm-run-all2/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "balanced-match": "^1.0.0" } }, - "node_modules/npm-run-all2/node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/npm-run-all2/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "shebang-regex": "^3.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-run-all2/node_modules/shebang-regex": { - "version": "3.0.0", + "node_modules/npm-run-all2/node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, "node_modules/npm-run-all2/node_modules/which": { - "version": "2.0.2", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-run-path": { - "version": "5.3.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^4.0.0" + "path-key": "^2.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4" } }, "node_modules/nth-check": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10360,20 +10061,31 @@ }, "node_modules/object-assign": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.13.1", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10381,6 +10093,9 @@ }, "node_modules/object.assign": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -10397,6 +10112,8 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", "dependencies": { @@ -10404,14 +10121,16 @@ } }, "node_modules/onetime": { - "version": "6.0.0", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^4.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=12" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10419,6 +10138,8 @@ }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -10435,6 +10156,8 @@ }, "node_modules/ora": { "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10455,37 +10178,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/ora/node_modules/cli-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "license": "MIT", "dependencies": { @@ -10495,54 +10191,10 @@ "node": ">=8" } }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ora/node_modules/restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { @@ -10553,19 +10205,10 @@ "node": ">=8" } }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10573,10 +10216,14 @@ }, "node_modules/outdent": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", "license": "MIT" }, "node_modules/p-cancelable": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, "license": "MIT", "engines": { @@ -10585,6 +10232,8 @@ }, "node_modules/p-defer": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", "dev": true, "license": "MIT", "engines": { @@ -10593,6 +10242,8 @@ }, "node_modules/p-filter": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", "license": "MIT", "dependencies": { "p-map": "^2.0.0" @@ -10601,8 +10252,19 @@ "node": ">=8" } }, + "node_modules/p-filter/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/p-finally": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { @@ -10611,6 +10273,8 @@ }, "node_modules/p-is-promise": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", "dev": true, "license": "MIT", "engines": { @@ -10618,44 +10282,79 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { - "version": "2.1.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", + "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -10667,6 +10366,8 @@ }, "node_modules/parse-author": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", + "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", "dev": true, "license": "MIT", "dependencies": { @@ -10677,23 +10378,23 @@ } }, "node_modules/parse-json": { - "version": "5.2.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/parse-passwd": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, "license": "MIT", "engines": { @@ -10701,11 +10402,13 @@ } }, "node_modules/parse5": { - "version": "7.1.2", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -10713,11 +10416,15 @@ }, "node_modules/path-browserify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true, "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "license": "MIT", "engines": { "node": ">=8" @@ -10725,6 +10432,8 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { @@ -10733,10 +10442,14 @@ }, "node_modules/path-is-inside": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "license": "(WTFPL OR MIT)" }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -10745,10 +10458,15 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -10763,19 +10481,32 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { - "node": "14 || >=16.14" + "node": ">=16 || 14 >=14.17" } }, "node_modules/path-to-regexp": { - "version": "2.2.1", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "license": "MIT", "engines": { "node": ">=8" @@ -10783,11 +10514,15 @@ }, "node_modules/pathe": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "dev": true, "license": "MIT" }, "node_modules/pathval": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "license": "MIT", "engines": { @@ -10796,76 +10531,152 @@ }, "node_modules/pe-library": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", + "integrity": "sha512-nh39Mo1eGWmZS7y+mK/dQIqg7S1lp38DpRxkyoHf0ZcUs/HDc+yyTjuOtTvSMZHmfSLuSQaX945u05Y2Q6UWZg==", "dev": true, "license": "MIT", "engines": { "node": ">=14", "npm": ">=7" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/pend": { - "version": "1.2.0", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=8" } }, - "node_modules/pidtree": { - "version": "0.6.0", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" + "dependencies": { + "p-locate": "^4.1.0" }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/pify": { - "version": "4.0.1", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, "node_modules/pkg-types": { - "version": "1.1.1", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.0", + "confbox": "^0.1.8", + "mlly": "^1.7.2", "pathe": "^1.1.2" } }, "node_modules/plist": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10879,13 +10690,18 @@ }, "node_modules/possible-typed-array-names": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.38", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -10903,15 +10719,17 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -10924,6 +10742,8 @@ }, "node_modules/postject": { "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", "dev": true, "license": "MIT", "dependencies": { @@ -10938,6 +10758,8 @@ }, "node_modules/postject/node_modules/commander": { "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "license": "MIT", "engines": { @@ -10946,6 +10768,8 @@ }, "node_modules/preact": { "version": "10.10.6", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.10.6.tgz", + "integrity": "sha512-w0mCL5vICUAZrh1DuHEdOWBjxdO62lvcO++jbzr8UhhYcTbFkpegLH9XX+7MadjTl/y0feoqwQ/zAnzkc/EGog==", "license": "MIT", "funding": { "type": "opencollective", @@ -10954,6 +10778,8 @@ }, "node_modules/preact-render-to-string": { "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", "license": "MIT", "dependencies": { "pretty-format": "^3.8.0" @@ -10962,74 +10788,10 @@ "preact": ">=10" } }, - "node_modules/preferred-pm": { - "version": "3.1.3", - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0", - "find-yarn-workspace-root2": "1.2.16", - "path-exists": "^4.0.0", - "which-pm": "2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/preferred-pm/node_modules/find-up": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/locate-path": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/p-limit": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/preferred-pm/node_modules/p-locate": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -11037,7 +10799,9 @@ } }, "node_modules/prettier": { - "version": "3.3.1", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", "bin": { @@ -11052,6 +10816,8 @@ }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { @@ -11063,10 +10829,24 @@ }, "node_modules/pretty-format": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", "license": "MIT" }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/progress": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", "engines": { @@ -11075,11 +10855,15 @@ }, "node_modules/promise-inflight": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, "license": "ISC" }, "node_modules/promise-retry": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "license": "MIT", "dependencies": { @@ -11092,6 +10876,8 @@ }, "node_modules/prop-types": { "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -11101,24 +10887,34 @@ }, "node_modules/proto-list": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true, "license": "ISC" }, "node_modules/proxy-from-env": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/pseudomap": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "license": "ISC" }, "node_modules/psl": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true, "license": "MIT" }, "node_modules/pump": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "license": "MIT", "dependencies": { @@ -11128,6 +10924,8 @@ }, "node_modules/punycode": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -11136,11 +10934,15 @@ }, "node_modules/querystringify": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true, "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -11158,37 +10960,31 @@ "license": "MIT" }, "node_modules/quick-lru": { - "version": "4.0.1", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/range-parser": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/rcedit": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn-windows-exe": "^1.1.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/rcinfo": { - "version": "0.1.3", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/react": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "peer": true, "dependencies": { @@ -11200,10 +10996,14 @@ }, "node_modules/react-fast-compare": { "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", "license": "MIT" }, "node_modules/react-helmet": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", "license": "MIT", "dependencies": { "object-assign": "^4.1.1", @@ -11217,10 +11017,14 @@ }, "node_modules/react-is": { "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/react-side-effect": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", "license": "MIT", "peerDependencies": { "react": "^16.3.0 || ^17.0.0 || ^18.0.0" @@ -11228,6 +11032,8 @@ }, "node_modules/read-binary-file-arch": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", "dev": true, "license": "MIT", "dependencies": { @@ -11239,6 +11045,8 @@ }, "node_modules/read-package-json-fast": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", "dev": true, "license": "ISC", "dependencies": { @@ -11249,58 +11057,192 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/read-pkg": { - "version": "5.2.0", + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "locate-path": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "license": "(MIT OR CC0-1.0)", + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "license": "(MIT OR CC0-1.0)", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/read-yaml-file": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.1.5", @@ -11312,8 +11254,47 @@ "node": ">=6" } }, + "node_modules/read-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/read-yaml-file/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/read-yaml-file/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/read-yaml-file/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/readable-stream": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { @@ -11327,6 +11308,8 @@ }, "node_modules/rechoir": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11336,29 +11319,23 @@ "node": ">= 10.13.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -11369,6 +11346,9 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11376,22 +11356,25 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "license": "ISC" - }, "node_modules/requires-port": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true, "license": "MIT" }, "node_modules/resedit": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", + "integrity": "sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==", "dev": true, "license": "MIT", "dependencies": { @@ -11408,6 +11391,9 @@ }, "node_modules/resolve": { "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -11423,11 +11409,15 @@ }, "node_modules/resolve-alpn": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, "license": "MIT" }, "node_modules/resolve-dir": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "license": "MIT", "dependencies": { @@ -11439,14 +11429,19 @@ } }, "node_modules/resolve-from": { - "version": "5.0.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/resolve-package": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-package/-/resolve-package-1.0.1.tgz", + "integrity": "sha512-rzB7NnQpOkPHBWFPP3prUMqOP6yg3HkRGgcvR+lDyvyHoY3fZLFLYDkPXh78SPVBAE6VTCk/V+j8we4djg6o4g==", "dev": true, "license": "MIT", "dependencies": { @@ -11459,6 +11454,8 @@ }, "node_modules/responselike": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "license": "MIT", "dependencies": { @@ -11470,6 +11467,8 @@ }, "node_modules/restore-cursor": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "license": "MIT", "dependencies": { @@ -11483,30 +11482,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/retry": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "license": "MIT", "engines": { @@ -11515,6 +11494,8 @@ }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -11523,11 +11504,16 @@ }, "node_modules/rfdc": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -11540,47 +11526,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/roarr": { "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -11596,18 +11545,14 @@ "node": ">=8.0" } }, - "node_modules/roarr/node_modules/sprintf-js": { - "version": "1.1.3", - "dev": true, - "license": "BSD-3-Clause", - "optional": true - }, "node_modules/rollup": { - "version": "4.18.0", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz", + "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -11617,32 +11562,38 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.24.3", + "@rollup/rollup-android-arm64": "4.24.3", + "@rollup/rollup-darwin-arm64": "4.24.3", + "@rollup/rollup-darwin-x64": "4.24.3", + "@rollup/rollup-freebsd-arm64": "4.24.3", + "@rollup/rollup-freebsd-x64": "4.24.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.3", + "@rollup/rollup-linux-arm-musleabihf": "4.24.3", + "@rollup/rollup-linux-arm64-gnu": "4.24.3", + "@rollup/rollup-linux-arm64-musl": "4.24.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3", + "@rollup/rollup-linux-riscv64-gnu": "4.24.3", + "@rollup/rollup-linux-s390x-gnu": "4.24.3", + "@rollup/rollup-linux-x64-gnu": "4.24.3", + "@rollup/rollup-linux-x64-musl": "4.24.3", + "@rollup/rollup-win32-arm64-msvc": "4.24.3", + "@rollup/rollup-win32-ia32-msvc": "4.24.3", + "@rollup/rollup-win32-x64-msvc": "4.24.3", "fsevents": "~2.3.2" } }, "node_modules/rrweb-cssom": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "dev": true, "license": "MIT" }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "funding": [ { "type": "github", @@ -11664,6 +11615,9 @@ }, "node_modules/safe-array-concat": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -11680,6 +11634,8 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -11699,6 +11655,9 @@ }, "node_modules/safe-regex-test": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -11714,10 +11673,14 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", "dependencies": { @@ -11728,7 +11691,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -11739,12 +11704,16 @@ }, "node_modules/semver-compare": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, "license": "MIT", "optional": true }, "node_modules/serialize-error": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, "license": "MIT", "optional": true, @@ -11758,30 +11727,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-handler": { - "version": "6.1.5", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", "license": "MIT", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", "mime-types": "2.1.18", "minimatch": "3.1.2", "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", + "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, - "node_modules/serve-handler/node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/serve-handler/node_modules/mime-db": { "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -11789,6 +11767,8 @@ }, "node_modules/serve-handler/node_modules/mime-types": { "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "license": "MIT", "dependencies": { "mime-db": "~1.33.0" @@ -11797,22 +11777,11 @@ "node": ">= 0.6" } }, - "node_modules/serve-handler/node_modules/minimatch": { - "version": "3.1.2", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "license": "ISC" - }, "node_modules/set-function-length": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -11828,6 +11797,9 @@ }, "node_modules/set-function-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -11840,24 +11812,32 @@ } }, "node_modules/shebang-command": { - "version": "1.2.0", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shebang-regex": { - "version": "1.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shell-quote": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true, "license": "MIT", "funding": { @@ -11866,6 +11846,9 @@ }, "node_modules/side-channel": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -11882,15 +11865,21 @@ }, "node_modules/siginfo": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "license": "MIT", "engines": { "node": ">=8" @@ -11898,6 +11887,8 @@ }, "node_modules/slice-ansi": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11913,6 +11904,8 @@ }, "node_modules/slice-ansi/node_modules/ansi-styles": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -11922,19 +11915,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", "engines": { @@ -11942,51 +11926,15 @@ "npm": ">= 3.0.0" } }, - "node_modules/smartwrap": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "array.prototype.flat": "^1.2.3", - "breakword": "^1.0.5", - "grapheme-splitter": "^1.0.4", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1", - "yargs": "^15.1.0" - }, - "bin": { - "smartwrap": "src/terminal-adapter.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/smartwrap/node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/socket.io-client": { - "version": "4.7.5", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", - "engine.io-client": "~6.5.2", + "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" }, "engines": { @@ -11995,6 +11943,8 @@ }, "node_modules/socket.io-parser": { "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -12006,6 +11956,8 @@ }, "node_modules/socks": { "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -12019,6 +11971,8 @@ }, "node_modules/socks-proxy-agent": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "license": "MIT", "dependencies": { @@ -12032,6 +11986,8 @@ }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12043,10 +11999,14 @@ }, "node_modules/sortablejs": { "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==", "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -12054,31 +12014,100 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spawndamnit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-2.0.0.tgz", + "integrity": "sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/spawndamnit/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/spawndamnit/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/spawndamnit/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawndamnit/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/spawndamnit": { - "version": "2.0.0", - "license": "MIT", + "node_modules/spawndamnit/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", "dependencies": { - "cross-spawn": "^5.1.0", - "signal-exit": "^3.0.2" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, + "node_modules/spawndamnit/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "license": "ISC" + }, "node_modules/spdx-correct": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -12087,10 +12116,16 @@ }, "node_modules/spdx-exceptions": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", @@ -12098,15 +12133,23 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.18", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true, "license": "CC0-1.0" }, "node_modules/sprintf-js": { - "version": "1.0.3", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssri": { "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "license": "ISC", "dependencies": { @@ -12116,41 +12159,24 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/ssri/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ssri/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/stackback": { "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", "dev": true, "license": "MIT" }, - "node_modules/stream-transform": { - "version": "2.1.3", - "license": "MIT", - "dependencies": { - "mixme": "^0.5.1" - } - }, "node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", "dependencies": { @@ -12158,20 +12184,28 @@ } }, "node_modules/string-width": { - "version": "4.2.3", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -12183,8 +12217,56 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/string.prototype.padend": { "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -12202,6 +12284,9 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12218,6 +12303,9 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12230,6 +12318,9 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12245,6 +12336,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12256,6 +12349,8 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -12267,6 +12362,8 @@ }, "node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "license": "MIT", "engines": { "node": ">=4" @@ -12274,6 +12371,8 @@ }, "node_modules/strip-eof": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "license": "MIT", "engines": { @@ -12282,6 +12381,8 @@ }, "node_modules/strip-final-newline": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "license": "MIT", "engines": { @@ -12291,18 +12392,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -12314,6 +12407,8 @@ }, "node_modules/strip-literal": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", "dev": true, "license": "MIT", "dependencies": { @@ -12325,11 +12420,15 @@ }, "node_modules/strip-literal/node_modules/js-tokens": { "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", "dev": true, "license": "MIT" }, "node_modules/strip-outer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "license": "MIT", "dependencies": { @@ -12339,16 +12438,27 @@ "node": ">=0.10.0" } }, - "node_modules/stubborn-fs": { - "version": "1.2.5" + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } }, "node_modules/sudo-prompt": { "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", "dev": true, "license": "MIT" }, "node_modules/sumchecker": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -12359,17 +12469,23 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12380,15 +12496,21 @@ }, "node_modules/svg-tags": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "node_modules/symbol-tree": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, "license": "MIT" }, "node_modules/synckit": { - "version": "0.8.8", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "license": "MIT", "dependencies": { @@ -12404,6 +12526,8 @@ }, "node_modules/tar": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "license": "ISC", "dependencies": { @@ -12420,6 +12544,8 @@ }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "license": "ISC", "engines": { @@ -12428,11 +12554,15 @@ }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "license": "ISC" }, "node_modules/temp": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", "optional": true, @@ -12444,50 +12574,10 @@ "node": ">=6.0.0" } }, - "node_modules/temp/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/temp/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/temp/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/temp/node_modules/mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", "optional": true, @@ -12500,6 +12590,9 @@ }, "node_modules/temp/node_modules/rimraf": { "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "optional": true, @@ -12512,6 +12605,8 @@ }, "node_modules/term-size": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "license": "MIT", "engines": { "node": ">=8" @@ -12522,11 +12617,15 @@ }, "node_modules/text-table": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, "license": "MIT" }, "node_modules/three": { - "version": "0.155.0", + "version": "0.159.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.159.0.tgz", + "integrity": "sha512-eCmhlLGbBgucuo4VEA9IO3Qpc7dh8Bd4VKzr7WfW4+8hMcIfoAVi1ev0pJYN9PTTsCslbcKgBwr2wNZ1EvLInA==", "license": "MIT" }, "node_modules/tiny-each-async": { @@ -12534,15 +12633,20 @@ "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/tinybench": { - "version": "2.8.0", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinypool": { "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "license": "MIT", "engines": { @@ -12551,6 +12655,8 @@ }, "node_modules/tinyspy": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, "license": "MIT", "engines": { @@ -12558,17 +12664,20 @@ } }, "node_modules/tmp": { - "version": "0.0.33", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, + "optional": true, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/tmp-promise": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, "license": "MIT", "optional": true, @@ -12576,25 +12685,10 @@ "tmp": "^0.2.0" } }, - "node_modules/tmp-promise/node_modules/tmp": { - "version": "0.2.3", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12605,6 +12699,8 @@ }, "node_modules/toast": { "version": "0.5.4", + "resolved": "https://registry.npmjs.org/toast/-/toast-0.5.4.tgz", + "integrity": "sha512-/0us8QyyJJ3jGKCOfJNYfxiDlABDmQDs8mePVcTEabwHH31KyFw3kS2nMeydTFVcHAhm4bWuNRHwIPPTcF8tfQ==", "license": "ISC", "dependencies": { "@changesets/cli": "^2.24.4", @@ -12621,6 +12717,8 @@ }, "node_modules/tough-cookie": { "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -12635,6 +12733,8 @@ }, "node_modules/tough-cookie/node_modules/universalify": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "license": "MIT", "engines": { @@ -12643,6 +12743,8 @@ }, "node_modules/tr46": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, "license": "MIT", "dependencies": { @@ -12652,119 +12754,53 @@ "node": ">=18" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/trim-repeated": { "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "dev": true, - "license": "0BSD" - }, - "node_modules/tty-table": { - "version": "4.2.3", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "csv": "^5.5.3", - "kleur": "^4.1.5", - "smartwrap": "^2.0.2", - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.1", - "yargs": "^17.7.1" - }, - "bin": { - "tty-table": "adapters/terminal-adapter.js" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/tty-table/node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tty-table/node_modules/chalk": { - "version": "4.1.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/tty-table/node_modules/color-convert": { - "version": "2.0.1", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "escape-string-regexp": "^1.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/tty-table/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/tty-table/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.8.0" } }, - "node_modules/tty-table/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -12775,7 +12811,9 @@ } }, "node_modules/type-detect": { - "version": "4.0.8", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "license": "MIT", "engines": { @@ -12783,7 +12821,10 @@ } }, "node_modules/type-fest": { - "version": "0.13.1", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -12794,6 +12835,9 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12806,6 +12850,9 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12823,6 +12870,9 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -12841,6 +12891,9 @@ }, "node_modules/typed-array-length": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -12859,6 +12912,8 @@ }, "node_modules/typescript": { "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -12870,22 +12925,17 @@ } }, "node_modules/ufo": { - "version": "1.5.3", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true, "license": "MIT" }, - "node_modules/uint8array-extras": { - "version": "1.4.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -12899,11 +12949,15 @@ }, "node_modules/undici-types": { "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, "license": "MIT" }, "node_modules/unique-filename": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "license": "ISC", "dependencies": { @@ -12915,6 +12969,8 @@ }, "node_modules/unique-slug": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "license": "ISC", "dependencies": { @@ -12925,14 +12981,19 @@ } }, "node_modules/universalify": { - "version": "0.1.2", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -12950,8 +13011,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -12962,6 +13023,8 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -12970,6 +13033,8 @@ }, "node_modules/url-parse": { "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12979,6 +13044,8 @@ }, "node_modules/username": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", + "integrity": "sha512-PCKbdWw85JsYMvmCv5GH3kXmM66rCd9m1hBEDutPNv94b/pqCMT4NtcKyeWYvLFiE8b+ha1Jdl8XAaUdPn5QTg==", "dev": true, "license": "MIT", "dependencies": { @@ -12989,91 +13056,18 @@ "node": ">=8" } }, - "node_modules/username/node_modules/cross-spawn": { - "version": "6.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/username/node_modules/execa": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/username/node_modules/get-stream": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/username/node_modules/is-stream": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/username/node_modules/npm-run-path": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/username/node_modules/path-key": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/username/node_modules/semver": { - "version": "5.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", @@ -13082,19 +13076,23 @@ }, "node_modules/vary": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/vite": { - "version": "5.2.12", + "version": "5.4.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", + "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -13113,6 +13111,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -13130,6 +13129,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -13143,6 +13145,8 @@ }, "node_modules/vite-node": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "license": "MIT", "dependencies": { @@ -13164,6 +13168,8 @@ }, "node_modules/vite-node/node_modules/cac": { "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", "engines": { @@ -13172,6 +13178,8 @@ }, "node_modules/vitest": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "license": "MIT", "dependencies": { @@ -13200,49 +13208,172 @@ "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" }, "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/vue": { - "version": "3.4.27", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.4.27", - "@vue/compiler-sfc": "3.4.27", - "@vue/runtime-dom": "3.4.27", - "@vue/server-renderer": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { "typescript": "*" @@ -13254,12 +13385,16 @@ } }, "node_modules/vue-component-type-helpers": { - "version": "2.0.19", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.1.10.tgz", + "integrity": "sha512-lfgdSLQKrUmADiSV6PbBvYgQ33KF3Ztv6gP85MfGaGaSGMTXORVaHT1EHfsqCgzRNBstPKYDmvAV9Do5CmJ07A==", "dev": true, "license": "MIT" }, "node_modules/vue-eslint-parser": { "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, "license": "MIT", "dependencies": { @@ -13282,10 +13417,12 @@ } }, "node_modules/vue-router": { - "version": "4.3.2", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz", + "integrity": "sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==", "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.5.1" + "@vue/devtools-api": "^6.6.4" }, "funding": { "url": "https://github.com/sponsors/posva" @@ -13296,6 +13433,8 @@ }, "node_modules/vue-socket.io-extended": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vue-socket.io-extended/-/vue-socket.io-extended-4.2.0.tgz", + "integrity": "sha512-3ZHy53W4CpzRdTZjWL7krq80FQlnK6IyOG0ur158pij1PXKyVBYDpjE0/3SluK7NeNTqefa/Y8RFCFy6BISG/g==", "license": "MIT", "dependencies": { "@types/socket.io-client": "1.4.36" @@ -13303,6 +13442,8 @@ }, "node_modules/vue-template-compiler": { "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13311,7 +13452,9 @@ } }, "node_modules/vue-toast-notification": { - "version": "3.1.2", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vue-toast-notification/-/vue-toast-notification-3.1.3.tgz", + "integrity": "sha512-XNyWqwLIGBFfX5G9sK+clq3N3IPlhDjzNdbZaXkEElcotPlWs0wWZailk1vqhdtLYT/93Y4FHAVuzyatLmPZRA==", "license": "MIT", "engines": { "node": ">=12.15.0" @@ -13322,6 +13465,8 @@ }, "node_modules/vue-tsc": { "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", "dev": true, "license": "MIT", "dependencies": { @@ -13338,6 +13483,8 @@ }, "node_modules/vue3-toast": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/vue3-toast/-/vue3-toast-0.0.1.tgz", + "integrity": "sha512-/B84E1HnnlsGZA90RtWn8zB7yiBXdbcui8dpeHmI1BtavKpeh/67vzycZuZSZz+Q5FYZFbAwBDSNUCZ9qudjpw==", "license": "MIT", "peerDependencies": { "vue": "^3.0.0-beta.13" @@ -13345,6 +13492,8 @@ }, "node_modules/vuedraggable": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", "license": "MIT", "dependencies": { "sortablejs": "1.14.0" @@ -13355,6 +13504,8 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", "dependencies": { @@ -13366,6 +13517,8 @@ }, "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -13374,6 +13527,9 @@ }, "node_modules/wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, "license": "MIT", "dependencies": { "defaults": "^1.0.3" @@ -13381,6 +13537,8 @@ }, "node_modules/webidl-conversions": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -13389,6 +13547,8 @@ }, "node_modules/whatwg-encoding": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13398,19 +13558,10 @@ "node": ">=18" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", "engines": { @@ -13419,6 +13570,8 @@ }, "node_modules/whatwg-url": { "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", "dev": true, "license": "MIT", "dependencies": { @@ -13429,21 +13582,27 @@ "node": ">=18" } }, - "node_modules/when-exit": { - "version": "2.1.3" - }, "node_modules/which": { - "version": "1.3.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-boxed-primitive": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", @@ -13456,23 +13615,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.1", - "license": "ISC" - }, - "node_modules/which-pm": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "load-yaml-file": "^0.2.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8.15" - } - }, "node_modules/which-typed-array": { "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -13489,7 +13636,9 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -13503,16 +13652,10 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -13520,20 +13663,28 @@ } }, "node_modules/wrap-ansi": { - "version": "6.2.0", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -13548,77 +13699,99 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, "node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" }, "node_modules/ws": { - "version": "8.11.0", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -13631,6 +13804,8 @@ }, "node_modules/xml-name-validator": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -13639,6 +13814,8 @@ }, "node_modules/xmlbuilder": { "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, "license": "MIT", "engines": { @@ -13647,25 +13824,41 @@ }, "node_modules/xmlchars": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, "license": "MIT" }, "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { - "version": "2.1.2", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -13681,86 +13874,51 @@ } }, "node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/yargs/node_modules/cliui": { - "version": "8.0.1", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, "engines": { "node": ">=12" } }, - "node_modules/yargs/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/yargs/node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/yargs/node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/yargs/node_modules/y18n": { - "version": "5.0.8", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "license": "ISC", "engines": { - "node": ">=12" + "node": ">=8" } }, "node_modules/yarn-or-npm": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz", + "integrity": "sha512-fTiQP6WbDAh5QZAVdbMQkecZoahnbOjClTQhzv74WX5h2Uaidj1isf9FDes11TKtsZ0/ZVfZsqZ+O3x6aLERHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13777,6 +13935,8 @@ }, "node_modules/yarn-or-npm/node_modules/cross-spawn": { "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13792,6 +13952,8 @@ }, "node_modules/yarn-or-npm/node_modules/path-key": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -13800,14 +13962,54 @@ }, "node_modules/yarn-or-npm/node_modules/semver": { "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" } }, + "node_modules/yarn-or-npm/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yarn-or-npm/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yarn-or-npm/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/yauzl": { "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { @@ -13817,6 +14019,9 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index bdde17e2..5c03c2ce 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -2,17 +2,21 @@ package main import ( "fmt" + "context" "printeremu/src" ) func main() { - extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "hwid:032uhb3293n2", "Testing Printer 1", "Init") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "EMU032uhb3293n2", "Testing Printer 1", "Init") if err != nil { fmt.Println(err) return } - src.Run(extruder, printer) + src.RunConnection(ctx, extruder, printer) } \ No newline at end of file diff --git a/printeremu/go.mod b/printeremu/go.mod index 559df761..4292ed0a 100644 --- a/printeremu/go.mod +++ b/printeremu/go.mod @@ -4,6 +4,12 @@ go 1.23.2 require ( github.com/creack/goselect v0.1.2 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gomodule/redigo v1.8.9 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f // indirect + github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4 // indirect go.bug.st/serial v1.6.2 // indirect - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/sys v0.1.0 // indirect ) diff --git a/printeremu/go.sum b/printeremu/go.sum index 2f35f6fb..a7587b7a 100644 --- a/printeremu/go.sum +++ b/printeremu/go.sum @@ -1,6 +1,77 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= +github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8= +github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= +github.com/googollee/go-socket.io v1.8.0-rc.1 h1:Y5DV+pKDw2KFBtdEyxBp8mSuuU4XS3eHGNb2E3bLACI= +github.com/googollee/go-socket.io v1.8.0-rc.1/go.mod h1:oZhC7XylbziHxXhVdXvz6qQB0/jmNc4V3d9jgaEBKHI= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f h1:tx1VqrLN1pol7xia95NVBbG09QHmMJjGvn67sR70qDA= +github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f/go.mod h1:9U9sAGG8VWujCrAnepe5aiOeqyEtBoKTcne9l0pztac= +github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4 h1:1/TmoDdySJm4tUorORqfPUjPgZVmF772DZVn5/JBaF8= +github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4/go.mod h1:gqWuIplvY8EL+k2pUZAe/G21MnuGElct4jKx0HaO+UM= go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 5e9bc0a9..095769d0 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -2,20 +2,30 @@ package src import ( "bufio" + "context" + "encoding/json" "fmt" "log" "os" "strings" "time" + + socketio_client "github.com/zhouhui8915/go-socket.io-client" ) +type PrintJob struct { + Command string +} + +var jobQueue = make(chan PrintJob) + // Main function to initialize and run the printer emulator func main() { extruder, printer, err := Init(1, "Device1", "Test Printer", "HWID1234", "Printer1", "Active") if err != nil { log.Fatalf("Failed to initialize printer: %v", err) } - Run(extruder, printer) + RunCommand(extruder, printer) } // Init function initializes the Extruder and Printer @@ -27,7 +37,7 @@ func Init(id int, device string, description string, hwid string, name string, s // Create a new heatbed instance heatbed := &Heatbed{ - Temp: 60.0, // Default initial temperature + Temp: 60.0, // Default initial temperature TargetTemp: 100.0, // Target temperature for the heatbed } @@ -38,8 +48,82 @@ func Init(id int, device string, description string, hwid string, name string, s return extruder, printer, nil } +func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { + opts := &socketio_client.Options{ + Transport: "websocket", + } + + uri := "http://127.0.0.1:8000/" + c, err := socketio_client.NewClient(uri, opts) + if err != nil { + log.Printf("NewClient error: %v\n", err) + return + } + + log.Println("Attempting to connect...") + + if c == nil { + log.Println("Failed to create socket.io client.") + return + } + + c.On("connection", func() { + log.Println("Connected to backend with virtual USB...") + RegisterPrinter(c, printer) + }) + + c.On("disconnection", func() { + log.Println("Disconnected from backend") + }) + + c.On("error", func(err error) { + log.Println("Socket error:", err) + }) + + go ProcessJobs(ctx, printer, c) + + select { + case <-ctx.Done(): + log.Println("Stopping connection...") + } +} + +func RegisterPrinter(c *socketio_client.Client, printer *Printer) { + jsonPrinter, err := json.Marshal(printer) + if err != nil { + log.Println("Failed to marshal printer object:", err) + return + } + + log.Println("Emitting emuprintconnect event with data:", string(jsonPrinter)) + c.Emit("emuprintconnect", jsonPrinter) +} + +func ProcessJobs(ctx context.Context, printer *Printer, c *socketio_client.Client) { + for { + select { + case job := <-jobQueue: + response := CommandHandler(job.Command, printer) + + // todo: handle weird edge case responses + /* if job.Command == "G29" { + + } else if job.Command == "G92" { + + } + */ + if !strings.Contains(response, "Unknown command") { + c.Emit("job_response", "ok\n") + } + case <-ctx.Done(): + log.Println("Stopping job processing...") + return + } + } +} + // Run function for G-code command input and processing -func Run(extruder *Extruder, printer *Printer) { +func RunCommand(extruder *Extruder, printer *Printer) { scanner := bufio.NewScanner(os.Stdin) fmt.Println("Enter G-code commands (type 'exit' or 'quit' to quit):") diff --git a/server/app.py b/server/app.py index 89a9ad2f..da4eb755 100644 --- a/server/app.py +++ b/server/app.py @@ -22,9 +22,6 @@ app = Flask(__name__, static_folder='../client/dist') app.config.from_object(__name__) # update application instantly -# moved this before importing the blueprints so that it can be accessed by the PrinterStatusService -printer_status_service = PrinterStatusService(app) - # Initialize SocketIO, which will be used to send printer status updates to the frontend # and this specific socketit will be used throughout the backend @@ -33,9 +30,12 @@ else: async_mode = 'threading' # Use 'threading' for development -socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode) # make it eventlet on production! +socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode, transport=['websocket', 'polling']) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object +# moved this before importing the blueprints so that it can be accessed by the PrinterStatusService +printer_status_service = PrinterStatusService(app, socketio) + # IMPORTING BLUEPRINTS from controllers.ports import ports_bp from controllers.jobs import jobs_bp @@ -83,6 +83,17 @@ def serve_assets(filename): @app.socketio.on('ping') def handle_ping(): app.socketio.emit('pong') + +@app.socketio.on('connect') +def handle_connect(): + print("Client connected") + +@app.socketio.on('emuprintconnect') +def handle_emuprintconnect(data): + printer_data = json.loads(data) + + print("Received emuprintconnect event with data:", printer_data) + printer_status_service.add_printer(data) # own thread with app.app_context(): diff --git a/server/models/PrinterStatusService.py b/server/models/PrinterStatusService.py index 600919d7..52af857a 100644 --- a/server/models/PrinterStatusService.py +++ b/server/models/PrinterStatusService.py @@ -14,8 +14,9 @@ def __init__(self, printer, *args, **kwargs): class PrinterStatusService: # in order to access the app context, we need to pass the app to the PrinterStatusService, mainly for the websockets - def __init__(self, app): + def __init__(self, app, socketio): self.app = app + self.socketio = socketio self.printer_threads = [] # array of printer threads def start_printer_thread(self, printer): @@ -230,4 +231,10 @@ def movePrinterList(self, printer_ids): break self.printer_threads = new_thread_list return jsonify({"success": True, "message": "Printer list reordered successfully"}) + + def add_printer(self, printer_data): + printer = Printer(**printer_data) + # add the printer to the list of printer threads + print("Adding printer:", printer) + self.printer_threads.append(PrinterThread(printer, target=self.update_thread, args=(printer, self.app))) diff --git a/server/models/printers.py b/server/models/printers.py index ecfdbcd8..00e26fbb 100644 --- a/server/models/printers.py +++ b/server/models/printers.py @@ -15,6 +15,7 @@ import json import requests from dotenv import load_dotenv +import socketio from models.config import Config @@ -44,6 +45,7 @@ class Printer(db.Model): prevMes = "" colorbuff = 0 terminated = 0 + emulated = False def __init__(self, device, description, hwid, name, status=status, id=None): self.device = device @@ -66,6 +68,9 @@ def __init__(self, device, description, hwid, name, status=status, id=None): if id is not None: self.id = id self.responseCount = 0 + + if hwid.beginsWith("EMU"): + self.emulated = True # general classes @classmethod @@ -324,6 +329,10 @@ def moveHead(cls, device): return def connect(self): + if (self.emulated): + self.socketio.emit("print", {"command": "M114"}) + return "ok" + try: self.ser = serial.Serial(self.device, 115200, timeout=10) self.ser.write(f"M155 S5\n".encode("utf-8")) From dc959f3677f007aa66a400327e9505947348cccc Mon Sep 17 00:00:00 2001 From: iron768 Date: Fri, 1 Nov 2024 23:44:52 -0400 Subject: [PATCH 037/194] fix: deadlock issues with go socketio client --- printeremu/cmd/test_printer.go | 29 ++++- printeremu/go.mod | 12 +- printeremu/go.sum | 75 ------------- printeremu/src/emulator.go | 70 ++++++------ printeremu/src/socketio_client.go | 180 ++++++++++++++++++++++++++++++ 5 files changed, 240 insertions(+), 126 deletions(-) create mode 100644 printeremu/src/socketio_client.go diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 5c03c2ce..cd4c5380 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -1,22 +1,39 @@ package main import ( - "fmt" "context" + "fmt" + "log" + "os" + "os/signal" + "syscall" "printeremu/src" ) func main() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "EMU032uhb3293n2", "Testing Printer 1", "Init") if err != nil { - fmt.Println(err) + fmt.Println("Error initializing printer:", err) return } - src.RunConnection(ctx, extruder, printer) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-signalChan + cancel() + }() + + go func() { + src.RunConnection(ctx, extruder, printer) + }() + + <-ctx.Done() + log.Println("Shutting down...") } \ No newline at end of file diff --git a/printeremu/go.mod b/printeremu/go.mod index 4292ed0a..bf706a19 100644 --- a/printeremu/go.mod +++ b/printeremu/go.mod @@ -2,14 +2,4 @@ module printeremu go 1.23.2 -require ( - github.com/creack/goselect v0.1.2 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect - github.com/gomodule/redigo v1.8.9 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f // indirect - github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4 // indirect - go.bug.st/serial v1.6.2 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/sys v0.1.0 // indirect -) +require github.com/gorilla/websocket v1.5.0 diff --git a/printeremu/go.sum b/printeremu/go.sum index a7587b7a..e5a03d4d 100644 --- a/printeremu/go.sum +++ b/printeremu/go.sum @@ -1,77 +1,2 @@ -github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= -github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= -github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= -github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8= -github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= -github.com/googollee/go-socket.io v1.8.0-rc.1 h1:Y5DV+pKDw2KFBtdEyxBp8mSuuU4XS3eHGNb2E3bLACI= -github.com/googollee/go-socket.io v1.8.0-rc.1/go.mod h1:oZhC7XylbziHxXhVdXvz6qQB0/jmNc4V3d9jgaEBKHI= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f h1:tx1VqrLN1pol7xia95NVBbG09QHmMJjGvn67sR70qDA= -github.com/zhouhui8915/engine.io-go v0.0.0-20150910083302-02ea08f0971f/go.mod h1:9U9sAGG8VWujCrAnepe5aiOeqyEtBoKTcne9l0pztac= -github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4 h1:1/TmoDdySJm4tUorORqfPUjPgZVmF772DZVn5/JBaF8= -github.com/zhouhui8915/go-socket.io-client v0.0.0-20200925034401-83ee73793ba4/go.mod h1:gqWuIplvY8EL+k2pUZAe/G21MnuGElct4jKx0HaO+UM= -go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8= -go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 095769d0..f03e593a 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -9,8 +9,6 @@ import ( "os" "strings" "time" - - socketio_client "github.com/zhouhui8915/go-socket.io-client" ) type PrintJob struct { @@ -49,47 +47,42 @@ func Init(id int, device string, description string, hwid string, name string, s } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { - opts := &socketio_client.Options{ - Transport: "websocket", - } + client := NewClient() + defer client.Close() - uri := "http://127.0.0.1:8000/" - c, err := socketio_client.NewClient(uri, opts) - if err != nil { - log.Printf("NewClient error: %v\n", err) - return - } - - log.Println("Attempting to connect...") - - if c == nil { - log.Println("Failed to create socket.io client.") - return - } + client.On("connection", func(data interface{}) { + fmt.Println("Connected to backend!") - c.On("connection", func() { - log.Println("Connected to backend with virtual USB...") - RegisterPrinter(c, printer) + RegisterPrinter(client, printer) }) - c.On("disconnection", func() { + client.On("disconnect", func(data interface{}) { log.Println("Disconnected from backend") + close(jobQueue) }) - - c.On("error", func(err error) { - log.Println("Socket error:", err) + + client.On("error", func(data interface{}) { + if err, ok := data.(error); ok { + log.Println("Socket error:", err) + } }) - go ProcessJobs(ctx, printer, c) + log.Println("Attempting to connect...") + err := client.Connect("http://127.0.0.1:8000") + if err != nil { + log.Printf("Connection error: %v\n", err) + return + } - select { - case <-ctx.Done(): - log.Println("Stopping connection...") - } + go ProcessJobs(ctx, printer, client) + + <-ctx.Done() + log.Println("Connection stopping...") } -func RegisterPrinter(c *socketio_client.Client, printer *Printer) { +func RegisterPrinter(c *SocketIOClient, printer *Printer) { jsonPrinter, err := json.Marshal(printer) + if err != nil { log.Println("Failed to marshal printer object:", err) return @@ -99,11 +92,17 @@ func RegisterPrinter(c *socketio_client.Client, printer *Printer) { c.Emit("emuprintconnect", jsonPrinter) } -func ProcessJobs(ctx context.Context, printer *Printer, c *socketio_client.Client) { +func ProcessJobs(ctx context.Context, printer *Printer, c *SocketIOClient) { for { select { - case job := <-jobQueue: + case job, ok := <-jobQueue: + if !ok { + log.Println("Job queue closed, exiting ProcessJobs.") + return + } + response := CommandHandler(job.Command, printer) + log.Printf("Processing job: %s, response: %s\n", job.Command, response) // todo: handle weird edge case responses /* if job.Command == "G29" { @@ -115,8 +114,11 @@ func ProcessJobs(ctx context.Context, printer *Printer, c *socketio_client.Clien if !strings.Contains(response, "Unknown command") { c.Emit("job_response", "ok\n") } + case <-c.close: + log.Println("Client connection closed.") + return case <-ctx.Done(): - log.Println("Stopping job processing...") + log.Println("Context done, exiting ProcessJobs.") return } } diff --git a/printeremu/src/socketio_client.go b/printeremu/src/socketio_client.go new file mode 100644 index 00000000..5257cf78 --- /dev/null +++ b/printeremu/src/socketio_client.go @@ -0,0 +1,180 @@ +package src + +import ( + "encoding/json" + "log" + "net/url" + "time" + + "sync" + + "github.com/gorilla/websocket" +) + +type SocketIOMessage struct { + Type int `json:"type"` + Event string `json:"event,omitempty"` + Data interface{} `json:"data,omitempty"` + Nsp string `json:"nsp,omitempty"` +} + +type SocketIOClient struct { + conn *websocket.Conn + send chan []byte + close chan struct{} + handlers map[string]func(interface{}) + wg sync.WaitGroup +} + +func NewClient() *SocketIOClient { + return &SocketIOClient{ + send: make(chan []byte), + handlers: make(map[string]func(interface{})), + close: make(chan struct{}), + } +} + +func (c *SocketIOClient) Connect(uri string) error { + c.handlers = make(map[string]func(interface{})) + + u, err := url.Parse(uri) + if err != nil { + return err + } + + switch u.Scheme { + case "http", "ws": + u.Scheme = "ws" + case "https", "wss": + u.Scheme = "wss" + } + + u.Path = "/socket.io/" + u.RawQuery = "EIO=4&transport=websocket" + + conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + + if err != nil { + return err + } + + c.conn = conn + c.wg.Add(2) + + go c.readPump() + go c.StartWritePump() + + c.send <- []byte("40") + + return nil +} + +func (c *SocketIOClient) readPump() { + defer func() { + close(c.close) + c.wg.Done() + c.conn.Close() + }() + + for { + _, message, err := c.conn.ReadMessage() + + if err != nil { + log.Printf("read error: %v", err) + return + } + + if len(message) > 0 { + switch message[0] { + case '0': // connection + log.Println("Connnected to server") + case '1': // keepalive? + c.send <- []byte("3") + case '4': // message + if len(message) > 1 { + c.handleMessage(message[1:]) + } + } + } + } +} + +func (c *SocketIOClient) StartWritePump() { + defer func() { + c.wg.Done() + c.conn.Close() + }() + + + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case message := <-c.send: + err := c.conn.WriteMessage(websocket.TextMessage, message) + if err != nil { + log.Println("write error:", err) + return + } + case <-ticker.C: + err := c.conn.WriteMessage(websocket.TextMessage, []byte("2")) + if err != nil { + log.Println("ping error:", err) + return + } + case <-c.close: + return + } + } +} + + +func (c *SocketIOClient) On(event string, handler func(interface{})) { + c.handlers[event] = handler +} + +func (c *SocketIOClient) Emit(event string, data interface{}) error { + msg := SocketIOMessage{ + Type: 4, + Event: event, + Data: data, + Nsp: "/", + } + + payload, err := json.Marshal(msg) + + if err != nil { + log.Println("Failed to marshal message:", err) + return err + } + + c.send <- payload + + return nil +} + +func (c *SocketIOClient) handleMessage(message []byte) { + if len(message) > 1 && message[0] == '0' { + message = message[1:] + } + + var msg SocketIOMessage + + if err := json.Unmarshal(message, &msg); err != nil { + log.Println("Failed to unmarshal message:", err) + //log.Println("Raw message:", string(message)) + return + } + + if handler, ok := c.handlers[msg.Event]; ok { + handler(msg.Data) + } else { + //log.Println("No handler registered for event:", msg.Event) + } +} + +func (c *SocketIOClient) Close() { + close(c.close) + c.wg.Wait() +} \ No newline at end of file From 574cb0b027dacb2f9fa45db65504309f5a7fd2f0 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Tue, 5 Nov 2024 17:48:43 -0500 Subject: [PATCH 038/194] Logging completed for tests. now i need to integrate it into the normal run of the system --- Tests/conftest.py | 177 + Tests/parallel_test_runner.py | 26 +- pytest.ini | 2 - requirements.txt | 13 +- server/Classes/Fabricators/Device.py | 14 + server/Classes/Fabricators/Fabricator.py | 120 +- .../Fabricators/Printers/Prusa/PrusaMK4.py | 1 + server/Classes/Jobs.py | 646 + server/Classes/Logger.py | 110 + server/Mixins/usesMarlinGcode.py | 76 +- server/xyz-cali-cube-mini_ENDER3.gcode | 11140 +++++++++++++--- 11 files changed, 10091 insertions(+), 2234 deletions(-) create mode 100644 Tests/conftest.py delete mode 100644 pytest.ini create mode 100644 server/Classes/Jobs.py create mode 100644 server/Classes/Logger.py diff --git a/Tests/conftest.py b/Tests/conftest.py new file mode 100644 index 00000000..5c84aa41 --- /dev/null +++ b/Tests/conftest.py @@ -0,0 +1,177 @@ +import math +import os +import platform +import re +import sys +import time +import pluggy +import pytest + +def pytest_addoption(parser): + parser.addoption( + "--myVerbose", + action="store", + default=1, + help="my verbose level" + ) + +def line_separator(interrupter: str, symbol: str = "-", length: int = 80) -> str: + if not interrupter: + return symbol * length + interrupterNoColor = re.sub(r'\033\[[0-9;]*m', '', interrupter) + side = (length - 2 - len(interrupterNoColor)) / 2 + return symbol * math.ceil(side) + " " + interrupter + " " + symbol * math.floor(side) + +def setup_logger(port): + # set up fie location for output logs + log_folder = "logs" + os.makedirs(log_folder, exist_ok=True) + from datetime import datetime + timestamp = datetime.now().strftime("%m-%d-%Y__%H-%M-%S") + subfolder = os.path.join(log_folder, timestamp) + os.makedirs(subfolder, exist_ok=True) + log_file_path = os.path.join(subfolder, f"test_{port}.log") + + from Classes.Logger import Logger + return Logger(port, "Test Printer", consoleLogger=sys.stdout, fileLogger=log_file_path) + +@pytest.hookimpl(tryfirst=True) +def pytest_sessionstart(session) -> None: + session.config.port = os.getenv("PORT") + session.config.start_time = time.time() + session.config.passed_count = 0 + session.config.failed_count = 0 + session.config.skipped_count = 0 + session.config.failNames = [] + session.config.fails = {} + session.config.logger = setup_logger(session.config.port) + + logger = session.config.logger + logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") + verinfo = platform.python_version() + msg = f"platform {sys.platform} -- Python {verinfo}" + pypy_version_info = getattr(sys, "pypy_version_info", None) + if pypy_version_info: + verinfo = ".".join(map(str, pypy_version_info[:3])) + msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" + msg += f", pytest-{pytest.__version__}, pluggy-{pluggy.__version__}" + logger.logMessageOnly(msg) + logger.logMessageOnly(f"rootdir: {session.config.rootdir}") + logger.logMessageOnly(f"verbosity: {session.config.verbosity}") + + + session.config.logger = logger + +def pytest_sessionfinish(session, exitstatus) -> None: + session_duration = time.time() - session.config.start_time + passes = session.config.passed_count + fails = session.config.failed_count + skips = session.config.skipped_count + logger = session.config.logger + + summary = f"" + if passes == 0 and fails == 0 and skips == 0: + pass + elif passes > 0 and fails == 0 and skips == 0: + summary += f"\033[32m\033[1m{passes} passed\033[0m" + elif passes == 0 and fails > 0 and skips == 0: + summary += f"\033[31m\033[1m{fails} failed\033[0m" + elif passes == 0 and fails == 0 and skips > 0: + summary += f"\033[33m{skips} skipped\033[0m" + elif passes > 0 and fails > 0 and skips == 0: + summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[31m\033[1m{fails} failed\033[0m" + elif passes > 0 and fails == 0 and skips > 0: + summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[33m{skips} skipped\033[0m" + elif passes == 0 and fails > 0 and skips > 0: + summary += f"\033[31m\033[1m{fails} failed,\033[0m \033[33m{skips} skipped\033[0m" + elif passes > 0 and fails > 0 and skips > 0: + summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[31m\033[1m{fails} failed,\033[0m \033[33m{skips} skipped\033[0m" + summary += f"\033[32m in {session_duration:.2f}s" + if session_duration > 3600: + summary += f" ({session_duration // 3600:.0f}:{session_duration % 3600 // 60:.0f}:{session_duration % 60:.2f})" + elif session_duration > 60: + summary += f" ({session_duration // 60:.0f}:{session_duration % 60:.2f})" + + if session.config.failed_count > 0: + headerText = "\n" + line_separator("FAILURES", symbol="=") + logger.logMessageOnly(headerText, logLevel=logger.ERROR) + for failTest in session.config.failNames: + logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) + #todo: break this out into something that can be called anywhere for the Logger class + args = list(session.config.fails[failTest].reprtraceback.reprentries[0].reprfuncargs.args)[0] + logger.logMessageOnly(f"{args[0]} = {args[1]}\n") + for line in list(session.config.fails[failTest].reprtraceback.reprentries[0].lines): + if line.startswith("E") or line.startswith(">"): + logger.logMessageOnly(line.__str__(), logLevel=logger.ERROR) + else: + logger.logMessageOnly(line.__str__()) + loc = session.config.fails[failTest].reprtraceback.reprentries[0].reprfileloc + logger.logMessageOnly("\n" + loc.path + ":" + loc.lineno.__str__() + ": " + loc.message, logLevel=logger.ERROR) + + logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) + +def pytest_configure(config): + port = os.getenv("PORT") + config.port = port + cli_args = config.invocation_params.args + for arg in cli_args: + if arg.startswith("--myVerbose="): + config.verbosity = int(arg.split("=")[1]) + break + if not hasattr(config, "verbosity"): + config.verbosity = 1 + if config.verbosity > 2: + config.verbosity = 2 + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() # Retrieve the TestReport object + # Only check the outcome after the "call" phase (i.e., after the test ran) + if (report.when == "setup" and report.skipped) or (report.when == "call"): + if report.passed: + item.config.passed_count += 1 + elif report.failed: + item.config.failed_count += 1 + failName = report.nodeid.split("::")[1] + "." + item.name + item.config.failNames.append(failName) + item.config.fails[failName] = report.longrepr + elif report.skipped: + item.config.skipped_count += 1 + report.port = item.config.port + report.verbosity = item.config.verbosity + report.logger = item.config.logger + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_logreport(report): + verbosity = report.verbosity + yield + logger = report.logger + if (report.when == "setup" and report.skipped) or (report.when == "call"): + port = report.port + if port is None and "test_runner.py::TestFabricator::" in report.nodeid: + # Retrieve port from the test function if it's set as an attribute + from test_runner import fabricator_setup + port = fabricator_setup(os.getenv("PORT")).devicePort + if verbosity <= 0: + if report.passed: + logger.info(f"\033[32m.\033[0m", end="") + elif report.failed: + logger.info(f"\033[31mF\033[0m", end="") + elif report.skipped: + logger.info(f"\033[33ms\033[0m", end="") + elif verbosity == 1: + loc = report.nodeid.split("::")[-1] + if report.passed: + logger.info(f"{loc}[{port}] \033[32mPASSED\033[0m") + elif report.failed: + logger.info(f"{loc}[{port}] \033[31mFAILED\033[0m") + elif report.skipped: + logger.info(f"{loc}[{port}] \033[33mSKIPPED\033[0m") + elif verbosity >= 2: + if report.passed: + logger.info(f"{report.nodeid}[{port}] \033[32mPASSED\033[0m") + elif report.failed: + logger.info(f"{report.nodeid}[{port}] \033[31mFAILED\033[0m:\n\n {report.longrepr}") + elif report.skipped: + logger.info(f"{report.nodeid}[{port}] \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") \ No newline at end of file diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 4a589efc..a423e4e3 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -4,7 +4,7 @@ import threading import platform -from Classes.Ports import Ports +from server.Classes.Ports import Ports red = '\033[31m' green = '\033[32m' @@ -40,30 +40,14 @@ def printColor(color, message): print(color + message + reset) - # Function to run pytest for a specific port def run_tests_for_port(port): env = os.environ.copy() + verbosity = 1 env["PORT"] = port - log_folder = "logs" - os.makedirs(log_folder, exist_ok=True) - - from datetime import datetime - timestamp = datetime.now().strftime("%m-%d-%Y_%H-%M-%S") - subfolder = os.path.join(log_folder, timestamp) - os.makedirs(subfolder, exist_ok=True) - - log_file_path = os.path.join(subfolder, f"test_{port}.log") - print(f"Running tests for {port}") - with open(log_file_path, "w") as log_file: - process = subprocess.Popen(["pytest", "test_runner.py", "-s"], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in process.stdout: - log_file.write(line.decode().rstrip('\n')) - print(line.decode(), end='') - for line in process.stderr: - log_file.write(line.decode().rstrip('\n')) - print(line.decode(), end='') - printColor(green,f"Tests for {port} completed. Log file: {log_file_path}") + showAnything = False + verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" + subprocess.Popen(["pytest", "test_runner.py", verbosityCommand, f"--myVerbose={verbosity}"], env=env).wait() # Create and start a thread for each port threads = [] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index c9bdce5d..00000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = --color=yes -s -vv \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7607bc2e..5c0c93d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,16 @@ +alembic~=1.13.2 Flask==3.0.3 -flask_cors==4.0.1 -flask_migrate==4.0.7 -flask_socketio==5.3.6 -flask_sqlalchemy==3.1.1 +Flask-Cors~=4.0.1 +Flask-Migrate~=4.0.7 +Flask-SocketIO~=5.3.6 +Flask_sqlalchemy==3.1.1 +pluggy~=1.5.0 pyserial==3.5 pytest==8.3.3 pytest-order==1.3.0 -pytest-xdist==3.6.1 python-dotenv==1.0.1 Requests==2.32.3 -SQLAlchemy +SQLAlchemy~=2.0.34 tzlocal==2.1 Werkzeug==3.0.3 eventlet==0.37.0 diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 8bc26eda..9d82ea24 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -8,6 +8,9 @@ import serial import serial.tools.list_ports +from Mixins.canPause import canPause + + class Device(ABC): # static variables MODEL: str | None = None @@ -19,6 +22,8 @@ class Device(ABC): serialConnection: serial.Serial | None = None serialPort: ListPortInfo | SysFS | None = None homePosition: Vector3 | None = None + status: str = "idle" + verdict: str = "" def __init__(self, serialPort: ListPortInfo | SysFS): self.serialPort = serialPort @@ -52,6 +57,12 @@ def goTo(self, loc: Vector3, isVerbose: bool = False): def parseGcode(self, file, isVerbose=False): pass + def pause(self: canPause): + pass + + def resume(self: canPause): + pass + @abstractmethod def sendGcode(self, gcode: Buffer, isVerbose: bool = False): pass @@ -64,6 +75,9 @@ def repair(self): pass def hardReset(self, newStatus: str): + if self.serialConnection.is_open: + self.serialConnection.close() + pass def getModel(self): diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index e7e832e5d..31d293fc 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,5 +1,5 @@ import serial -from flask import current_app, jsonify, Response +from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError @@ -18,8 +18,8 @@ from Classes.Queue import Queue from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasStartupSequence import hasStartupSequence -#from models.jobs import Job +#from Mixins.hasStartupSequence import hasStartupSequence +from Classes.Jobs import Job from models.db import db from datetime import datetime, timezone @@ -41,10 +41,7 @@ def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = F assert isinstance(name, str) self.device: Device = Fabricator.createDevice(port) - - self.verdict: str = "" - self.prevMsg: str = "" - #self.job: Job | None = None + self.job: Job | None = None self.queue: Queue = Queue() self.status: str = "idle" @@ -52,13 +49,14 @@ def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = F self.description = self.device.getDescription() self.hwid = self.device.getHWID() self.devicePort = self.device.getSerialPort().device - self.date = datetime.now(timezone.utc).astimezone() self.device.connect() if addToDB: db.session.add(self) db.session.commit() + def __repr__(self): + return f"Fabricator: {self.name}, {self.description}, {self.hwid}, {self.devicePort}, {self.status}" @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: """returns the model of the printer based on the response to M997""" @@ -116,91 +114,99 @@ def addToDB(self): db.session.commit() def begin(self): + """starts the fabrication process""" assert self.status == "idle" assert self.queue is not None assert len(self.queue) > 0 - self.job = self.queue.getJob(self.id) - - # TODO: test if the file has been sliced for the device of this fabricator - # if issubclass(self.device, PrusaPrinter): - # self.device.sendGcode(f"M862.3 P {self.device.getDescription(self.device).split(' ')[1]}\n".encode("utf-8"), lambda x: x == b'ok\n') + self.job = self.queue.getNext() - if isinstance(self.device, hasStartupSequence): - self.device.startupSequence() + # if isinstance(self.device, hasStartupSequence): + # self.device.startupSequence() - self.job = self.queue.getJob(self.id) if self.job is None: - self.setStatus("idle") - - self.setStatus("printing") - self.device.parseGcode(self.job.file) # this is the actual command to read the file and fabricate. - self.handleVerdict(self.verdict, self.job) - + return # TODO: return error message + assert self.setStatus("printing"), "Failed to set status to printing" + assert self.device.parseGcode(self.job.file_name_original), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. + self.handleVerdict() def pause(self): + """pauses the fabrication process if the fabricator supports it""" if not isinstance(self.device, canPause): return # TODO: return error message - self.device.pause() + if self.status != "printing": + return # TODO: return error message self.setStatus("paused") - #self.job.setStatus("paused") + assert isinstance(self.device, Device) + return self.status == self.device.status == "paused" def resume(self): + """resumes the fabrication process if the fabricator supports it""" if not isinstance(self.device, canPause): return #TODO: return error message - self.device.resume() + if self.status != "paused": + return #TODO: return error message self.setStatus("printing") - #self.job.setStatus("printing") + assert isinstance(self.device, Device) + return self.status == self.device.status == "printing" def cancel(self): - # TODO: implement cancel print - pass + """cancels the fabrication process""" + if self.status != "printing" and self.status != "paused": + print("status:", self.status) + return #TODO: return error message + self.setStatus("cancelled") + assert isinstance(self.device, Device) + return self.status == self.device.status == "cancelled" def getStatus(self): return self.status def setStatus(self, newStatus): try: - print("SETTING STATUS TO:", newStatus) if self.status == "error" and newStatus!= "error": self.device.hardReset(newStatus) - else: - self.status = newStatus + self.status = newStatus + self.device.status = newStatus + if self.job is not None: + self.job.status = newStatus - current_app.socketio.emit( - "status_update", {"printer_id": self.id, "status": newStatus} - ) + + # current_app.socketio.emit( + # "status_update", {"fabricator_id": self.dbID, "status": newStatus} + # ) + return True except Exception as e: print("Error setting status:", e) + return False + def resetToIdle(self): + #TODO: send message to front end insuring that the print bed is clear and that the job is done + self.setStatus("idle") - def handleVerdict(self, verdict: str, job): - if verdict == "complete": - self.device.disconnect() - #self.sendStatusToJob(job, job.id, "complete") + def handleVerdict(self): + assert self.device.verdict in ["complete", "error", "cancelled", "misprint"], f"Invalid verdict: {self.device.verdict}" + assert self.job is not None + if self.device.verdict == "complete": + #self.device.disconnect() self.setStatus("complete") - #self.job.setStatus("complete") self.queue.removeJob() - elif verdict == "error": - self.device.disconnect() - #self.queue.deleteJob(job.id, self.id) + self.job = None + # todo: reset to idle + elif self.device.verdict == "error": + #self.device.disconnect() self.setStatus("error") - #self.sendStatusToJob(job, job.id, "error") - # self.setError("Error") - elif verdict == "cancelled": - if isinstance(self.device, hasEndingSequence): - self.device.endSequence() - else: - self.device.home() - #self.sendStatusToJob(job, job.id, "cancelled") + elif self.device.verdict == "cancelled": + if isinstance(self.device, hasEndingSequence): self.device.endSequence() + else: self.device.home() + #self.device.disconnect() self.setStatus("cancelled") - #self.job.setStatus("cancelled") - self.device.disconnect() - elif verdict== "misprint": + self.queue.removeJob() + self.job = None + #todo: reset to idle + elif self.device.verdict== "misprint": self.setStatus("misprint") - #self.job.setStatus("misprint") - #self.sendStatusToJob(job, job.id, "cancelled") def getName(self): return self.name @@ -210,10 +216,10 @@ def setName(self, name: str) -> Response: Fabricator.query.filter_by(hwid=self.hwid).first().name = name self.name = name db.session.commit() - return jsonify({"success": True, "message": "Printer name successfully updated.", "code": 200}) + return jsonify({"success": True, "message": "Fabricator name successfully updated.", "code": 200}) except SQLAlchemyError as e: print(f"Database error: {e}") - return jsonify({"error": "Failed to update printer name. Database error", "code": 500}) + return jsonify({"error": "Failed to update fabricator name. Database error", "code": 500}) def getHwid(self): return self.hwid diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index 83a7c5ea..7f2f99f5 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -13,6 +13,7 @@ def endSequence(self): self.sendGcode(b"M104 S0\n") # ; turn off temperature self.sendGcode(b"M140 S0\n") # ; turn off heatbed self.sendGcode(b"M107\n") # ; turn off fan + self.sendGcode(b"G1 X241 Y170 F3600") def getPrintTime(self): pass diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py new file mode 100644 index 00000000..f54c74a4 --- /dev/null +++ b/server/Classes/Jobs.py @@ -0,0 +1,646 @@ +from operator import or_ +import os +import re +from models.db import db + +from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory +from datetime import timezone, timedelta +from flask import jsonify, current_app +from sqlalchemy.exc import SQLAlchemyError +from datetime import datetime +from tzlocal import get_localzone +import gzip +import csv +from flask import send_file +# from app import printer_status_service +# model for job history table + +class Job(db.Model): + id = db.Column(db.Integer, primary_key=True) + file = db.Column(db.LargeBinary(16777215), nullable=True) + name = db.Column(db.String(50), nullable=False) + status = db.Column(db.String(50), nullable=False) + date = db.Column(db.DateTime, default=lambda: datetime.now( + timezone.utc).astimezone(), nullable=False) + # foreign key relationship to match jobs to the printer printed on + fabricator_id = db.Column(db.Integer, db.ForeignKey('fabricator.dbID'), nullable=True) + + fabricator = db.relationship('Fabricator', backref='Job') + + fabricator_name = db.Column(db.String(50), nullable=True) + + # TeamDynamics ID + td_id = db.Column(db.Integer, nullable=True) + + # FK to issue + error_id = db.Column(db.Integer, db.ForeignKey('issue.id'), nullable=True) + error = db.relationship('Issue', backref='Issue') + + # comments + comments = db.Column(db.String(500), nullable=True) + + file_name_original = db.Column(db.String(50), nullable=False) + favorite = db.Column(db.Boolean, nullable=False) + file_name_pk = None + max_layer_height = 0.0 + current_layer_height = 0.0 + filament = '' + released = 0 + filePause = 0 + progress = 0.0 + sent_lines = 0 + time_started = 0 + extruded = 0 + # total, eta, timestart, pause time + job_time = [0, datetime.min, datetime.min, datetime.min] + + def __init__(self, file, name, fabricator_id, status, file_name_original, favorite, td_id, fabricator_name): + self.path = None + self.file = file + self.name = name + self.fabricator_id = fabricator_id + self.status = status + self.file_name_original = file_name_original # original file name without PK identifier + self.td_id = td_id + self.file_name_pk = None + self.favorite = favorite + self.released = 0 + self.filePause = 0 + self.progress = 0.0 + self.sent_lines = 0 + self.time_started = 0 + self.extruded = 0 + self.job_time = [0, datetime.min, datetime.min, datetime.min] + self.error_id = 0 + self.fabricator_name = fabricator_name + self.max_layer_height = 0.0 + self.current_layer_height = 0.0 + self.filament = '' + + def __repr__(self): + return f"Job(id={self.id}, name={self.name}, printer_id={self.fabricator_id}, status={self.status})" + + def getPrinterId(self): + return self.fabricator_id + + @classmethod + def get_job_history( + cls, + page, + pageSize, + printerIds=None, + oldestFirst=False, + searchJob="", + searchCriteria="", + searchTicketId=None, + favoriteOnly=False, + issueIds=None, + startDate=None, + endDate=None, + fromError=None, + countOnly=None + ): + try: + query = cls.query + + print("fromError: ", fromError) + if (fromError == 1): + print("here") + query = cls.query.filter_by(status="error") + + if printerIds: + query = query.filter(cls.fabricator_id.in_(printerIds)) + + if issueIds: + query = query.filter(cls.error_id.in_(issueIds)) + + if searchJob: + searchJob = f"%{searchJob}%" + query = query.filter( + or_( + cls.name.ilike(searchJob), + cls.file_name_original.ilike(searchJob), + ) + ) + + if "searchByJobName" in searchCriteria: + searchByJobName = f"%{searchJob}%" + query = query.filter(cls.name.ilike(searchByJobName)) + elif "searchByFileName" in searchCriteria: + searchByFileName = f"%{searchJob}%" + query = query.filter(cls.file_name_original.ilike(searchByFileName)) + + if searchTicketId: + searchTicketId = int(searchTicketId) + query = query.filter(cls.td_id == searchTicketId) + + if favoriteOnly: + query = query.filter(cls.favorite == True) + + if oldestFirst: + query = query.order_by(cls.date.asc()) + else: + query = query.order_by(cls.date.desc()) # Change this line + + if startDate != '' or endDate != '': + if (endDate == ''): + cls.date.between( + datetime.fromisoformat(startDate), + datetime.fromisoformat(startDate), + ) + + query = query.filter( + cls.date.between( + datetime.fromisoformat(startDate), + datetime.fromisoformat(endDate), + ) + ) + + pagination = query.paginate(page=page, per_page=pageSize, error_out=False) + jobs = pagination.items + + jobs_data = [ + { + "id": job.id, + "name": job.name, + "status": job.status, + "date": job.date.strftime("%a, %d %b %Y %H:%M:%S"), + "printerid": job.fabricator_id, + "errorid": job.error_id, + "file_name_original": job.file_name_original, + "comments": job.comments, + "td_id": job.td_id, + "printer": job.printer.name if job.printer else "None", + "error": job.error.issue if job.error else 'None', + "printer_name": job.printer_name + } + for job in jobs + ] + if countOnly == 0: + return jobs_data, pagination.total + else: + return pagination.total + + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to retrieve jobs. Database error"}), 500 + + @classmethod + def jobHistoryInsert(cls, name, fabricator_id, status, file, file_name_original, favorite, td_id): + try: + if isinstance(file, bytes): + file_data = file + else: + file.seek(0) + file_data = file.read() + + try: + gzip.decompress(file_data) + # If it decompresses successfully, it's already compressed + compressed_data = file_data + except OSError: + compressed_data = gzip.compress(file_data) + from Classes.Fabricators.Fabricator import Fabricator + fabricator = Fabricator.query.get(fabricator_id) + + job = cls( + file=compressed_data, + name=name, + fabricator_id=fabricator_id, + status=status, + file_name_original = file_name_original, + favorite = favorite, + td_id = td_id, + fabricator_name = fabricator.name + ) + + db.session.add(job) + db.session.commit() + + return {"success": True, "message": "Job added to collection.", "id": job.id} + except SQLAlchemyError as e: + print(f"Database error: {e}") + return ( + jsonify({"error": "Failed to add job. Database error"}), + 500, + ) + + @classmethod + def update_job_status(cls, job_id, new_status): + try: + # Retrieve the job from the database based on its primary key + job = cls.query.get(job_id) + if job: + # Update the status attribute of the job + job.status = new_status + # Commit the changes to the database + db.session.commit() + + # current_app.socketio.emit('job_status_update', { + # 'job_id': job_id, 'status': new_status}) + + return {"success": True, "message": f"Job {job_id} status updated successfully."} + else: + return {"success": False, "message": f"Job {job_id} not found."}, 404 + except SQLAlchemyError as e: + print(f"Database error: {e}") + return ( + jsonify({"error": "Failed to update job status. Database error"}), + 500, + ) + + @classmethod + def delete_job(cls, job_id): + try: + job = cls.query.get(job_id) + if job: + db.session.delete(job) + db.session.commit() + return {"success": True, "message": f"Job with ID {job_id} deleted from the database."} + else: + return {"error": f"Job with ID {job_id} not found in the database."} + except Exception as e: + print(f"Unexpected error: {e}") + # When an error occurs or an exception is raised during a database operation (such as adding, + # updating, or deleting records), it may leave the database in an inconsistent state. To handle such + # situations, a rollback is performed to revert any changes made within the current session to maintain the integrity of the database. + db.session.rollback() + return {"error": "Unexpected error occurred during job deletion."} + + @classmethod + def findJob(cls, job_id): + try: + job = cls.query.filter_by(id=job_id).first() + return job + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to retrieve job. Database error"}), 500 + + # @classmethod + # def findPrinterObject(self, printer_id): + # threads = printer_status_service.getThreadArray() + # return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer + + @classmethod + def removeFileFromPath(cls, file_path): + # file_path = self.generatePath() # Get the file path + if os.path.exists(file_path): # Check if the file exists + os.remove(file_path) # Remove the file + + @classmethod + def setDBstatus(cls, jobid, status): + cls.update_job_status(jobid, status) + + @classmethod + def getPathForDelete(cls, file_name): + return os.path.join('../uploads', file_name) + + @classmethod + def nullifyPrinterId(cls, printer_id): + try: + jobs = cls.query.filter_by(printer_id=printer_id).all() + for job in jobs: + job.fabricator_id = 0 + db.session.commit() + return {"success": True, "message": "Printer ID nullified successfully."} + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to nullify printer ID. Database error"}), 500 + + @classmethod + def clearSpace(cls): + try: + six_months_ago = datetime.now() - timedelta(days=182) # 6 months ago + old_jobs = Job.query.filter(Job.date < six_months_ago).all() + + # thirty_seconds_ago = datetime.now() - timedelta(seconds=30) # 30 seconds ago + # old_jobs = Job.query.filter(Job.date < thirty_seconds_ago).all() + + for job in old_jobs: + if (job.favorite == 0): + job.file = None # Set file to None + if "Removed after 6 months" not in job.file_name_original: + job.file_name_original = f"{job.file_name_original}: Removed after 6 months" + db.session.commit() # Commit the changes + return {"success": True, "message": "Space cleared successfully."} + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to clear space. Database error"}), 500 + + @classmethod + def getFavoriteJobs(cls): + try: + jobs = cls.query.filter_by(favorite=True).all() + + jobs_data = [{ + "id": job.id, + "name": job.name, + "status": job.status, + "date": f"{job.date.strftime('%a, %d %b %Y %H:%M:%S')} {get_localzone().tzname(job.date)}", + "printer": job.printer.name if job.printer else 'None', + "file_name_original": job.file_name_original, + "favorite": job.favorite + } for job in jobs] + + return jobs_data + except SQLAlchemyError as e: + print(f"Database error: {e}") + return jsonify({"error": "Failed to retrieve favorite jobs. Database error"}), 500 + + @classmethod + def setIssue(cls, job_id, issue_id): + job = cls.query.get(job_id) + + if job is None: + return None + + # Set the job's error_id to the given issue_id + job.error_id = issue_id + + # Commit the changes to the database + try: + db.session.commit() + return {"success": True, "message": "Issue assigned successfully."} + except Exception as e: + db.session.rollback() + print(f"Error setting issue: {e}") + return None + + @classmethod + def unsetIssue(cls, job_id): + job = cls.query.get(job_id) + + if job is None: + return None + + # Set the job's error_id to None + job.error_id = None + + # Commit the changes to the database + try: + db.session.commit() + return {"success": True, "message": "Issue removed successfully."} + except Exception as e: + db.session.rollback() + print(f"Error unsetting issue: {e}") + return None + + @classmethod + def setComment(cls, job_id, comments): + job = cls.query.get(job_id) + + if job is None: + return None + + # Set the job's comments to the given comments + job.comments = comments + + # Commit the changes to the database + try: + db.session.commit() + return {"success": True, "message": "Comments added successfully."} + except Exception as e: + db.session.rollback() + print(f"Error setting comments: {e}") + return None + + @classmethod + def downloadCSV(cls, alljobs, jobids=None): + try: + if (jobids != None): + # Join Job and Issue on error_id and filter by jobids + jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).filter( + cls.id.in_(jobids)).all() + else: + # Join Job and Issue on error_id + jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).all() + + # Specify the columns you want to include + column_names = ['td_id', 'printer', 'name', 'file_name_original', 'status', 'date', 'issue', 'comments'] + + date_string = datetime.now().strftime("%m%d%Y") + + csv_file_name = f'../tempcsv/jobs_{date_string}.csv' + + # Write to CSV + + with open(csv_file_name, 'w', newline='') as file: + writer = csv.writer(file) + writer.writerow(column_names) # write headers + for job, issue in jobs: + row = [getattr(job, 'td_id', ''), getattr(job, 'printer_name', ''), getattr(job, 'name', ''), + getattr(job, 'file_name_original', ''), getattr(job, 'status', ''), getattr(job, 'date', ''), + getattr(issue, 'issue', '') if issue else '', getattr(job, 'comments', '')] + writer.writerow(row) # write data rows + + csv_file_path = f'./{csv_file_name}' + + return send_file(csv_file_path, as_attachment=True) + + + except Exception as e: + print(f"Error downloading CSV: {e}") + return {"status": "error", "message": f"Error downloading CSV: {e}"} + + def saveToFolder(self): + file_data = self.getFile() + decompressed_data = gzip.decompress(file_data) + with open(self.generatePath(), 'wb') as f: + f.write(decompressed_data) + + def generatePath(self): + return os.path.join('../uploads', self.getFileNamePk()) + + # getters + def getName(self): + return self.name + + def getFilePath(self): + return self.path + + def getFile(self): + return self.file + + def getStatus(self): + return self.status + + def getFileNamePk(self): + return self.file_name_pk + + def getFileNameOriginal(self): + return self.file_name_original + + def getFileFavorite(self): + return self.favorite + + def setFileFavorite(self, favorite): + self.favorite = favorite + db.session.commit() + return {"success": True, "message": "Favorite status updated successfully."} + + def getJobId(self): + return self.id + + def getFilePause(self): + return self.filePause + + def setFilePause(self, pause): + self.filePause = pause + # current_app.socketio.emit('file_pause_update', { + # 'job_id': self.id, 'file_pause': self.filePause}) + + def getExtruded(self): + return self.extruded + + def setExtruded(self, extruded): + self.extruded = extruded + # current_app.socketio.emit('extruded_update', { + # 'job_id': self.id, 'extruded': self.extruded}) + + # setters + + def setStatus(self, status): + self.status = status + # self.setDBstatus(self.id, status) + + # added a setProgress method to update the progress of a job + # which sends it to the frontend using socketio + def setProgress(self, progress): + if self.status == 'printing': + self.progress = progress + # # Emit a 'progress_update' event with the new progress + # current_app.socketio.emit( + # 'progress_update', {'job_id': self.id, 'progress': self.progress}) + + # added a getProgress method to get the progress of a job + def getProgress(self): + return self.progress + + def setSentLines(self, sent_lines): + self.sent_lines = sent_lines + # current_app.socketio.emit('gcode_viewer', {'job_id': self.id, 'gcode_num': self.sent_lines}) + + def getSentLines(self): + return self.sent_lines + + def getTimeFromFile(self, comment_lines): + # job_line can look two ways: + # 1. ;TIME:seconds + # 2. ; estimated printing time (normal mode) = minutes seconds + # if first line contains "FLAVOR", then the second line contains the time estimate in the format of ";TIME:seconds" + if "FLAVOR" in comment_lines[0]: + time_line = comment_lines[1] + time_seconds = int(time_line.split(":")[1]) + else: + # search for the line that contains "printing time", then the time estimate is in the format of "; estimated printing time (normal mode) = minutes seconds" + time_line = next(line for line in comment_lines if "time" in line) + time_values = re.findall(r'\d+', time_line) + + # Initialize all time units to 0 + time_days = time_hours = time_minutes = time_seconds = 0 + + # Assign values from right to left (seconds, minutes, hours, days) + time_values = time_values[::-1] + if len(time_values) > 0: + time_seconds = int(time_values[0]) + if len(time_values) > 1: + time_minutes = int(time_values[1]) + if len(time_values) > 2: + time_hours = int(time_values[2]) + if len(time_values) > 3: + time_days = int(time_values[3]) + + # Calculate total time in seconds + time_seconds = time_days * 24 * 60 * 60 + time_hours * 60 * 60 + time_minutes * 60 + time_seconds + # date = datetime.fromtimestamp(time_seconds) + return time_seconds + + def getTimeStarted(self): + return self.time_started + + def calculateEta(self): + now = datetime.now() + eta = timedelta(seconds=self.job_time[0]) + now + return eta + + def updateEta(self): + now = datetime.now() + pause_time = self.getJobTime()[3] + + duration = now - pause_time + + new_eta = self.getJobTime()[1] + timedelta(seconds=1) + return new_eta + + def colorEta(self): + print("before ETA: ", self.getJobTime()[1]) + + now = datetime.now() + pause_time = self.getJobTime()[3] + duration = now - pause_time + eta = self.getJobTime()[1] + duration + return eta + + def calculateTotalTime(self): + total_time = self.getJobTime()[0] + + # Add one second to total_time + total_time += 1 + return total_time + + def calculateColorChangeTotal(self): + print("before Total Time: ", self.getJobTime()[0]) + + now = datetime.now() + pause_time = self.getJobTime()[3] + duration = now - pause_time + duration_in_seconds = duration.total_seconds() + total_time = self.getJobTime()[0] + duration_in_seconds + return total_time + + def getJobTime(self): + return self.job_time + + def getReleased(self): + return self.released + + def getTdId(self): + return self.td_id + + def setMaxLayerHeight(self, max_layer_height): + self.max_layer_height = max_layer_height + # current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) + + def setCurrentLayerHeight(self, current_layer_height): + print("Current Layer Height: ", current_layer_height) + self.current_layer_height = current_layer_height + # current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) + + def setFilament(self, filament): + self.filament = filament + + def setPath(self, path): + self.path = path + + def setFileName(self, filename): + self.file_name_pk = filename + + def setFile(self, file): + self.file = file + + def setReleased(self, released): + self.released = released + # current_app.socketio.emit('release_job', {'job_id': self.id, 'released': released}) + + def setTimeStarted(self, time_started): + self.time_started = time_started + # current_app.socketio.emit('set_time_started', {'job_id': self.id, 'started': time_started}) + + def setTime(self, timeData, index): + # timeData = datetime(y, m, d, h, min, s) + # print("TimeData: ", timeData, " Index: ", index) + self.job_time[index] = timeData + # if index == 0: + # current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData, 'index': index}) + # else: + # current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData.isoformat(), 'index': index}) \ No newline at end of file diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py new file mode 100644 index 00000000..2b04a534 --- /dev/null +++ b/server/Classes/Logger.py @@ -0,0 +1,110 @@ +import logging +import os +import sys +import traceback + +from _pytest._code.code import ExceptionChainRepr + + +class Logger(logging.Logger): + DEBUG = logging.DEBUG + INFO = logging.INFO + WARNING = logging.WARNING + ERROR = logging.ERROR + CRITICAL = logging.CRITICAL + + def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None): + super().__init__(f"Logger_{port}_{deviceName}") + console_handler = logging.StreamHandler(consoleLogger) + console_handler.setFormatter(CustomFormatter("%(asctime)s - %(message)s")) + self.addHandler(console_handler) + self.setLevel(logging.INFO) # Adjust this as needed + if fileLogger is None: + log_folder = "./server/logs" + os.makedirs(log_folder, exist_ok=True) + subfolder = os.path.join(log_folder, port) + os.makedirs(subfolder, exist_ok=True) + fileLogger = logging.FileHandler(os.path.join(subfolder, f"test_{port}.log")) + else: + fileLogger = logging.FileHandler(fileLogger) + fileLogger.setFormatter(logging.Formatter("%(message)s")) + self.addHandler(fileLogger) + self.setLevel(logging.INFO) # Adjust this as needed + + def formatLog(self, msg): + if isinstance(msg, str): + pass + elif isinstance(msg, Exception): + msg = traceback.format_exception(msg.__traceback__) + elif isinstance(msg, ExceptionChainRepr): + msg = msg.reprtraceback.__repr__() + elif isinstance(msg, list) or isinstance(msg, tuple): + msgList = msg + msg = "" + for line in msgList: + msg += self.formatLog(line) + else: + msg = str(msg) + return msg + + def info(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + """Log a message with level INFO and append `end` after the message.""" + msg = self.formatLog(msg) + super().info(msg + end, *args, **kwargs) + + def debug(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + """Log a message with level DEBUG and append `end` after the message.""" + msg = self.formatLog(msg) + super().debug(msg + end, *args, **kwargs) + + def warning(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + """Log a message with level WARNING and append `end` after the message.""" + msg = self.formatLog(msg) + super().warning(msg + end, *args, **kwargs) + + def error(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + """Log a message with level ERROR and append `end` after the message.""" + msg = self.formatLog(msg) + super().error(msg + end, *args, **kwargs) + + def critical(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + """Log a message with level CRITICAL and append `end` after the message.""" + msg = self.formatLog(msg) + super().critical(msg + end, *args, **kwargs) + + def logMessageOnly(self, msg: str, logLevel: int = None, *args, **kwargs): + if logLevel is None: + logLevel = self.level + """Log a message without any additional formatting.""" + oldFormatters = [handler.formatter for handler in self.handlers] + for handler in self.handlers: + handler.setFormatter(CustomFormatter("%(message)s")) + if logLevel == self.DEBUG: + self.debug(msg, *args, **kwargs) + elif logLevel == self.INFO: + self.info(msg, *args, **kwargs) + elif logLevel == self.WARNING: + self.warning(msg, *args, **kwargs) + elif logLevel == self.ERROR: + self.error(msg, *args, **kwargs) + elif logLevel == self.CRITICAL: + self.critical(msg, *args, **kwargs) + for handler, formatter in zip(self.handlers, oldFormatters): + handler.setFormatter(formatter) + +class CustomFormatter(logging.Formatter): + # ANSI escape codes for colors + COLOR_CODES = { + "DEBUG": "\033[94m", # Blue + "INFO": "\033[0m", # white + "WARNING": "\033[93m", # Yellow + "ERROR": "\033[91m", # Red + "CRITICAL": "\033[95m" # Magenta + } + RESET_CODE = "\033[0m" # Reset to default color + + def format(session, record): + # Apply color based on log level + color = session.COLOR_CODES.get(record.levelname, session.RESET_CODE) + message = super().format(record) + return f"{color}{message}{session.RESET_CODE}" diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index 72960eff..16c00c57 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -1,3 +1,4 @@ +import logging from abc import ABCMeta from time import sleep from typing_extensions import Buffer @@ -6,6 +7,7 @@ from Classes.LocationResponse import LocationResponse from Classes.Vector3 import Vector3 from Mixins.canPause import canPause +from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue, checkBedTemp, checkExtruderTemp, hasResponsecodes @@ -23,36 +25,63 @@ class usesMarlinGcode(canPause, hasResponsecodes, metaclass=ABCMeta): callablesHashtable = { "G0": [alwaysTrue, checkOK], # Move "G1": [alwaysTrue, checkOK], # Move - "G28": [alwaysTrue, checkOK], # Home - "G29": [alwaysTrue, checkOK], # Auto bed leveling + "G28": [alwaysTrue, checkXYZ], # Home + "G29": [checkXYZ, checkXYZ], # Auto bed leveling "M73": [alwaysTrue, checkOK], # Set build percentage + "M104": [alwaysTrue, alwaysTrue], # Set hotend temp "M109": [alwaysTrue, checkExtruderTemp], # Wait for hotend to reach target temp "M114": [alwaysTrue, checkXYZ], # Get current position - "M140": [alwaysTrue, checkOK], # Set bed temp + "M140": [alwaysTrue, alwaysTrue], # Set bed temp + "M155": [alwaysTrue, alwaysTrue], # Set temperature auto report "M190": [alwaysTrue, checkBedTemp], # Wait for bed to reach target temp } def goTo(self: Device, loc: Vector3, isVerbose: bool = False): + assert isinstance(loc, Vector3) + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) return loc == self.getToolHeadLocation() - def parseGcode(self: Device, file, isVerbose: bool = False): + def parseGcode(self: Device, file: str, isVerbose: bool = False): + assert isinstance(file, str) + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) try: with open(file, "r") as f: for line in f: if line.startswith(";") or line == "\n": continue + if self.status == "paused": + self.pause() + while self.status == "paused": + sleep(1) + if self.status == "cancelled": + if isinstance(self, hasEndingSequence): + self.endSequence() + self.verdict = "cancelled" + return True + elif self.status == "printing": + self.resume() + if self.status == "cancelled": + if isinstance(self, hasEndingSequence): + self.endSequence() + self.verdict = "cancelled" + return True if isVerbose: print(line, end="") self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) + self.verdict = "complete" return True except Exception as e: print(e) - return False + self.verdict = "error" + return True def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): + assert self.serialConnection.is_open assert isinstance(gcode, bytes) self.serialConnection.write(gcode) callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) @@ -69,7 +98,6 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): if callables[1](line): return True except Exception as e: - print(e) return False @@ -83,33 +111,53 @@ def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: return Vector3(loc.x, loc.y, loc.z) - def home(self: Device, isVerbose: bool = False): + def home(self, isVerbose: bool = False): self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) + assert isinstance(self, Device) return self.getHomePosition() == self.getToolHeadLocation() def pause(self): - self.sendGcode(usesMarlinGcode.keepAliveCMD) - self.sendGcode(usesMarlinGcode.pauseCMD) - + assert isinstance(self, canPause) + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + assert self.status == "printing" + try: + self.sendGcode(usesMarlinGcode.keepAliveCMD) + self.sendGcode(usesMarlinGcode.pauseCMD) + return True + except Exception as e: + print(e) + return False def resume(self): - self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) - self.sendGcode(usesMarlinGcode.resumeCMD) + assert isinstance(self, canPause) + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + assert self.status == "paused" + try: + self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) + self.sendGcode(usesMarlinGcode.resumeCMD) + return True + except Exception as e: + print(e) + return False def connect(self: Device): Device.connect(self) try: if self.serialConnection: - self.serialConnection.write(b"M155 S1\n") + #self.serialConnection.write(b"M155 S1\n") return True except Exception as e: return e def disconnect(self: Device): - if self.serialConnection: + if self.serialConnection and self.serialConnection.is_open: self.sendGcode(b"M155 S0\n") self.sendGcode(b"M104 S0\n") self.sendGcode(b"M140 S0\n") diff --git a/server/xyz-cali-cube-mini_ENDER3.gcode b/server/xyz-cali-cube-mini_ENDER3.gcode index ec3864d6..527f9e00 100644 --- a/server/xyz-cali-cube-mini_ENDER3.gcode +++ b/server/xyz-cali-cube-mini_ENDER3.gcode @@ -1,4 +1,4 @@ -; generated by PrusaSlicer 2.8.1+win64 on 2024-10-28 at 17:53:53 UTC +; generated by PrusaSlicer 2.8.1+win64 on 2024-10-31 at 15:29:52 UTC ; @@ -27,7 +27,7 @@ G4 S30 ; allow partial nozzle warmup G28 ; home all axis G1 Z50 F240 G1 X2.0 Y10 F3000 -M140 S60 ; wait for bed temp to stabilize +M140 S60 ; set final bed temp M109 S210 ; wait for nozzle temp to stabilize G1 Z0.28 F240 G92 E0 @@ -46,415 +46,1311 @@ M107 ;HEIGHT:0.2 G1 E-5 F3600 G1 Z.2 F9000 -G1 X115.579 Y98.508 +G1 X112.579 Y95.508 G1 Z.2 G1 E5 F2400 M204 S500 ;TYPE:Skirt/Brim ;WIDTH:0.42 G1 F1200 -G1 X118.508 Y95.579 E.12988 -G1 X119.939 Y94.705 E.05257 -G1 X121.029 Y94.534 E.03459 -G1 X128.971 Y94.534 E.24902 -G1 X130.601 Y94.929 E.05259 -G1 X131.492 Y95.579 E.03458 -G1 X134.421 Y98.508 E.12988 -G1 X135.295 Y99.939 E.05257 -G1 X135.466 Y101.029 E.03459 -G1 X135.466 Y108.971 E.24902 -G1 X135.071 Y110.601 E.05259 -G1 X134.421 Y111.492 E.03458 -G1 X131.492 Y114.421 E.12988 -G1 X130.061 Y115.295 E.05257 -G1 X128.971 Y115.466 E.03459 -G1 X121.029 Y115.466 E.24902 -G1 X119.399 Y115.071 E.05259 -G1 X118.508 Y114.421 E.03458 -G1 X115.579 Y111.492 E.12988 -G1 X114.705 Y110.061 E.05257 -G1 X114.534 Y108.971 E.03459 -G1 X114.534 Y101.029 E.24902 -G1 X114.929 Y99.399 E.05259 -G1 X115.543 Y98.556 E.0327 -G1 X115.543 Y98.556 F9000 -G1 X115.841 Y98.78 +G1 X115.508 Y92.579 E.12988 +G1 X116.939 Y91.705 E.05257 +G1 X118.029 Y91.534 E.03459 +G1 X131.971 Y91.534 E.43714 +G1 X133.601 Y91.929 E.05259 +G1 X134.492 Y92.579 E.03458 +G1 X137.421 Y95.508 E.12988 +G1 X138.295 Y96.939 E.05257 +G1 X138.466 Y98.029 E.03459 +G1 X138.466 Y111.971 E.43714 +G1 X138.071 Y113.601 E.05259 +G1 X137.421 Y114.492 E.03458 +G1 X134.492 Y117.421 E.12988 +G1 X133.061 Y118.295 E.05257 +G1 X131.971 Y118.466 E.03459 +G1 X118.029 Y118.466 E.43714 +G1 X116.399 Y118.071 E.05259 +G1 X115.508 Y117.421 E.03458 +G1 X112.579 Y114.492 E.12988 +G1 X111.705 Y113.061 E.05257 +G1 X111.534 Y111.973 E.03453 +G1 X111.534 Y98.028 E.43724 +G1 X111.929 Y96.398 E.05259 +G1 X112.543 Y95.556 E.03267 +G1 X112.543 Y95.556 F9000 +G1 X112.841 Y95.78 G1 F1200 -G1 X115.845 Y98.774 E.00023 -G1 X118.774 Y95.845 E.12988 -G1 X120.138 Y95.038 E.04969 -G1 X121.029 Y94.911 E.02822 -G1 X128.971 Y94.911 E.24902 -G1 X130.506 Y95.305 E.04969 -G1 X131.226 Y95.845 E.02822 -G1 X134.155 Y98.774 E.12988 -G1 X134.962 Y100.138 E.04969 -G1 X135.089 Y101.029 E.02822 -G1 X135.089 Y108.971 E.24902 -G1 X134.695 Y110.506 E.04969 -G1 X134.155 Y111.226 E.02822 -G1 X131.226 Y114.155 E.12988 -G1 X129.862 Y114.962 E.04969 -G1 X128.971 Y115.089 E.02822 -G1 X121.029 Y115.089 E.24902 -G1 X119.494 Y114.695 E.04969 -G1 X118.774 Y114.155 E.02822 -G1 X115.845 Y111.226 E.12988 -G1 X115.038 Y109.862 E.04969 -G1 X114.911 Y108.971 E.02822 -G1 X114.911 Y101.029 E.24902 -G1 X115.305 Y99.494 E.04969 -G1 X115.805 Y98.828 E.02611 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X115.841 Y98.78 E-.0285 -G1 X115.845 Y98.774 E-.00343 -G1 X118.031 Y96.589 E-1.46807 -;WIPE_END -G1 X119.104 Y100.532 F9000 +G1 X112.845 Y95.774 E.00023 +G1 X115.774 Y92.845 E.12988 +G1 X117.138 Y92.038 E.04969 +G1 X118.029 Y91.911 E.02822 +G1 X131.971 Y91.911 E.43714 +G1 X133.506 Y92.305 E.04969 +G1 X134.226 Y92.845 E.02822 +G1 X137.155 Y95.774 E.12988 +G1 X137.962 Y97.138 E.04969 +G1 X138.089 Y98.029 E.02822 +G1 X138.089 Y111.971 E.43714 +G1 X137.695 Y113.506 E.04969 +G1 X137.155 Y114.226 E.02822 +G1 X134.226 Y117.155 E.12988 +G1 X132.862 Y117.962 E.04969 +G1 X131.971 Y118.089 E.02822 +G1 X118.029 Y118.089 E.43714 +G1 X116.494 Y117.695 E.04969 +G1 X115.774 Y117.155 E.02822 +G1 X112.845 Y114.226 E.12988 +G1 X112.038 Y112.862 E.04969 +G1 X111.911 Y111.973 E.02816 +G1 X111.911 Y98.028 E.43724 +G1 X112.306 Y96.493 E.0497 +G1 X112.805 Y95.828 E.02607 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X112.841 Y95.78 E-.0285 +G1 X112.845 Y95.774 E-.00343 +G1 X115.031 Y93.589 E-1.46807 +;WIPE_END +G1 X116.069 Y97.668 F9000 G1 E5 F2400 G1 F1200 -G1 X120.525 Y99.109 E.06305 -G1 X121.301 Y98.708 E.02739 -G1 X122.123 Y98.454 E.02698 -G1 X127.72 Y98.429 E.17549 -G1 X128.504 Y98.63 E.02538 -G1 X129.264 Y98.977 E.0262 -G1 X130.453 Y99.981 E.04879 -G1 X131.112 Y100.91 E.03571 -G1 X131.516 Y101.981 E.03589 -G1 X131.615 Y107.39 E.16962 -G1 X131.316 Y108.631 E.04002 -G1 X130.896 Y109.468 E.02936 -G1 X129.468 Y110.896 E.06332 -G1 X128.633 Y111.315 E.02929 -G1 X127.502 Y111.602 E.03659 -G1 X122.59 Y111.613 E.15401 -G1 X121.367 Y111.315 E.03947 -G1 X120.532 Y110.896 E.02929 -G1 X119.104 Y109.468 E.06332 -G1 X118.685 Y108.633 E.02929 -G1 X118.398 Y107.502 E.03659 -G1 X118.387 Y102.59 E.15401 -G1 X118.685 Y101.367 E.03947 -G1 X119.077 Y100.585 E.02743 -G1 X119.077 Y100.585 F9000 -G1 X119.413 Y100.756 +G1 X116.266 Y97.318 E.01259 +G1 X117.033 Y96.5 E.03516 +G1 X117.639 Y96.03 E.02405 +G1 X119.554 Y95.386 E.06335 +G1 X130.387 Y95.39 E.33966 +G1 X131.335 Y95.574 E.03028 +G1 X132.243 Y95.965 E.031 +G1 X133.46 Y96.987 E.04983 +G1 X134.172 Y98.03 E.0396 +G1 X134.576 Y99.318 E.04232 +G1 X134.614 Y110.408 E.34772 +G1 X134.305 Y111.668 E.04068 +G1 X133.734 Y112.682 E.03649 +G1 X132.967 Y113.5 E.03516 +G1 X132.361 Y113.97 E.02405 +G1 X130.447 Y114.613 E.06331 +G1 X119.59 Y114.613 E.34041 +G1 X118.332 Y114.305 E.04061 +G1 X117.318 Y113.734 E.03649 +G1 X116.5 Y112.967 E.03516 +G1 X116.03 Y112.361 E.02405 +G1 X115.387 Y110.447 E.06331 +G1 X115.387 Y99.59 E.34041 +G1 X115.695 Y98.332 E.04061 +G1 X116.04 Y97.721 E.022 +G1 X116.04 Y97.721 F9000 +G1 X116.365 Y97.906 G1 F1200 -G1 X119.427 Y100.729 E.00095 -G1 X120.725 Y99.43 E.05758 -G1 X121.429 Y99.063 E.02489 -G1 X122.208 Y98.823 E.02556 -G1 X127.649 Y98.8 E.1706 -G1 X128.393 Y98.991 E.02408 -G1 X129.082 Y99.308 E.02378 -G1 X130.189 Y100.251 E.0456 -G1 X130.789 Y101.106 E.03275 -G1 X131.152 Y102.082 E.03265 -G1 X131.237 Y107.371 E.16585 -G1 X130.955 Y108.519 E.03706 -G1 X130.573 Y109.271 E.02645 -G1 X129.271 Y110.573 E.05773 -G1 X128.52 Y110.955 E.02642 -G1 X127.432 Y111.231 E.03519 -G1 X122.614 Y111.236 E.15107 -G1 X121.48 Y110.955 E.03663 -G1 X120.729 Y110.573 E.02642 -G1 X119.427 Y109.271 E.05773 -G1 X119.045 Y108.52 E.02642 -G1 X118.769 Y107.432 E.03519 -G1 X118.764 Y102.614 E.15107 -G1 X119.045 Y101.48 E.03663 -G1 X119.386 Y100.809 E.0236 -G1 X119.386 Y100.809 F9000 -G1 X119.721 Y100.982 +G1 X116.582 Y97.526 E.01372 +G1 X117.296 Y96.771 E.03258 +G1 X117.844 Y96.348 E.02171 +G1 X119.585 Y95.764 E.05758 +G1 X130.368 Y95.767 E.33809 +G1 X131.241 Y95.94 E.0279 +G1 X132.066 Y96.299 E.02821 +G1 X133.196 Y97.257 E.04645 +G1 X133.844 Y98.217 E.03632 +G1 X134.207 Y99.401 E.03883 +G1 X134.236 Y110.386 E.34443 +G1 X133.946 Y111.551 E.03764 +G1 X133.418 Y112.474 E.03334 +G1 X132.704 Y113.229 E.03258 +G1 X132.156 Y113.652 E.02171 +G1 X130.416 Y114.236 E.05755 +G1 X119.614 Y114.236 E.33869 +G1 X118.449 Y113.946 E.03764 +G1 X117.526 Y113.418 E.03334 +G1 X116.771 Y112.704 E.03258 +G1 X116.348 Y112.156 E.02171 +G1 X115.764 Y110.416 E.05755 +G1 X115.764 Y99.614 E.33869 +G1 X116.054 Y98.449 E.03764 +G1 X116.335 Y97.958 E.01774 +G1 X116.335 Y97.958 F9000 +G1 X116.658 Y98.146 G1 F1200 -G1 X119.748 Y100.928 E.00189 -G1 X120.925 Y99.75 E.05221 -G1 X121.557 Y99.418 E.02238 -G1 X122.292 Y99.191 E.02412 -G1 X127.58 Y99.171 E.1658 -G1 X128.252 Y99.341 E.02173 -G1 X128.9 Y99.639 E.02236 -G1 X129.925 Y100.522 E.04242 -G1 X130.444 Y101.26 E.02829 -G1 X130.787 Y102.183 E.03087 -G1 X130.86 Y107.351 E.16206 -G1 X130.595 Y108.405 E.03408 -G1 X130.252 Y109.072 E.02352 -G1 X129.072 Y110.252 E.05232 -G1 X128.406 Y110.595 E.02349 -G1 X127.362 Y110.859 E.03376 -G1 X122.638 Y110.859 E.14812 -G1 X121.594 Y110.595 E.03376 -G1 X120.928 Y110.252 E.02349 -G1 X119.748 Y109.072 E.05232 -G1 X119.405 Y108.406 E.02349 -G1 X119.141 Y107.362 E.03376 -G1 X119.141 Y102.638 E.14812 -G1 X119.405 Y101.594 E.03376 -G1 X119.693 Y101.035 E.01972 -G1 X119.693 Y101.035 F9000 -G1 X120.026 Y101.209 +G1 X116.896 Y97.735 E.01489 +G1 X117.558 Y97.042 E.03005 +G1 X118.048 Y96.666 E.01937 +G1 X119.615 Y96.141 E.05182 +G1 X130.349 Y96.144 E.33656 +G1 X131.146 Y96.305 E.02549 +G1 X131.888 Y96.633 E.02544 +G1 X132.93 Y97.525 E.04301 +G1 X133.514 Y98.402 E.03304 +G1 X133.838 Y99.482 E.03535 +G1 X133.859 Y110.362 E.34114 +G1 X133.587 Y111.431 E.03459 +G1 X133.104 Y112.265 E.03022 +G1 X132.442 Y112.958 E.03005 +G1 X131.952 Y113.334 E.01937 +G1 X130.385 Y113.859 E.05182 +G1 X119.638 Y113.859 E.33696 +G1 X118.569 Y113.587 E.03459 +G1 X117.735 Y113.104 E.03022 +G1 X117.042 Y112.442 E.03005 +G1 X116.666 Y111.952 E.01937 +G1 X116.141 Y110.385 E.05182 +G1 X116.141 Y99.638 E.33696 +G1 X116.413 Y98.569 E.03459 +G1 X116.628 Y98.198 E.01344 +G1 X116.628 Y98.198 F9000 +G1 X116.949 Y98.387 G1 F1200 -G1 X120.069 Y101.129 E.00285 -G1 X121.127 Y100.069 E.04696 -G1 X121.685 Y99.773 E.0198 -G1 X122.377 Y99.56 E.0227 -G1 X127.511 Y99.542 E.16097 -G1 X128.142 Y99.702 E.02041 -G1 X128.716 Y99.969 E.01985 -G1 X129.66 Y100.79 E.03923 -G1 X130.12 Y101.454 E.02533 -G1 X130.422 Y102.281 E.0276 -G1 X130.483 Y107.33 E.15832 -G1 X130.236 Y108.289 E.03105 -G1 X129.931 Y108.871 E.0206 -G1 X128.871 Y109.931 E.047 -G1 X128.289 Y110.235 E.02059 -G1 X127.338 Y110.482 E.03081 -G1 X122.662 Y110.482 E.14661 -G1 X121.711 Y110.235 E.03081 -G1 X121.129 Y109.931 E.02059 -G1 X120.069 Y108.871 E.047 -G1 X119.765 Y108.289 E.02059 -G1 X119.518 Y107.338 E.03081 -G1 X119.518 Y102.662 E.14661 -G1 X119.765 Y101.711 E.03081 -G1 X119.999 Y101.262 E.01588 -G1 X119.999 Y101.262 F9000 -G1 X120.331 Y101.439 +G1 X117.209 Y97.947 E.01602 +G1 X117.819 Y97.315 E.02754 +G1 X118.252 Y96.985 E.01707 +G1 X119.646 Y96.518 E.0461 +G1 X130.329 Y96.521 E.33496 +G1 X131.048 Y96.67 E.02302 +G1 X131.708 Y96.965 E.02267 +G1 X132.662 Y97.792 E.03959 +G1 X133.183 Y98.584 E.02972 +G1 X133.468 Y99.562 E.03194 +G1 X133.482 Y110.338 E.33787 +G1 X133.229 Y111.31 E.03149 +G1 X132.791 Y112.053 E.02704 +G1 X132.181 Y112.685 E.02754 +G1 X131.748 Y113.015 E.01707 +G1 X130.355 Y113.482 E.04607 +G1 X119.662 Y113.482 E.33527 +G1 X118.69 Y113.229 E.03149 +G1 X117.947 Y112.791 E.02704 +G1 X117.315 Y112.181 E.02754 +G1 X116.985 Y111.748 E.01707 +G1 X116.518 Y110.355 E.04607 +G1 X116.518 Y99.662 E.33527 +G1 X116.771 Y98.69 E.03149 +G1 X116.919 Y98.439 E.00914 +G1 X116.919 Y98.439 F9000 +G1 X117.238 Y98.631 G1 F1200 -G1 X120.388 Y101.331 E.00383 -G1 X121.33 Y100.388 E.04179 -G1 X121.815 Y100.128 E.01725 -G1 X122.461 Y99.929 E.02119 -G1 X127.443 Y99.914 E.15621 -G1 X128 Y100.052 E.01799 -G1 X128.532 Y100.3 E.0184 -G1 X129.392 Y101.057 E.03592 -G1 X129.795 Y101.646 E.02238 -G1 X130.056 Y102.377 E.02434 -G1 X130.105 Y107.308 E.15462 -G1 X129.877 Y108.17 E.02796 -G1 X129.612 Y108.669 E.01772 -G1 X128.669 Y109.612 E.04181 -G1 X128.171 Y109.877 E.01769 -G1 X127.313 Y110.105 E.02784 -G1 X122.687 Y110.105 E.14504 -G1 X121.829 Y109.877 E.02784 -G1 X121.331 Y109.612 E.01769 -G1 X120.388 Y108.669 E.04181 -G1 X120.123 Y108.171 E.01769 -G1 X119.895 Y107.313 E.02784 -G1 X119.895 Y102.687 E.14504 -G1 X120.123 Y101.829 E.02784 -G1 X120.302 Y101.492 E.01196 -G1 X120.302 Y101.492 F9000 -G1 X120.632 Y101.671 +G1 X117.52 Y98.161 E.01719 +G1 X118.455 Y97.304 E.03977 +G1 X119.676 Y96.895 E.04037 +G1 X130.31 Y96.898 E.33342 +G1 X130.913 Y97.023 E.01931 +G1 X131.528 Y97.297 E.02111 +G1 X132.393 Y98.058 E.03612 +G1 X132.85 Y98.764 E.02637 +G1 X133.098 Y99.639 E.02852 +G1 X133.105 Y110.313 E.33468 +G1 X132.872 Y111.186 E.02833 +G1 X132.48 Y111.839 E.02388 +G1 X131.545 Y112.696 E.03977 +G1 X130.324 Y113.105 E.04037 +G1 X119.687 Y113.105 E.33352 +G1 X118.814 Y112.872 E.02833 +G1 X118.161 Y112.48 E.02388 +G1 X117.304 Y111.545 E.03977 +G1 X116.895 Y110.324 E.04037 +G1 X116.895 Y99.687 E.33352 +G1 X117.128 Y98.814 E.02833 +G1 X117.207 Y98.682 E.00482 +G1 X117.207 Y98.682 F9000 +G1 X117.523 Y98.876 G1 F1200 -G1 X120.706 Y101.535 E.00485 -G1 X121.535 Y100.706 E.03676 -G1 X121.944 Y100.483 E.01461 -G1 X122.545 Y100.297 E.01973 -G1 X127.375 Y100.286 E.15144 -G1 X127.89 Y100.413 E.01663 -G1 X128.346 Y100.628 E.01581 -G1 X129.122 Y101.321 E.03262 -G1 X129.467 Y101.835 E.01941 -G1 X129.69 Y102.471 E.02113 -G1 X129.728 Y107.284 E.15091 -G1 X129.52 Y108.049 E.02486 -G1 X129.294 Y108.465 E.01484 -G1 X128.465 Y109.294 E.03676 -G1 X128.049 Y109.52 E.01484 -G1 X127.287 Y109.728 E.02477 -G1 X122.713 Y109.728 E.14341 -G1 X121.951 Y109.52 E.02477 -G1 X121.535 Y109.294 E.01484 -G1 X120.706 Y108.465 E.03676 -G1 X120.48 Y108.049 E.01484 -G1 X120.272 Y107.287 E.02477 -G1 X120.272 Y102.713 E.14341 -G1 X120.48 Y101.951 E.02477 -G1 X120.604 Y101.723 E.00814 -G1 X120.604 Y101.723 F9000 -G1 X120.932 Y101.905 +G1 X117.829 Y98.378 E.01833 +G1 X118.336 Y97.864 E.02264 +G1 X118.656 Y97.624 E.01254 +G1 X119.707 Y97.272 E.03475 +G1 X130.29 Y97.276 E.33182 +G1 X130.814 Y97.387 E.01679 +G1 X131.345 Y97.628 E.01828 +G1 X132.122 Y98.321 E.03264 +G1 X132.516 Y98.941 E.02303 +G1 X132.728 Y99.713 E.0251 +G1 X132.728 Y110.287 E.33154 +G1 X132.516 Y111.059 E.0251 +G1 X132.171 Y111.622 E.0207 +G1 X131.664 Y112.136 E.02264 +G1 X131.344 Y112.376 E.01254 +G1 X130.294 Y112.728 E.03472 +G1 X119.713 Y112.728 E.33176 +G1 X118.941 Y112.516 E.0251 +G1 X118.378 Y112.171 E.0207 +G1 X117.864 Y111.664 E.02264 +G1 X117.624 Y111.344 E.01254 +G1 X117.272 Y110.294 E.03472 +G1 X117.272 Y99.713 E.33176 +G1 X117.484 Y98.941 E.0251 +G1 X117.492 Y98.928 E.00048 +G1 X117.492 Y98.928 F9000 +G1 X117.838 Y99.071 G1 F1200 -G1 X121.022 Y101.742 E.00584 -G1 X121.742 Y101.022 E.03193 -G1 X122.63 Y100.666 E.03 -G1 X127.309 Y100.658 E.14671 -G1 X127.747 Y100.763 E.01412 -G1 X128.159 Y100.957 E.01428 -G1 X128.851 Y101.584 E.02928 -G1 X129.139 Y102.02 E.01638 -G1 X129.323 Y102.563 E.01798 -G1 X129.351 Y107.259 E.14724 -G1 X129.163 Y107.924 E.02167 -G1 X128.978 Y108.258 E.01197 -G1 X128.258 Y108.978 E.03193 -G1 X127.924 Y109.163 E.01197 -G1 X127.26 Y109.351 E.02164 -G1 X122.74 Y109.351 E.14172 -G1 X122.076 Y109.163 E.02164 -G1 X121.742 Y108.978 E.01197 -G1 X121.022 Y108.258 E.03193 -G1 X120.837 Y107.924 E.01197 -G1 X120.649 Y107.26 E.02164 -G1 X120.649 Y102.74 E.14172 -G1 X120.837 Y102.076 E.02164 -G1 X120.903 Y101.958 E.00424 -G1 X120.903 Y101.958 F9000 -G1 X121.28 Y102.101 +G1 X118.136 Y98.597 E.01756 +G1 X118.857 Y97.945 E.03048 +G1 X119.737 Y97.649 E.02911 +G1 X130.268 Y97.653 E.33019 +G1 X130.711 Y97.75 E.01422 +G1 X131.159 Y97.958 E.01549 +G1 X131.85 Y98.583 E.02921 +G1 X132.162 Y99.071 E.01816 +G1 X132.351 Y99.74 E.0218 +G1 X132.351 Y110.26 E.32985 +G1 X132.162 Y110.929 E.0218 +G1 X131.864 Y111.403 E.01756 +G1 X131.143 Y112.055 E.03048 +G1 X130.263 Y112.351 E.02911 +G1 X119.74 Y112.351 E.32994 +G1 X119.071 Y112.162 E.0218 +G1 X118.597 Y111.864 E.01756 +G1 X117.945 Y111.143 E.03048 +G1 X117.649 Y110.263 E.02911 +G1 X117.649 Y99.74 E.32994 +G1 X117.822 Y99.129 E.01991 +G1 X118.183 Y99.235 F9000 G1 F1200 -G1 X121.337 Y101.951 E.00503 -G1 X121.951 Y101.337 E.02723 -G1 X122.714 Y101.034 E.02574 -G1 X127.246 Y101.03 E.1421 -G1 X127.971 Y101.285 E.0241 -G1 X128.576 Y101.843 E.02581 -G1 X128.808 Y102.203 E.01343 -G1 X128.955 Y102.651 E.01478 -G1 X128.974 Y107.232 E.14364 -G1 X128.663 Y108.049 E.02741 -G1 X128.049 Y108.663 E.02723 -G1 X127.232 Y108.974 E.02741 -G1 X122.768 Y108.974 E.13997 -G1 X121.951 Y108.663 E.02741 -G1 X121.337 Y108.049 E.02723 -G1 X121.026 Y107.232 E.02741 -G1 X121.026 Y102.768 E.13997 -G1 X121.258 Y102.157 E.02049 -G1 X121.258 Y102.157 F9000 -G1 X121.6 Y102.289 +G1 X118.192 Y99.204 E.00101 +G1 X118.417 Y98.85 E.01315 +G1 X119.056 Y98.266 E.02714 +G1 X119.768 Y98.026 E.02356 +G1 X130.246 Y98.03 E.32853 +G1 X130.971 Y98.285 E.0241 +G1 X131.576 Y98.843 E.02581 +G1 X131.808 Y99.205 E.01348 +G1 X131.974 Y99.768 E.0184 +G1 X131.974 Y110.232 E.32809 +G1 X131.808 Y110.796 E.01843 +G1 X131.583 Y111.15 E.01315 +G1 X131.152 Y111.581 E.01911 +G1 X130.944 Y111.734 E.0081 +G1 X130.232 Y111.974 E.02356 +G1 X119.768 Y111.974 E.32809 +G1 X119.204 Y111.808 E.01843 +G1 X118.85 Y111.583 E.01315 +G1 X118.419 Y111.152 E.01911 +G1 X118.266 Y110.944 E.0081 +G1 X118.026 Y110.232 E.02356 +G1 X118.026 Y99.768 E.32809 +G1 X118.166 Y99.293 E.01553 +G1 X118.166 Y99.293 F9000 +G1 X118.548 Y99.455 G1 F1200 -G1 X121.648 Y102.164 E.0042 -G1 X122.164 Y101.648 E.02288 -G1 X122.798 Y101.403 E.02131 -G1 X127.224 Y101.407 E.13877 -G1 X127.782 Y101.613 E.01865 -G1 X128.301 Y102.101 E.02234 -G1 X128.587 Y102.74 E.02195 -G1 X128.597 Y107.203 E.13993 -G1 X128.352 Y107.836 E.02128 -G1 X127.836 Y108.352 E.02288 -G1 X127.202 Y108.597 E.02131 -G1 X122.798 Y108.597 E.13808 -G1 X122.164 Y108.352 E.02131 -G1 X121.648 Y107.836 E.02288 -G1 X121.403 Y107.202 E.02131 -G1 X121.403 Y102.798 E.13808 -G1 X121.578 Y102.345 E.01523 -G1 X121.578 Y102.345 F9000 -G1 X121.918 Y102.479 +G1 X118.698 Y99.102 E.01203 +G1 X119.254 Y98.588 E.02374 +G1 X119.799 Y98.403 E.01805 +G1 X130.224 Y98.407 E.32687 +G1 X130.782 Y98.613 E.01865 +G1 X131.301 Y99.101 E.02234 +G1 X131.597 Y99.798 E.02374 +G1 X131.597 Y110.202 E.32621 +G1 X131.302 Y110.898 E.0237 +G1 X130.746 Y111.412 E.02374 +G1 X130.201 Y111.597 E.01805 +G1 X119.798 Y111.597 E.32618 +G1 X119.102 Y111.302 E.0237 +G1 X118.588 Y110.746 E.02374 +G1 X118.403 Y110.201 E.01805 +G1 X118.403 Y99.798 E.32618 +G1 X118.525 Y99.51 E.00981 +G1 X118.525 Y99.51 F9000 +G1 X118.856 Y99.648 G1 F1200 -G1 X121.956 Y102.383 E.00324 -G1 X122.383 Y101.956 E.01893 -G1 X122.829 Y101.78 E.01503 -G1 X127.202 Y101.784 E.13711 -G1 X127.592 Y101.94 E.01317 -G1 X128.023 Y102.357 E.0188 -G1 X128.22 Y102.829 E.01604 -G1 X128.22 Y107.171 E.13614 -G1 X128.044 Y107.617 E.01503 -G1 X127.617 Y108.044 E.01893 -G1 X127.171 Y108.22 E.01503 -G1 X122.829 Y108.22 E.13614 -G1 X122.383 Y108.044 E.01503 -G1 X121.956 Y107.617 E.01893 -G1 X121.78 Y107.171 E.01503 -G1 X121.78 Y102.829 E.13614 -G1 X121.896 Y102.535 E.00991 -G1 X121.896 Y102.535 F9000 -G1 X122.234 Y102.667 +G1 X118.978 Y99.356 E.00992 +G1 X119.358 Y98.975 E.01687 +G1 X119.831 Y98.78 E.01604 +G1 X130.202 Y98.784 E.32518 +G1 X130.592 Y98.94 E.01317 +G1 X131.023 Y99.357 E.0188 +G1 X131.22 Y99.829 E.01604 +G1 X131.22 Y110.171 E.32427 +G1 X131.022 Y110.644 E.01608 +G1 X130.642 Y111.025 E.01687 +G1 X130.169 Y111.22 E.01604 +G1 X119.829 Y111.22 E.3242 +G1 X119.356 Y111.022 E.01608 +G1 X118.975 Y110.642 E.01687 +G1 X118.78 Y110.169 E.01604 +G1 X118.78 Y99.829 E.3242 +G1 X118.833 Y99.704 E.00426 +G1 X118.833 Y99.704 F9000 +G1 X119.169 Y99.835 G1 F1200 -G1 X122.257 Y102.61 E.00193 -G1 X122.61 Y102.257 E.01565 -G1 X122.864 Y102.157 E.00856 -G1 X127.182 Y102.164 E.13539 -G1 X127.4 Y102.267 E.00756 -G1 X127.743 Y102.61 E.01521 -G1 X127.843 Y102.864 E.00856 -G1 X127.843 Y107.136 E.13395 -G1 X127.743 Y107.39 E.00856 -G1 X127.39 Y107.743 E.01565 -G1 X127.136 Y107.843 E.00856 -G1 X122.864 Y107.843 E.13395 -G1 X122.61 Y107.743 E.00856 -G1 X122.257 Y107.39 E.01565 -G1 X122.157 Y107.136 E.00856 -G1 X122.157 Y102.864 E.13395 -G1 X122.213 Y102.723 E.00476 -G1 X122.213 Y102.723 F9000 -G1 X122.563 Y102.837 +G1 X119.257 Y99.61 E.00758 +G1 X119.61 Y99.257 E.01565 +G1 X119.864 Y99.157 E.00856 +G1 X130.182 Y99.164 E.32351 +G1 X130.4 Y99.267 E.00756 +G1 X130.743 Y99.61 E.01521 +G1 X130.843 Y99.864 E.00856 +G1 X130.843 Y110.136 E.32207 +G1 X130.743 Y110.39 E.00856 +G1 X130.39 Y110.743 E.01565 +G1 X130.136 Y110.843 E.00856 +G1 X119.864 Y110.843 E.32207 +G1 X119.61 Y110.743 E.00856 +G1 X119.257 Y110.39 E.01565 +G1 X119.157 Y110.136 E.00856 +G1 X119.157 Y99.893 E.32116 +G1 X119.157 Y99.893 F9000 +G1 X119.563 Y99.894 G1 F1200 -G1 X122.906 Y102.534 E.01435 -G1 X127.094 Y102.534 E.13131 -G1 X127.466 Y102.906 E.0165 -G1 X127.437 Y107.163 E.13348 -G1 X127.094 Y107.466 E.01435 -G1 X122.837 Y107.437 E.13348 -G1 X122.534 Y107.094 E.01435 -G1 X122.563 Y102.897 E.1316 -G1 X122.563 Y102.897 F9000 -G1 X122.911 Y103.022 +G1 X119.563 Y99.837 E.00179 +G1 X119.906 Y99.534 E.01435 +G1 X130.094 Y99.534 E.31944 +G1 X130.466 Y99.906 E.0165 +G1 X130.437 Y110.163 E.3216 +G1 X130.094 Y110.466 E.01435 +G1 X119.837 Y110.437 E.3216 +G1 X119.534 Y110.094 E.01435 +G1 X119.563 Y99.954 E.31793 +G1 X119.563 Y99.954 F9000 +G1 X119.911 Y100.022 G1 F1200 -G1 X123.022 Y102.911 E.00492 -G1 X126.978 Y102.911 E.12404 -G1 X127.089 Y103.022 E.00492 -G1 X127.089 Y106.978 E.12404 -G1 X126.978 Y107.089 E.00492 -G1 X123.022 Y107.089 E.12404 -G1 X122.911 Y106.978 E.00492 -G1 X122.911 Y103.082 E.12216 +G1 X120.022 Y99.911 E.00492 +G1 X129.978 Y99.911 E.31216 +G1 X130.089 Y100.022 E.00492 +G1 X130.089 Y109.978 E.31216 +G1 X129.978 Y110.089 E.00492 +G1 X120.022 Y110.089 E.31216 +G1 X119.911 Y109.978 E.00492 +G1 X119.911 Y100.082 E.31028 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X119.911 Y100.022 E-.0285 +G1 X120.022 Y99.911 E-.07456 +G1 X122.963 Y99.911 E-1.39694 +;WIPE_END ; printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 -G1 X122.911 Y103.082 F9000 -G1 X124.064 Y104.064 +G1 X124.447 Y103.678 F9000 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1200 +G1 X124.479 Y103.598 E.00244 +G1 X124.479 Y103.598 F9000 +G1 X125.396 Y103.044 +G1 F1200 +G1 X125.124 Y103.179 E.00858 +G1 X125.124 Y103.179 F9000 +G1 X126.482 Y104.349 +;WIDTH:0.423559 +G1 F1200 +G1 X126.457 Y104.151 E.00632 +;WIDTH:0.464198 +G1 X126.431 Y103.954 E.00696 +;WIDTH:0.473534 +G1 X126.427 Y103.002 E.03409 +;WIDTH:0.479042 +G1 X126.424 Y102.955 E.00171 +G1 X126.424 Y102.955 F9000 +G1 X127.287 Y101.815 +;WIDTH:0.38292 +G1 F1200 +G1 X127.288 Y102.319 E.01425 +G1 X127.307 Y102.397 E.00227 +G1 X127.307 Y102.397 F9000 +G1 X127.938 Y102.397 +G1 F1200 +G1 X127.307 Y102.397 E.01784 +G1 X127.287 Y102.497 E.00288 +G1 X127.287 Y104.346 E.05227 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.938 Y102.397 E-.97605 +G1 X127.307 Y102.397 E-.29973 +G1 X127.287 Y102.497 E-.04844 +G1 X127.287 Y102.867 E-.17578 +;WIPE_END +G1 X128.157 Y107.636 F9000 +G1 E5 F2400 +;WIDTH:0.424802 +G1 F1200 +G1 X128.251 Y107.625 E.00301 +;WIDTH:0.40164 +G1 X128.474 Y107.518 E.00738 +;WIDTH:0.38292 +G1 X128.983 Y107.56 E.01444 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.157 Y107.636 E-.39401 +G1 X128.251 Y107.625 E-.04495 +G1 X128.474 Y107.518 E-.11749 +G1 X128.983 Y107.56 E-.2426 +;WIPE_END +G1 E-.70095 F3600 +G1 X125.344 Y108.217 F9000 +G1 E5 F2400 +G1 F1200 +G1 X125.344 Y106.367 E.0523 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.344 Y108.217 E-.87875 +G1 X125.344 Y106.909 E-.62125 +;WIPE_END +G1 X122.237 Y106.859 F9000 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.415688 +G1 F1200 +G1 X122.167 Y106.899 E.0025 +G1 X122.237 Y106.94 E.00251 +G1 X122.261 Y106.899 E.00147 +G1 X122.261 Y106.899 F9000 +G1 X122.604 Y107.081 +;TYPE:External perimeter +;WIDTH:0.420559 +G1 F1200 +G1 X122.574 Y107.085 E.00095 +;WIDTH:0.419999 +G1 X122.311 Y107.411 E.01313 +G1 X122.186 Y107.473 E.00437 +G1 X121.975 Y107.449 E.00666 +G1 X121.859 Y107.239 E.00752 +G1 X121.841 Y106.732 E.01591 +G1 X121.896 Y106.549 E.00599 +G1 X122.029 Y106.42 E.00581 +G1 X122.256 Y106.416 E.00712 +G1 X122.566 Y106.698 E.01314 +;WIDTH:0.420559 +G1 X122.727 Y106.705 E.00506 +;WIDTH:0.451368 +G1 X122.975 Y106.661 E.00855 +;WIDTH:0.482176 +G1 X123.224 Y106.617 E.00924 +G1 X123.291 Y106.526 E.00413 +;WIDTH:0.451095 +G1 X123.359 Y106.435 E.00386 +;WIDTH:0.462841 +G1 X123.339 Y106.666 E.0081 +;WIDTH:0.505667 +G1 X123.32 Y106.898 E.00896 +G1 X123.344 Y107.155 E.00993 +;WIDTH:0.462732 +G1 X123.367 Y107.412 E.00901 +;WIDTH:0.450998 +G1 X123.306 Y107.308 E.00409 +;WIDTH:0.482199 +G1 X123.245 Y107.204 E.0044 +;WIDTH:0.505667 +G1 X123.019 Y107.12 E.00928 +G1 X122.866 Y107.094 E.00597 +;WIDTH:0.463113 +G1 X122.713 Y107.068 E.00542 +;WIDTH:0.420559 +G1 X122.663 Y107.074 E.00158 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X122.604 Y107.081 E-.02822 +G1 X122.574 Y107.085 E-.01438 +G1 X122.311 Y107.411 E-.19896 +G1 X122.186 Y107.473 E-.06628 +G1 X121.975 Y107.449 E-.10087 +G1 X121.859 Y107.239 E-.11396 +G1 X121.841 Y106.732 E-.24098 +G1 X121.896 Y106.549 E-.09077 +G1 X122.029 Y106.42 E-.08801 +G1 X122.256 Y106.416 E-.10784 +G1 X122.566 Y106.698 E-.19906 +G1 X122.727 Y106.705 E-.07655 +G1 X122.975 Y106.661 E-.11964 +G1 X123.088 Y106.641 E-.05448 +;WIPE_END +G1 X123.394 Y102.989 F9000 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.415612 +G1 F1200 +G1 X123.324 Y103.029 E.0025 +G1 X123.394 Y103.07 E.00251 +G1 X123.418 Y103.029 E.00147 +G1 X123.418 Y103.029 F9000 +G1 X123.761 Y103.211 +;TYPE:External perimeter +;WIDTH:0.42056 +G1 F1200 +G1 X123.731 Y103.215 E.00095 +;WIDTH:0.419999 +G1 X123.468 Y103.541 E.01313 +G1 X123.343 Y103.603 E.00437 +G1 X123.132 Y103.579 E.00666 +G1 X123.016 Y103.369 E.00752 +G1 X122.998 Y102.862 E.01591 +G1 X123.053 Y102.679 E.00599 +G1 X123.186 Y102.55 E.00581 +G1 X123.413 Y102.546 E.00712 +G1 X123.65 Y102.779 E.01042 +G1 X123.786 Y102.841 E.00469 +;WIDTH:0.448517 +G1 X124.043 Y102.807 E.00874 +;WIDTH:0.494073 +G1 X124.299 Y102.773 E.00969 +;WIDTH:0.507232 +G1 X124.521 Y102.665 E.00953 +G1 X124.406 Y103.016 E.01426 +;WIDTH:0.489034 +G1 X124.488 Y103.175 E.00664 +;WIDTH:0.485906 +G1 X124.659 Y103.224 E.00655 +;WIDTH:0.468874 +G1 X124.556 Y103.379 E.00659 +;WIDTH:0.441887 +G1 X124.499 Y103.479 E.00382 +;WIDTH:0.450403 +G1 X124.451 Y103.406 E.00296 +;WIDTH:0.485906 +G1 X124.402 Y103.333 E.00324 +G1 X124.136 Y103.265 E.01011 +;WIDTH:0.453233 +G1 X123.87 Y103.198 E.00936 +;WIDTH:0.42056 +G1 X123.82 Y103.204 E.00158 +G1 X123.82 Y103.204 F9000 +G1 X124.558 Y102.6 +;WIDTH:0.518522 +G1 F1200 +G1 X124.688 Y102.507 E.00632 +;WIDTH:0.469261 +G1 X124.818 Y102.414 E.00567 +;WIDTH:0.419999 +G1 X125.292 Y102.215 E.01612 +G1 X125.782 Y102.248 E.0154 +G1 X126.16 Y102.466 E.01368 +;WIDTH:0.437878 +G1 X126.342 Y102.69 E.00948 +;WIDTH:0.479042 +G1 X126.424 Y102.955 E.01006 +G1 X126.439 Y102.654 E.01093 +;WIDTH:0.449521 +G1 X126.454 Y102.352 E.01022 +;WIDTH:0.419999 +G1 X126.421 Y101.84 E.01609 +G1 X126.438 Y101.694 E.00461 +G1 X126.619 Y101.59 E.00655 +G1 X127.215 Y101.635 E.01874 +G1 X127.287 Y101.815 E.00608 +G1 X127.385 Y101.629 E.00659 +G1 X127.955 Y101.59 E.01791 +G1 X128.122 Y101.669 E.00579 +G1 X128.156 Y101.791 E.00397 +G1 X128.118 Y102.31 E.01632 +G1 X127.938 Y102.397 E.00627 +G1 X128.115 Y102.469 E.00599 +G1 X128.187 Y102.96 E.01556 +G1 X128.214 Y103.861 E.02826 +G1 X128.185 Y104.416 E.01743 +G1 X128.112 Y104.555 E.00492 +G1 X127.974 Y104.593 E.00449 +G1 X127.39 Y104.521 E.01845 +G1 X127.287 Y104.346 E.00637 +G1 X127.181 Y104.524 E.0065 +G1 X126.642 Y104.525 E.0169 +G1 X126.564 Y104.503 E.00254 +;WIDTH:0.414383 +G1 X126.482 Y104.349 E.00539 +;WIDTH:0.419999 +G1 X126.362 Y104.517 E.00647 +G1 X125.79 Y104.592 E.01809 +G1 X125.658 Y104.546 E.00438 +G1 X125.317 Y104.632 E.01103 +G1 X124.863 Y104.564 E.01439 +G1 X124.475 Y104.15 E.01779 +G1 X124.423 Y103.937 E.00687 +;WIDTH:0.407423 +G1 X124.437 Y103.716 E.00671 +;WIDTH:0.419999 +G1 X124.302 Y104.07 E.01188 +G1 X123.955 Y104.434 E.01577 +G1 X123.687 Y104.583 E.00961 +G1 X123.244 Y104.659 E.01409 +G1 X122.749 Y104.573 E.01575 +G1 X122.283 Y104.268 E.01746 +G1 X121.938 Y103.724 E.0202 +G1 X121.914 Y103.656 E.00226 +G1 X121.839 Y103.124 E.01685 +G1 X121.912 Y102.522 E.01901 +G1 X122.212 Y101.961 E.01995 +G1 X122.498 Y101.718 E.01177 +G1 X122.904 Y101.534 E.01398 +G1 X123.339 Y101.486 E.01372 +G1 X123.852 Y101.591 E.01642 +G1 X124.277 Y101.912 E.0167 +;WIDTH:0.452035 +G1 X124.496 Y102.4 E.0182 +;WIDTH:0.485279 +G1 X124.527 Y102.5 E.00385 +;WIDTH:0.518522 +G1 X124.541 Y102.542 E.00175 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.558 Y102.6 E-.02871 +G1 X124.688 Y102.507 E-.07592 +G1 X124.818 Y102.414 E-.07592 +G1 X125.292 Y102.215 E-.24419 +G1 X125.782 Y102.248 E-.23328 +G1 X126.16 Y102.466 E-.20727 +G1 X126.342 Y102.69 E-.13709 +G1 X126.424 Y102.955 E-.13176 +G1 X126.439 Y102.654 E-.14315 +G1 X126.454 Y102.352 E-.14363 +G1 X126.443 Y102.186 E-.07908 +;WIPE_END +G1 X124.58 Y106.272 F9000 +G1 E5 F2400 +;WIDTH:0.419999 +G1 F1200 +G1 X124.604 Y106.209 E.00211 +G1 X124.734 Y106.179 E.00418 +G1 X125.285 Y106.232 E.01736 +;WIDTH:0.398053 +G1 X125.344 Y106.367 E.00435 +;WIDTH:0.419999 +G1 X125.214 Y105.643 E.02306 +G1 X125.271 Y105.486 E.00524 +G1 X125.401 Y105.434 E.00439 +G1 X126.047 Y105.434 E.02025 +G1 X126.191 Y105.5 E.00497 +G1 X126.234 Y105.645 E.00474 +G1 X126.176 Y106.129 E.01528 +G1 X126.276 Y106.098 E.00328 +G1 X126.774 Y106.147 E.01569 +G1 X127.204 Y106.476 E.01698 +G1 X127.286 Y106.613 E.00501 +G1 X127.426 Y106.415 E.0076 +G1 X127.873 Y106.109 E.01698 +G1 X128.24 Y106.077 E.01155 +G1 X128.619 Y106.148 E.01209 +G1 X129.011 Y106.448 E.01548 +;WIDTH:0.448152 +G1 X129.12 Y106.64 E.00744 +;WIDTH:0.476304 +G1 X129.23 Y106.833 E.00801 +G1 X129.264 Y107.354 E.01881 +;WIDTH:0.497131 +G1 X129.228 Y107.482 E.00502 +G1 X129.152 Y107.526 E.00332 +;WIDTH:0.468951 +G1 X129.076 Y107.57 E.00311 +;WIDTH:0.468524 +G1 X129.146 Y107.629 E.00324 +;WIDTH:0.496277 +G1 X129.216 Y107.689 E.00348 +G1 X129.201 Y107.861 E.00651 +;WIDTH:0.465331 +G1 X128.877 Y108.296 E.01905 +;WIDTH:0.419999 +G1 X128.449 Y108.496 E.01481 +G1 X127.883 Y108.467 E.01777 +G1 X127.433 Y108.154 E.01719 +G1 X127.265 Y107.97 E.00781 +G1 X127.146 Y108.146 E.00666 +G1 X126.687 Y108.444 E.01716 +G1 X126.418 Y108.456 E.00844 +G1 X126.062 Y108.309 E.01208 +G1 X125.947 Y108.395 E.0045 +G1 X125.438 Y108.393 E.01596 +G1 X125.344 Y108.217 E.00626 +G1 X125.251 Y108.393 E.00624 +G1 X124.739 Y108.395 E.01605 +G1 X124.556 Y108.338 E.00601 +G1 X124.448 Y108.455 E.00499 +G1 X123.931 Y108.455 E.01621 +G1 X123.688 Y108.349 E.00831 +G1 X123.431 Y107.981 E.01407 +;WIDTH:0.42071 +G1 X123.37 Y107.732 E.00805 +;WIDTH:0.422363 +G1 X123.355 Y107.485 E.00781 +G1 X123.153 Y107.924 E.01525 +;WIDTH:0.419999 +G1 X122.826 Y108.308 E.01581 +G1 X122.429 Y108.489 E.01368 +G1 X122.086 Y108.529 E.01083 +G1 X121.524 Y108.413 E.01799 +G1 X121.111 Y108.128 E.01573 +;WIDTH:0.443233 +G1 X120.771 Y107.599 E.02093 +G1 X120.685 Y106.993 E.02037 +;WIDTH:0.454774 +G1 X120.738 Y106.391 E.0207 +G1 X121.055 Y105.831 E.02204 +;WIDTH:0.419999 +G1 X121.541 Y105.475 E.01889 +G1 X122.031 Y105.368 E.01573 +G1 X122.504 Y105.407 E.01488 +G1 X122.923 Y105.627 E.01484 +G1 X123.242 Y106.064 E.01696 +;WIDTH:0.420121 +G1 X123.361 Y106.334 E.00925 +G1 X123.493 Y106.187 E.0062 +;WIDTH:0.420002 +G1 X124.065 Y106.179 E.01794 +G1 X124.167 Y106.209 E.00333 +G1 X124.196 Y106.288 E.00264 +;WIDTH:0.391588 +G1 X124.225 Y106.368 E.00247 +;WIDTH:0.363177 +G1 X124.228 Y107.258 E.0237 +;WIDTH:0.361437 +G1 X124.233 Y107.425 E.00442 +;WIDTH:0.35783 +G1 X124.288 Y107.574 E.00416 +G1 X124.384 Y107.611 E.00269 +G1 X124.537 Y107.439 E.00603 +;WIDTH:0.363177 +G1 X124.545 Y106.367 E.02855 +;WIDTH:0.391588 +G1 X124.56 Y106.328 E.00121 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.58 Y106.272 E-.02825 +G1 X124.604 Y106.209 E-.03202 +G1 X124.734 Y106.179 E-.06337 +G1 X125.285 Y106.232 E-.26293 +G1 X125.344 Y106.367 E-.06998 +G1 X125.214 Y105.643 E-.3494 +G1 X125.271 Y105.486 E-.07934 +G1 X125.401 Y105.434 E-.06651 +G1 X126.047 Y105.434 E-.30685 +G1 X126.191 Y105.5 E-.07524 +G1 X126.234 Y105.645 E-.07184 +G1 X126.21 Y105.842 E-.09427 +;WIPE_END +G1 X121.621 Y104.662 F9000 +G1 E5 F2400 ;TYPE:Perimeter +;WIDTH:0.448995 +G1 F1200 +G1 X121.473 Y104.69 E.00509 +G1 X121.079 Y104.86 E.01449 +G1 X121.079 Y104.53 E.01114 +;WIDTH:0.489079 +G1 X121.099 Y104.166 E.01352 +;WIDTH:0.529162 +G1 X121.119 Y103.802 E.01474 +G1 X121.194 Y103.956 E.00693 +;WIDTH:0.492582 +G1 X121.27 Y104.111 E.00645 +;WIDTH:0.456002 +G1 X121.588 Y104.612 E.02038 +G1 X121.588 Y104.612 F9000 +G1 X121.119 Y103.802 +;WIDTH:0.529162 +G1 F1200 +G1 X121.099 Y103.517 E.01155 +;WIDTH:0.488712 +G1 X121.078 Y103.232 E.01059 +;WIDTH:0.484459 +G1 X121.096 Y102.824 E.01499 +;WIDTH:0.520656 +G1 X121.114 Y102.416 E.01622 +G1 X121.114 Y102.416 F9000 +G1 X121.129 Y102.372 +;WIDTH:0.523759 +G1 F1200 +G1 X121.114 Y102.416 E.00186 +G1 X121.116 Y101.116 E.05198 +G1 X121.953 Y101.116 E.03346 +G1 X121.751 Y101.28 E.0104 +G1 X121.509 Y101.566 E.01498 +G1 X121.232 Y102.06 E.02264 +G1 X121.148 Y102.315 E.01073 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.129 Y102.372 E-.02854 +G1 X121.114 Y102.416 E-.02208 +G1 X121.116 Y101.116 E-.6175 +G1 X121.953 Y101.116 E-.39758 +G1 X121.751 Y101.28 E-.12359 +G1 X121.509 Y101.566 E-.17796 +G1 X121.372 Y101.81 E-.13275 +;WIPE_END +G1 X125.711 Y101.464 F9000 +G1 E5 F2400 +;WIDTH:0.433241 +G1 F1200 +G1 X125.292 Y101.456 E.0136 +;WIDTH:0.446624 +G1 X124.934 Y101.521 E.01221 +G1 X124.859 Y101.405 E.00464 +G1 X124.458 Y101.077 E.01739 +G1 X125.583 Y101.071 E.03776 +;WIDTH:0.433241 +G1 X126.136 Y101.071 E.01795 +G1 X125.757 Y101.353 E.01533 +G1 X125.734 Y101.409 E.00196 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.711 Y101.464 E-.02832 +G1 X125.292 Y101.456 E-.19906 +G1 X124.934 Y101.521 E-.17283 +G1 X124.859 Y101.405 E-.06561 +G1 X124.458 Y101.077 E-.24608 +G1 X125.583 Y101.071 E-.53438 +G1 X126.117 Y101.071 E-.25372 +;WIPE_END +G1 X128.827 Y101.173 F9000 +G1 E5 F2400 +;WIDTH:0.637126 +G1 F1200 +G1 X128.851 Y101.324 E.00755 +;WIDTH:0.589214 +G1 X128.875 Y101.475 E.00695 +;WIDTH:0.541301 +G1 X128.899 Y101.627 E.00638 +;WIDTH:0.493389 +G1 X128.923 Y101.778 E.00573 +;WIDTH:0.485542 +G1 X128.903 Y102.367 E.02169 +G1 X128.921 Y102.652 E.01051 +;WIDTH:0.450382 +G1 X128.938 Y102.937 E.00967 +;WIDTH:0.415222 +G1 X128.952 Y103.838 E.0279 +;WIDTH:0.417272 +G1 X128.937 Y104.456 E.01924 +;WIDTH:0.430196 +G1 X128.931 Y104.57 E.00368 +G1 X128.931 Y104.57 F9000 +G1 X128.586 Y105.118 +;WIDTH:0.405026 +G1 F1200 +G1 X128.639 Y105.085 E.00188 +;WIDTH:0.430196 +G1 X128.931 Y104.57 E.01906 +G1 X128.944 Y105.491 E.02966 +;WIDTH:0.403817 +G1 X128.629 Y105.375 E.01007 +;WIDTH:0.406446 +G1 X128.124 Y105.333 E.01532 +G1 X128.328 Y105.281 E.00636 +;WIDTH:0.405026 +G1 X128.536 Y105.15 E.0074 +G1 X128.536 Y105.15 F9000 +G1 X128.124 Y105.333 +;WIDTH:0.429004 +G1 F1200 +G1 X127.881 Y105.346 E.00781 +G1 X127.881 Y105.346 F9000 +G1 X126.943 Y105.436 +;WIDTH:0.368275 +G1 F1200 +G1 X126.864 Y105.253 E.00539 +G1 X127.316 Y105.246 E.01223 +;WIDTH:0.39864 +G1 X127.599 Y105.296 E.0085 +;WIDTH:0.429004 +G1 X127.881 Y105.346 E.00919 +G1 X127.798 Y105.361 E.00271 +;WIDTH:0.416157 +G1 X127.279 Y105.611 E.01788 +;WIDTH:0.368275 +G1 X126.997 Y105.464 E.0086 +G1 X126.997 Y105.464 F9000 +G1 X127.285 Y106.049 +;WIDTH:0.419999 +G1 F1200 +G1 X126.944 Y105.81 E.01306 +G1 X126.602 Y105.742 E.01093 +G1 X126.606 Y105.535 E.00649 +G1 X126.478 Y105.256 E.00962 +G1 X126.382 Y105.166 E.00413 +;WIDTH:0.4592 +G1 X126.27 Y105.11 E.00433 +;WIDTH:0.498401 +G1 X126.159 Y105.054 E.00471 +;WIDTH:0.537602 +G1 X126.047 Y104.998 E.00515 +G1 X126.164 Y104.965 E.005 +;WIDTH:0.501687 +G1 X126.28 Y104.932 E.0046 +;WIDTH:0.465772 +G1 X126.396 Y104.899 E.00424 +;WIDTH:0.429857 +G1 X126.642 Y104.902 E.00792 +;WIDTH:0.419999 +G1 X127.344 Y104.895 E.02201 +G1 X127.928 Y104.967 E.01845 +G1 X128.197 Y104.935 E.00849 +G1 X128.386 Y104.817 E.00699 +G1 X128.562 Y104.436 E.01316 +G1 X128.591 Y103.849 E.01843 +G1 X128.558 Y102.882 E.03034 +G1 X128.494 Y102.337 E.01721 +G1 X128.532 Y101.819 E.01629 +G1 X128.43 Y101.452 E.01194 +;WIDTH:0.457493 +G1 X128.179 Y101.314 E.00987 +;WIDTH:0.494987 +G1 X127.927 Y101.176 E.0108 +;WIDTH:0.51366 +G1 X127.201 Y101.204 E.02844 +G1 X126.502 Y101.181 E.02738 +;WIDTH:0.500333 +G1 X126.301 Y101.353 E.01006 +;WIDTH:0.460166 +G1 X126.1 Y101.525 E.00918 +;WIDTH:0.419999 +G1 X126.072 Y101.593 E.00231 +G1 X126.052 Y101.966 E.01171 +G1 X125.962 Y101.917 E.00321 +G1 X125.705 Y101.848 E.00834 +G1 X125.194 Y101.847 E.01602 +G1 X124.737 Y102.037 E.01552 +G1 X124.611 Y101.738 E.01017 +G1 X124.432 Y101.535 E.00849 +;WIDTH:0.451795 +G1 X124.138 Y101.346 E.01188 +;WIDTH:0.483591 +G1 X123.844 Y101.156 E.01283 +G1 X123.404 Y101.101 E.01625 +;WIDTH:0.471654 +G1 X122.826 Y101.138 E.02065 +G1 X122.581 Y101.258 E.00973 +;WIDTH:0.445827 +G1 X122.335 Y101.378 E.00917 +;WIDTH:0.419999 +G1 X122.021 Y101.613 E.0123 +G1 X121.883 Y101.776 E.0067 +G1 X121.606 Y102.27 E.01776 +G1 X121.538 Y102.472 E.00668 +G1 X121.466 Y103.177 E.02222 +G1 X121.541 Y103.709 E.01685 +G1 X121.612 Y103.913 E.00677 +G1 X121.872 Y104.362 E.01627 +G1 X122.068 Y104.578 E.00915 +;WIDTH:0.467435 +G1 X122.205 Y104.7 E.00648 +;WIDTH:0.514871 +G1 X122.341 Y104.822 E.00717 +;WIDTH:0.562306 +G1 X122.478 Y104.944 E.00792 +G1 X122.362 Y104.955 E.00503 +;WIDTH:0.52029 +G1 X122.246 Y104.967 E.00463 +;WIDTH:0.478274 +G1 X122.131 Y104.978 E.00418 +;WIDTH:0.436258 +G1 X121.571 Y105.07 E.01856 +;WIDTH:0.419999 +G1 X121.329 Y105.164 E.00814 +G1 X120.901 Y105.456 E.01625 +G1 X120.687 Y105.716 E.01056 +G1 X120.687 Y100.687 E.15768 +G1 X122.178 Y100.687 E.04675 +;WIDTH:0.445827 +G1 X122.484 Y100.7 E.01026 +;WIDTH:0.471654 +G1 X122.789 Y100.713 E.01088 +G1 X123.307 Y100.698 E.01847 +;WIDTH:0.483591 +G1 X123.881 Y100.719 E.02105 +G1 X124.289 Y100.703 E.01496 +;WIDTH:0.451795 +G1 X124.698 Y100.687 E.01392 +;WIDTH:0.419999 +G1 X125.583 Y100.687 E.02775 +;WIDTH:0.457497 +G1 X126.122 Y100.706 E.01859 +;WIDTH:0.494994 +G1 X126.662 Y100.725 E.02031 +;WIDTH:0.513661 +G1 X127.968 Y100.724 E.05112 +;WIDTH:0.494052 +G1 X128.64 Y100.706 E.02522 +;WIDTH:0.457026 +G1 X129.313 Y100.687 E.02318 +;WIDTH:0.419999 +G1 X129.313 Y101.173 E.01524 +G1 X129.313 Y106.235 E.15872 +G1 X129.17 Y106.073 E.00678 +G1 X128.836 Y105.839 E.01279 +G1 X128.639 Y105.751 E.00677 +G1 X128.288 Y105.703 E.01111 +G1 X127.919 Y105.726 E.01159 +G1 X127.578 Y105.826 E.01114 +G1 X127.333 Y106.013 E.00966 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.285 Y106.049 E-.0285 +G1 X126.944 Y105.81 E-.1978 +G1 X126.602 Y105.742 E-.16563 +G1 X126.606 Y105.535 E-.09834 +G1 X126.478 Y105.256 E-.14581 +G1 X126.382 Y105.166 E-.06251 +G1 X126.27 Y105.11 E-.05948 +G1 X126.159 Y105.054 E-.05905 +G1 X126.047 Y104.998 E-.05948 +G1 X126.164 Y104.965 E-.05774 +G1 X126.28 Y104.932 E-.05729 +G1 X126.396 Y104.899 E-.05729 +G1 X126.642 Y104.902 E-.11686 +G1 X127.344 Y104.895 E-.33347 +G1 X127.346 Y104.895 E-.00075 +;WIPE_END +G1 X127.288 Y108.92 F9000 +G1 E5 F2400 +;WIDTH:0.379688 +G1 F1200 +G1 X127.225 Y108.956 E.00203 +G1 X127.288 Y108.992 E.00203 +G1 X127.309 Y108.956 E.00117 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.288 Y108.92 E-.0198 +G1 X127.225 Y108.956 E-.03447 +G1 X127.288 Y108.992 E-.03447 +G1 X127.309 Y108.956 E-.0198 +;WIPE_END +G1 E-1.39146 F3600 +G1 X121.058 Y108.927 F9000 +G1 E5 F2400 +;WIDTH:0.367482 +G1 F1200 +G1 X120.997 Y108.962 E.0019 +G1 X121.058 Y108.997 E.0019 +G1 X121.078 Y108.962 E.00109 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.058 Y108.927 E-.01915 +G1 X120.997 Y108.962 E-.03341 +G1 X121.058 Y108.997 E-.03341 +G1 X121.078 Y108.962 E-.01915 +;WIPE_END +G1 E-1.39488 F3600 +G1 X123.252 Y108.901 F9000 +G1 E5 F2400 +;WIDTH:0.409498 +G1 F1200 +G1 X123.183 Y108.941 E.00243 +G1 X123.252 Y108.981 E.00243 +G1 X123.275 Y108.941 E.00141 +G1 X123.275 Y108.941 F9000 +G1 X123.494 Y108.665 +;WIDTH:0.44611 +G1 F1200 +G1 X123.686 Y108.753 E.00708 +;WIDTH:0.47222 +G1 X123.915 Y108.858 E.00899 +G1 X124.346 Y108.87 E.01539 +;WIDTH:0.501858 +G1 X124.739 Y108.813 E.01515 +G1 X125.465 Y108.813 E.0277 +;WIDTH:0.508192 +G1 X126.052 Y108.807 E.02271 +G1 X126.49 Y108.869 E.01711 +;WIDTH:0.482211 +G1 X126.758 Y108.846 E.00983 +G1 X127.018 Y108.679 E.01129 +;WIDTH:0.451105 +G1 X127.279 Y108.512 E.01052 +;WIDTH:0.419999 +G1 X127.487 Y108.675 E.00829 +;WIDTH:0.46692 +G1 X127.849 Y108.866 E.01443 +G1 X128.38 Y108.898 E.01875 +;WIDTH:0.490631 +G1 X128.649 Y108.839 E.01025 +;WIDTH:0.536366 +G1 X128.917 Y108.779 E.01127 +G1 X129.255 Y108.518 E.01752 +G1 X129.255 Y109.255 E.03024 +G1 X128.694 Y109.278 E.02304 +;WIDTH:0.490631 +G1 X128.38 Y109.3 E.01172 +;WIDTH:0.46692 +G1 X127.831 Y109.289 E.01936 +G1 X127.163 Y109.311 E.02356 +;WIDTH:0.46425 +G1 X126.49 Y109.291 E.02359 +;WIDTH:0.501858 +G1 X125.224 Y109.272 E.04832 +G1 X124.337 Y109.291 E.03386 +;WIDTH:0.463737 +G1 X123.415 Y109.313 E.03227 +;WIDTH:0.43747 +G1 X122.119 Y109.306 E.04252 +;WIDTH:0.48214 +G1 X121.49 Y109.282 E.02299 +G1 X121.089 Y109.297 E.01466 +;WIDTH:0.45107 +G1 X120.687 Y109.313 E.01365 +;WIDTH:0.419999 +G1 X120.687 Y108.184 E.0354 +G1 X120.897 Y108.438 E.01033 +;WIDTH:0.45107 +G1 X121.212 Y108.642 E.01274 +;WIDTH:0.48214 +G1 X121.528 Y108.846 E.01374 +G1 X122.02 Y108.91 E.01812 +;WIDTH:0.459667 +G1 X122.503 Y108.879 E.01677 +G1 X123.037 Y108.623 E.02052 +;WIDTH:0.419999 +G1 X123.255 Y108.405 E.00967 +G1 X123.445 Y108.633 E.00931 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.494 Y108.665 E-.0278 +G1 X123.686 Y108.753 E-.10032 +G1 X123.915 Y108.858 E-.11966 +G1 X124.346 Y108.87 E-.2048 +G1 X124.739 Y108.813 E-.18863 +G1 X125.465 Y108.813 E-.34485 +G1 X126.052 Y108.807 E-.27884 +G1 X126.49 Y108.869 E-.21012 +G1 X126.542 Y108.864 E-.02498 +;WIPE_END +G1 X124.329 Y105.079 F9000 +G1 E5 F2400 +;WIDTH:0.391269 +G1 F1200 +G1 X124.56 Y105.239 E.00814 +G1 X124.488 Y105.467 E.00693 +G1 X124.421 Y105.483 E.002 +G1 X123.734 Y105.439 E.01994 +G1 X123.638 Y105.347 E.00385 +G1 X124.024 Y105.247 E.01155 +G1 X124.277 Y105.108 E.00836 +G1 X124.277 Y105.108 F9000 +G1 X124.4 Y105.891 +;WIDTH:0.419999 +G1 F1200 +G1 X124.238 Y105.829 E.00544 +G1 X124.065 Y105.802 E.00549 +G1 X123.513 Y105.785 E.01732 +G1 X123.085 Y105.268 E.02104 +;WIDTH:0.459402 +G1 X122.943 Y105.179 E.0058 +;WIDTH:0.498804 +G1 X122.801 Y105.089 E.00637 +;WIDTH:0.538206 +G1 X122.659 Y104.999 E.00692 +G1 X122.682 Y105 E.00095 +;WIDTH:0.531422 +G1 X122.912 Y105.01 E.00935 +;WIDTH:0.494281 +G1 X123.142 Y105.021 E.00864 +;WIDTH:0.45714 +G1 X123.372 Y105.031 E.00793 ;WIDTH:0.419999 +G1 X123.762 Y104.952 E.01248 +G1 X124.05 Y104.823 E.00989 +G1 X124.341 Y104.576 E.01197 +G1 X124.754 Y104.934 E.01714 +;WIDTH:0.469371 +G1 X125.283 Y105.032 E.01908 +G1 X125.011 Y105.212 E.01157 +;WIDTH:0.419999 +G1 X124.859 Y105.462 E.00917 +G1 X124.852 Y105.802 E.01066 +G1 X124.6 Y105.811 E.00791 +G1 X124.456 Y105.869 E.00487 +G1 X124.456 Y105.869 F9000 +G1 X122.659 Y104.999 +;WIDTH:0.562306 G1 F1200 -G1 X125.936 Y104.064 E.0587 -G1 X125.936 Y105 E.02935 -G1 X125.936 Y105.936 E.02935 -G1 X124.064 Y105.936 E.0587 -G1 X124.064 Y104.124 E.05681 -G1 X124.064 Y104.124 F9000 -G1 X123.687 Y103.687 +G1 X122.478 Y104.944 E.00817 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X122.659 Y104.999 E-.08986 +G1 X122.478 Y104.944 E-.08986 +;WIPE_END +G1 E-1.32028 F3600 +G1 X125.315 Y105.036 F9000 +G1 E5 F2400 +;WIDTH:0.511506 G1 F1200 -G1 X126.313 Y103.687 E.08234 -G1 X126.313 Y105 E.04117 -G1 X126.313 Y106.313 E.04117 -G1 X123.687 Y106.313 E.08234 -G1 X123.687 Y103.747 E.08045 -G1 X123.31 Y103.31 F9000 +G1 X125.845 Y105.011 E.02067 +;WIDTH:0.537602 +G1 X126.047 Y104.998 E.00833 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.315 Y105.036 E-.34817 +G1 X125.845 Y105.011 E-.25203 +G1 X126.047 Y104.998 E-.09615 +;WIPE_END +G1 E-.80365 F3600 +G1 X129.69 Y100.31 F9000 +G1 E5 F2400 ;TYPE:External perimeter +;WIDTH:0.419999 G1 F1200 -G1 X126.69 Y103.31 E.10598 -G1 X126.69 Y105 E.05299 -G1 X126.69 Y106.69 E.05299 -G1 X123.31 Y106.69 E.10598 -G1 X123.31 Y103.37 E.1041 +G1 X129.69 Y101.173 E.02706 +G1 X129.69 Y106.279 E.16009 +;WIDTH:0.448152 +G1 X129.676 Y106.545 E.00898 +;WIDTH:0.476304 +G1 X129.662 Y106.811 E.0096 +G1 X129.673 Y107.39 E.02087 +;WIDTH:0.454948 +G1 X129.673 Y108.034 E.02206 +;WIDTH:0.453367 +G1 X129.69 Y108.566 E.01817 +;WIDTH:0.419999 +G1 X129.69 Y109.69 E.03524 +G1 X120.31 Y109.69 E.2941 +G1 X120.31 Y108.267 E.04462 +;WIDTH:0.456155 +G1 X120.328 Y107.555 E.02447 +G1 X120.31 Y106.92 E.02183 +;WIDTH:0.440442 +G1 X120.31 Y105.636 E.04244 +;WIDTH:0.419999 +G1 X120.31 Y100.31 E.16699 +G1 X129.63 Y100.31 E.29222 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X123.31 Y103.31 E-.0285 -G1 X126.408 Y103.31 E-1.4715 +G1 X129.69 Y100.31 E-.0285 +G1 X129.69 Y101.173 E-.40992 +G1 X129.69 Y103.408 E-1.06158 ;WIPE_END -G1 X125.815 Y104.981 F9000 +G1 X126.418 Y106.95 F9000 G1 E5 F2400 -;TYPE:Solid infill -;WIDTH:0.436085 +;WIDTH:0.399723 +G1 F1200 +G1 X126.478 Y106.973 E.00191 +G1 X126.534 Y107.14 E.00523 +;WIDTH:0.401347 +G1 X126.533 Y107.47 E.00984 +G1 X126.432 Y107.63 E.00564 +G1 X126.289 Y107.62 E.00427 +G1 X126.198 Y107.492 E.00468 +G1 X126.178 Y107.137 E.0106 +;WIDTH:0.399723 +G1 X126.231 Y106.973 E.00511 +G1 X126.345 Y106.922 E.00371 +G1 X126.362 Y106.929 E.00055 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.418 Y106.95 E-.02841 +G1 X126.478 Y106.973 E-.03052 +G1 X126.534 Y107.14 E-.08367 +G1 X126.533 Y107.47 E-.15675 +G1 X126.432 Y107.63 E-.08988 +G1 X126.289 Y107.62 E-.06809 +G1 X126.198 Y107.492 E-.0746 +G1 X126.178 Y107.137 E-.16889 +G1 X126.231 Y106.973 E-.08187 +G1 X126.345 Y106.922 E-.05932 +G1 X126.362 Y106.929 E-.00873 +;WIPE_END +G1 E-.64927 F3600 +G1 X125.329 Y103.787 F9000 +G1 E5 F2400 +;WIDTH:0.4417 G1 F1200 -G1 X125.188 Y104.355 E.02897 -G1 X124.632 Y104.355 E.01818 -G1 X125.645 Y105.368 E.04683 -G1 X125.645 Y105.645 E.00906 -G1 X125.367 Y105.645 E.00909 -G1 X124.355 Y104.633 E.04679 -G1 X124.355 Y105.189 E.01818 -G1 X124.981 Y105.815 E.02894 +G1 X125.435 Y103.764 E.0036 M106 S255 ;LAYER_CHANGE ;Z:0.4 @@ -467,131 +1363,155 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.355 Y105.189 E-.42052 -G1 X124.355 Y104.633 E-.2641 -G1 X125.367 Y105.645 E-.67981 -G1 X125.645 Y105.645 E-.13205 -G1 X125.645 Y105.638 E-.00352 +G1 X125.329 Y103.787 E-.05152 +G1 X125.435 Y103.764 E-.05152 ;WIPE_END +G1 E-1.39696 F3600 G1 Z.4 F9000 ;AFTER_LAYER_CHANGE ;0.4 M104 S205 ; set temperature ; stop printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 G1 Z.4 -G1 X124.981 Y115.466 +G1 X125.435 Y91.534 G1 E5 F2400 ;TYPE:Skirt/Brim ;WIDTH:0.42 -G1 F900 -G1 X121.029 Y115.466 E.12391 -G1 X119.399 Y115.071 E.05259 -G1 X118.508 Y114.421 E.03458 -G1 X115.579 Y111.492 E.12988 -G1 X114.705 Y110.061 E.05257 -G1 X114.534 Y108.971 E.03459 -G1 X114.534 Y101.029 E.24902 -G1 X114.929 Y99.399 E.05259 -G1 X115.579 Y98.508 E.03458 -G1 X118.508 Y95.579 E.12988 -G1 X119.939 Y94.705 E.05257 -G1 X121.029 Y94.534 E.03459 -G1 X128.971 Y94.534 E.24902 -G1 X130.601 Y94.929 E.05259 -G1 X131.492 Y95.579 E.03458 -G1 X134.421 Y98.508 E.12988 -G1 X135.295 Y99.939 E.05257 -G1 X135.466 Y101.029 E.03459 -G1 X135.466 Y108.971 E.24902 -G1 X135.071 Y110.601 E.05259 -G1 X134.421 Y111.492 E.03458 -G1 X131.492 Y114.421 E.12988 -G1 X130.061 Y115.295 E.05257 -G1 X128.971 Y115.466 E.03459 -G1 X125.041 Y115.466 E.12322 -G1 X125.041 Y115.466 F9000 -G1 X125.041 Y115.089 -G1 F900 -G1 X121.029 Y115.089 E.12579 -G1 X119.494 Y114.695 E.04969 -G1 X118.774 Y114.155 E.02822 -G1 X115.845 Y111.226 E.12988 -G1 X115.038 Y109.862 E.04969 -G1 X114.911 Y108.971 E.02822 -G1 X114.911 Y101.029 E.24902 -G1 X115.305 Y99.494 E.04969 -G1 X115.845 Y98.774 E.02822 -G1 X118.774 Y95.845 E.12988 -G1 X120.138 Y95.038 E.04969 -G1 X121.029 Y94.911 E.02822 -G1 X128.971 Y94.911 E.24902 -G1 X130.506 Y95.305 E.04969 -G1 X131.226 Y95.845 E.02822 -G1 X134.155 Y98.774 E.12988 -G1 X134.962 Y100.138 E.04969 -G1 X135.089 Y101.029 E.02822 -G1 X135.089 Y108.971 E.24902 -G1 X134.695 Y110.506 E.04969 -G1 X134.155 Y111.226 E.02822 -G1 X131.226 Y114.155 E.12988 -G1 X129.862 Y114.962 E.04969 -G1 X128.971 Y115.089 E.02822 -G1 X125.101 Y115.089 E.12134 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X125.041 Y115.089 E-.0285 -G1 X121.943 Y115.089 E-1.4715 +G1 F1339 +G1 X131.971 Y91.534 E.20493 +G1 X133.601 Y91.929 E.05259 +G1 X134.492 Y92.579 E.03458 +G1 X137.421 Y95.508 E.12988 +G1 X138.295 Y96.939 E.05257 +G1 X138.466 Y98.029 E.03459 +G1 X138.466 Y111.971 E.43714 +G1 X138.071 Y113.601 E.05259 +G1 X137.421 Y114.492 E.03458 +G1 X134.492 Y117.421 E.12988 +G1 X133.061 Y118.295 E.05257 +G1 X131.971 Y118.466 E.03459 +G1 X118.029 Y118.466 E.43714 +G1 X116.399 Y118.071 E.05259 +G1 X115.508 Y117.421 E.03458 +G1 X112.579 Y114.492 E.12988 +G1 X111.705 Y113.061 E.05257 +G1 X111.534 Y111.973 E.03453 +G1 X111.534 Y98.028 E.43724 +G1 X111.929 Y96.398 E.05259 +G1 X112.579 Y95.508 E.03456 +G1 X115.508 Y92.579 E.12988 +G1 X116.939 Y91.705 E.05257 +G1 X118.029 Y91.534 E.03459 +G1 X125.375 Y91.534 E.23033 +G1 X125.375 Y91.534 F9000 +G1 X125.375 Y91.911 +G1 F1339 +G1 X131.971 Y91.911 E.20681 +G1 X133.506 Y92.305 E.04969 +G1 X134.226 Y92.845 E.02822 +G1 X137.155 Y95.774 E.12988 +G1 X137.962 Y97.138 E.04969 +G1 X138.089 Y98.029 E.02822 +G1 X138.089 Y111.971 E.43714 +G1 X137.695 Y113.506 E.04969 +G1 X137.155 Y114.226 E.02822 +G1 X134.226 Y117.155 E.12988 +G1 X132.862 Y117.962 E.04969 +G1 X131.971 Y118.089 E.02822 +G1 X118.029 Y118.089 E.43714 +G1 X116.494 Y117.695 E.04969 +G1 X115.774 Y117.155 E.02822 +G1 X112.845 Y114.226 E.12988 +G1 X112.038 Y112.862 E.04969 +G1 X111.911 Y111.973 E.02816 +G1 X111.911 Y98.028 E.43724 +G1 X112.306 Y96.493 E.0497 +G1 X112.845 Y95.774 E.02817 +G1 X115.774 Y92.845 E.12988 +G1 X117.138 Y92.038 E.04969 +G1 X118.029 Y91.911 E.02822 +G1 X125.315 Y91.911 E.22845 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.375 Y91.911 E-.0285 +G1 X128.473 Y91.911 E-1.4715 ;WIPE_END ; printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 -G1 X126.006 Y106.006 F9000 +G1 X129.006 Y100.994 F9000 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 -G1 F900 -G1 X123.994 Y106.006 E.06643 -G1 X123.994 Y103.994 E.06643 -G1 X126.006 Y103.994 E.06643 -G1 X126.006 Y105 E.03322 -G1 X126.006 Y105.946 E.03123 -G1 X126.006 Y105.946 F9000 -G1 X126.403 Y106.403 -G1 F900 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y103.597 E.09265 -G1 X126.403 Y103.597 E.09265 -G1 X126.403 Y105 E.04632 -G1 X126.403 Y106.343 E.04434 -G1 X126.79 Y106.79 F9000 +G1 F1339 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F1339 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 -G1 F900 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y103.21 E.11225 -G1 X126.79 Y103.21 E.11225 -G1 X126.79 Y105 E.05612 -G1 X126.79 Y106.73 E.05424 +G1 F1339 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X126.79 Y106.79 E-.0285 -G1 X123.692 Y106.79 E-1.4715 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 ;WIPE_END -G1 X125.502 Y104.3 F9000 +G1 X121.331 Y101.123 F9000 G1 E5 F2400 -;TYPE:Solid infill -;WIDTH:0.439999 -G1 F900 -G1 X124.3 Y104.3 E.03969 -G1 X124.3 Y105.7 E.04622 -G1 X125.7 Y105.7 E.04622 -G1 X125.7 Y104.3 E.04622 -G1 X125.251 Y104.749 E.02097 -;WIDTH:0.544473 -G1 X125.251 Y105.251 E.02094 -G1 X124.749 Y105.251 E.02094 -G1 X124.749 Y104.749 E.02094 -G1 X125.052 Y104.749 E.01264 +M204 S250 +;TYPE:Bridge infill +;WIDTH:0.408679 +;HEIGHT:0.389872 +G1 F1500 +G1 X121.331 Y108.679 E.41208 +G1 X121.789 Y108.679 E.02498 +G1 X121.789 Y101.321 E.40128 +G1 X122.248 Y101.321 E.02503 +G1 X122.248 Y108.679 E.40128 +G1 X122.707 Y108.679 E.02503 +G1 X122.707 Y101.321 E.40128 +G1 X123.165 Y101.321 E.02498 +G1 X123.165 Y108.679 E.40128 +G1 X123.624 Y108.679 E.02503 +G1 X123.624 Y101.321 E.40128 +G1 X124.083 Y101.321 E.02503 +G1 X124.083 Y108.679 E.40128 +G1 X124.542 Y108.679 E.02503 +G1 X124.542 Y101.321 E.40128 +G1 X125 Y101.321 E.02498 +G1 X125 Y108.679 E.40128 +G1 X125.459 Y108.679 E.02503 +G1 X125.459 Y101.321 E.40128 +G1 X125.918 Y101.321 E.02503 +G1 X125.918 Y108.679 E.40128 +G1 X126.376 Y108.679 E.02498 +G1 X126.376 Y101.321 E.40128 +G1 X126.835 Y101.321 E.02503 +G1 X126.835 Y108.679 E.40128 +G1 X127.294 Y108.679 E.02503 +G1 X127.294 Y101.321 E.40128 +G1 X127.752 Y101.321 E.02498 +G1 X127.752 Y108.679 E.40128 +G1 X128.211 Y108.679 E.02503 +G1 X128.211 Y101.321 E.40128 +G1 X128.67 Y101.321 E.02503 +G1 X128.67 Y108.877 E.41208 +M204 S500 ;LAYER_CHANGE ;Z:0.6 ;HEIGHT:0.2 @@ -603,66 +1523,136 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.749 Y104.749 E-.14392 -G1 X124.749 Y105.251 E-.23845 -G1 X125.251 Y105.251 E-.23845 -G1 X125.251 Y104.749 E-.23845 -G1 X125.7 Y104.3 E-.30162 -G1 X125.7 Y105.014 E-.33911 +G1 X128.67 Y105.719 E-1.5 ;WIPE_END G1 Z.6 F9000 ;AFTER_LAYER_CHANGE ;0.6 G1 Z.6 -G1 X126.006 Y103.994 +G1 X129.006 Y109.006 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X126.006 Y105 E.03322 -G1 X126.006 Y106.006 E.03322 -G1 X123.994 Y106.006 E.06643 -G1 X123.994 Y103.994 E.06643 -G1 X125.946 Y103.994 E.06445 -G1 X125.946 Y103.994 F9000 -G1 X126.403 Y103.597 -G1 F900 -G1 X126.403 Y105 E.04632 -G1 X126.403 Y106.403 E.04632 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y103.597 E.09265 -G1 X126.343 Y103.597 E.09067 -G1 X126.79 Y103.21 F9000 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X126.79 Y105 E.05612 -G1 X126.79 Y106.79 E.05612 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y103.21 E.11225 -G1 X126.73 Y103.21 E.11037 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X126.79 Y103.21 E-.0285 -G1 X126.79 Y105 E-.85025 -G1 X126.79 Y106.308 E-.62125 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 ;WIPE_END -G1 X124.3 Y105.7 F9000 +G1 X121.332 Y108.592 F9000 G1 E5 F2400 ;TYPE:Solid infill +;WIDTH:0.53634 +G1 F900 +G1 X121.289 Y108.636 E.00252 +G1 X121.304 Y108.696 E.00254 +G1 X121.364 Y108.712 E.00255 +G1 X121.408 Y108.668 E.00255 +G1 X121.392 Y108.608 E.00255 +;WIDTH:0.439999 +G1 X121.3 Y107.974 E.02115 +G1 X122.026 Y108.7 E.0339 +G1 X122.587 Y108.7 E.01852 +G1 X121.3 Y107.413 E.06009 +G1 X121.3 Y106.851 E.01856 +G1 X123.149 Y108.7 E.08634 +G1 X123.711 Y108.7 E.01856 +G1 X121.3 Y106.289 E.11258 +G1 X121.3 Y105.728 E.01852 +G1 X124.272 Y108.7 E.13877 +G1 X124.834 Y108.7 E.01856 +G1 X121.3 Y105.166 E.16501 +G1 X121.3 Y104.605 E.01852 +G1 X125.395 Y108.7 E.19121 +G1 X125.957 Y108.7 E.01856 +G1 X121.3 Y104.043 E.21745 +G1 X121.3 Y103.482 E.01852 +G1 X126.518 Y108.7 E.24365 +G1 X127.08 Y108.7 E.01856 +G1 X121.3 Y102.92 E.26989 +G1 X121.3 Y102.359 E.01852 +G1 X127.641 Y108.7 E.29608 +G1 X128.203 Y108.7 E.01856 +G1 X121.3 Y101.797 E.32232 +G1 X121.152 Y101.323 E.0164 +;WIDTH:0.38292 +G1 X121.162 Y101.162 E.00456 +;WIDTH:0.41146 +G1 X121.263 Y101.231 E.00375 +;WIDTH:0.439999 +G1 X121.364 Y101.3 E.00404 +G1 X128.7 Y108.636 E.34254 +G1 X128.689 Y108.742 E.00352 +;WIDTH:0.41146 +G1 X128.677 Y108.848 E.00327 +;WIDTH:0.38292 +G1 X128.838 Y108.838 E.00456 +;WIDTH:0.439999 +G1 X128.7 Y108.074 E.02563 +G1 X121.926 Y101.3 E.3163 +G1 X122.488 Y101.3 E.01856 +G1 X128.7 Y107.512 E.29006 +G1 X128.7 Y106.951 E.01852 +G1 X123.049 Y101.3 E.26386 +G1 X123.611 Y101.3 E.01856 +G1 X128.7 Y106.389 E.23762 +G1 X128.7 Y105.828 E.01852 +G1 X124.172 Y101.3 E.21143 +G1 X124.734 Y101.3 E.01856 +G1 X128.7 Y105.266 E.18519 +G1 X128.7 Y104.705 E.01852 +G1 X125.295 Y101.3 E.15899 +G1 X125.857 Y101.3 E.01856 +G1 X128.7 Y104.143 E.13275 +G1 X128.7 Y103.582 E.01852 +G1 X126.418 Y101.3 E.10655 +G1 X126.817 Y101.089 E.0149 +;WIDTH:0.38292 +G1 X126.797 Y101.16 E.00209 +G1 X126.869 Y101.141 E.00211 +G1 X126.858 Y101.1 E.0012 +;WIDTH:0.41146 +G1 X126.919 Y101.2 E.00359 ;WIDTH:0.439999 +G1 X126.98 Y101.3 E.00387 +G1 X128.7 Y103.02 E.08031 +G1 X128.8 Y103.081 E.00387 +;WIDTH:0.41146 +G1 X128.9 Y103.142 E.00359 +;WIDTH:0.38292 +G1 X128.911 Y103.183 E.0012 +G1 X128.84 Y103.203 E.00209 +G1 X128.859 Y103.131 E.00211 +G1 X128.859 Y103.131 F9000 +G1 X127.852 Y101.511 +;WIDTH:0.580797 G1 F900 -G1 X124.3 Y104.3 E.04622 -G1 X125.7 Y104.3 E.04622 -G1 X125.7 Y105.7 E.04622 -G1 X124.498 Y105.7 E.03969 -G1 X124.749 Y105.251 E.01698 -;WIDTH:0.544473 -G1 X124.749 Y104.749 E.02094 -G1 X125.251 Y104.749 E.02094 -G1 X125.251 Y105.251 E.02094 -G1 X124.948 Y105.251 E.01264 +G1 X128.63 Y102.289 E.04921 +G1 X128.63 Y101.37 E.0411 +G1 X127.711 Y101.37 E.0411 ;LAYER_CHANGE ;Z:0.8 ;HEIGHT:0.2 @@ -674,131 +1664,122 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.251 Y105.251 E-.14392 -G1 X125.251 Y104.749 E-.23845 -G1 X124.749 Y104.749 E-.23845 -G1 X124.749 Y105.251 E-.23845 -G1 X124.498 Y105.7 E-.24434 -G1 X125.333 Y105.7 E-.39639 +G1 X128.63 Y101.37 E-.43652 +G1 X128.63 Y102.289 E-.43652 +G1 X127.852 Y101.511 E-.52262 ;WIPE_END +G1 E-.10434 F3600 G1 Z.8 F9000 ;AFTER_LAYER_CHANGE ;0.8 G1 Z.8 -G1 X123.996 Y105.988 +G1 X129.006 Y100.994 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.008 Y105.894 E.00313 -G1 X124.194 Y105.321 E.01989 -G1 X124.194 Y104.679 E.0212 -G1 X124.136 Y104.347 E.01113 -G1 X124.049 Y104.194 E.00581 -G1 X124.441 Y104.174 E.01296 -G1 X124.655 Y104.103 E.00744 -G1 X124.768 Y103.994 E.00518 -G1 X125.22 Y103.994 E.01492 -G1 X125.261 Y104.065 E.00271 -G1 X125.509 Y104.165 E.00883 -G1 X126.003 Y104.197 E.01634 -G1 X125.884 Y104.406 E.00794 -G1 X125.818 Y104.634 E.00784 -G1 X125.806 Y105.189 E.01833 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y106.006 E.00878 -G1 X124.036 Y106.006 E.06504 -G1 X124.036 Y106.006 F9000 -G1 X123.602 Y105.703 -G1 F900 -G1 X123.687 Y105.66 E.00315 -G1 X123.797 Y105.321 E.01177 -G1 X123.791 Y104.596 E.02394 -G1 X123.668 Y104.316 E.0101 -G1 X123.597 Y104.282 E.0026 -G1 X123.597 Y103.69 E.01955 -G1 X123.787 Y103.797 E.0072 -G1 X124.243 Y103.797 E.01506 -G1 X124.487 Y103.743 E.00825 -G1 X124.601 Y103.597 E.00612 -G1 X125.387 Y103.597 E.02595 -G1 X125.458 Y103.721 E.00472 -G1 X125.605 Y103.78 E.00523 -G1 X126.213 Y103.797 E.02008 -G1 X126.403 Y103.69 E.0072 -G1 X126.403 Y104.399 E.02341 -G1 X126.321 Y104.44 E.00303 -G1 X126.21 Y104.697 E.00924 -G1 X126.203 Y105.176 E.01582 -G1 X126.303 Y105.5 E.0112 -G1 X126.403 Y105.553 E.00374 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.76 E.02123 -G1 X123.345 Y105.448 F9000 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F900 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.374 Y105.432 E.00104 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.368 Y104.56 E.00396 -G1 X123.21 Y104.49 E.00542 -G1 X123.21 Y103.21 E.04013 -G1 X123.587 Y103.21 E.01182 -G1 X123.625 Y103.318 E.00359 -G1 X123.787 Y103.41 E.00584 -G1 X124.307 Y103.399 E.01631 -G1 X124.431 Y103.21 E.00709 -G1 X125.545 Y103.21 E.03493 -G1 X125.651 Y103.385 E.00642 -G1 X125.699 Y103.404 E.00162 -G1 X126.213 Y103.41 E.01612 -G1 X126.375 Y103.318 E.00584 -G1 X126.402 Y103.221 E.00316 -G1 X126.79 Y103.21 E.01217 -G1 X126.779 Y104.6 E.04358 -G1 X126.605 Y104.715 E.00654 -G1 X126.59 Y105.176 E.01446 -G1 X126.623 Y105.282 E.00348 -G1 X126.79 Y105.376 E.00601 -G1 X126.79 Y106.79 E.04433 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.292 Y105.477 E.00292 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X123.345 Y105.448 E-.0287 -G1 X123.374 Y105.432 E-.01573 -G1 X123.41 Y105.321 E-.05543 -G1 X123.41 Y104.679 E-.30495 -G1 X123.368 Y104.56 E-.05994 -G1 X123.21 Y104.49 E-.08209 -G1 X123.21 Y103.21 E-.608 -G1 X123.587 Y103.21 E-.17907 -G1 X123.625 Y103.318 E-.05438 -G1 X123.787 Y103.41 E-.08849 -G1 X123.836 Y103.409 E-.02322 -;WIPE_END -G1 X124.995 Y104.3 F9000 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 +;WIPE_END +G1 X121.408 Y101.332 F9000 G1 E5 F2400 ;TYPE:Solid infill -;WIDTH:0.439999 +;WIDTH:0.53634 G1 F900 -G1 X125.547 Y104.476 E.01913 -G1 X125.515 Y104.612 E.00461 -G1 X125.501 Y105.185 E.01892 -G1 X125.655 Y105.7 E.01775 -G1 X124.39 Y105.7 E.04177 -G1 X124.491 Y105.364 E.01158 -G1 X124.5 Y104.834 E.0175 -G1 X124.464 Y104.463 E.01231 -G1 X124.644 Y104.432 E.00603 -G1 X124.808 Y104.347 E.0061 -G1 X125.004 Y104.834 E.01733 -;WIDTH:0.654978 -G1 X125 Y105.2 E.01863 +G1 X121.364 Y101.289 E.00252 +G1 X121.304 Y101.304 E.00254 +G1 X121.289 Y101.364 E.00254 +G1 X121.332 Y101.408 E.00252 +G1 X121.392 Y101.392 E.00255 +;WIDTH:0.439999 +G1 X121.3 Y102.026 E.02115 +G1 X122.026 Y101.3 E.0339 +G1 X122.587 Y101.3 E.01852 +G1 X121.3 Y102.587 E.06009 +G1 X121.3 Y103.149 E.01856 +G1 X123.149 Y101.3 E.08634 +G1 X123.711 Y101.3 E.01856 +G1 X121.3 Y103.711 E.11258 +G1 X121.3 Y104.272 E.01852 +G1 X124.272 Y101.3 E.13877 +G1 X124.834 Y101.3 E.01856 +G1 X121.3 Y104.834 E.16501 +G1 X121.3 Y105.395 E.01852 +G1 X125.395 Y101.3 E.19121 +G1 X125.957 Y101.3 E.01856 +G1 X121.3 Y105.957 E.21745 +G1 X121.3 Y106.518 E.01852 +G1 X126.518 Y101.3 E.24365 +G1 X127.08 Y101.3 E.01856 +G1 X121.3 Y107.08 E.26989 +G1 X121.3 Y107.641 E.01852 +G1 X127.641 Y101.3 E.29608 +G1 X128.203 Y101.3 E.01856 +G1 X121.3 Y108.203 E.32232 +G1 X121.152 Y108.677 E.0164 +;WIDTH:0.38292 +G1 X121.162 Y108.838 E.00456 +;WIDTH:0.41146 +G1 X121.263 Y108.769 E.00375 +;WIDTH:0.439999 +G1 X121.364 Y108.7 E.00404 +G1 X128.7 Y101.364 E.34254 +G1 X128.769 Y101.263 E.00404 +;WIDTH:0.41146 +G1 X128.838 Y101.162 E.00375 +;WIDTH:0.38292 +G1 X128.677 Y101.152 E.00456 +;WIDTH:0.439999 +G1 X128.7 Y101.926 E.02557 +G1 X121.926 Y108.7 E.3163 +G1 X122.488 Y108.7 E.01856 +G1 X128.7 Y102.488 E.29006 +G1 X128.7 Y103.049 E.01852 +G1 X123.049 Y108.7 E.26386 +G1 X123.611 Y108.7 E.01856 +G1 X128.7 Y103.611 E.23762 +G1 X128.7 Y104.172 E.01852 +G1 X124.172 Y108.7 E.21143 +G1 X124.734 Y108.7 E.01856 +G1 X128.7 Y104.734 E.18519 +G1 X128.7 Y105.295 E.01852 +G1 X125.295 Y108.7 E.15899 +G1 X125.857 Y108.7 E.01856 +G1 X128.7 Y105.857 E.13275 +G1 X128.7 Y106.418 E.01852 +G1 X126.418 Y108.7 E.10655 +G1 X126.98 Y108.7 E.01856 +G1 X128.7 Y106.98 E.08031 +G1 X128.63 Y107.711 E.02425 +;WIDTH:0.580797 +G1 X128.63 Y108.63 E.0411 +G1 X127.711 Y108.63 E.0411 +G1 X128.489 Y107.852 E.04921 ;LAYER_CHANGE ;Z:1 ;HEIGHT:0.2 @@ -810,119 +1791,653 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.004 Y104.834 E-.17386 -G1 X124.808 Y104.347 E-.24936 -G1 X124.644 Y104.432 E-.08774 -G1 X124.464 Y104.463 E-.08676 -G1 X124.5 Y104.834 E-.17705 -G1 X124.491 Y105.364 E-.25179 -G1 X124.39 Y105.7 E-.16665 -G1 X125.036 Y105.7 E-.30679 +G1 X127.711 Y108.63 E-.52262 +G1 X128.63 Y108.63 E-.43652 +G1 X128.63 Y107.711 E-.43652 +G1 X128.651 Y107.492 E-.10434 ;WIPE_END G1 Z1 F9000 ;AFTER_LAYER_CHANGE ;1 G1 Z1 -G1 X126.006 Y105.823 +G1 X129.006 Y109.006 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X126.006 Y106.006 E.00604 -G1 X123.994 Y106.006 E.06643 -G1 X124.008 Y105.894 E.00373 -G1 X124.194 Y105.321 E.01989 -G1 X124.189 Y104.583 E.02437 -G1 X124.043 Y104.194 E.01372 -G1 X124.387 Y104.194 E.01136 -G1 X124.721 Y104.135 E.0112 -G1 X124.969 Y103.994 E.00942 -G1 X125.177 Y104.096 E.00765 -G1 X125.446 Y104.181 E.00931 -G1 X126.003 Y104.197 E.0184 -G1 X125.845 Y104.516 E.01175 -G1 X125.813 Y104.671 E.00523 -G1 X125.806 Y105.189 E.0171 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y105.763 E.00076 -G1 X126.006 Y105.763 F9000 -G1 X126.403 Y105.553 -G1 F900 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.706 E.02301 -G1 X123.687 Y105.66 E.00334 -G1 X123.797 Y105.321 E.01177 -G1 X123.794 Y104.622 E.02308 -G1 X123.676 Y104.326 E.01052 -G1 X123.597 Y104.282 E.00299 -G1 X123.597 Y103.632 E.02146 -G1 X123.935 Y103.797 E.01242 -G1 X124.458 Y103.793 E.01727 -G1 X124.728 Y103.685 E.0096 -G1 X124.773 Y103.597 E.00326 -G1 X125.218 Y103.597 E.01469 -G1 X125.262 Y103.685 E.00325 -G1 X125.51 Y103.789 E.00888 -G1 X126.07 Y103.797 E.01849 -G1 X126.403 Y103.632 E.01227 -G1 X126.403 Y104.399 E.02532 -G1 X126.321 Y104.44 E.00303 -G1 X126.226 Y104.627 E.00693 -G1 X126.203 Y105.176 E.01814 -G1 X126.303 Y105.5 E.0112 -G1 X126.35 Y105.525 E.00176 -G1 X126.35 Y105.525 F9000 -G1 X126.661 Y105.301 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X126.79 Y105.364 E.0045 -G1 X126.79 Y106.79 E.04471 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.374 Y105.432 E.00585 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.37 Y104.563 E.00385 -G1 X123.21 Y104.49 E.00551 -G1 X123.21 Y103.21 E.04013 -G1 X123.735 Y103.21 E.01646 -G1 X123.785 Y103.336 E.00425 -G1 X123.935 Y103.41 E.00524 -G1 X124.47 Y103.391 E.01679 -G1 X124.575 Y103.21 E.00656 -G1 X125.404 Y103.21 E.02599 -G1 X125.521 Y103.391 E.00676 -G1 X126.003 Y103.41 E.01512 -G1 X126.22 Y103.335 E.0072 -G1 X126.259 Y103.21 E.00411 -G1 X126.79 Y103.21 E.01665 -G1 X126.79 Y103.527 E.00994 -G1 X126.79 Y104.589 E.0333 -G1 X126.629 Y104.674 E.00571 -G1 X126.59 Y104.894 E.00701 -G1 X126.621 Y105.264 E.01164 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X126.661 Y105.301 E-.02588 -G1 X126.79 Y105.364 E-.06819 -G1 X126.79 Y106.79 E-.67735 -G1 X125.256 Y106.79 E-.72858 -;WIPE_END -G1 X124.5 Y104.671 F9000 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 +;WIPE_END +G1 X126.34 Y108.514 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X126.864 Y108.455 E.01741 +G1 X127.272 Y108.145 E.01692 +G1 X127.671 Y108.44 E.01638 +G1 X128.151 Y108.527 E.01611 +G1 X128.49 Y108.49 E.01126 +G1 X128.7 Y108.7 E.00981 +G1 X121.3 Y108.7 E.24433 +G1 X121.571 Y108.429 E.01265 +G1 X122.13 Y108.529 E.01875 +G1 X122.659 Y108.435 E.01774 +G1 X122.982 Y108.254 E.01222 +G1 X123.301 Y107.867 E.01656 +G1 X123.468 Y108.223 E.01298 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.301 Y107.867 E-.18678 +G1 X122.982 Y108.254 E-.23823 +G1 X122.659 Y108.435 E-.17587 +G1 X122.13 Y108.529 E-.25521 +G1 X121.571 Y108.429 E-.26974 +G1 X121.3 Y108.7 E-.18204 +G1 X121.704 Y108.7 E-.19213 +;WIPE_END +G1 X121.978 Y106.429 F9000 +G1 E5 F2400 +G1 F900 +G1 X121.836 Y106.643 E.00848 +G1 X121.809 Y107.01 E.01215 +G1 X121.853 Y107.287 E.00926 +G1 X122.008 Y107.479 E.00815 +G1 X122.255 Y107.447 E.00822 +G1 X122.405 Y107.124 E.01176 +G1 X122.707 Y106.972 E.01116 +G1 X122.951 Y107.049 E.00845 +G1 X123.266 Y106.734 E.01471 +G1 X123.266 Y106.67 E.00211 +G1 X122.671 Y106.812 E.0202 +G1 X122.396 Y106.67 E.01022 +G1 X122.238 Y106.417 E.00985 +G1 X121.978 Y106.429 E.00859 +G1 X121.978 Y106.429 F9000 +G1 X121.326 Y107.441 +;TYPE:Solid infill +;WIDTH:0.492899 +G1 F900 +G1 X121.308 Y107.534 E.00354 +;WIDTH:0.456237 +G1 X121.29 Y107.626 E.00322 +;WIDTH:0.419576 +G1 X121.271 Y107.718 E.00294 +;WIDTH:0.382914 +G1 X121.464 Y107.924 E.00798 +;WIDTH:0.37153 +G1 X121.725 Y108.089 E.00844 +;WIDTH:0.366615 +G1 X122.03 Y108.157 E.00841 +;WIDTH:0.365647 +G1 X122.41 Y108.128 E.01023 +;WIDTH:0.380428 +G1 X122.752 Y107.942 E.01093 +;WIDTH:0.394909 +G1 X122.89 Y107.798 E.00584 +;WIDTH:0.398108 +G1 X123.051 Y107.505 E.00987 +G1 X123.015 Y107.462 E.00166 +G1 X122.717 Y107.388 E.00907 +G1 X122.479 Y107.726 E.01221 +;WIDTH:0.385015 +G1 X122.23 Y107.837 E.00775 +;WIDTH:0.366615 +G1 X121.857 Y107.801 E.01009 +;WIDTH:0.382914 +G1 X121.593 Y107.586 E.00963 +;WIDTH:0.429767 +G1 X121.536 Y107.501 E.00329 +;WIDTH:0.476619 +G1 X121.479 Y107.417 E.00366 +;WIDTH:0.523472 +G1 X121.422 Y107.332 E.00409 +;WIDTH:0.570324 +G1 X121.365 Y107.247 E.00449 +G1 X121.363 Y107.202 E.00198 +;WIDTH:0.56636 +G1 X121.362 Y106.691 E.02224 +;WIDTH:0.564518 +G1 X121.339 Y106.578 E.005 +;WIDTH:0.518525 +G1 X121.316 Y106.464 E.0046 +;WIDTH:0.472531 +G1 X121.293 Y106.35 E.00415 +;WIDTH:0.426537 +G1 X121.27 Y106.237 E.00368 +;WIDTH:0.380543 +G1 X121.334 Y106.09 E.0045 +;WIDTH:0.37783 +G1 X121.644 Y105.839 E.01111 +;WIDTH:0.367274 +G1 X121.873 Y105.762 E.00652 +;WIDTH:0.356412 +G1 X122.286 Y105.741 E.01078 +;WIDTH:0.382499 +G1 X122.627 Y105.841 E.01003 +;WIDTH:0.390544 +G1 X122.83 Y105.995 E.00737 +;WIDTH:0.396516 +G1 X123.037 Y106.314 E.01118 +G1 X122.719 Y106.414 E.0098 +G1 X122.573 Y106.22 E.00714 +;WIDTH:0.390544 +G1 X122.297 Y106.059 E.00924 +;WIDTH:0.368706 +G1 X121.926 Y106.07 E.01005 +;WIDTH:0.372649 +G1 X121.644 Y106.248 E.00914 +;WIDTH:0.412427 +G1 X121.586 Y106.34 E.00334 +;WIDTH:0.452204 +G1 X121.527 Y106.432 E.00372 +;WIDTH:0.491982 +G1 X121.469 Y106.524 E.00406 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.527 Y106.432 E-.05166 +G1 X121.586 Y106.34 E-.05191 +G1 X121.644 Y106.248 E-.05166 +G1 X121.926 Y106.07 E-.1584 +G1 X122.297 Y106.059 E-.1763 +G1 X122.573 Y106.22 E-.15177 +G1 X122.719 Y106.414 E-.11533 +G1 X123.037 Y106.314 E-.15834 +G1 X122.83 Y105.995 E-.18063 +G1 X122.627 Y105.841 E-.12103 +G1 X122.286 Y105.741 E-.1688 +G1 X122.046 Y105.753 E-.11417 +;WIPE_END +G1 X123.556 Y107.492 F9000 +G1 E5 F2400 +;WIDTH:0.399892 +G1 F900 +G1 X123.63 Y107.429 E.00288 +G1 X123.636 Y107.361 E.00203 +;WIDTH:0.386067 +G1 X123.65 Y106.47 E.02543 +;WIDTH:0.38432 +G1 X123.948 Y106.456 E.00847 +G1 X123.977 Y106.626 E.0049 +;WIDTH:0.388664 +G1 X123.979 Y107.418 E.02277 +G1 X124.025 Y107.759 E.00989 +;WIDTH:0.410921 +G1 X124.133 Y107.855 E.00442 +;WIDTH:0.437608 +G1 X124.24 Y107.951 E.00472 +;WIDTH:0.472404 +G1 X124.324 Y107.961 E.00302 +;WIDTH:0.507199 +G1 X124.408 Y107.97 E.00326 +;WIDTH:0.541994 +G1 X124.492 Y107.98 E.00351 +G1 X124.402 Y108.032 E.00431 +;WIDTH:0.501661 +G1 X124.312 Y108.085 E.00398 +;WIDTH:0.461328 +G1 X124.172 Y108.107 E.00493 +;WIDTH:0.423381 +G1 X124.032 Y108.129 E.00448 +;WIDTH:0.385434 +G1 X123.862 Y108.056 E.00527 +G1 X123.73 Y107.93 E.0052 +;WIDTH:0.392333 +G1 X123.653 Y107.66 E.00816 +G1 X123.653 Y107.66 F9000 +G1 X124.557 Y107.961 +;WIDTH:0.538188 +G1 F900 +G1 X124.624 Y107.889 E.00405 +;WIDTH:0.511106 +G1 X124.691 Y107.817 E.00383 +;WIDTH:0.484024 +G1 X124.785 Y107.626 E.00781 +;WIDTH:0.439999 +G1 X124.842 Y106.484 E.03775 +G1 X125.361 Y106.469 E.01714 +G1 X125.519 Y106.323 E.0071 +;WIDTH:0.426506 +G1 X125.568 Y105.787 E.01717 +;WIDTH:0.383814 +G1 X125.846 Y105.773 E.00789 +G1 X125.894 Y105.821 E.00192 +;WIDTH:0.426506 +G1 X125.925 Y106.329 E.01623 +;WIDTH:0.475628 +G1 X126.019 Y106.427 E.00489 +;WIDTH:0.52475 +G1 X126.113 Y106.525 E.00544 +;WIDTH:0.554407 +G1 X126.184 Y106.543 E.00312 +;WIDTH:0.584064 +G1 X126.255 Y106.562 E.00331 +G1 X126.184 Y106.603 E.00369 +;WIDTH:0.554407 +G1 X126.113 Y106.645 E.00351 +;WIDTH:0.52475 +G1 X126.002 Y106.841 E.00902 +;WIDTH:0.482375 +G1 X125.891 Y107.037 E.00823 +;WIDTH:0.439999 +G1 X125.892 Y107.482 E.01469 +;WIDTH:0.466594 +G1 X125.96 Y107.65 E.00638 +;WIDTH:0.493188 +G1 X126.029 Y107.819 E.00683 +;WIDTH:0.530648 +G1 X126.091 Y107.886 E.0037 +;WIDTH:0.568108 +G1 X126.153 Y107.954 E.00402 +G1 X126.058 Y107.963 E.00417 +;WIDTH:0.53507 +G1 X125.944 Y108.022 E.00525 +;WIDTH:0.487535 +G1 X125.83 Y108.081 E.00475 +;WIDTH:0.439999 +G1 X125.706 Y108.09 E.0041 +G1 X125.424 Y107.992 E.00986 +G1 X125.226 Y107.998 E.00654 +G1 X125.095 Y108.09 E.00529 +G1 X124.882 Y108.09 E.00703 +;WIDTH:0.463737 +G1 X124.791 Y108.023 E.00395 +;WIDTH:0.477087 +G1 X124.749 Y108.012 E.00157 +G1 X124.749 Y108.012 F9000 +G1 X125.181 Y107.046 +;WIDTH:0.388584 +G1 F900 +G1 X125.156 Y107.635 E.01694 +G1 X125.443 Y107.621 E.00826 +G1 X125.563 Y107.66 E.00363 +G1 X125.517 Y107.437 E.00654 +G1 X125.514 Y107.036 E.01153 +;WIDTH:0.372953 +G1 X125.65 Y106.705 E.00982 +G1 X125.5 Y106.805 E.00495 +G1 X125.186 Y106.848 E.0087 +G1 X125.186 Y106.848 F9000 +G1 X126.153 Y107.954 +;WIDTH:0.608684 +G1 F900 +G1 X126.39 Y108.027 E.01167 +G1 X126.449 Y107.991 E.00325 +;WIDTH:0.57401 +G1 X126.508 Y107.956 E.00303 +;WIDTH:0.539335 +G1 X126.567 Y107.921 E.00283 +;WIDTH:0.50466 +G1 X126.699 Y107.702 E.00982 +;WIDTH:0.47233 +G1 X126.832 Y107.484 E.00912 +;WIDTH:0.439999 +G1 X126.822 Y107.078 E.01341 +;WIDTH:0.423631 +G1 X126.767 Y106.961 E.00409 +;WIDTH:0.434587 +G1 X126.71 Y106.833 E.00456 +;WIDTH:0.474399 +G1 X126.653 Y106.705 E.00503 +;WIDTH:0.512067 +G1 X126.608 Y106.666 E.00232 +;WIDTH:0.549735 +G1 X126.564 Y106.628 E.00245 +;WIDTH:0.587403 +G1 X126.519 Y106.59 E.00267 +;WIDTH:0.62507 +G1 X126.474 Y106.551 E.00288 +G1 X126.534 Y106.544 E.00292 +;WIDTH:0.59154 +G1 X126.593 Y106.536 E.00272 +;WIDTH:0.558009 +G1 X126.652 Y106.528 E.00255 +;WIDTH:0.524478 +G1 X126.733 Y106.547 E.00333 +;WIDTH:0.48244 +G1 X126.814 Y106.566 E.00304 +;WIDTH:0.440401 +G1 X126.895 Y106.584 E.00274 +;WIDTH:0.398362 +G1 X127.046 Y106.746 E.00655 +;WIDTH:0.439999 +G1 X127.189 Y106.849 E.00582 +G1 X127.452 Y106.832 E.0087 +G1 X127.716 Y106.573 E.01221 +G1 X127.939 Y106.47 E.00811 +G1 X128.287 Y106.474 E.01149 +G1 X128.467 Y106.519 E.00613 +G1 X128.7 Y106.71 E.00995 +G1 X128.7 Y107.91 E.03962 +G1 X128.596 Y107.998 E.0045 +G1 X128.369 Y108.093 E.00812 +G1 X128.039 Y108.105 E.0109 +G1 X127.814 Y108.05 E.00765 +G1 X127.437 Y107.745 E.01601 +G1 X127.137 Y107.735 E.00991 +G1 X126.989 Y107.889 E.00705 +;WIDTH:0.410674 +G1 X126.841 Y108.044 E.00655 +;WIDTH:0.422136 +G1 X126.763 Y108.056 E.00249 +;WIDTH:0.462924 +G1 X126.685 Y108.068 E.00276 +;WIDTH:0.511414 +G1 X126.587 Y108.054 E.00386 +G1 X126.587 Y108.054 F9000 +G1 X127.27 Y107.288 +;WIDTH:0.524486 +G1 F900 +G1 X127.414 Y107.288 E.00577 +;WIDTH:0.538729 +G1 X127.64 Y107.237 E.00955 +;WIDTH:0.547111 +G1 X127.811 Y107.127 E.00852 +G1 X127.984 Y106.946 E.0105 +G1 X128.177 Y106.911 E.00822 +G1 X128.249 Y106.929 E.00311 +G1 X128.249 Y107.642 E.02989 +G1 X128.064 Y107.655 E.00778 +G1 X127.638 Y107.347 E.02204 +;WIDTH:0.538729 +G1 X127.606 Y107.338 E.00137 +;WIDTH:0.536682 +G1 X127.33 Y107.297 E.01146 +G1 X127.33 Y107.297 F9000 +G1 X126.474 Y106.551 +;WIDTH:0.62507 +G1 F900 +G1 X126.255 Y106.562 E.01061 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.474 Y106.551 E-.10416 +;WIPE_END +G1 E-1.39584 F3600 +G1 X128.7 Y103.682 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.7 Y101.3 E.07865 +G1 X128.212 Y101.788 E.02279 +G1 X128.212 Y102.332 E.01796 +G1 X128.147 Y102.397 E.00304 +G1 X128.212 Y102.461 E.00301 +G1 X128.212 Y104.372 E.0631 +G1 X127.967 Y104.617 E.01144 +G1 X127.378 Y104.617 E.01945 +G1 X127.287 Y104.526 E.00425 +G1 X127.196 Y104.617 E.00425 +G1 X126.606 Y104.617 E.01948 +G1 X126.451 Y104.461 E.00726 +G1 X126.337 Y104.617 E.00638 +G1 X125.772 Y104.617 E.01865 +G1 X125.677 Y104.547 E.0039 +G1 X125.353 Y104.647 E.0112 +G1 X123.913 Y106.087 E.06724 +G1 X124.101 Y106.087 E.00621 +G1 X124.345 Y106.331 E.01139 +G1 X124.358 Y107.559 E.04055 +G1 X124.422 Y107.373 E.00649 +G1 X124.425 Y106.331 E.0344 +G1 X124.67 Y106.087 E.01142 +G1 X125.185 Y106.087 E.017 +G1 X125.185 Y105.648 E.01449 +G1 X125.416 Y105.416 E.01081 +G1 X124.261 Y104.261 E.05393 +G1 X123.816 Y104.565 E.01779 +G1 X123.287 Y104.659 E.01774 +G1 X122.727 Y104.558 E.01879 +G1 X122.261 Y104.243 E.01857 +G1 X121.956 Y103.756 E.01897 +G1 X121.853 Y103.243 E.01728 +G1 X121.903 Y102.577 E.02205 +G1 X122.115 Y102.115 E.01678 +G1 X121.3 Y101.3 E.03806 +G1 X121.3 Y103.682 E.07865 +G1 X121.3 Y103.682 F9000 +G1 X122.236 Y103.194 +;TYPE:Solid infill +;WIDTH:0.406901 +G1 F900 +G1 X122.285 Y102.652 E.01647 +;WIDTH:0.391811 +G1 X122.486 Y102.231 E.01353 +;WIDTH:0.376723 +G1 X122.805 Y101.971 E.01142 +;WIDTH:0.364988 +G1 X123.037 Y101.893 E.00655 +;WIDTH:0.35579 +G1 X123.462 Y101.874 E.01107 +;WIDTH:0.3802 +G1 X123.802 Y101.983 E.01001 +;WIDTH:0.399374 +G1 X124.045 Y102.19 E.00946 +G1 X124.188 Y102.452 E.00885 +;WIDTH:0.398402 +G1 X123.876 Y102.543 E.00961 +G1 X123.728 Y102.35 E.00719 +;WIDTH:0.387148 +G1 X123.454 Y102.19 E.00908 +;WIDTH:0.367243 +G1 X123.061 Y102.205 E.01061 +;WIDTH:0.377653 +G1 X122.774 Y102.401 E.00967 +;WIDTH:0.396861 +G1 X122.603 Y102.822 E.01337 +G1 X122.606 Y103.332 E.01501 +;WIDTH:0.394579 +G1 X122.752 Y103.716 E.01201 +;WIDTH:0.379458 +G1 X123.014 Y103.931 E.00948 +;WIDTH:0.367747 +G1 X123.387 Y103.967 E.01012 +;WIDTH:0.382777 +G1 X123.667 Y103.835 E.00875 +;WIDTH:0.426048 +G1 X123.745 Y103.748 E.00372 +;WIDTH:0.469318 +G1 X123.824 Y103.661 E.00417 +;WIDTH:0.512588 +G1 X123.902 Y103.574 E.00456 +G1 X124.07 Y103.637 E.00701 +;WIDTH:0.476327 +G1 X124.237 Y103.7 E.00643 +;WIDTH:0.478611 +G1 X124.175 Y103.719 E.00235 +;WIDTH:0.517155 +G1 X124.112 Y103.738 E.00259 +G1 X124.086 Y103.803 E.00276 +;WIDTH:0.475121 +G1 X124.059 Y103.867 E.0025 +;WIDTH:0.433086 +G1 X124.033 Y103.932 E.00227 +;WIDTH:0.391051 +G1 X123.924 Y104.067 E.00502 +;WIDTH:0.386186 +G1 X123.641 Y104.231 E.00934 +;WIDTH:0.369772 +G1 X123.243 Y104.29 E.01093 +;WIDTH:0.367717 +G1 X122.889 Y104.224 E.00973 +;WIDTH:0.377808 +G1 X122.552 Y104.001 E.01125 +;WIDTH:0.402664 +G1 X122.328 Y103.645 E.01258 +;WIDTH:0.405071 +G1 X122.276 Y103.389 E.00787 +G1 X122.276 Y103.389 F9000 +G1 X124.237 Y103.7 +;WIDTH:0.440066 +G1 F900 +G1 X124.543 Y103.743 E.0102 +;WIDTH:0.43913 +G1 X124.698 Y103.867 E.00654 +;WIDTH:0.439999 +G1 X124.819 Y104.109 E.00893 +G1 X124.993 Y104.222 E.00685 +G1 X125.272 Y104.253 E.00927 +G1 X125.718 Y104.15 E.01511 +G1 X126.031 Y104.22 E.01059 +G1 X126.136 Y104.22 E.00347 +G1 X126.253 Y104.084 E.00592 +G1 X126.555 Y104.047 E.01005 +G1 X126.771 Y104.22 E.00914 +G1 X127.032 Y104.22 E.00862 +G1 X127.154 Y104.126 E.00509 +G1 X127.356 Y104.116 E.00668 +G1 X127.622 Y104.22 E.00943 +G1 X127.815 Y104.199 E.00641 +G1 X127.815 Y102.626 E.05194 +G1 X127.737 Y102.466 E.00588 +G1 X127.779 Y102.206 E.0087 +G1 X127.815 Y102.101 E.00366 +G1 X127.809 Y101.937 E.00542 +G1 X127.542 Y101.931 E.00882 +G1 X127.388 Y102.032 E.00608 +G1 X127.218 Y102.034 E.00561 +G1 X126.952 Y101.931 E.00942 +G1 X126.759 Y101.951 E.00641 +G1 X126.759 Y102.825 E.02886 +G1 X126.653 Y103.001 E.00678 +G1 X126.286 Y103.068 E.01232 +G1 X126.134 Y103.006 E.00542 +G1 X125.978 Y102.715 E.0109 +G1 X125.684 Y102.605 E.01036 +G1 X125.31 Y102.589 E.01236 +G1 X125.022 Y102.669 E.00987 +G1 X124.876 Y102.787 E.0062 +G1 X124.811 Y102.929 E.00516 +G1 X124.909 Y103.05 E.00514 +G1 X124.969 Y103.349 E.01007 +G1 X124.748 Y103.635 E.01193 +;WIDTH:0.439121 +G1 X124.543 Y103.743 E.00763 +;WIDTH:0.439999 +G1 X125.188 Y103.801 E.02138 +;WIDTH:0.525539 +G1 X125.253 Y103.728 E.00392 +G1 X125.397 Y103.447 E.01267 +G1 X125.406 Y103.209 E.00956 +G1 X125.36 Y103.028 E.00749 +G1 X125.645 Y103.043 E.01145 +G1 X125.814 Y103.308 E.01261 +;WIDTH:0.534466 +G1 X126.002 Y103.45 E.00963 +;WIDTH:0.584444 +G1 X126.154 Y103.506 E.00729 +;WIDTH:0.634422 +G1 X126.306 Y103.562 E.00797 +G1 X126.136 Y103.657 E.00958 +;WIDTH:0.584444 +G1 X125.965 Y103.752 E.00881 +;WIDTH:0.534466 +G1 X125.667 Y103.713 E.01228 +;WIDTH:0.525539 +G1 X125.384 Y103.765 E.01155 +G1 X125.384 Y103.765 F9000 +G1 X126.306 Y103.562 +;WIDTH:0.650366 +G1 F900 +G1 X126.525 Y103.54 E.01112 +G1 X126.614 Y103.583 E.00499 +;WIDTH:0.605732 +G1 X126.702 Y103.625 E.00456 +;WIDTH:0.561097 +G1 X126.809 Y103.709 E.00586 +;WIDTH:0.51132 +G1 X126.915 Y103.792 E.00524 +;WIDTH:0.508448 +G1 X127.218 Y103.684 E.01245 +G1 X127.384 Y103.695 E.00644 +G1 X127.406 Y103.405 E.01126 +;WIDTH:0.463096 +G1 X127.429 Y103.115 E.01016 +;WIDTH:0.417744 +G1 X127.452 Y102.825 E.00907 +;WIDTH:0.372391 +G1 X127.393 Y102.376 E.01241 +;WIDTH:0.334807 +G1 X127.218 Y102.379 E.00425 +G1 X127.103 Y102.334 E.003 +;WIDTH:0.372391 +G1 X127.122 Y102.825 E.01346 +;WIDTH:0.417744 +G1 X127.079 Y102.979 E.00498 +;WIDTH:0.463096 +G1 X127.035 Y103.132 E.00556 +;WIDTH:0.508448 +G1 X126.991 Y103.285 E.00616 +;WIDTH:0.555754 +G1 X126.836 Y103.37 E.00754 +;WIDTH:0.60306 +G1 X126.68 Y103.455 E.00827 +;WIDTH:0.650366 +G1 X126.525 Y103.54 E.00893 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.68 Y103.455 E-.08397 +G1 X126.836 Y103.37 E-.08439 +G1 X126.991 Y103.285 E-.08397 +G1 X127.035 Y103.132 E-.07562 +G1 X127.079 Y102.979 E-.07562 +G1 X127.122 Y102.825 E-.07595 +G1 X127.103 Y102.334 E-.2334 +G1 X127.218 Y102.379 E-.05866 +G1 X127.393 Y102.376 E-.08314 +G1 X127.452 Y102.825 E-.21511 +G1 X127.429 Y103.115 E-.13818 +G1 X127.406 Y103.405 E-.13818 +G1 X127.384 Y103.695 E-.13815 +G1 X127.351 Y103.693 E-.01566 +;WIPE_END +G1 X123.874 Y102.931 F9000 G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.5 Y105.338 E.02202 -G1 X124.422 Y105.578 E.00833 -G1 X125.51 Y104.49 E.0508 -G1 X124.991 Y104.339 E.01785 -G1 X124.487 Y104.487 E.01734 -G1 X125.655 Y105.7 E.0556 -G1 X124.452 Y105.7 E.03972 +G1 X123.553 Y102.8 E.01145 +G1 X123.462 Y102.601 E.00722 +G1 X123.284 Y102.524 E.0064 +G1 X123.073 Y102.61 E.00752 +G1 X122.967 Y102.967 E.0123 +G1 X123.481 Y103.481 E.024 +G1 X123.562 Y103.254 E.00796 +G1 X123.862 Y103.102 E.0111 +G1 X124.475 Y103.327 E.02156 +G1 X124.533 Y103.258 E.00298 +G1 X124.352 Y102.916 E.01278 +G1 X124.401 Y102.805 E.00401 +G1 X123.874 Y102.931 E.01789 ;LAYER_CHANGE ;Z:1.2 ;HEIGHT:0.2 @@ -934,101 +2449,63 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.655 Y105.7 E-.57143 -G1 X124.487 Y104.487 E-.79986 -G1 X124.747 Y104.411 E-.12871 +G1 X124.401 Y102.805 E-.25738 +G1 X124.352 Y102.916 E-.05763 +G1 X124.533 Y103.258 E-.1838 +G1 X124.475 Y103.327 E-.04282 +G1 X123.862 Y103.102 E-.31017 +G1 X123.562 Y103.254 E-.15975 +G1 X123.481 Y103.481 E-.11448 +G1 X122.967 Y102.967 E-.34528 +G1 X122.984 Y102.909 E-.02869 ;WIPE_END G1 Z1.2 F9000 ;AFTER_LAYER_CHANGE ;1.2 G1 Z1.2 -G1 X123.996 Y105.988 +G1 X120.994 Y100.994 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.008 Y105.894 E.00313 -G1 X124.194 Y105.321 E.01989 -G1 X124.194 Y104.679 E.0212 -G1 X124.084 Y104.194 E.01642 -G1 X124.786 Y104.16 E.02321 -G1 X124.997 Y104.059 E.00772 -G1 X125.077 Y104.114 E.00321 -G1 X125.462 Y104.194 E.01298 -G1 X126.001 Y104.196 E.0178 -G1 X125.821 Y104.621 E.01524 -G1 X125.806 Y105.189 E.01876 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y106.006 E.00878 -G1 X124.036 Y106.006 E.06504 -G1 X124.036 Y106.006 F9000 -G1 X123.602 Y105.703 -G1 F900 -G1 X123.687 Y105.66 E.00315 -G1 X123.797 Y105.321 E.01177 -G1 X123.797 Y104.679 E.0212 -G1 X123.726 Y104.401 E.00947 -G1 X123.597 Y104.283 E.00577 -G1 X123.597 Y103.597 E.02265 -G1 X123.983 Y103.788 E.01422 -G1 X124.084 Y103.797 E.00335 -G1 X124.682 Y103.777 E.01976 -G1 X124.997 Y103.597 E.01198 -G1 X125.234 Y103.75 E.00931 -G1 X125.363 Y103.788 E.00444 -G1 X125.927 Y103.797 E.01862 -G1 X126.403 Y103.597 E.01705 -G1 X126.403 Y104.399 E.02648 -G1 X126.321 Y104.44 E.00303 -G1 X126.212 Y104.689 E.00897 -G1 X126.203 Y105.176 E.01608 -G1 X126.303 Y105.5 E.0112 -G1 X126.403 Y105.553 E.00374 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.76 E.02123 -G1 X123.343 Y105.449 F9000 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y101.054 E.26255 +G1 X120.994 Y101.054 F9000 +G1 X120.597 Y100.597 +G1 F900 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.657 E.28877 +G1 X120.21 Y100.21 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.374 Y105.432 E.00111 -G1 X123.41 Y105.176 E.00811 -G1 X123.387 Y104.588 E.01845 -G1 X123.21 Y104.49 E.00634 -G1 X123.21 Y103.21 E.04013 -G1 X123.884 Y103.21 E.02113 -G1 X123.944 Y103.348 E.00472 -G1 X124.051 Y103.407 E.00383 -G1 X124.531 Y103.41 E.01505 -G1 X124.668 Y103.351 E.00468 -G1 X124.719 Y103.21 E.0047 -G1 X125.262 Y103.21 E.01703 -G1 X125.326 Y103.351 E.00486 -G1 X125.413 Y103.404 E.00319 -G1 X125.927 Y103.41 E.01612 -G1 X126.067 Y103.347 E.00481 -G1 X126.115 Y103.21 E.00455 -G1 X126.79 Y103.21 E.02116 -G1 X126.79 Y104.589 E.04324 -G1 X126.62 Y104.686 E.00614 -G1 X126.59 Y104.939 E.00799 -G1 X126.623 Y105.282 E.0108 -G1 X126.79 Y105.376 E.00601 -G1 X126.79 Y106.79 E.04433 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.29 Y105.478 E.00285 -G1 X124.452 Y105.7 F9000 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.27 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X120.21 Y100.21 E-.0285 +G1 X123.308 Y100.21 E-1.4715 +;WIPE_END +G1 X128.7 Y103.682 F9000 +G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X125.655 Y105.7 E.03972 -G1 X124.497 Y104.497 E.05513 -G1 X124.993 Y104.411 E.01662 -G1 X125.5 Y104.5 E.017 -G1 X124.422 Y105.578 E.05034 -G1 X124.5 Y105.338 E.00833 -G1 X124.494 Y104.677 E.02183 +G1 X128.7 Y101.3 E.07865 +G1 X121.3 Y108.7 E.34553 +G1 X121.3 Y101.3 E.24433 +G1 X128.7 Y108.7 E.34553 +G1 X126.318 Y108.7 E.07865 ;LAYER_CHANGE ;Z:1.4 ;HEIGHT:0.2 @@ -1040,110 +2517,56 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.5 Y105.338 E-.31399 -G1 X124.422 Y105.578 E-.11987 -G1 X125.5 Y104.5 E-.72415 -G1 X124.993 Y104.411 E-.24451 -G1 X124.791 Y104.446 E-.09748 +G1 X128.7 Y108.7 E-1.13145 +G1 X128.151 Y108.151 E-.36855 ;WIPE_END G1 Z1.4 F9000 ;AFTER_LAYER_CHANGE ;1.4 G1 Z1.4 -G1 X124.034 Y104.163 +G1 X129.006 Y109.006 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.675 Y104.194 E.02119 -G1 X124.987 Y104.143 E.01044 -G1 X125.321 Y104.194 E.01116 -G1 X126.006 Y104.161 E.02264 -G1 X125.898 Y104.375 E.00791 -G1 X125.808 Y104.718 E.01171 -G1 X125.806 Y105.189 E.01555 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y106.006 E.00878 -G1 X123.994 Y106.006 E.06643 -G1 X124.008 Y105.894 E.00373 -G1 X124.194 Y105.321 E.01989 -G1 X124.194 Y104.679 E.0212 -G1 X124.132 Y104.338 E.01144 -G1 X124.063 Y104.215 E.00466 -G1 X124.063 Y104.215 F9000 -G1 X123.608 Y104.29 -G1 F900 -G1 X123.597 Y104.285 E.0004 -G1 X123.597 Y103.597 E.02272 -G1 X123.736 Y103.597 E.00459 -G1 X124.056 Y103.769 E.012 -G1 X124.232 Y103.797 E.00588 -G1 X124.86 Y103.767 E.02076 -G1 X124.998 Y103.646 E.00606 -G1 X125.137 Y103.767 E.00608 -G1 X125.321 Y103.797 E.00616 -G1 X125.902 Y103.785 E.01919 -G1 X126.201 Y103.597 E.01166 -G1 X126.403 Y103.597 E.00667 -G1 X126.403 Y104.4 E.02651 -G1 X126.319 Y104.442 E.0031 -G1 X126.204 Y104.747 E.01076 -G1 X126.203 Y105.176 E.01416 -G1 X126.303 Y105.5 E.0112 -G1 X126.403 Y105.553 E.00374 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.706 E.02301 -G1 X123.687 Y105.66 E.00334 -G1 X123.797 Y105.321 E.01177 -G1 X123.797 Y104.971 E.01156 -G1 X123.761 Y104.477 E.01635 -G1 X123.673 Y104.321 E.00591 -G1 X123.662 Y104.316 E.0004 -G1 X123.662 Y104.316 F9000 -G1 X123.344 Y104.55 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.21 Y104.49 E.0046 -G1 X123.21 Y103.21 E.04013 -G1 X124.032 Y103.21 E.02577 -G1 X124.101 Y103.357 E.00509 -G1 X124.174 Y103.401 E.00267 -G1 X124.675 Y103.41 E.01571 -G1 X124.844 Y103.306 E.00622 -G1 X124.864 Y103.221 E.00274 -G1 X125.121 Y103.21 E.00807 -G1 X125.153 Y103.306 E.00317 -G1 X125.261 Y103.4 E.00449 -G1 X125.784 Y103.41 E.0164 -G1 X125.915 Y103.356 E.00444 -G1 X125.972 Y103.21 E.00491 -G1 X126.79 Y103.21 E.02565 -G1 X126.79 Y104.589 E.04324 -G1 X126.608 Y104.709 E.00684 -G1 X126.59 Y104.971 E.00823 -G1 X126.623 Y105.282 E.00981 -G1 X126.79 Y105.376 E.00601 -G1 X126.79 Y106.79 E.04433 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.374 Y105.432 E.00585 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.38 Y104.592 E.00289 -G1 X123.38 Y104.592 F9000 -G1 X124.5 Y104.665 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 +;WIPE_END +G1 X126.318 Y108.7 F9000 +G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.5 Y105.338 E.02222 -G1 X124.422 Y105.578 E.00833 -G1 X125.506 Y104.494 E.05062 -G1 X124.992 Y104.454 E.01702 -G1 X124.5 Y104.5 E.01632 -G1 X125.655 Y105.7 E.05499 -G1 X124.452 Y105.7 E.03972 +G1 X128.7 Y108.7 E.07865 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.682 E.07865 ;LAYER_CHANGE ;Z:1.6 ;HEIGHT:0.2 @@ -1155,100 +2578,56 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.655 Y105.7 E-.57143 -G1 X124.5 Y104.5 E-.79113 -G1 X124.788 Y104.473 E-.13744 +G1 X128.7 Y101.3 E-1.13145 +G1 X128.151 Y101.849 E-.36855 ;WIPE_END G1 Z1.6 F9000 ;AFTER_LAYER_CHANGE ;1.6 G1 Z1.6 -G1 X123.996 Y105.988 +G1 X129.006 Y100.994 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.008 Y105.894 E.00313 -G1 X124.194 Y105.321 E.01989 -G1 X124.169 Y104.46 E.02844 -G1 X124.011 Y104.117 E.01247 -G1 X124.38 Y104.194 E.01245 -G1 X125.64 Y104.194 E.0416 -G1 X126.006 Y104.111 E.01239 -G1 X126.006 Y104.184 E.00241 -G1 X125.854 Y104.488 E.01122 -G1 X125.806 Y104.789 E.01006 -G1 X125.806 Y105.189 E.01321 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y106.006 E.00878 -G1 X124.036 Y106.006 E.06504 -G1 X124.036 Y106.006 F9000 -G1 X123.602 Y105.703 -G1 F900 -G1 X123.687 Y105.66 E.00315 -G1 X123.797 Y105.321 E.01177 -G1 X123.782 Y104.549 E.02549 -G1 X123.677 Y104.327 E.00811 -G1 X123.597 Y104.287 E.00295 -G1 X123.597 Y103.597 E.02278 -G1 X123.977 Y103.597 E.01255 -G1 X124.004 Y103.657 E.00217 -G1 X124.271 Y103.787 E.00981 -G1 X124.877 Y103.794 E.02001 -G1 X125 Y103.712 E.00488 -G1 X125.065 Y103.785 E.00323 -G1 X125.211 Y103.797 E.00484 -G1 X125.824 Y103.767 E.02026 -G1 X126.02 Y103.654 E.00747 -G1 X126.046 Y103.597 E.00207 -G1 X126.403 Y103.597 E.01179 -G1 X126.403 Y104.402 E.02658 -G1 X126.316 Y104.446 E.00322 -G1 X126.231 Y104.611 E.00613 -G1 X126.203 Y105.176 E.01868 -G1 X126.303 Y105.5 E.0112 -G1 X126.403 Y105.553 E.00374 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.76 E.02123 -G1 X123.343 Y105.449 F9000 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F900 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.374 Y105.432 E.00111 -G1 X123.41 Y105.176 E.00811 -G1 X123.394 Y104.602 E.018 -G1 X123.21 Y104.49 E.00675 -G1 X123.21 Y103.21 E.04013 -G1 X124.192 Y103.221 E.03079 -G1 X124.302 Y103.393 E.0064 -G1 X124.819 Y103.41 E.01622 -G1 X125 Y103.237 E.00785 -G1 X125.143 Y103.406 E.00694 -G1 X125.64 Y103.41 E.01558 -G1 X125.765 Y103.363 E.00419 -G1 X125.829 Y103.21 E.0052 -G1 X126.79 Y103.21 E.03013 -G1 X126.79 Y104.589 E.04324 -G1 X126.627 Y104.676 E.00579 -G1 X126.599 Y104.73 E.00191 -G1 X126.59 Y104.991 E.00819 -G1 X126.623 Y105.282 E.00918 -G1 X126.79 Y105.376 E.00601 -G1 X126.79 Y106.79 E.04433 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.29 Y105.478 E.00285 -G1 X124.452 Y105.7 F9000 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 +;WIPE_END +G1 X128.7 Y103.682 F9000 +G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X125.655 Y105.7 E.03972 -G1 X124.497 Y104.497 E.05513 -G1 X125.507 Y104.493 E.03335 -G1 X124.422 Y105.578 E.05066 -G1 X124.5 Y105.338 E.00833 -G1 X124.5 Y104.674 E.02192 +G1 X128.7 Y101.3 E.07865 +G1 X121.3 Y108.7 E.34553 +G1 X121.3 Y101.3 E.24433 +G1 X128.7 Y108.7 E.34553 +G1 X126.318 Y108.7 E.07865 ;LAYER_CHANGE ;Z:1.8 ;HEIGHT:0.2 @@ -1260,99 +2639,56 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.5 Y105.338 E-.3154 -G1 X124.422 Y105.578 E-.11987 -G1 X125.507 Y104.493 E-.72885 -G1 X124.8 Y104.496 E-.33588 +G1 X128.7 Y108.7 E-1.13145 +G1 X128.151 Y108.151 E-.36855 ;WIPE_END G1 Z1.8 F9000 ;AFTER_LAYER_CHANGE ;1.8 G1 Z1.8 -G1 X123.994 Y104.032 +G1 X129.006 Y109.006 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.307 Y104.168 E.01127 -G1 X124.529 Y104.194 E.00738 -G1 X125.681 Y104.177 E.03804 -G1 X126.006 Y104.042 E.01162 -G1 X125.992 Y104.217 E.0058 -G1 X125.825 Y104.595 E.01364 -G1 X125.806 Y105.189 E.01962 -G1 X126.006 Y105.74 E.01935 -G1 X126.006 Y106.006 E.00878 -G1 X123.994 Y106.006 E.06643 -G1 X124.008 Y105.894 E.00373 -G1 X124.194 Y105.321 E.01989 -G1 X124.188 Y104.574 E.02466 -G1 X124.014 Y104.088 E.01704 -G1 X124.014 Y104.088 F9000 -G1 X123.605 Y104.294 -G1 F900 -G1 X123.597 Y104.291 E.00028 -G1 X123.597 Y103.597 E.02291 -G1 X124.135 Y103.597 E.01776 -G1 X124.235 Y103.716 E.00513 -G1 X124.529 Y103.797 E.01007 -G1 X125.606 Y103.787 E.03556 -G1 X125.857 Y103.671 E.00913 -G1 X125.892 Y103.597 E.0027 -G1 X126.403 Y103.597 E.01687 -G1 X126.403 Y104.404 E.02664 -G1 X126.313 Y104.45 E.00334 -G1 X126.214 Y104.674 E.00809 -G1 X126.203 Y105.176 E.01658 -G1 X126.303 Y105.5 E.0112 -G1 X126.403 Y105.553 E.00374 -G1 X126.403 Y106.403 E.02806 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.706 E.02301 -G1 X123.687 Y105.66 E.00334 -G1 X123.797 Y105.321 E.01177 -G1 X123.794 Y104.616 E.02328 -G1 X123.682 Y104.333 E.01005 -G1 X123.658 Y104.321 E.00089 -G1 X123.658 Y104.321 F9000 -G1 X123.343 Y104.552 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.21 Y104.49 E.0046 -G1 X123.21 Y103.21 E.04013 -G1 X124.329 Y103.21 E.03509 -G1 X124.433 Y103.384 E.00636 -G1 X124.957 Y103.41 E.01645 -G1 X124.999 Y103.378 E.00166 -G1 X125.033 Y103.41 E.00146 -G1 X125.497 Y103.41 E.01455 -G1 X125.615 Y103.369 E.00392 -G1 X125.686 Y103.21 E.00546 -G1 X126.79 Y103.21 E.03462 -G1 X126.779 Y104.6 E.04358 -G1 X126.604 Y104.717 E.0066 -G1 X126.59 Y104.958 E.00757 -G1 X126.623 Y105.282 E.01021 -G1 X126.79 Y105.376 E.00601 -G1 X126.79 Y106.79 E.04433 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.374 Y105.432 E.00585 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.381 Y104.591 E.00291 -G1 X123.381 Y104.591 F9000 -G1 X124.5 Y104.678 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 +;WIPE_END +G1 X126.318 Y108.7 F9000 +G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.5 Y105.338 E.02179 -G1 X124.422 Y105.578 E.00833 -G1 X125.5 Y104.5 E.05034 -G1 X124.497 Y104.497 E.03312 -G1 X125.655 Y105.7 E.05513 -G1 X124.452 Y105.7 E.03972 +G1 X128.7 Y108.7 E.07865 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.682 E.07865 ;LAYER_CHANGE ;Z:2 ;HEIGHT:0.2 @@ -1364,101 +2700,166 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.655 Y105.7 E-.57143 -G1 X124.497 Y104.497 E-.79315 -G1 X124.782 Y104.498 E-.13542 +G1 X128.7 Y101.3 E-1.13145 +G1 X128.151 Y101.849 E-.36855 ;WIPE_END G1 Z2 F9000 ;AFTER_LAYER_CHANGE ;2 G1 Z2 -G1 X123.996 Y105.988 +G1 X128.842 Y103.849 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.008 Y105.894 E.00313 -G1 X124.194 Y105.321 E.01989 -G1 X124.161 Y104.427 E.02954 -G1 X124.008 Y104.106 E.01174 -G1 X123.994 Y103.994 E.00373 -G1 X124.063 Y103.994 E.00228 -G1 X124.427 Y104.161 E.01322 -G1 X125 Y104.194 E.01895 -G1 X125.588 Y104.166 E.01944 -G1 X126.006 Y103.994 E.01492 -G1 X126.006 Y104.105 E.00366 -G1 X125.848 Y104.44 E.01223 -G1 X125.806 Y104.723 E.00945 -G1 X125.806 Y105.244 E.0172 -G1 X126.006 Y105.815 E.01998 -G1 X126.006 Y106.006 E.00631 -G1 X124.036 Y106.006 E.06504 -G1 X124.036 Y106.006 F9000 -G1 X123.602 Y105.703 -G1 F900 -G1 X123.687 Y105.66 E.00315 -G1 X123.797 Y105.321 E.01177 -G1 X123.778 Y104.53 E.02612 -G1 X123.687 Y104.34 E.00696 -G1 X123.597 Y104.294 E.00334 -G1 X123.597 Y103.597 E.02301 -G1 X124.293 Y103.597 E.02298 -G1 X124.338 Y103.687 E.00332 -G1 X124.529 Y103.778 E.00699 -G1 X124.677 Y103.797 E.00493 -G1 X125.354 Y103.797 E.02235 -G1 X125.667 Y103.705 E.01077 -G1 X125.74 Y103.597 E.0043 -G1 X126.403 Y103.597 E.02189 -G1 X126.403 Y104.339 E.0245 -G1 X126.312 Y104.385 E.00337 -G1 X126.204 Y104.693 E.01078 -G1 X126.203 Y105.2 E.01674 -G1 X126.307 Y105.575 E.01285 -G1 X126.403 Y105.624 E.00356 -G1 X126.403 Y106.403 E.02572 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.76 E.02123 -G1 X123.345 Y105.448 F9000 +G1 X128.701 Y103.919 E.0052 +G1 X128.576 Y104.141 E.00841 +G1 X128.517 Y104.355 E.00733 +G1 X128.506 Y104.628 E.00902 +G1 X128.522 Y105.586 E.03163 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.443 Y103.918 E.01041 +G1 X121.281 Y103.621 E.01117 +G1 X120.994 Y103.483 E.01051 +G1 X120.994 Y100.994 E.08218 +G1 X121.283 Y100.994 E.00954 +G1 X121.306 Y101.069 E.00259 +G1 X121.72 Y101.413 E.01777 +G1 X122.11 Y101.494 E.01315 +G1 X123.183 Y101.494 E.03543 +G1 X123.576 Y101.411 E.01326 +G1 X123.894 Y101.185 E.01288 +G1 X123.969 Y100.994 E.00678 +G1 X126.002 Y100.994 E.06712 +G1 X126.077 Y101.185 E.00678 +G1 X126.395 Y101.411 E.01288 +G1 X126.788 Y101.494 E.01326 +G1 X127.894 Y101.494 E.03652 +G1 X128.284 Y101.412 E.01316 +G1 X128.698 Y101.068 E.01777 +G1 X128.721 Y100.994 E.00256 +G1 X129.006 Y100.994 E.00941 +G1 X129.006 Y103.767 E.09156 +G1 X128.895 Y103.822 E.00409 +G1 X128.895 Y103.822 F9000 +G1 X129.103 Y104.116 +G1 F900 +G1 X129.019 Y104.158 E.0031 +G1 X128.945 Y104.289 E.00497 +G1 X128.903 Y104.628 E.01128 +G1 X128.913 Y105.513 E.02922 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.088 Y105.872 E.00845 +G1 X121.097 Y105.407 E.01536 +G1 X121.092 Y104.151 E.04147 +G1 X120.971 Y103.869 E.01013 +G1 X120.597 Y103.689 E.0137 +G1 X120.597 Y100.597 E.10209 +G1 X121.558 Y100.597 E.03173 +G1 X121.635 Y100.845 E.00857 +G1 X121.88 Y101.049 E.01053 +G1 X122.11 Y101.097 E.00776 +G1 X123.183 Y101.097 E.03543 +G1 X123.367 Y101.067 E.00616 +G1 X123.604 Y100.914 E.00931 +G1 X123.729 Y100.597 E.01125 +G1 X126.242 Y100.597 E.08297 +G1 X126.367 Y100.914 E.01125 +G1 X126.527 Y101.034 E.0066 +G1 X126.788 Y101.097 E.00886 +G1 X127.894 Y101.097 E.03652 +G1 X128.125 Y101.049 E.00779 +G1 X128.37 Y100.845 E.01053 +G1 X128.446 Y100.597 E.00856 +G1 X129.403 Y100.597 E.0316 +G1 X129.403 Y103.966 E.11124 +G1 X129.157 Y104.089 E.00908 +G1 X129.157 Y104.089 F9000 +G1 X129.367 Y104.391 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.374 Y105.432 E.00104 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.374 Y104.567 E.00369 -G1 X123.21 Y104.49 E.00568 -G1 X123.21 Y103.21 E.04013 -G1 X124.477 Y103.21 E.03973 -G1 X124.566 Y103.374 E.00585 -G1 X124.677 Y103.41 E.00366 -G1 X125.277 Y103.41 E.01881 -G1 X125.456 Y103.38 E.00569 -G1 X125.542 Y103.21 E.00597 -G1 X126.79 Y103.21 E.03913 -G1 X126.79 Y104.523 E.04117 -G1 X126.626 Y104.612 E.00585 -G1 X126.598 Y104.668 E.00196 -G1 X126.59 Y105 E.01041 -G1 X126.624 Y105.352 E.01109 -G1 X126.79 Y105.444 E.00595 -G1 X126.79 Y106.79 E.0422 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.292 Y105.477 E.00292 -G1 X123.292 Y105.477 F9000 -G1 X124.452 Y105.7 -;TYPE:Internal infill +G1 X129.304 Y104.434 E.00239 +G1 X129.29 Y104.628 E.0061 +G1 X129.303 Y105.476 E.02659 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.669 Y104.111 E.00392 +G1 X120.521 Y104.04 E.00515 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X121.922 Y100.21 E.05368 +G1 X121.954 Y100.628 E.01314 +G1 X122.035 Y100.694 E.00328 +G1 X122.303 Y100.71 E.00842 +G1 X123.269 Y100.689 E.0303 +G1 X123.372 Y100.521 E.00618 +G1 X123.372 Y100.21 E.00975 +G1 X126.6 Y100.21 E.10121 +G1 X126.6 Y100.521 E.00975 +G1 X126.65 Y100.65 E.00434 +G1 X126.788 Y100.71 E.00472 +G1 X127.894 Y100.71 E.03468 +G1 X128.05 Y100.627 E.00554 +G1 X128.082 Y100.21 E.01311 +G1 X129.79 Y100.21 E.05355 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.416 Y104.358 E.00237 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.367 Y104.391 E-.02806 +G1 X129.304 Y104.434 E-.03623 +G1 X129.29 Y104.628 E-.09239 +G1 X129.303 Y105.476 E-.40285 +G1 X129.479 Y105.596 E-.10118 +G1 X129.79 Y105.596 E-.14772 +G1 X129.79 Y105.907 E-.14772 +G1 X129.79 Y107.052 E-.54385 +;WIPE_END +G1 X128.7 Y106.318 F9000 +G1 E5 F2400 +;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X125.639 Y105.7 E.03919 -G1 X125.611 Y105.611 E.00308 -G1 X124.474 Y104.474 E.05309 -G1 X125.527 Y104.473 E.03477 -G1 X124.422 Y105.578 E.0516 -G1 X124.5 Y105.338 E.00833 -G1 X124.5 Y104.671 E.02202 +G1 X128.7 Y108.7 E.07865 +G1 X121.721 Y101.721 E.32587 +G1 X122.099 Y101.8 E.01275 +G1 X123.427 Y101.777 E.04385 +G1 X123.765 Y101.661 E.0118 +G1 X124.128 Y101.388 E.015 +G1 X124.162 Y101.3 E.00311 +G1 X125.809 Y101.3 E.05438 +G1 X125.844 Y101.388 E.00313 +G1 X126.205 Y101.661 E.01494 +G1 X126.782 Y101.8 E.0196 +G1 X127.905 Y101.8 E.03708 +G1 X128.278 Y101.722 E.01258 +G1 X121.3 Y108.7 E.32583 +G1 X121.3 Y106.664 E.06722 +G1 X121.582 Y106.479 E.01114 ;LAYER_CHANGE ;Z:2.2 ;HEIGHT:0.2 @@ -1470,105 +2871,159 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.5 Y105.338 E-.31683 -G1 X124.422 Y105.578 E-.11987 -G1 X125.527 Y104.473 E-.74229 -G1 X124.851 Y104.474 E-.32101 +G1 X121.3 Y106.664 E-.1602 +G1 X121.3 Y108.7 E-.9671 +G1 X121.855 Y108.145 E-.3727 ;WIPE_END G1 Z2.2 F9000 ;AFTER_LAYER_CHANGE ;2.2 G1 Z2.2 -G1 X123.994 Y103.996 +G1 X121.154 Y106.42 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X123.994 Y103.994 E.00007 -G1 X124.134 Y103.994 E.00462 -G1 X124.479 Y104.153 E.01254 -G1 X124.707 Y104.193 E.00764 -G1 X125.248 Y104.194 E.01786 -G1 X125.567 Y104.14 E.01068 -G1 X125.819 Y104.009 E.00938 -G1 X126.003 Y103.997 E.00609 -G1 X125.83 Y104.375 E.01373 -G1 X125.806 Y104.589 E.00711 -G1 X125.806 Y105.383 E.02622 -G1 X126.006 Y105.969 E.02044 -G1 X126.006 Y106.006 E.00122 -G1 X123.994 Y106.006 E.06643 -G1 X124.008 Y105.894 E.00373 -G1 X124.194 Y105.321 E.01989 -G1 X124.167 Y104.449 E.0288 -G1 X124.008 Y104.106 E.01248 -G1 X124.002 Y104.056 E.00166 -G1 X124.002 Y104.056 F9000 -G1 X123.602 Y104.298 -G1 F900 -G1 X123.597 Y104.294 E.00021 -G1 X123.597 Y103.597 E.02301 -G1 X124.378 Y103.597 E.02579 -G1 X124.427 Y103.69 E.00347 -G1 X124.594 Y103.773 E.00616 -G1 X124.76 Y103.797 E.00554 -G1 X125.312 Y103.793 E.01823 -G1 X125.586 Y103.687 E.0097 -G1 X125.632 Y103.597 E.00334 -G1 X126.403 Y103.597 E.02546 -G1 X126.403 Y104.199 E.01988 -G1 X126.278 Y104.306 E.00543 -G1 X126.203 Y104.589 E.00967 -G1 X126.203 Y105.383 E.02622 -G1 X126.318 Y105.728 E.01201 -G1 X126.403 Y105.771 E.00315 -G1 X126.403 Y106.403 E.02087 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.706 E.02301 -G1 X123.687 Y105.66 E.00334 -G1 X123.797 Y105.321 E.01177 -G1 X123.797 Y104.679 E.0212 -G1 X123.708 Y104.371 E.01059 -G1 X123.651 Y104.332 E.00228 -G1 X123.651 Y104.332 F9000 -G1 X123.339 Y104.556 +G1 X121.311 Y106.34 E.00582 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.458 Y103.964 E.00883 +G1 X121.281 Y103.621 E.01274 +G1 X120.994 Y103.483 E.01051 +G1 X120.994 Y100.994 E.08218 +G1 X121.443 Y100.994 E.01482 +G1 X121.479 Y101.102 E.00376 +G1 X121.883 Y101.419 E.01696 +G1 X122.259 Y101.494 E.01266 +G1 X123.327 Y101.494 E.03526 +G1 X123.591 Y101.458 E.0088 +G1 X123.864 Y101.332 E.00993 +G1 X124.046 Y100.994 E.01267 +G1 X125.928 Y100.994 E.06214 +G1 X126.11 Y101.332 E.01267 +G1 X126.285 Y101.424 E.00653 +G1 X126.647 Y101.494 E.01217 +G1 X127.75 Y101.494 E.03642 +G1 X128.126 Y101.418 E.01267 +G1 X128.532 Y101.101 E.01701 +G1 X128.567 Y100.994 E.00372 +G1 X129.006 Y100.994 E.01449 +G1 X129.006 Y103.767 E.09156 +G1 X128.701 Y103.919 E.01125 +G1 X128.56 Y104.185 E.00994 +G1 X128.515 Y104.37 E.00629 +G1 X128.506 Y104.686 E.01044 +G1 X128.522 Y105.586 E.02972 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.1 Y106.448 E.00393 +G1 X121.1 Y106.448 F9000 +G1 X120.895 Y106.156 +G1 F900 +G1 X120.989 Y106.108 E.00348 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.093 Y104.161 E.05316 +G1 X120.971 Y103.869 E.01045 +G1 X120.597 Y103.689 E.0137 +G1 X120.597 Y100.597 E.10209 +G1 X121.708 Y100.597 E.03668 +G1 X121.797 Y100.865 E.00932 +G1 X122.037 Y101.053 E.01007 +G1 X122.259 Y101.097 E.00747 +G1 X123.417 Y101.09 E.03823 +G1 X123.645 Y101.001 E.00808 +G1 X123.862 Y100.597 E.01514 +G1 X126.112 Y100.597 E.07429 +G1 X126.329 Y101.001 E.01514 +G1 X126.39 Y101.036 E.00232 +G1 X126.647 Y101.097 E.00872 +G1 X127.492 Y101.097 E.0279 +G1 X127.973 Y101.052 E.01595 +G1 X128.213 Y100.864 E.01007 +G1 X128.301 Y100.597 E.00928 +G1 X129.403 Y100.597 E.03639 +G1 X129.403 Y103.966 E.11124 +G1 X129.019 Y104.158 E.01418 +G1 X128.935 Y104.315 E.00588 +G1 X128.903 Y104.686 E.01229 +G1 X128.913 Y105.513 E.02731 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.841 Y106.183 E.00905 +G1 X120.841 Y106.183 F9000 +G1 X120.643 Y105.898 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.21 Y104.49 E.00454 -G1 X123.21 Y103.21 E.04013 -G1 X124.56 Y103.21 E.04233 -G1 X124.651 Y103.375 E.00591 -G1 X124.706 Y103.402 E.00192 -G1 X125.248 Y103.41 E.017 -G1 X125.359 Y103.374 E.00366 -G1 X125.437 Y103.21 E.00569 -G1 X126.79 Y103.21 E.04242 -G1 X126.79 Y104.389 E.03697 -G1 X126.629 Y104.475 E.00572 -G1 X126.59 Y104.589 E.00378 -G1 X126.59 Y105.2 E.01916 -G1 X126.628 Y105.496 E.00936 -G1 X126.79 Y105.583 E.00577 -G1 X126.79 Y106.79 E.03784 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.374 Y105.432 E.00585 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y105 E.01006 -G1 X123.382 Y104.591 E.01285 -G1 X123.382 Y104.591 F9000 -G1 X124.483 Y105.389 +G1 X120.675 Y105.882 E.00112 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.669 Y104.111 E.00392 +G1 X120.521 Y104.04 E.00515 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.07 Y100.21 E.05832 +G1 X122.07 Y100.521 E.00975 +G1 X122.108 Y100.634 E.00374 +G1 X122.259 Y100.71 E.0053 +G1 X123.327 Y100.71 E.03349 +G1 X123.397 Y100.696 E.00224 +G1 X123.516 Y100.521 E.00664 +G1 X123.516 Y100.21 E.00975 +G1 X126.458 Y100.21 E.09224 +G1 X126.458 Y100.521 E.00975 +G1 X126.543 Y100.679 E.00563 +G1 X126.647 Y100.71 E.0034 +G1 X127.75 Y100.71 E.03458 +G1 X127.902 Y100.634 E.00533 +G1 X127.939 Y100.521 E.00373 +G1 X127.939 Y100.21 E.00975 +G1 X129.79 Y100.21 E.05804 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.328 Y104.391 E.00529 +G1 X129.3 Y104.442 E.00182 +G1 X129.29 Y104.686 E.00766 +G1 X129.303 Y105.476 E.02477 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.59 Y105.925 E.00243 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X120.643 Y105.898 E-.02825 +G1 X120.675 Y105.882 E-.01699 +G1 X120.71 Y105.771 E-.05528 +G1 X120.71 Y104.229 E-.73245 +G1 X120.669 Y104.111 E-.05934 +G1 X120.521 Y104.04 E-.07797 +G1 X120.21 Y104.04 E-.14772 +G1 X120.21 Y103.236 E-.382 +;WIPE_END +G1 X121.451 Y103.385 F9000 +G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.476 Y104.476 E.03015 -G1 X125.55 Y105.55 E.05015 -G1 X125.5 Y105.4 E.00522 -G1 X125.509 Y104.491 E.03001 -G1 X124.422 Y105.578 E.05076 -G1 X124.382 Y105.7 E.00424 -G1 X125.56 Y105.7 E.03889 +G1 X121.3 Y103.312 E.00554 +G1 X121.3 Y101.342 E.06504 +G1 X128.7 Y108.7 E.34455 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.348 E.34441 +G1 X128.7 Y103.446 E.06927 ;LAYER_CHANGE ;Z:2.4 ;HEIGHT:0.2 @@ -1580,114 +3035,146 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.382 Y105.7 E-.55955 -G1 X124.422 Y105.578 E-.06099 -G1 X125.509 Y104.491 E-.73019 -G1 X125.506 Y104.805 E-.14927 +G1 X128.7 Y101.348 E-.99655 +G1 X127.948 Y102.095 E-.50345 ;WIPE_END G1 Z2.4 F9000 ;AFTER_LAYER_CHANGE ;2.4 G1 Z2.4 -G1 X126.868 Y105.006 +G1 X128.842 Y103.849 G1 E5 F2400 -;TYPE:External perimeter -;WIDTH:0.38292 -G1 F900 -G1 X126.769 Y105.006 E.0028 -G1 X126.769 Y105.006 F9000 -G1 X125.855 Y105.66 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X125.978 Y106.006 E.01212 -G1 X123.994 Y106.006 E.06551 -G1 X124.008 Y105.894 E.00373 -G1 X124.194 Y105.321 E.01989 -G1 X124.194 Y105.002 E.01053 -G1 X124.153 Y104.399 E.01996 -G1 X124.004 Y104.101 E.011 -G1 X123.994 Y103.994 E.00355 -G1 X124.378 Y104.164 E.01387 -G1 X124.618 Y104.194 E.00799 -G1 X125.397 Y104.194 E.02572 -G1 X125.73 Y104.135 E.01117 -G1 X125.914 Y104.034 E.00693 -G1 X125.833 Y104.227 E.00691 -G1 X125.806 Y104.455 E.00758 -G1 X125.806 Y105.521 E.0352 -G1 X125.835 Y105.604 E.0029 -G1 X125.835 Y105.604 F9000 -G1 X126.392 Y105.913 -G1 F900 -G1 X126.403 Y105.918 E.0004 -G1 X126.403 Y106.403 E.01601 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.706 E.02301 -G1 X123.687 Y105.66 E.00334 -G1 X123.797 Y105.321 E.01177 -G1 X123.797 Y105.2 E.004 -G1 X123.797 Y104.654 E.01803 -G1 X123.685 Y104.337 E.0111 -G1 X123.597 Y104.293 E.00325 -G1 X123.597 Y103.597 E.02298 -G1 X124.23 Y103.597 E.0209 -G1 X124.272 Y103.682 E.00313 -G1 X124.476 Y103.779 E.00746 -G1 X124.679 Y103.797 E.00673 -G1 X125.462 Y103.793 E.02585 -G1 X125.752 Y103.675 E.01034 -G1 X125.79 Y103.597 E.00286 -G1 X126.403 Y103.597 E.02024 -G1 X126.403 Y104.057 E.01519 -G1 X126.334 Y104.089 E.00251 -G1 X126.219 Y104.32 E.00852 -G1 X126.203 Y104.895 E.01899 -G1 X126.248 Y105.006 E.00395 -G1 X126.207 Y105.045 E.00187 -G1 X126.203 Y105.521 E.01572 -G1 X126.332 Y105.884 E.01272 -G1 X126.338 Y105.887 E.00022 -G1 X126.338 Y105.887 F9000 -G1 X126.653 Y105.651 +G1 X128.701 Y103.919 E.0052 +G1 X128.546 Y104.227 E.01138 +G1 X128.506 Y104.739 E.01696 +G1 X128.522 Y105.586 E.02797 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.489 Y104.131 E.05415 +G1 X121.308 Y103.656 E.01678 +G1 X120.994 Y103.483 E.01184 +G1 X120.994 Y100.994 E.08218 +G1 X121.601 Y100.994 E.02004 +G1 X121.65 Y101.132 E.00484 +G1 X122.018 Y101.413 E.01529 +G1 X122.407 Y101.494 E.01312 +G1 X123.471 Y101.494 E.03513 +G1 X123.802 Y101.436 E.0111 +G1 X124.049 Y101.304 E.00925 +G1 X124.205 Y100.994 E.01146 +G1 X125.772 Y100.994 E.05174 +G1 X125.928 Y101.304 E.01146 +G1 X126.134 Y101.42 E.00781 +G1 X126.506 Y101.494 E.01252 +G1 X127.492 Y101.494 E.03256 +G1 X127.971 Y101.424 E.01598 +G1 X128.367 Y101.129 E.0163 +G1 X128.414 Y100.994 E.00472 +G1 X129.006 Y100.994 E.01955 +G1 X129.006 Y103.767 E.09156 +G1 X128.895 Y103.822 E.00409 +G1 X128.895 Y103.822 F9000 +G1 X129.103 Y104.116 +G1 F900 +G1 X129.019 Y104.158 E.0031 +G1 X128.927 Y104.34 E.00673 +G1 X128.903 Y104.739 E.0132 +G1 X128.913 Y105.513 E.02556 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.094 Y104.171 E.05283 +G1 X120.987 Y103.89 E.00993 +G1 X120.597 Y103.689 E.01449 +G1 X120.597 Y100.597 E.10209 +G1 X121.858 Y100.597 E.04163 +G1 X121.959 Y100.882 E.00998 +G1 X122.177 Y101.049 E.00907 +G1 X122.407 Y101.097 E.00776 +G1 X123.544 Y101.092 E.03754 +G1 X123.813 Y100.984 E.00957 +G1 X124.009 Y100.597 E.01432 +G1 X125.968 Y100.597 E.06468 +G1 X126.164 Y100.984 E.01432 +G1 X126.255 Y101.039 E.00351 +G1 X126.506 Y101.097 E.00851 +G1 X127.492 Y101.097 E.03256 +G1 X127.822 Y101.055 E.01098 +G1 X128.057 Y100.881 E.00965 +G1 X128.156 Y100.597 E.00993 +G1 X129.403 Y100.597 E.04117 +G1 X129.403 Y103.966 E.11124 +G1 X129.157 Y104.089 E.00908 +G1 X129.157 Y104.089 F9000 +G1 X129.356 Y104.377 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X126.79 Y105.721 E.00482 -G1 X126.79 Y106.79 E.03352 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.521 E.03979 -G1 X123.374 Y105.432 E.00585 -G1 X123.41 Y105.321 E.00366 -G1 X123.41 Y104.679 E.02013 -G1 X123.373 Y104.567 E.0037 -G1 X123.21 Y104.49 E.00565 -G1 X123.21 Y103.21 E.04013 -G1 X124.418 Y103.21 E.03788 -G1 X124.505 Y103.372 E.00577 -G1 X124.618 Y103.41 E.00374 -G1 X125.397 Y103.41 E.02442 -G1 X125.513 Y103.37 E.00385 -G1 X125.586 Y103.21 E.00551 -G1 X126.79 Y103.21 E.03775 -G1 X126.79 Y104.255 E.03277 -G1 X126.618 Y104.357 E.00627 -G1 X126.59 Y104.895 E.01689 -G1 X126.769 Y105.006 E.0066 -G1 X126.591 Y105.093 E.00621 -G1 X126.59 Y105.521 E.01342 -G1 X126.62 Y105.605 E.0028 -G1 X126.62 Y105.605 F9000 -G1 X125.553 Y105.69 +G1 X129.328 Y104.391 E.00098 +G1 X129.298 Y104.45 E.00208 +G1 X129.29 Y104.739 E.00906 +G1 X129.303 Y105.476 E.02311 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.674 Y104.118 E.00366 +G1 X120.521 Y104.04 E.00538 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.219 Y100.21 E.06299 +G1 X122.219 Y100.521 E.00975 +G1 X122.26 Y100.64 E.00395 +G1 X122.407 Y100.71 E.0051 +G1 X123.471 Y100.71 E.03336 +G1 X123.543 Y100.696 E.0023 +G1 X123.66 Y100.521 E.0066 +G1 X123.66 Y100.21 E.00975 +G1 X126.317 Y100.21 E.08331 +G1 X126.317 Y100.521 E.00975 +G1 X126.394 Y100.673 E.00534 +G1 X126.506 Y100.71 E.0037 +G1 X127.607 Y100.71 E.03452 +G1 X127.754 Y100.639 E.00512 +G1 X127.796 Y100.521 E.00393 +G1 X127.796 Y100.21 E.00975 +G1 X129.79 Y100.21 E.06252 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.409 Y104.35 E.00244 +G1 X129.409 Y104.35 F9000 +G1 X128.628 Y103.638 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.382 Y105.7 E.03866 -G1 X124.422 Y105.578 E.00424 -G1 X125.51 Y104.49 E.0508 -G1 X125.5 Y105.5 E.03335 -G1 X124.476 Y104.483 E.04765 -G1 X124.483 Y105.389 E.02991 +G1 X128.7 Y103.601 E.00267 +G1 X128.7 Y101.3 E.07597 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.313 E.06646 +G1 X121.516 Y103.417 E.00792 +G1 X121.573 Y103.533 E.00427 ;LAYER_CHANGE ;Z:2.6 ;HEIGHT:0.2 @@ -1699,108 +3186,147 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.476 Y104.483 E-.43036 -G1 X125.5 Y105.5 E-.68553 -G1 X125.508 Y104.692 E-.38411 +G1 X121.516 Y103.417 E-.06139 +G1 X121.3 Y103.313 E-.11387 +G1 X121.3 Y101.3 E-.95618 +G1 X121.849 Y101.849 E-.36856 ;WIPE_END G1 Z2.6 F9000 ;AFTER_LAYER_CHANGE ;2.6 G1 Z2.6 -G1 X125.021 Y103.121 +G1 X121.164 Y103.565 G1 E5 F2400 -;TYPE:External perimeter -;WIDTH:0.38292 -G1 F900 -G1 X125.021 Y103.23 E.00308 -G1 X125.021 Y103.23 F9000 -G1 X125.648 Y104.177 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X125.828 Y104.146 E.00603 -G1 X125.806 Y104.454 E.0102 -G1 X125.806 Y105.659 E.03979 -G1 X125.937 Y106.006 E.01225 -G1 X124.117 Y106.006 E.06009 -G1 X124.194 Y105.821 E.00662 -G1 X124.194 Y104.179 E.05421 -G1 X125.546 Y104.194 E.04464 -G1 X125.589 Y104.187 E.00144 -G1 X125.589 Y104.187 F9000 -G1 X125.941 Y103.613 -G1 F900 -G1 X125.949 Y103.597 E.00059 -G1 X126.403 Y103.597 E.01499 -G1 X126.403 Y103.913 E.01043 -G1 X126.302 Y103.999 E.00438 -G1 X126.203 Y104.322 E.01115 -G1 X126.222 Y104.916 E.01962 -G1 X126.321 Y105.007 E.00444 -G1 X126.222 Y105.098 E.00444 -G1 X126.203 Y105.659 E.01853 -G1 X126.348 Y106.042 E.01352 -G1 X126.403 Y106.066 E.00198 -G1 X126.403 Y106.403 E.01113 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y106.302 E.00333 -G1 X123.797 Y105.821 E.0172 -G1 X123.797 Y104.179 E.05421 -G1 X123.739 Y103.926 E.00857 -G1 X123.597 Y103.758 E.00726 -G1 X123.597 Y103.597 E.00532 -G1 X124.07 Y103.597 E.01562 -G1 X124.096 Y103.654 E.00207 -G1 X124.324 Y103.777 E.00855 -G1 X124.922 Y103.797 E.01976 -G1 X125.021 Y103.758 E.00351 -G1 X125.088 Y103.796 E.00254 -G1 X125.611 Y103.793 E.01727 -G1 X125.912 Y103.663 E.01083 -G1 X125.912 Y103.663 F9000 -G1 X125.677 Y103.345 +G1 X120.994 Y103.483 E.00623 +G1 X120.994 Y100.994 E.08218 +G1 X121.759 Y100.994 E.02526 +G1 X121.819 Y101.157 E.00573 +G1 X122.142 Y101.402 E.01339 +G1 X122.556 Y101.494 E.014 +G1 X123.615 Y101.494 E.03497 +G1 X123.914 Y101.447 E.00999 +G1 X124.24 Y101.267 E.0123 +G1 X124.367 Y100.994 E.00994 +G1 X125.613 Y100.994 E.04114 +G1 X125.74 Y101.267 E.00994 +G1 X126.018 Y101.43 E.01064 +G1 X126.365 Y101.494 E.01165 +G1 X127.464 Y101.494 E.03629 +G1 X127.822 Y101.426 E.01203 +G1 X128.203 Y101.154 E.01546 +G1 X128.262 Y100.994 E.00563 +G1 X129.006 Y100.994 E.02456 +G1 X129.006 Y103.767 E.09156 +G1 X128.701 Y103.919 E.01125 +G1 X128.535 Y104.269 E.01279 +G1 X128.506 Y104.787 E.01713 +G1 X128.522 Y105.586 E.02639 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.478 Y104.054 E.05669 +G1 X121.282 Y103.622 E.01566 +G1 X121.218 Y103.591 E.00235 +G1 X121.218 Y103.591 F9000 +G1 X120.9 Y103.835 +G1 F900 +G1 X120.597 Y103.689 E.01111 +G1 X120.597 Y100.597 E.10209 +G1 X122.008 Y100.597 E.04659 +G1 X122.12 Y100.898 E.0106 +G1 X122.311 Y101.043 E.00792 +G1 X122.556 Y101.097 E.00828 +G1 X123.718 Y101.088 E.03837 +G1 X123.985 Y100.963 E.00973 +G1 X124.155 Y100.597 E.01332 +G1 X125.824 Y100.597 E.05511 +G1 X125.995 Y100.963 E.01334 +G1 X126.121 Y101.043 E.00493 +G1 X126.365 Y101.097 E.00825 +G1 X127.464 Y101.097 E.03629 +G1 X127.676 Y101.057 E.00712 +G1 X127.901 Y100.896 E.00913 +G1 X128.012 Y100.597 E.01053 +G1 X129.403 Y100.597 E.04593 +G1 X129.403 Y103.966 E.11124 +G1 X129.019 Y104.158 E.01418 +G1 X128.92 Y104.365 E.00758 +G1 X128.903 Y104.787 E.01394 +G1 X128.913 Y105.513 E.02397 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.095 Y104.181 E.0525 +G1 X120.971 Y103.87 E.01105 +G1 X120.954 Y103.861 E.00064 +G1 X120.954 Y103.861 F9000 +G1 X120.641 Y104.106 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X125.735 Y103.21 E.00461 -G1 X126.79 Y103.21 E.03308 -G1 X126.79 Y104.122 E.0286 -G1 X126.638 Y104.196 E.0053 -G1 X126.596 Y104.274 E.00278 -G1 X126.596 Y104.818 E.01706 -G1 X126.779 Y104.959 E.00724 -G1 X126.79 Y105.007 E.00154 -G1 X126.596 Y105.195 E.00847 -G1 X126.59 Y105.659 E.01455 -G1 X126.638 Y105.785 E.00423 -G1 X126.79 Y105.859 E.0053 -G1 X126.79 Y106.79 E.02919 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y106.021 E.02411 -G1 X123.354 Y105.955 E.00497 -G1 X123.41 Y105.821 E.00455 -G1 X123.41 Y104.179 E.05148 -G1 X123.354 Y104.045 E.00455 -G1 X123.21 Y103.99 E.00483 -G1 X123.21 Y103.21 E.02446 -G1 X124.275 Y103.21 E.03339 -G1 X124.39 Y103.39 E.0067 -G1 X124.922 Y103.41 E.01669 -G1 X125.021 Y103.23 E.00644 -G1 X125.1 Y103.409 E.00613 -G1 X125.546 Y103.41 E.01398 -G1 X125.633 Y103.378 E.00291 -G1 X125.633 Y103.378 F9000 -G1 X125.5 Y105.302 +G1 X120.521 Y104.04 E.00429 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.367 Y100.21 E.06763 +G1 X122.367 Y100.521 E.00975 +G1 X122.413 Y100.645 E.00415 +G1 X122.556 Y100.71 E.00493 +G1 X123.615 Y100.71 E.0332 +G1 X123.695 Y100.692 E.00257 +G1 X123.804 Y100.521 E.00636 +G1 X123.804 Y100.21 E.00975 +G1 X126.176 Y100.21 E.07437 +G1 X126.176 Y100.521 E.00975 +G1 X126.244 Y100.666 E.00502 +G1 X126.365 Y100.71 E.00404 +G1 X127.464 Y100.71 E.03446 +G1 X127.607 Y100.644 E.00494 +G1 X127.652 Y100.521 E.00411 +G1 X127.652 Y100.21 E.00975 +G1 X129.79 Y100.21 E.06704 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.328 Y104.391 E.00529 +G1 X129.296 Y104.459 E.00236 +G1 X129.29 Y104.787 E.01029 +G1 X129.303 Y105.476 E.02161 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.685 Y104.142 E.00284 +G1 X120.685 Y104.142 F9000 +G1 X121.589 Y103.526 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X125.501 Y104.499 E.02651 -G1 X124.5 Y105.5 E.04674 -G1 X124.5 Y104.5 E.03302 -G1 X125.5 Y105.5 E.04669 -G1 X125.508 Y105.7 E.00661 -G1 X124.5 Y105.699 E.03328 +G1 X121.508 Y103.414 E.00456 +G1 X121.3 Y103.313 E.00763 +G1 X121.3 Y101.3 E.06646 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.603 E.07604 +G1 X128.63 Y103.64 E.00261 ;LAYER_CHANGE ;Z:2.8 ;HEIGHT:0.2 @@ -1812,124 +3338,150 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.508 Y105.7 E-.4788 -G1 X125.5 Y105.5 E-.09508 -G1 X124.5 Y104.5 E-.67175 -G1 X124.5 Y105.036 E-.25437 +G1 X128.7 Y103.603 E-.03761 +G1 X128.7 Y101.3 E-1.09393 +G1 X128.151 Y101.849 E-.36846 ;WIPE_END G1 Z2.8 F9000 ;AFTER_LAYER_CHANGE ;2.8 G1 Z2.8 -G1 X124.051 Y106.006 +G1 X128.842 Y103.849 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.194 Y105.621 E.01356 -G1 X124.194 Y104.783 E.02767 -G1 X124.162 Y104.17 E.02027 -G1 X124.379 Y104.194 E.00721 -G1 X125.806 Y104.188 E.04712 -G1 X125.806 Y104.641 E.01496 -G1 X125.866 Y104.976 E.01124 -G1 X125.811 Y105.277 E.0101 -G1 X125.806 Y105.798 E.0172 -G1 X125.891 Y106.006 E.00742 -G1 X124.111 Y106.006 E.05877 -G1 X124.111 Y106.006 F9000 -G1 X123.61 Y106.02 -G1 F900 -G1 X123.657 Y105.998 E.00171 -G1 X123.797 Y105.621 E.01328 -G1 X123.797 Y104.783 E.02767 -;WIDTH:0.443938 -G1 X123.769 Y104.195 E.01963 -;WIDTH:0.47111 -G1 X123.667 Y103.99 E.00815 -G1 X123.613 Y103.965 E.00212 -;WIDTH:0.470966 -G1 X123.613 Y103.613 E.01253 -G1 X123.917 Y103.613 E.01082 -G1 X123.939 Y103.662 E.00191 -G1 X124.171 Y103.776 E.0092 -;WIDTH:0.443938 -G1 X124.379 Y103.797 E.00697 -;WIDTH:0.439999 -G1 X124.932 Y103.777 E.01827 -G1 X125.026 Y103.676 E.00456 -G1 X125.121 Y103.777 E.00458 -G1 X125.192 Y103.792 E.0024 -G1 X125.695 Y103.797 E.01661 -G1 X125.911 Y103.748 E.00731 -;WIDTH:0.427554 -G1 X126.096 Y103.574 E.00812 -;WIDTH:0.394268 -G1 X126.426 Y103.574 E.00964 -G1 X126.426 Y103.781 E.00605 -G1 X126.388 Y103.797 E.0012 -;WIDTH:0.427554 -G1 X126.23 Y104.034 E.00911 -;WIDTH:0.439999 -G1 X126.203 Y104.188 E.00516 -G1 X126.215 Y104.757 E.01879 -G1 X126.373 Y105.007 E.00976 -G1 X126.215 Y105.257 E.00976 -G1 X126.203 Y105.798 E.01787 -G1 X126.403 Y106.217 E.01533 -G1 X126.403 Y106.403 E.00614 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y106.072 E.01093 -G1 X123.597 Y106.072 F9000 -G1 X123.348 Y105.753 +G1 X128.701 Y103.92 E.00521 +G1 X128.525 Y104.31 E.01413 +G1 X128.506 Y104.83 E.01718 +G1 X128.522 Y105.586 E.02497 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.372 Y103.757 E.0161 +G1 X121.283 Y103.624 E.00528 +G1 X120.994 Y103.484 E.0106 +G1 X120.994 Y100.994 E.08221 +G1 X121.915 Y100.994 E.03041 +G1 X121.988 Y101.179 E.00657 +G1 X122.27 Y101.392 E.01167 +G1 X122.704 Y101.494 E.01472 +G1 X123.829 Y101.492 E.03714 +G1 X124.153 Y101.411 E.01103 +G1 X124.437 Y101.219 E.01132 +G1 X124.532 Y100.994 E.00806 +G1 X125.451 Y100.994 E.03034 +G1 X125.546 Y101.219 E.00806 +G1 X125.718 Y101.352 E.00718 +G1 X125.904 Y101.44 E.00679 +G1 X126.224 Y101.494 E.01071 +G1 X127.321 Y101.494 E.03622 +G1 X127.702 Y101.416 E.01284 +G1 X128.04 Y101.176 E.01369 +G1 X128.111 Y100.994 E.00645 +G1 X129.006 Y100.994 E.02955 +G1 X129.006 Y103.767 E.09156 +G1 X128.896 Y103.822 E.00406 +G1 X128.896 Y103.822 F9000 +G1 X129.103 Y104.116 +G1 F900 +G1 X129.018 Y104.158 E.00313 +G1 X128.914 Y104.39 E.00839 +G1 X128.903 Y104.83 E.01453 +G1 X128.913 Y105.513 E.02255 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y104.229 E.05091 +G1 X121.001 Y103.91 E.011 +G1 X120.597 Y103.689 E.0152 +G1 X120.597 Y100.597 E.10209 +G1 X122.158 Y100.597 E.05154 +G1 X122.28 Y100.911 E.01112 +G1 X122.447 Y101.036 E.00689 +G1 X122.704 Y101.097 E.00872 +G1 X123.759 Y101.097 E.03483 +G1 X123.918 Y101.075 E.0053 +G1 X124.16 Y100.934 E.00925 +G1 X124.303 Y100.597 E.01209 +G1 X125.68 Y100.597 E.04546 +G1 X125.823 Y100.934 E.01209 +G1 X125.991 Y101.048 E.0067 +G1 X126.224 Y101.097 E.00786 +G1 X127.321 Y101.097 E.03622 +G1 X127.546 Y101.051 E.00758 +G1 X127.747 Y100.909 E.00813 +G1 X127.867 Y100.597 E.01104 +G1 X129.403 Y100.597 E.05071 +G1 X129.403 Y103.966 E.11124 +G1 X129.157 Y104.089 E.00908 +G1 X129.157 Y104.089 F9000 +G1 X129.356 Y104.377 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.364 Y105.745 E.00056 -G1 X123.41 Y105.621 E.00415 -G1 X123.41 Y104.379 E.03894 -G1 X123.363 Y104.254 E.00419 -G1 X123.21 Y104.19 E.0052 -G1 X123.21 Y103.21 E.03073 -G1 X124.133 Y103.21 E.02894 -G1 X124.207 Y103.362 E.0053 -G1 X124.28 Y103.402 E.00261 -G1 X124.831 Y103.404 E.01728 -G1 X124.971 Y103.221 E.00722 -G1 X125.082 Y103.221 E.00348 -G1 X125.221 Y103.404 E.00721 -G1 X125.695 Y103.41 E.01486 -G1 X125.824 Y103.359 E.00435 -G1 X125.884 Y103.21 E.00504 -G1 X126.79 Y103.21 E.02841 -G1 X126.79 Y103.988 E.02439 -G1 X126.645 Y104.055 E.00501 -G1 X126.597 Y104.137 E.00298 -G1 X126.59 Y104.641 E.0158 -G1 X126.676 Y104.799 E.00564 -G1 X126.779 Y104.829 E.00336 -G1 X126.79 Y105.174 E.01082 -G1 X126.676 Y105.216 E.00381 -G1 X126.602 Y105.309 E.00373 -G1 X126.59 Y105.798 E.01534 -G1 X126.644 Y105.93 E.00447 -G1 X126.79 Y105.998 E.00505 -G1 X126.79 Y106.79 E.02483 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.821 E.03038 -G1 X123.294 Y105.78 E.00293 -G1 X123.294 Y105.78 F9000 -G1 X124.48 Y105.695 +G1 X129.328 Y104.391 E.00098 +G1 X129.294 Y104.467 E.00261 +G1 X129.29 Y104.83 E.01138 +G1 X129.303 Y105.476 E.02026 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.818 E.02988 +G1 X120.686 Y104.137 E.02137 +G1 X120.521 Y104.04 E.006 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.516 Y100.21 E.0723 +G1 X122.516 Y100.521 E.00975 +G1 X122.565 Y100.649 E.0043 +G1 X122.704 Y100.71 E.00476 +G1 X123.759 Y100.71 E.03308 +G1 X123.857 Y100.683 E.00319 +G1 X123.948 Y100.521 E.00583 +G1 X123.948 Y100.21 E.00975 +G1 X126.035 Y100.21 E.06544 +G1 X126.035 Y100.521 E.00975 +G1 X126.092 Y100.657 E.00462 +G1 X126.224 Y100.71 E.00446 +G1 X127.321 Y100.71 E.0344 +G1 X127.46 Y100.648 E.00477 +G1 X127.509 Y100.521 E.00427 +G1 X127.509 Y100.21 E.00975 +G1 X129.79 Y100.21 E.07152 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.41 Y104.35 E.00241 +G1 X129.41 Y104.35 F9000 +G1 X128.628 Y103.638 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X125.504 Y105.7 E.03381 -G1 X125.505 Y105.505 E.00644 -G1 X124.5 Y104.5 E.04693 -G1 X124.5 Y105.5 E.03302 -G1 X125.501 Y104.499 E.04674 -G1 X125.559 Y105.006 E.01685 -G1 X125.506 Y105.307 E.01009 +G1 X128.7 Y103.602 E.00266 +G1 X128.7 Y101.3 E.07601 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.314 E.0665 +G1 X121.509 Y103.415 E.00766 +G1 X121.589 Y103.525 E.00449 ;LAYER_CHANGE ;Z:3 ;HEIGHT:0.2 @@ -1941,141 +3493,157 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.559 Y105.006 E-.14517 -G1 X125.501 Y104.499 E-.2424 -G1 X124.5 Y105.5 E-.67242 -G1 X124.5 Y104.574 E-.44001 +G1 X121.509 Y103.415 E-.06461 +G1 X121.3 Y103.314 E-.11026 +G1 X121.3 Y101.3 E-.95665 +G1 X121.849 Y101.849 E-.36848 ;WIPE_END G1 Z3 F9000 ;AFTER_LAYER_CHANGE ;3 G1 Z3 -G1 X125.918 Y104.924 +G1 X121.163 Y103.567 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X125.959 Y105.008 E.00309 -G1 X125.888 Y105.114 E.00421 -G1 X125.821 Y105.332 E.00753 -G1 X125.806 Y105.936 E.01995 -G1 X125.837 Y106.006 E.00253 -G1 X123.996 Y106.006 E.06078 -G1 X124.194 Y105.421 E.02039 -G1 X124.194 Y104.641 E.02575 -G1 X124.141 Y104.263 E.0126 -G1 X124.093 Y104.176 E.00328 -G1 X124.579 Y104.194 E.01606 -G1 X124.988 Y104.13 E.01367 -G1 X125.302 Y104.187 E.01054 -G1 X125.806 Y104.194 E.01664 -G1 X125.841 Y104.768 E.01899 -G1 X125.891 Y104.871 E.00378 -G1 X125.891 Y104.871 F9000 -G1 X126.385 Y104.903 -G1 F900 -G1 X126.403 Y104.925 E.00094 -G1 X126.403 Y105.091 E.00548 -G1 X126.363 Y105.108 E.00144 -G1 X126.212 Y105.403 E.01094 -G1 X126.203 Y105.936 E.0176 -G1 X126.403 Y106.369 E.01575 -G1 X126.403 Y106.403 E.00112 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y105.812 E.01951 -G1 X123.678 Y105.772 E.00298 -G1 X123.797 Y105.421 E.01224 -G1 X123.797 Y104.989 E.01426 -G1 X123.766 Y104.392 E.01974 -G1 X123.663 Y104.21 E.0069 -G1 X123.597 Y104.179 E.00241 -G1 X123.597 Y103.597 E.01922 -G1 X123.707 Y103.597 E.00363 -G1 X123.985 Y103.759 E.01062 -G1 X124.19 Y103.797 E.00688 -G1 X124.765 Y103.784 E.01899 -G1 X125.031 Y103.617 E.01037 -G1 X125.217 Y103.759 E.00773 -G1 X125.49 Y103.797 E.0091 -;WIDTH:0.484531 -G1 X126.085 Y103.769 E.02187 -;WIDTH:0.514814 -G1 X126.18 Y103.724 E.00412 -;WIDTH:0.545096 -G1 X126.275 Y103.679 E.00439 -G1 X126.241 Y103.778 E.00437 -;WIDTH:0.514814 -G1 X126.207 Y103.877 E.00411 -;WIDTH:0.484531 -G1 X126.203 Y104.51 E.02324 -;WIDTH:0.439999 -G1 X126.252 Y104.742 E.00783 -G1 X126.347 Y104.857 E.00492 -G1 X126.347 Y104.857 F9000 -G1 X126.653 Y104.644 +G1 X120.994 Y103.485 E.0062 +G1 X120.994 Y100.994 E.08225 +G1 X122.072 Y100.994 E.03559 +G1 X122.155 Y101.199 E.0073 +G1 X122.64 Y101.471 E.01836 +G1 X122.852 Y101.494 E.00704 +G1 X123.903 Y101.494 E.0347 +G1 X124.275 Y101.42 E.01252 +G1 X124.641 Y101.156 E.0149 +G1 X124.701 Y100.994 E.0057 +G1 X125.285 Y100.994 E.01928 +G1 X125.345 Y101.156 E.0057 +G1 X125.711 Y101.42 E.0149 +G1 X126.083 Y101.494 E.01252 +G1 X127.177 Y101.494 E.03612 +G1 X127.579 Y101.407 E.01358 +G1 X127.879 Y101.195 E.01213 +G1 X127.96 Y100.994 E.00716 +G1 X129.006 Y100.994 E.03454 +G1 X129.006 Y103.768 E.09159 +G1 X128.7 Y103.921 E.0113 +G1 X128.518 Y104.35 E.01539 +G1 X128.506 Y104.867 E.01707 +G1 X128.522 Y105.586 E.02375 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.398 Y103.807 E.01429 +G1 X121.285 Y103.626 E.00705 +G1 X121.217 Y103.593 E.0025 +G1 X121.217 Y103.593 F9000 +G1 X120.9 Y103.836 +G1 F900 +G1 X120.597 Y103.69 E.01111 +G1 X120.597 Y100.597 E.10212 +G1 X122.308 Y100.597 E.05649 +G1 X122.44 Y100.922 E.01158 +G1 X122.727 Y101.083 E.01087 +G1 X122.852 Y101.097 E.00415 +G1 X123.903 Y101.097 E.0347 +G1 X124.075 Y101.071 E.00574 +G1 X124.34 Y100.897 E.01047 +G1 X124.451 Y100.597 E.01056 +G1 X125.535 Y100.597 E.03579 +G1 X125.646 Y100.897 E.01056 +G1 X125.863 Y101.053 E.00882 +G1 X126.083 Y101.097 E.00741 +G1 X127.177 Y101.097 E.03612 +G1 X127.415 Y101.046 E.00804 +G1 X127.593 Y100.92 E.0072 +G1 X127.723 Y100.597 E.0115 +G1 X129.403 Y100.597 E.05547 +G1 X129.403 Y103.966 E.11124 +G1 X129.018 Y104.159 E.01422 +G1 X128.91 Y104.413 E.00911 +G1 X128.903 Y104.867 E.01499 +G1 X128.913 Y105.513 E.02133 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.095 Y104.176 E.05266 +G1 X120.973 Y103.872 E.01082 +G1 X120.954 Y103.862 E.00071 +G1 X120.954 Y103.862 F9000 +G1 X120.636 Y104.112 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X126.79 Y104.698 E.00462 -G1 X126.79 Y105.306 E.01906 -G1 X126.642 Y105.376 E.00513 -G1 X126.606 Y105.43 E.00203 -G1 X126.59 Y105.936 E.01587 -G1 X126.653 Y106.077 E.00484 -G1 X126.79 Y106.136 E.00468 -G1 X126.79 Y106.79 E.02051 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.621 E.03665 -G1 X123.371 Y105.536 E.00571 -G1 X123.41 Y105.421 E.00381 -G1 X123.41 Y104.641 E.02446 -G1 X123.366 Y104.458 E.0059 -G1 X123.21 Y104.39 E.00534 -G1 X123.21 Y103.21 E.037 -G1 X123.99 Y103.21 E.02446 -G1 X124.057 Y103.355 E.00501 -G1 X124.123 Y103.398 E.00247 -G1 X124.641 Y103.41 E.01625 -G1 X124.794 Y103.332 E.00538 -G1 X124.83 Y103.221 E.00366 -G1 X125.222 Y103.21 E.0123 -G1 X125.269 Y103.332 E.0041 -G1 X125.355 Y103.398 E.0034 -G1 X125.844 Y103.41 E.01534 -G1 X125.973 Y103.359 E.00435 -G1 X126.033 Y103.21 E.00504 -G1 X126.79 Y103.21 E.02374 -G1 X126.79 Y103.854 E.02019 -G1 X126.654 Y103.913 E.00465 -G1 X126.598 Y103.998 E.00319 -G1 X126.59 Y104.51 E.01606 -G1 X126.624 Y104.595 E.00287 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X126.653 Y104.644 E-.02705 -G1 X126.79 Y104.698 E-.06995 -G1 X126.79 Y105.306 E-.2888 -G1 X126.642 Y105.376 E-.07777 -G1 X126.606 Y105.43 E-.03083 -G1 X126.59 Y105.936 E-.24047 -G1 X126.653 Y106.077 E-.07336 -G1 X126.79 Y106.136 E-.07085 -G1 X126.79 Y106.79 E-.31065 -G1 X126.137 Y106.79 E-.31027 -;WIPE_END -G1 X124.5 Y104.686 F9000 +G1 X120.521 Y104.04 E.00425 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.664 Y100.21 E.07694 +G1 X122.664 Y100.521 E.00975 +G1 X122.717 Y100.653 E.00446 +G1 X122.852 Y100.71 E.00459 +G1 X123.903 Y100.71 E.03295 +G1 X124.001 Y100.682 E.0032 +G1 X124.092 Y100.521 E.0058 +G1 X124.092 Y100.21 E.00975 +G1 X125.894 Y100.21 E.0565 +G1 X125.894 Y100.521 E.00975 +G1 X125.94 Y100.644 E.00412 +G1 X126.083 Y100.71 E.00494 +G1 X127.177 Y100.71 E.0343 +G1 X127.313 Y100.652 E.00464 +G1 X127.366 Y100.521 E.00443 +G1 X127.366 Y100.21 E.00975 +G1 X129.79 Y100.21 E.076 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.315 Y104.41 E.00593 +G1 X129.29 Y104.867 E.01435 +G1 X129.303 Y105.476 E.0191 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.691 Y104.147 E.05092 +G1 X120.687 Y104.144 E.00016 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X120.636 Y104.112 E-.0286 +G1 X120.521 Y104.04 E-.06445 +G1 X120.21 Y104.04 E-.14772 +G1 X120.21 Y101.389 E-1.25923 +;WIPE_END +G1 X123.512 Y101.799 F9000 G1 E5 F2400 ;TYPE:Internal infill ;WIDTH:0.44 G1 F900 -G1 X124.469 Y105.531 E.02792 -G1 X125.506 Y104.494 E.04842 -G1 X125.031 Y104.436 E.0158 -G1 X124.5 Y104.5 E.01766 -G1 X125.511 Y105.511 E.04721 -G1 X125.504 Y105.7 E.00624 -G1 X124.432 Y105.7 E.03539 +G1 X122.563 Y101.769 E.03135 +G1 X121.927 Y101.413 E.02406 +G1 X121.881 Y101.3 E.00403 +G1 X121.3 Y101.3 E.01918 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.602 E.07601 +G1 X128.628 Y103.638 E.00266 ;LAYER_CHANGE ;Z:3.2 ;HEIGHT:0.2 @@ -2087,142 +3655,151 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.504 Y105.7 E-.5092 -G1 X125.511 Y105.511 E-.08984 -G1 X124.5 Y104.5 E-.67914 -G1 X124.964 Y104.444 E-.22182 +G1 X128.7 Y103.602 E-.03824 +G1 X128.7 Y101.3 E-1.09345 +G1 X128.152 Y101.848 E-.36831 ;WIPE_END G1 Z3.2 F9000 ;AFTER_LAYER_CHANGE ;3.2 G1 Z3.2 -G1 X124.008 Y105.886 +G1 X128.843 Y103.85 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X124.02 Y105.776 E.00365 -G1 X124.194 Y105.169 E.02085 -G1 X124.176 Y104.589 E.01916 -G1 X123.998 Y104.193 E.01433 -G1 X124.5 Y104.194 E.01657 -G1 X124.787 Y104.151 E.00958 -G1 X125.037 Y104.021 E.0093 -G1 X125.295 Y104.153 E.00957 -G1 X125.806 Y104.194 E.01693 -G1 X125.806 Y104.379 E.00611 -G1 X125.859 Y104.695 E.01058 -G1 X126.006 Y104.952 E.00978 -G1 X125.984 Y105.076 E.00416 -G1 X125.839 Y105.386 E.0113 -G1 X125.806 Y106.006 E.0205 -G1 X123.994 Y106.006 E.05983 -G1 X124.001 Y105.945 E.00203 -G1 X124.001 Y105.945 F9000 -G1 X123.599 Y105.6 -G1 F900 -G1 X123.694 Y105.55 E.00354 -G1 X123.797 Y105.221 E.01138 -G1 X123.797 Y104.779 E.01459 -G1 X123.698 Y104.456 E.01115 -G1 X123.597 Y104.388 E.00402 -G1 X123.597 Y103.607 E.02579 -G1 X124.048 Y103.797 E.01616 -G1 X124.612 Y103.786 E.01863 -G1 X124.876 Y103.657 E.0097 -G1 X124.904 Y103.597 E.00219 -G1 X125.17 Y103.597 E.00878 -G1 X125.197 Y103.657 E.00217 -G1 X125.473 Y103.788 E.01009 -G1 X125.993 Y103.797 E.01717 -G1 X126.233 Y103.743 E.00812 -G1 X126.203 Y103.92 E.00593 -G1 X126.216 Y104.502 E.01922 -G1 X126.309 Y104.711 E.00755 -G1 X126.403 Y104.76 E.0035 -G1 X126.403 Y105.257 E.01641 -G1 X126.309 Y105.305 E.00348 -G1 X126.223 Y105.489 E.00671 -G1 X126.203 Y106.075 E.01936 -G1 X126.366 Y106.403 E.01209 -G1 X123.597 Y106.403 E.09142 -G1 X123.597 Y105.658 E.0246 -G1 X123.597 Y105.658 F9000 -G1 X123.342 Y105.348 +G1 X128.699 Y103.922 E.00532 +G1 X128.65 Y103.994 E.00288 +G1 X128.513 Y104.389 E.0138 +G1 X128.506 Y104.9 E.01687 +G1 X128.522 Y105.586 E.02266 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.42 Y103.857 E.01252 +G1 X121.287 Y103.628 E.00874 +G1 X120.994 Y103.486 E.01075 +G1 X120.994 Y100.994 E.08228 +G1 X122.227 Y100.994 E.04071 +G1 X122.321 Y101.217 E.00799 +G1 X122.738 Y101.458 E.0159 +G1 X123.001 Y101.494 E.00876 +G1 X124.047 Y101.494 E.03454 +G1 X124.285 Y101.465 E.00792 +G1 X124.629 Y101.301 E.01258 +G1 X124.85 Y101.07 E.01056 +G1 X124.874 Y100.994 E.00263 +G1 X125.115 Y100.994 E.00796 +G1 X125.138 Y101.07 E.00262 +G1 X125.359 Y101.301 E.01056 +G1 X125.598 Y101.431 E.00898 +G1 X125.941 Y101.494 E.01151 +G1 X127.034 Y101.494 E.03609 +G1 X127.455 Y101.398 E.01426 +G1 X127.718 Y101.213 E.01062 +G1 X127.81 Y100.994 E.00784 +G1 X129.006 Y100.994 E.03949 +G1 X129.006 Y103.768 E.09159 +G1 X128.896 Y103.823 E.00406 +G1 X128.896 Y103.823 F9000 +G1 X129.103 Y104.117 +G1 F900 +G1 X129.017 Y104.16 E.00317 +G1 X128.907 Y104.436 E.00981 +G1 X128.903 Y104.9 E.01532 +G1 X128.913 Y105.513 E.02024 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y104.229 E.05091 +G1 X121.053 Y104.009 E.00741 +G1 X120.974 Y103.873 E.00519 +G1 X120.597 Y103.69 E.01384 +G1 X120.597 Y100.597 E.10212 +G1 X122.457 Y100.597 E.06141 +G1 X122.598 Y100.933 E.01203 +G1 X122.846 Y101.076 E.00945 +G1 X123.001 Y101.097 E.00516 +G1 X124.047 Y101.097 E.03454 +G1 X124.305 Y101.036 E.00875 +G1 X124.523 Y100.846 E.00955 +G1 X124.599 Y100.597 E.0086 +G1 X125.389 Y100.597 E.02608 +G1 X125.466 Y100.846 E.00861 +G1 X125.683 Y101.036 E.00952 +G1 X125.941 Y101.097 E.00875 +G1 X127.034 Y101.097 E.03609 +G1 X127.283 Y101.04 E.00843 +G1 X127.439 Y100.931 E.00628 +G1 X127.578 Y100.597 E.01194 +G1 X129.403 Y100.597 E.06026 +G1 X129.403 Y103.966 E.11124 +G1 X129.157 Y104.09 E.0091 +G1 X129.157 Y104.09 F9000 +G1 X129.361 Y104.381 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.376 Y105.329 E.00122 -G1 X123.41 Y105.008 E.01012 -G1 X123.378 Y104.673 E.01055 -G1 X123.21 Y104.59 E.00588 -G1 X123.21 Y103.21 E.04327 -G1 X123.848 Y103.21 E.02 -G1 X123.906 Y103.346 E.00464 -G1 X124.037 Y103.41 E.00457 -G1 X124.5 Y103.41 E.01452 -G1 X124.623 Y103.364 E.00412 -G1 X124.688 Y103.21 E.00524 -G1 X125.374 Y103.21 E.02151 -G1 X125.45 Y103.364 E.00538 -G1 X125.621 Y103.41 E.00555 -;WIDTH:0.457644 -G1 X125.759 Y103.411 E.00476 -;WIDTH:0.495289 -G1 X125.898 Y103.412 E.00523 -;WIDTH:0.532934 -G1 X126.036 Y103.413 E.00562 -;WIDTH:0.570578 -G1 X126.174 Y103.413 E.00605 -G1 X126.235 Y103.285 E.00622 -;WIDTH:0.569467 -G1 X126.715 Y103.285 E.02102 -G1 X126.715 Y103.678 E.01721 -;WIDTH:0.570578 -G1 X126.567 Y103.763 E.00749 -G1 X126.573 Y103.802 E.00173 -;WIDTH:0.532934 -G1 X126.578 Y103.842 E.00164 -;WIDTH:0.495289 -G1 X126.584 Y103.881 E.00148 -;WIDTH:0.457644 -G1 X126.59 Y103.92 E.00136 -;WIDTH:0.419999 -G1 X126.6 Y104.44 E.01631 -G1 X126.79 Y104.579 E.00738 -G1 X126.779 Y105.449 E.02728 -G1 X126.601 Y105.573 E.0068 -G1 X126.59 Y106.075 E.01574 -G1 X126.665 Y106.225 E.00526 -G1 X126.79 Y106.275 E.00422 -G1 X126.79 Y106.79 E.01615 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y105.421 E.04292 -G1 X123.289 Y105.377 E.00284 -G1 X123.289 Y105.377 F9000 -G1 X124.875 Y104.849 -;TYPE:Solid infill -;WIDTH:0.403669 -G1 F900 -G1 X124.862 Y105.321 E.01416 -G1 X125.16 Y105.321 E.00894 -G1 X125.239 Y104.984 E.01038 -G1 X125.176 Y104.822 E.00521 -G1 X125.044 Y104.771 E.00424 -;WIDTH:0.439999 -G1 X124.855 Y104.425 E.01302 -G1 X124.684 Y104.48 E.00593 -G1 X124.449 Y104.492 E.00777 -G1 X124.496 Y104.721 E.00772 -G1 X124.492 Y105.264 E.01793 -G1 X124.363 Y105.7 E.01501 -G1 X125.513 Y105.7 E.03797 -G1 X125.564 Y105.257 E.01472 -G1 X125.678 Y105.014 E.00886 -G1 X125.56 Y104.758 E.00931 -G1 X125.518 Y104.493 E.00886 -G1 X125.243 Y104.441 E.00924 -G1 X125.044 Y104.364 E.00705 +G1 X129.318 Y104.405 E.00154 +G1 X129.29 Y104.9 E.01555 +G1 X129.303 Y105.476 E.01806 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.696 Y104.157 E.05061 +G1 X120.521 Y104.04 E.0066 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.812 Y100.21 E.08158 +G1 X122.812 Y100.521 E.00975 +G1 X122.869 Y100.656 E.00459 +G1 X123.001 Y100.71 E.00447 +G1 X124.057 Y100.71 E.03311 +G1 X124.203 Y100.628 E.00525 +G1 X124.236 Y100.21 E.01315 +G1 X125.753 Y100.21 E.04756 +G1 X125.753 Y100.521 E.00975 +G1 X125.829 Y100.673 E.00533 +G1 X125.941 Y100.71 E.0037 +G1 X127.034 Y100.71 E.03427 +G1 X127.116 Y100.691 E.00264 +G1 X127.223 Y100.521 E.0063 +G1 X127.223 Y100.21 E.00975 +G1 X129.79 Y100.21 E.08049 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.413 Y104.352 E.00236 +G1 X129.413 Y104.352 F9000 +G1 X128.629 Y103.639 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.7 Y103.603 E.00263 +G1 X128.7 Y101.3 E.07604 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X122.041 Y101.3 E.02447 +G1 X122.097 Y101.433 E.00476 +G1 X122.646 Y101.751 E.02095 +G1 X123.505 Y101.795 E.0284 ;LAYER_CHANGE ;Z:3.4 ;HEIGHT:0.2 @@ -2234,137 +3811,150 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.243 Y104.441 E-.10135 -G1 X125.518 Y104.493 E-.13294 -G1 X125.56 Y104.758 E-.12745 -G1 X125.678 Y105.014 E-.1339 -G1 X125.564 Y105.257 E-.1275 -G1 X125.513 Y105.7 E-.21181 -G1 X124.363 Y105.7 E-.54625 -G1 X124.434 Y105.46 E-.1188 +G1 X122.646 Y101.751 E-.40856 +G1 X122.097 Y101.433 E-.30136 +G1 X122.041 Y101.3 E-.06855 +G1 X121.3 Y101.3 E-.35197 +G1 X121.85 Y101.85 E-.36956 ;WIPE_END G1 Z3.4 F9000 ;AFTER_LAYER_CHANGE ;3.4 G1 Z3.4 -G1 X125.058 Y103.994 +G1 X122.463 Y101.18 G1 E5 F2400 ;TYPE:Perimeter +;WIDTH:0.439999 G1 F900 -G1 X125.175 Y103.994 E.00386 -G1 X125.201 Y104.041 E.00177 -G1 X125.372 Y104.128 E.00633 -G1 X125.806 Y104.194 E.01449 -G1 X125.806 Y104.273 E.00261 -G1 X125.905 Y104.677 E.01373 -G1 X126.006 Y104.77 E.00453 -G1 X126.006 Y105.248 E.01578 -G1 X125.932 Y105.291 E.00283 -G1 X125.806 Y105.77 E.01635 -G1 X125.806 Y106.006 E.00779 -G1 X123.994 Y106.006 E.05983 -G1 X123.994 Y104.194 E.05983 -G1 X124.358 Y104.194 E.01202 -G1 X124.782 Y104.097 E.01436 -G1 X124.908 Y103.994 E.00537 -G1 X124.998 Y103.994 E.00297 -G1 X124.998 Y103.994 F9000 -G1 X124.731 Y103.597 -G1 F900 -G1 X125.355 Y103.597 E.0206 -G1 X125.415 Y103.706 E.00411 -G1 X125.589 Y103.781 E.00626 -G1 X126.203 Y103.794 E.02028 -G1 X126.203 Y104.248 E.01499 -G1 X126.251 Y104.478 E.00776 -G1 X126.403 Y104.605 E.00654 -G1 X126.403 Y105.413 E.02668 -G1 X126.277 Y105.486 E.00481 -G1 X126.203 Y105.77 E.00969 -G1 X126.203 Y106.213 E.01463 -G1 X126.31 Y106.403 E.0072 -G1 X123.597 Y106.403 E.08958 -G1 X123.597 Y103.641 E.09119 -G1 X123.906 Y103.797 E.01143 -G1 X124.358 Y103.797 E.01492 -G1 X124.609 Y103.74 E.0085 -G1 X124.692 Y103.641 E.00427 -G1 X124.692 Y103.641 F9000 -G1 X124.468 Y103.33 +G1 X122.486 Y101.233 E.00191 +G1 X122.84 Y101.444 E.01361 +G1 X123.149 Y101.494 E.01034 +G1 X124.191 Y101.494 E.0344 +G1 X124.438 Y101.462 E.00822 +G1 X124.778 Y101.297 E.01248 +G1 X124.996 Y101.034 E.01128 +G1 X125.214 Y101.297 E.01128 +G1 X125.491 Y101.444 E.01035 +G1 X125.8 Y101.494 E.01034 +G1 X126.891 Y101.494 E.03602 +G1 X127.104 Y101.471 E.00707 +G1 X127.559 Y101.228 E.01703 +G1 X127.66 Y100.994 E.00841 +G1 X129.006 Y100.994 E.04444 +G1 X129.006 Y103.769 E.09162 +G1 X128.698 Y103.924 E.01138 +G1 X128.636 Y104.019 E.00375 +G1 X128.509 Y104.427 E.01411 +G1 X128.506 Y104.928 E.01654 +G1 X128.522 Y105.586 E.02173 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.439 Y103.906 E.01082 +G1 X121.289 Y103.631 E.01034 +G1 X120.994 Y103.487 E.01084 +G1 X120.994 Y100.994 E.08231 +G1 X122.382 Y100.994 E.04583 +G1 X122.439 Y101.125 E.00472 +G1 X122.439 Y101.125 F9000 +G1 X122.742 Y100.909 +G1 F900 +G1 X122.757 Y100.942 E.0012 +G1 X122.966 Y101.067 E.00804 +G1 X123.149 Y101.097 E.00612 +G1 X124.295 Y101.088 E.03784 +G1 X124.538 Y100.981 E.00877 +G1 X124.707 Y100.776 E.00877 +G1 X124.749 Y100.597 E.00607 +G1 X125.242 Y100.597 E.01628 +G1 X125.284 Y100.776 E.00607 +G1 X125.453 Y100.981 E.00877 +G1 X125.558 Y101.044 E.00404 +G1 X125.8 Y101.097 E.00818 +G1 X126.951 Y101.094 E.038 +G1 X127.286 Y100.94 E.01217 +G1 X127.434 Y100.597 E.01233 +G1 X129.403 Y100.597 E.06501 +G1 X129.403 Y103.967 E.11127 +G1 X129.017 Y104.161 E.01426 +G1 X128.905 Y104.458 E.01048 +G1 X128.903 Y104.928 E.01552 +G1 X128.913 Y105.513 E.01932 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y104.229 E.05091 +G1 X121.065 Y104.038 E.00639 +G1 X120.976 Y103.875 E.00613 +G1 X120.597 Y103.69 E.01392 +G1 X120.597 Y100.597 E.10212 +G1 X122.607 Y100.597 E.06636 +G1 X122.718 Y100.854 E.00924 +G1 X122.718 Y100.854 F9000 +G1 X123.016 Y100.648 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X124.547 Y103.21 E.0045 -G1 X125.525 Y103.21 E.03066 -G1 X125.643 Y103.391 E.00677 -G1 X126.142 Y103.41 E.01566 -;WIDTH:0.445034 -G1 X126.216 Y103.393 E.00254 -;WIDTH:0.470069 -G1 X126.289 Y103.377 E.00265 -;WIDTH:0.473996 -G1 X126.353 Y103.237 E.00552 -G1 X126.763 Y103.237 E.0147 -G1 X126.763 Y103.575 E.01212 -G1 X126.607 Y103.659 E.00635 -;WIDTH:0.470069 -G1 X126.586 Y103.787 E.00461 -;WIDTH:0.427403 -G1 X126.605 Y104.321 E.01708 -;WIDTH:0.419999 -G1 X126.79 Y104.437 E.00685 -G1 X126.79 Y105.57 E.03552 -G1 X126.604 Y105.699 E.0071 -G1 X126.59 Y106.213 E.01612 -G1 X126.682 Y106.375 E.00584 -G1 X126.79 Y106.413 E.00359 -G1 X126.79 Y106.79 E.01182 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y103.21 E.11225 -G1 X123.706 Y103.21 E.01555 -G1 X123.754 Y103.333 E.00414 -G1 X123.906 Y103.41 E.00534 -G1 X124.424 Y103.398 E.01625 -G1 X124.436 Y103.381 E.00065 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X124.468 Y103.33 E-.0286 -G1 X124.547 Y103.21 E-.06824 -G1 X125.525 Y103.21 E-.46455 -G1 X125.643 Y103.391 E-.10263 -G1 X126.142 Y103.41 E-.2372 -G1 X126.216 Y103.393 E-.03607 -G1 X126.289 Y103.377 E-.0355 -G1 X126.353 Y103.237 E-.07312 -G1 X126.763 Y103.237 E-.19475 -G1 X126.763 Y103.575 E-.16055 -G1 X126.607 Y103.659 E-.08416 -G1 X126.602 Y103.689 E-.01463 -;WIPE_END -G1 X124.3 Y105.7 F9000 -G1 E5 F2400 -;TYPE:Solid infill -;WIDTH:0.439999 +G1 X123.021 Y100.659 E.00038 +G1 X123.149 Y100.71 E.00432 +G1 X124.225 Y100.707 E.03374 +G1 X124.36 Y100.605 E.00531 +G1 X124.38 Y100.21 E.0124 +G1 X125.612 Y100.21 E.03863 +G1 X125.612 Y100.521 E.00975 +G1 X125.687 Y100.672 E.00529 +G1 X125.8 Y100.71 E.00374 +G1 X126.891 Y100.71 E.03421 +G1 X127.02 Y100.658 E.00436 +G1 X127.08 Y100.521 E.00469 +G1 X127.08 Y100.21 E.00975 +G1 X129.79 Y100.21 E.08497 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.306 Y104.428 E.00646 +G1 X129.29 Y104.928 E.01569 +G1 X129.303 Y105.476 E.01719 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.688 Y104.14 E.05114 +G1 X120.521 Y104.04 E.0061 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.961 Y100.21 E.08626 +G1 X122.961 Y100.521 E.00975 +G1 X122.992 Y100.593 E.00246 +G1 X122.992 Y100.593 F9000 +G1 X123.499 Y101.8 +;TYPE:Internal infill +;WIDTH:0.44 G1 F900 -G1 X124.3 Y104.483 E.04018 -G1 X124.709 Y104.446 E.01356 -G1 X125.009 Y104.306 E.01093 -G1 X125.539 Y104.478 E.0184 -G1 X125.586 Y104.695 E.00733 -G1 X125.7 Y104.903 E.00783 -G1 X125.7 Y105.115 E.007 -G1 X125.606 Y105.263 E.00579 -G1 X125.518 Y105.7 E.01472 -G1 X124.498 Y105.7 E.03368 -G1 X124.726 Y105.274 E.01595 -;WIDTH:0.49769 -G1 X124.726 Y104.894 E.01437 -G1 X125.047 Y104.767 E.01305 -G1 X125.181 Y104.826 E.00554 -G1 X125.256 Y105.009 E.00748 -G1 X125.165 Y105.25 E.00974 -G1 X124.924 Y105.263 E.00913 +G1 X122.736 Y101.732 E.02529 +G1 X122.266 Y101.452 E.01806 +G1 X122.199 Y101.3 E.00548 +G1 X121.3 Y101.3 E.02968 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.604 E.07607 +G1 X128.63 Y103.639 E.00258 ;LAYER_CHANGE ;Z:3.6 ;HEIGHT:0.2 @@ -2376,68 +3966,147 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X125.165 Y105.25 E-.11464 -G1 X125.256 Y105.009 E-.12236 -G1 X125.181 Y104.826 E-.09394 -G1 X125.047 Y104.767 E-.06955 -G1 X124.726 Y104.894 E-.16397 -G1 X124.726 Y105.274 E-.1805 -G1 X124.498 Y105.7 E-.22951 -G1 X125.518 Y105.7 E-.4845 -G1 X125.535 Y105.615 E-.04103 +G1 X128.7 Y103.604 E-.03717 +G1 X128.7 Y101.3 E-1.0944 +G1 X128.152 Y101.848 E-.36843 ;WIPE_END G1 Z3.6 F9000 ;AFTER_LAYER_CHANGE ;3.6 G1 Z3.6 -G1 X123.994 Y106.006 +G1 X128.844 Y103.852 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X123.994 Y103.994 E.06643 -G1 X126.006 Y103.994 E.06643 -G1 X126.006 Y105 E.03322 -G1 X126.006 Y106.006 E.03322 -G1 X124.054 Y106.006 E.06445 -G1 X124.054 Y106.006 F9000 -G1 X123.597 Y106.403 -G1 F900 -G1 X123.597 Y103.597 E.09265 -G1 X126.403 Y103.597 E.09265 -G1 X126.403 Y105 E.04632 -G1 X126.403 Y106.403 E.04632 -G1 X123.657 Y106.403 E.09067 -G1 X123.21 Y106.79 F9000 +G1 X128.696 Y103.926 E.00546 +G1 X128.622 Y104.043 E.00457 +G1 X128.507 Y104.463 E.01438 +G1 X128.506 Y104.951 E.01611 +G1 X128.522 Y105.586 E.02097 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.335 E.04741 +G1 X121.455 Y103.955 E.01261 +G1 X121.292 Y103.635 E.01186 +G1 X120.994 Y103.488 E.01097 +G1 X120.994 Y100.994 E.08235 +G1 X122.537 Y100.994 E.05095 +G1 X122.65 Y101.247 E.00915 +G1 X122.945 Y101.428 E.01143 +G1 X123.298 Y101.494 E.01186 +G1 X124.544 Y101.472 E.04115 +G1 X124.914 Y101.303 E.01343 +G1 X124.997 Y101.183 E.00482 +G1 X125.081 Y101.303 E.00484 +G1 X125.288 Y101.42 E.00785 +G1 X125.659 Y101.494 E.01249 +G1 X126.748 Y101.494 E.03596 +G1 X127.005 Y101.46 E.00856 +G1 X127.401 Y101.243 E.01491 +G1 X127.511 Y100.994 E.00899 +G1 X129.006 Y100.994 E.04936 +G1 X129.006 Y103.77 E.09166 +G1 X128.897 Y103.825 E.00403 +G1 X128.897 Y103.825 F9000 +G1 X129.104 Y104.117 +G1 F900 +G1 X129.016 Y104.162 E.00326 +G1 X128.972 Y104.232 E.00273 +G1 X128.903 Y104.48 E.0085 +G1 X128.903 Y104.951 E.01555 +G1 X128.913 Y105.513 E.01856 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.074 Y104.067 E.05627 +G1 X120.977 Y103.877 E.00704 +G1 X120.597 Y103.69 E.01398 +G1 X120.597 Y100.597 E.10212 +G1 X122.756 Y100.597 E.07128 +G1 X122.914 Y100.951 E.0128 +G1 X123.089 Y101.058 E.00677 +G1 X123.298 Y101.097 E.00702 +G1 X124.42 Y101.091 E.03705 +G1 X124.678 Y100.984 E.00922 +G1 X124.889 Y100.679 E.01225 +G1 X124.9 Y100.597 E.00273 +G1 X125.094 Y100.597 E.00641 +G1 X125.106 Y100.679 E.00274 +G1 X125.317 Y100.984 E.01225 +G1 X125.439 Y101.053 E.00463 +G1 X125.659 Y101.097 E.00741 +G1 X126.819 Y101.093 E.0383 +G1 X127.134 Y100.948 E.01145 +G1 X127.29 Y100.597 E.01268 +G1 X129.403 Y100.597 E.06977 +G1 X129.403 Y103.967 E.11127 +G1 X129.158 Y104.09 E.00905 +G1 X129.158 Y104.09 F9000 +G1 X129.367 Y104.392 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.21 Y103.21 E.11225 -G1 X126.79 Y103.21 E.11225 -G1 X126.79 Y105 E.05612 -G1 X126.79 Y106.79 E.05612 -G1 X123.27 Y106.79 E.11037 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X123.21 Y106.79 E-.0285 -G1 X123.21 Y103.692 E-1.4715 -;WIPE_END -G1 X125.502 Y104.3 F9000 -G1 E5 F2400 -;TYPE:Solid infill -;WIDTH:0.439999 +G1 X129.303 Y104.436 E.00244 +G1 X129.29 Y104.951 E.01615 +G1 X129.303 Y105.476 E.01647 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.691 Y104.146 E.05095 +G1 X120.521 Y104.04 E.00628 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.109 Y100.21 E.0909 +G1 X123.109 Y100.521 E.00975 +G1 X123.172 Y100.662 E.00484 +G1 X123.298 Y100.71 E.00423 +G1 X124.387 Y100.703 E.03415 +G1 X124.517 Y100.573 E.00576 +G1 X124.524 Y100.21 E.01138 +G1 X125.471 Y100.21 E.02969 +G1 X125.471 Y100.521 E.00975 +G1 X125.547 Y100.673 E.00533 +G1 X125.659 Y100.71 E.0037 +G1 X126.748 Y100.71 E.03414 +G1 X126.874 Y100.661 E.00424 +G1 X126.936 Y100.521 E.0048 +G1 X126.936 Y100.21 E.00975 +G1 X129.79 Y100.21 E.08949 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.417 Y104.358 E.00235 +G1 X129.417 Y104.358 F9000 +G1 X128.632 Y103.64 +;TYPE:Internal infill +;WIDTH:0.44 G1 F900 -G1 X124.3 Y104.3 E.03969 -G1 X124.3 Y105.7 E.04622 -G1 X125.7 Y105.7 E.04622 -G1 X125.7 Y104.3 E.04622 -G1 X125.251 Y104.749 E.02097 -;WIDTH:0.544473 -G1 X125.251 Y105.251 E.02094 -G1 X124.749 Y105.251 E.02094 -G1 X124.749 Y104.749 E.02094 -G1 X125.052 Y104.749 E.01264 +G1 X128.7 Y103.606 E.00251 +G1 X128.7 Y101.3 E.07614 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X122.357 Y101.3 E.0349 +G1 X122.433 Y101.469 E.00612 +G1 X122.829 Y101.712 E.01534 +G1 X123.494 Y101.798 E.02214 ;LAYER_CHANGE ;Z:3.8 ;HEIGHT:0.2 @@ -2449,57 +4118,154 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.749 Y104.749 E-.14392 -G1 X124.749 Y105.251 E-.23845 -G1 X125.251 Y105.251 E-.23845 -G1 X125.251 Y104.749 E-.23845 -G1 X125.7 Y104.3 E-.30162 -G1 X125.7 Y105.014 E-.33911 +G1 X122.829 Y101.712 E-.31851 +G1 X122.433 Y101.469 E-.22069 +G1 X122.357 Y101.3 E-.08802 +G1 X121.3 Y101.3 E-.50208 +G1 X121.852 Y101.852 E-.3707 ;WIPE_END G1 Z3.8 F9000 ;AFTER_LAYER_CHANGE ;3.8 G1 Z3.8 -G1 X126.006 Y103.994 +G1 X122.772 Y101.171 G1 E5 F2400 ;TYPE:Perimeter ;WIDTH:0.439999 G1 F900 -G1 X126.006 Y105 E.03322 -G1 X126.006 Y106.006 E.03322 -G1 X123.994 Y106.006 E.06643 -G1 X123.994 Y103.994 E.06643 -G1 X125.946 Y103.994 E.06445 -G1 X125.946 Y103.994 F9000 -G1 X126.403 Y103.597 -G1 F900 -G1 X126.403 Y105 E.04632 -G1 X126.403 Y106.403 E.04632 -G1 X123.597 Y106.403 E.09265 -G1 X123.597 Y103.597 E.09265 -G1 X126.343 Y103.597 E.09067 -G1 X126.79 Y103.21 F9000 +G1 X122.813 Y101.26 E.00324 +G1 X123.052 Y101.411 E.00933 +G1 X123.446 Y101.494 E.01329 +G1 X124.647 Y101.48 E.03966 +G1 X124.999 Y101.339 E.01252 +G1 X125.199 Y101.44 E.0074 +G1 X125.513 Y101.494 E.01052 +G1 X126.605 Y101.494 E.03605 +G1 X126.903 Y101.447 E.00996 +G1 X127.243 Y101.255 E.01289 +G1 X127.362 Y100.994 E.00947 +G1 X129.006 Y100.994 E.05428 +G1 X129.006 Y103.771 E.09169 +G1 X128.694 Y103.929 E.01155 +G1 X128.609 Y104.069 E.00541 +G1 X128.506 Y104.499 E.0146 +G1 X128.506 Y104.97 E.01555 +G1 X128.522 Y105.586 E.02035 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.467 Y104.002 E.05841 +G1 X121.295 Y103.638 E.01329 +G1 X120.994 Y103.49 E.01107 +G1 X120.994 Y100.994 E.08241 +G1 X122.691 Y100.994 E.05603 +G1 X122.747 Y101.117 E.00446 +G1 X122.747 Y101.117 F9000 +G1 X123.047 Y100.904 +G1 F900 +G1 X123.071 Y100.958 E.00195 +G1 X123.213 Y101.048 E.00555 +G1 X123.446 Y101.097 E.00786 +G1 X124.546 Y101.093 E.03632 +G1 X124.805 Y100.996 E.00913 +G1 X124.999 Y100.664 E.0127 +G1 X125.193 Y100.996 E.0127 +G1 X125.329 Y101.065 E.00504 +G1 X125.515 Y101.097 E.00623 +G1 X126.605 Y101.097 E.03599 +G1 X126.687 Y101.091 E.00271 +G1 X126.982 Y100.956 E.01071 +G1 X127.146 Y100.597 E.01303 +G1 X129.403 Y100.597 E.07452 +G1 X129.403 Y103.967 E.11127 +G1 X129.014 Y104.164 E.0144 +G1 X128.932 Y104.323 E.00591 +G1 X128.903 Y104.97 E.02138 +G1 X128.913 Y105.513 E.01793 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.081 Y104.095 E.05534 +G1 X120.979 Y103.879 E.00789 +G1 X120.597 Y103.69 E.01407 +G1 X120.597 Y100.597 E.10212 +G1 X122.905 Y100.597 E.0762 +G1 X123.021 Y100.85 E.00919 +G1 X123.021 Y100.85 F9000 +G1 X123.315 Y100.646 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X126.79 Y105 E.05612 -G1 X126.79 Y106.79 E.05612 -G1 X123.21 Y106.79 E.11225 -G1 X123.21 Y103.21 E.11225 -G1 X126.73 Y103.21 E.11037 -G1 X125.881 Y104.854 F9000 -;TYPE:Top solid infill -;WIDTH:0.406026 -G1 F900 -G1 X125.307 Y104.28 E.02451 -G1 X124.793 Y104.28 E.01552 -G1 X125.72 Y105.207 E.03958 -G1 X125.72 Y105.72 E.01549 -G1 X124.28 Y104.28 E.06149 -G1 X124.28 Y104.793 E.01549 -G1 X125.207 Y105.72 E.03958 -G1 X124.693 Y105.72 E.01552 -G1 X124.119 Y105.146 E.02451 +G1 X123.323 Y100.665 E.00065 +G1 X123.446 Y100.71 E.00411 +G1 X124.501 Y100.709 E.03308 +;WIDTH:0.468106 +G1 X124.58 Y100.654 E.0034 +;WIDTH:0.516213 +G1 X124.66 Y100.599 E.00382 +;WIDTH:0.564319 +G1 X124.739 Y100.544 E.00417 +G1 X124.739 Y100.281 E.0114 +;WIDTH:0.562712 +G1 X125.258 Y100.281 E.02243 +G1 X125.258 Y100.521 E.01037 +G1 X125.309 Y100.573 E.00315 +;WIDTH:0.515141 +G1 X125.36 Y100.625 E.00286 +;WIDTH:0.46757 +G1 X125.412 Y100.677 E.0026 +;WIDTH:0.419999 +G1 X125.517 Y100.71 E.00345 +G1 X126.605 Y100.71 E.03411 +G1 X126.728 Y100.664 E.00412 +G1 X126.793 Y100.521 E.00493 +G1 X126.793 Y100.21 E.00975 +G1 X129.79 Y100.21 E.09397 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.327 Y104.393 E.00534 +G1 X129.3 Y104.445 E.00184 +G1 X129.29 Y104.97 E.01646 +G1 X129.303 Y105.476 E.01587 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.694 Y104.151 E.0508 +G1 X120.521 Y104.04 E.00644 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.258 Y100.21 E.09557 +G1 X123.258 Y100.521 E.00975 +G1 X123.29 Y100.592 E.00244 +G1 X123.49 Y101.8 F9000 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X122.926 Y101.69 E.01897 +G1 X122.599 Y101.484 E.01276 +G1 X122.515 Y101.3 E.00668 +G1 X121.3 Y101.3 E.04012 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.607 E.07617 +G1 X128.633 Y103.641 E.00248 ;LAYER_CHANGE ;Z:4 ;HEIGHT:0.2 @@ -2511,215 +4277,5321 @@ G92 E0 G1 E-3.5 F3600 ;WIPE_START G1 F7200 -G1 X124.693 Y105.72 E-.38559 -G1 X125.207 Y105.72 E-.24415 -G1 X124.28 Y104.793 E-.62271 -G1 X124.28 Y104.28 E-.24368 -G1 X124.286 Y104.286 E-.00387 +G1 X128.7 Y103.607 E-.03569 +G1 X128.7 Y101.3 E-1.09583 +G1 X128.151 Y101.849 E-.36848 ;WIPE_END G1 Z4 F9000 ;AFTER_LAYER_CHANGE ;4 G1 Z4 -G1 X123.992 Y105.862 +G1 X128.845 Y103.854 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.692 Y103.932 E.00567 +G1 X128.542 Y104.241 E.01134 +G1 X128.506 Y104.984 E.02456 +G1 X128.522 Y105.586 E.01988 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.421 Y103.859 E.01245 +G1 X121.298 Y103.642 E.00824 +G1 X120.994 Y103.491 E.01121 +G1 X120.994 Y100.994 E.08244 +G1 X122.845 Y100.994 E.06111 +G1 X122.975 Y101.271 E.0101 +G1 X123.162 Y101.393 E.00737 +G1 X123.399 Y101.474 E.00827 +G1 X123.595 Y101.494 E.0065 +G1 X124.705 Y101.491 E.03665 +G1 X125 Y101.413 E.01007 +G1 X125.138 Y101.464 E.00486 +G1 X125.377 Y101.494 E.00795 +G1 X126.461 Y101.494 E.03579 +G1 X126.8 Y101.433 E.01137 +G1 X127.086 Y101.267 E.01092 +G1 X127.213 Y100.994 E.00994 +G1 X129.006 Y100.994 E.0592 +G1 X129.006 Y103.772 E.09172 +G1 X128.899 Y103.827 E.00397 +G1 X128.899 Y103.827 F9000 +G1 X129.105 Y104.119 +G1 F900 +G1 X129.013 Y104.165 E.0034 +G1 X128.924 Y104.348 E.00672 +G1 X128.903 Y104.984 E.02101 +G1 X128.913 Y105.513 E.01747 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.087 Y104.122 E.05445 +G1 X120.981 Y103.882 E.00866 +G1 X120.597 Y103.691 E.01416 +G1 X120.597 Y100.597 E.10216 +G1 X123.055 Y100.597 E.08116 +G1 X123.228 Y100.965 E.01343 +G1 X123.339 Y101.037 E.00437 +G1 X123.595 Y101.097 E.00868 +G1 X124.623 Y101.097 E.03394 +G1 X124.884 Y101.035 E.00886 +G1 X125 Y100.862 E.00688 +G1 X125.117 Y101.035 E.0069 +G1 X125.236 Y101.079 E.00419 +G1 X125.496 Y101.097 E.00861 +G1 X126.554 Y101.09 E.03493 +G1 X126.831 Y100.963 E.01006 +G1 X127.002 Y100.597 E.01334 +G1 X129.403 Y100.597 E.07927 +G1 X129.403 Y103.967 E.11127 +G1 X129.158 Y104.091 E.00907 +G1 X129.158 Y104.091 F9000 +G1 X129.358 Y104.377 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.326 Y104.393 E.00112 +G1 X129.29 Y104.623 E.0073 +G1 X129.303 Y105.476 E.02675 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.696 Y104.157 E.05061 +G1 X120.521 Y104.04 E.0066 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.406 Y100.21 E.10021 +G1 X123.406 Y100.521 E.00975 +G1 X123.475 Y100.667 E.00506 +G1 X123.595 Y100.71 E.004 +G1 X124.67 Y100.704 E.03371 +;WIDTH:0.421205 +G1 X124.812 Y100.538 E.00687 +G1 X124.812 Y100.21 E.01032 +;WIDTH:0.419765 +G1 X125.189 Y100.21 E.01181 +G1 X125.189 Y100.521 E.00975 +;WIDTH:0.419999 +G1 X125.292 Y100.69 E.00621 +G1 X125.377 Y100.71 E.00274 +G1 X126.461 Y100.71 E.03399 +G1 X126.582 Y100.666 E.00404 +G1 X126.65 Y100.521 E.00502 +G1 X126.65 Y100.21 E.00975 +G1 X129.79 Y100.21 E.09845 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.411 Y104.35 E.00238 +G1 X129.411 Y104.35 F9000 +G1 X128.634 Y103.642 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.7 Y103.609 E.00244 +G1 X128.7 Y101.3 E.07624 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.325 E.06686 +G1 X121.533 Y103.441 E.00859 +G1 X121.581 Y103.525 E.00319 +;LAYER_CHANGE +;Z:4.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;4.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.533 Y103.441 E-.04595 +G1 X121.3 Y103.325 E-.12363 +G1 X121.3 Y101.3 E-.96188 +G1 X121.849 Y101.849 E-.36854 +;WIPE_END +G1 Z4.2 F9000 +;AFTER_LAYER_CHANGE +;4.2 +G1 Z4.2 +G1 X125 Y100.259 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.519004 +G1 F900 +G1 X125 Y100.542 E.0112 +G1 X125 Y100.542 F9000 +G1 X124.555 Y101.494 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.318 Y101.494 E.05821 +G1 X126.694 Y101.419 E.01266 +G1 X126.93 Y101.278 E.00908 +G1 X127.065 Y100.994 E.01038 +G1 X129.006 Y100.994 E.06409 +G1 X129.006 Y103.773 E.09175 +G1 X128.69 Y103.935 E.01172 +G1 X128.531 Y104.283 E.01263 +G1 X128.506 Y104.994 E.02349 +G1 X128.522 Y105.586 E.01955 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.433 Y103.888 E.01144 +G1 X121.301 Y103.646 E.0091 +G1 X120.994 Y103.493 E.01133 +G1 X120.994 Y100.994 E.08251 +G1 X122.998 Y100.994 E.06617 +G1 X123.137 Y101.282 E.01056 +G1 X123.533 Y101.471 E.01449 +G1 X124.229 Y101.494 E.02299 +G1 X124.495 Y101.494 E.00878 +G1 X124.495 Y101.494 F9000 +G1 X125.004 Y100.997 +G1 F900 +G1 X125.062 Y101.07 E.00308 +G1 X125.496 Y101.097 E.01436 +G1 X126.42 Y101.088 E.03051 +G1 X126.68 Y100.969 E.00944 +G1 X126.858 Y100.597 E.01362 +G1 X129.403 Y100.597 E.08403 +G1 X129.403 Y103.967 E.11127 +G1 X129.012 Y104.167 E.0145 +G1 X128.918 Y104.373 E.00748 +G1 X128.903 Y104.994 E.02051 +G1 X128.913 Y105.513 E.01714 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.091 Y104.148 E.05359 +G1 X120.983 Y103.884 E.00942 +G1 X120.597 Y103.691 E.01425 +G1 X120.597 Y100.597 E.10216 +G1 X123.204 Y100.597 E.08608 +G1 X123.384 Y100.972 E.01373 +G1 X123.619 Y101.083 E.00858 +G1 X124.229 Y101.097 E.02015 +G1 X124.937 Y101.07 E.02339 +G1 X124.966 Y101.033 E.00155 +G1 X124.966 Y101.033 F9000 +G1 X124.875 Y100.634 +;TYPE:External perimeter +;WIDTH:0.469502 +G1 F900 +G1 X124.886 Y100.626 E.00048 +;WIDTH:0.519004 +G1 X125 Y100.542 E.00561 +G1 X125.09 Y100.622 E.00477 +;WIDTH:0.469502 +G1 X125.18 Y100.701 E.00425 +;WIDTH:0.419999 +G1 X126.391 Y100.695 E.03797 +G1 X126.507 Y100.521 E.00656 +G1 X126.507 Y100.21 E.00975 +G1 X129.79 Y100.21 E.10294 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.305 Y104.43 E.00652 +G1 X129.29 Y104.763 E.01045 +G1 X129.303 Y105.476 E.02236 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.698 Y104.162 E.05045 +G1 X120.521 Y104.04 E.00674 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.555 Y100.21 E.10488 +G1 X123.555 Y100.521 E.00975 +G1 X123.626 Y100.669 E.00515 +G1 X123.702 Y100.706 E.00265 +G1 X124.773 Y100.71 E.03358 +;WIDTH:0.469502 +G1 X124.827 Y100.67 E.00238 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.875 Y100.634 E-.0285 +G1 X124.886 Y100.626 E-.00646 +G1 X125 Y100.542 E-.06726 +G1 X125.09 Y100.622 E-.0572 +G1 X125.18 Y100.701 E-.05688 +G1 X126.391 Y100.695 E-.57523 +G1 X126.507 Y100.521 E-.09933 +G1 X126.507 Y100.21 E-.14772 +G1 X127.478 Y100.21 E-.46142 +;WIPE_END +G1 X121.58 Y103.525 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.538 Y103.447 E.00292 +G1 X121.3 Y103.327 E.0088 +G1 X121.3 Y101.3 E.06693 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.611 E.0763 +G1 X128.636 Y103.643 E.00236 +;LAYER_CHANGE +;Z:4.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;4.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.7 Y103.611 E-.03399 +G1 X128.7 Y101.3 E-1.09773 +G1 X128.152 Y101.848 E-.36828 +;WIPE_END +G1 Z4.4 F9000 +;AFTER_LAYER_CHANGE +;4.4 +G1 Z4.4 +G1 X124.997 Y100.128 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F900 +G1 X124.997 Y100.531 E.01139 +G1 X124.997 Y100.531 F9000 +G1 X124.422 Y101.494 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.175 Y101.494 E.05788 +G1 X126.585 Y101.403 E.01387 +G1 X126.774 Y101.287 E.00732 +G1 X126.917 Y100.994 E.01076 +G1 X129.006 Y100.994 E.06897 +G1 X129.006 Y103.774 E.09179 +G1 X128.688 Y103.938 E.01181 +G1 X128.523 Y104.324 E.01386 +G1 X128.506 Y104.999 E.02229 +G1 X128.522 Y105.586 E.01939 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.443 Y103.916 E.01047 +G1 X121.304 Y103.65 E.00991 +G1 X120.994 Y103.494 E.01146 +G1 X120.994 Y100.994 E.08254 +G1 X123.151 Y100.994 E.07122 +G1 X123.298 Y101.292 E.01097 +G1 X123.668 Y101.468 E.01353 +G1 X124.229 Y101.494 E.01854 +G1 X124.362 Y101.494 E.00439 +G1 X124.362 Y101.494 F9000 +G1 X123.501 Y100.898 +G1 F900 +G1 X123.54 Y100.977 E.00291 +G1 X123.595 Y101.015 E.00221 +G1 X123.891 Y101.097 E.01014 +G1 X124.891 Y101.097 E.03302 +G1 X124.997 Y101.065 E.00366 +G1 X125.022 Y101.091 E.00119 +G1 X126.285 Y101.086 E.0417 +G1 X126.53 Y100.975 E.00888 +G1 X126.714 Y100.597 E.01388 +G1 X129.403 Y100.597 E.08878 +G1 X129.403 Y103.967 E.11127 +G1 X129.011 Y104.169 E.01456 +G1 X128.913 Y104.398 E.00822 +G1 X128.903 Y104.999 E.01985 +G1 X128.913 Y105.513 E.01697 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.094 Y104.174 E.05273 +G1 X120.984 Y103.886 E.01018 +G1 X120.597 Y103.691 E.01431 +G1 X120.597 Y100.597 E.10216 +G1 X123.353 Y100.597 E.091 +G1 X123.475 Y100.845 E.00913 +G1 X123.475 Y100.845 F9000 +G1 X123.763 Y100.645 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.776 Y100.671 E.00091 +G1 X123.891 Y100.71 E.00381 +G1 X124.891 Y100.71 E.03135 +G1 X124.997 Y100.531 E.00652 +G1 X125.077 Y100.708 E.00609 +G1 X126.254 Y100.692 E.03691 +G1 X126.363 Y100.521 E.00636 +G1 X126.363 Y100.21 E.00975 +G1 X129.79 Y100.21 E.10745 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.303 Y104.434 E.00664 +G1 X129.29 Y104.896 E.01449 +G1 X129.303 Y105.476 E.01819 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.673 Y104.116 E.00373 +G1 X120.521 Y104.04 E.00533 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.703 Y100.21 E.10952 +G1 X123.703 Y100.521 E.00975 +G1 X123.737 Y100.591 E.00244 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.763 Y100.645 E-.02847 +G1 X123.776 Y100.671 E-.01381 +G1 X123.891 Y100.71 E-.05768 +G1 X124.891 Y100.71 E-.475 +G1 X124.997 Y100.531 E-.09881 +G1 X125.077 Y100.708 E-.09226 +G1 X126.254 Y100.692 E-.55913 +G1 X126.363 Y100.521 E-.09632 +G1 X126.363 Y100.356 E-.07852 +;WIPE_END +G1 X121.58 Y103.524 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.542 Y103.452 E.00269 +G1 X121.3 Y103.33 E.00895 +G1 X121.3 Y101.3 E.06703 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.612 E.07634 +G1 X128.638 Y103.644 E.0023 +;LAYER_CHANGE +;Z:4.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;4.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.7 Y103.612 E-.03314 +G1 X128.7 Y101.3 E-1.0982 +G1 X128.151 Y101.849 E-.36866 +;WIPE_END +G1 Z4.6 F9000 +;AFTER_LAYER_CHANGE +;4.6 +G1 Z4.6 +G1 X128.848 Y103.857 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.685 Y103.941 E.00605 +G1 X128.565 Y104.17 E.00854 +G1 X128.516 Y104.365 E.00664 +G1 X128.506 Y105 E.02097 +G1 X128.522 Y105.586 E.01936 +G1 X128.672 Y105.95 E.013 +G1 X129.006 Y106.128 E.0125 +G1 X129.006 Y109.006 E.09502 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.452 Y103.944 E.00951 +G1 X121.307 Y103.655 E.01068 +G1 X120.994 Y103.496 E.01159 +G1 X120.994 Y100.994 E.08261 +G1 X123.304 Y100.994 E.07627 +G1 X123.458 Y101.301 E.01134 +G1 X123.805 Y101.465 E.01267 +G1 X124.04 Y101.494 E.00782 +G1 X125.496 Y101.494 E.04807 +G1 X126.231 Y101.473 E.02428 +G1 X126.619 Y101.296 E.01408 +G1 X126.77 Y100.994 E.01115 +G1 X129.006 Y100.994 E.07383 +G1 X129.006 Y103.776 E.09185 +G1 X128.901 Y103.83 E.0039 +G1 X128.901 Y103.83 F9000 +G1 X129.106 Y104.121 +G1 F900 +G1 X129.009 Y104.171 E.0036 +G1 X128.909 Y104.422 E.00892 +G1 X128.903 Y105 E.01908 +G1 X128.913 Y105.513 E.01694 +G1 X129.001 Y105.729 E.0077 +G1 X129.403 Y105.943 E.01504 +G1 X129.403 Y109.403 E.11424 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.096 Y104.199 E.0519 +G1 X120.986 Y103.889 E.01086 +G1 X120.597 Y103.691 E.01441 +G1 X120.597 Y100.597 E.10216 +G1 X123.502 Y100.597 E.09592 +G1 X123.725 Y101.003 E.01529 +G1 X124.04 Y101.097 E.01085 +G1 X126.15 Y101.085 E.06967 +G1 X126.379 Y100.98 E.00832 +G1 X126.57 Y100.597 E.01413 +G1 X129.403 Y100.597 E.09354 +G1 X129.403 Y103.968 E.1113 +G1 X129.16 Y104.093 E.00902 +G1 X129.16 Y104.093 F9000 +G1 X129.357 Y104.378 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.325 Y104.395 E.00114 +G1 X129.29 Y104.504 E.00359 +G1 X129.303 Y105.476 E.03048 +G1 X129.479 Y105.596 E.00668 +G1 X129.79 Y105.596 E.00975 +G1 X129.79 Y105.907 E.00975 +G1 X129.79 Y109.79 E.12175 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.229 E.04835 +G1 X120.674 Y104.117 E.00369 +G1 X120.521 Y104.04 E.00537 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.851 Y100.21 E.11416 +G1 X123.851 Y100.521 E.00975 +G1 X123.927 Y100.673 E.00533 +G1 X124.04 Y100.71 E.00373 +G1 X125.496 Y100.71 E.04565 +G1 X126.118 Y100.689 E.01951 +G1 X126.22 Y100.521 E.00616 +G1 X126.22 Y100.21 E.00975 +G1 X129.79 Y100.21 E.11193 +G1 X129.79 Y104.316 E.12874 +G1 X129.479 Y104.316 E.00975 +G1 X129.41 Y104.351 E.00243 +G1 X129.41 Y104.351 F9000 +G1 X128.64 Y103.646 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.7 Y103.614 E.00225 +G1 X128.7 Y101.3 E.0764 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.332 E.06709 +G1 X121.58 Y103.523 E.01119 +;LAYER_CHANGE +;Z:4.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;4.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y103.332 E-.161 +G1 X121.3 Y101.3 E-.9652 +G1 X121.856 Y101.856 E-.3738 +;WIPE_END +G1 Z4.8 F9000 +;AFTER_LAYER_CHANGE +;4.8 +G1 Z4.8 +G1 X121.154 Y103.579 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y103.498 E.00592 +G1 X120.994 Y100.994 E.08268 +G1 X123.457 Y100.994 E.08132 +G1 X123.617 Y101.309 E.01167 +G1 X123.942 Y101.463 E.01187 +G1 X124.188 Y101.494 E.00819 +G1 X126.114 Y101.468 E.0636 +G1 X126.465 Y101.305 E.01278 +G1 X126.622 Y100.994 E.0115 +G1 X129.006 Y100.994 E.07871 +G1 X129.006 Y103.643 E.08746 +G1 X128.687 Y103.807 E.01184 +G1 X128.55 Y104.081 E.01011 +G1 X128.506 Y104.373 E.00975 +G1 X128.52 Y105.709 E.04411 +G1 X128.678 Y106.095 E.01377 +G1 X129.006 Y106.268 E.01224 +G1 X129.006 Y109.006 E.0904 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.46 Y103.972 E.00856 +G1 X121.31 Y103.659 E.01146 +G1 X121.207 Y103.607 E.00381 +G1 X121.207 Y103.607 F9000 +G1 X120.895 Y103.844 +G1 F900 +G1 X120.597 Y103.692 E.01105 +G1 X120.597 Y100.597 E.10219 +G1 X123.651 Y100.597 E.10083 +G1 X123.85 Y100.987 E.01446 +G1 X123.952 Y101.046 E.00389 +G1 X124.188 Y101.097 E.00797 +G1 X125.888 Y101.097 E.05613 +G1 X126.189 Y101.012 E.01033 +G1 X126.426 Y100.597 E.01578 +G1 X129.403 Y100.597 E.09829 +G1 X129.403 Y103.836 E.10694 +G1 X129.01 Y104.038 E.01459 +G1 X128.929 Y104.2 E.00598 +G1 X128.903 Y104.373 E.00578 +G1 X128.911 Y105.642 E.0419 +G1 X129.005 Y105.87 E.00814 +G1 X129.403 Y106.079 E.01484 +G1 X129.403 Y109.403 E.10975 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.093 Y104.156 E.05332 +G1 X120.988 Y103.892 E.00938 +G1 X120.948 Y103.871 E.00149 +G1 X120.948 Y103.871 F9000 +G1 X120.636 Y104.113 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.521 Y104.04 E.00427 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X124 Y100.21 E.11883 +G1 X124 Y100.521 E.00975 +G1 X124.078 Y100.674 E.00538 +G1 X124.188 Y100.71 E.00363 +G1 X125.627 Y100.71 E.04512 +G1 X125.987 Y100.682 E.01132 +G1 X126.077 Y100.521 E.00578 +G1 X126.077 Y100.21 E.00975 +G1 X129.79 Y100.21 E.11642 +G1 X129.79 Y104.184 E.1246 +G1 X129.479 Y104.184 E.00975 +G1 X129.325 Y104.263 E.00543 +G1 X129.29 Y104.373 E.00362 +G1 X129.303 Y105.612 E.03885 +G1 X129.479 Y105.732 E.00668 +G1 X129.79 Y105.732 E.00975 +G1 X129.79 Y106.043 E.00975 +G1 X129.79 Y109.79 E.11748 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.693 Y104.149 E.05086 +G1 X120.686 Y104.145 E.00025 +G1 X120.686 Y104.145 F9000 +G1 X121.583 Y103.521 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.3 Y103.335 E.01118 +G1 X121.3 Y101.3 E.06719 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.482 E.07204 +G1 X128.522 Y103.573 E.0066 +;LAYER_CHANGE +;Z:5 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;5 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.7 Y103.482 E-.09496 +G1 X128.7 Y101.3 E-1.03645 +G1 X128.151 Y101.849 E-.36859 +;WIPE_END +G1 Z5 F9000 +;AFTER_LAYER_CHANGE +;5 +G1 Z5 +G1 X128.847 Y103.59 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.688 Y103.672 E.00591 +G1 X128.538 Y103.992 E.01167 +G1 X128.506 Y104.239 E.00822 +G1 X128.521 Y105.851 E.05323 +G1 X128.684 Y106.243 E.01402 +G1 X129.006 Y106.41 E.01198 +G1 X129.006 Y109.006 E.08571 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y105 E.02546 +G1 X121.465 Y103.991 E.03333 +G1 X121.33 Y103.688 E.01095 +G1 X120.994 Y103.498 E.01274 +G1 X120.994 Y100.994 E.08268 +G1 X123.608 Y100.994 E.08631 +G1 X123.773 Y101.314 E.01189 +G1 X124.072 Y101.457 E.01094 +G1 X124.337 Y101.494 E.00883 +G1 X125.745 Y101.494 E.04649 +G1 X125.996 Y101.461 E.00836 +G1 X126.312 Y101.312 E.01154 +G1 X126.475 Y100.994 E.0118 +G1 X129.006 Y100.994 E.08357 +G1 X129.006 Y103.509 E.08304 +G1 X128.9 Y103.563 E.00393 +G1 X128.9 Y103.563 F9000 +G1 X129.106 Y103.855 +G1 F900 +G1 X129.011 Y103.903 E.00351 +G1 X128.954 Y104.002 E.00377 +G1 X128.903 Y104.239 E.008 +G1 X128.912 Y105.782 E.05095 +G1 X129.009 Y106.014 E.0083 +G1 X129.403 Y106.218 E.01465 +G1 X129.403 Y109.403 E.10516 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.093 Y104.164 E.05306 +G1 X121 Y103.909 E.00896 +G1 X120.597 Y103.692 E.01511 +G1 X120.597 Y100.597 E.10219 +G1 X123.8 Y100.597 E.10575 +G1 X124.003 Y100.991 E.01463 +G1 X124.094 Y101.043 E.00346 +G1 X124.337 Y101.097 E.00822 +G1 X125.814 Y101.093 E.04877 +G1 X126.081 Y100.989 E.00946 +G1 X126.282 Y100.597 E.01455 +G1 X129.403 Y100.597 E.10305 +G1 X129.403 Y103.702 E.10252 +G1 X129.159 Y103.827 E.00905 +G1 X129.159 Y103.827 F9000 +G1 X129.357 Y104.113 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.325 Y104.129 E.00112 +G1 X129.29 Y104.239 E.00362 +G1 X129.303 Y105.751 E.04741 +G1 X129.479 Y105.87 E.00666 +G1 X129.79 Y105.87 E.00975 +G1 X129.79 Y106.182 E.00978 +G1 X129.79 Y109.79 E.11313 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.695 Y104.154 E.0507 +G1 X120.521 Y104.04 E.00652 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X124.148 Y100.21 E.12347 +G1 X124.148 Y100.521 E.00975 +G1 X124.228 Y100.675 E.00544 +G1 X124.337 Y100.71 E.00359 +G1 X125.824 Y100.693 E.04663 +G1 X125.934 Y100.521 E.0064 +G1 X125.934 Y100.21 E.00975 +G1 X129.79 Y100.21 E.1209 +G1 X129.79 Y104.05 E.1204 +G1 X129.479 Y104.05 E.00975 +G1 X129.41 Y104.086 E.00244 +G1 X129.41 Y104.086 F9000 +G1 X128.422 Y103.522 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.7 Y103.347 E.01085 +G1 X128.7 Y101.3 E.06759 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X123.447 Y101.3 E.07089 +G1 X123.555 Y101.509 E.00777 +;LAYER_CHANGE +;Z:5.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;5.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.447 Y101.3 E-.11175 +G1 X121.3 Y101.3 E-1.01983 +G1 X121.848 Y101.848 E-.36842 +;WIPE_END +G1 Z5.2 F9000 +;AFTER_LAYER_CHANGE +;5.2 +G1 Z5.2 +G1 X123.84 Y101.151 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X123.928 Y101.319 E.00626 +G1 X124.2 Y101.451 E.00998 +G1 X124.485 Y101.494 E.00952 +G1 X125.602 Y101.494 E.03688 +G1 X125.898 Y101.448 E.00989 +G1 X126.166 Y101.314 E.00989 +G1 X126.331 Y100.994 E.01189 +G1 X129.006 Y100.994 E.08832 +G1 X129.006 Y103.371 E.07848 +G1 X128.696 Y103.528 E.01147 +G1 X128.532 Y103.883 E.01291 +G1 X128.506 Y104.105 E.00738 +G1 X128.521 Y105.992 E.06231 +G1 X128.692 Y106.392 E.01436 +G1 X129.006 Y106.552 E.01164 +G1 X129.006 Y109.006 E.08102 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.468 Y104.006 E.05828 +G1 X121.311 Y103.66 E.01255 +G1 X120.994 Y103.498 E.01175 +G1 X120.994 Y100.994 E.08268 +G1 X123.759 Y100.994 E.09129 +G1 X123.813 Y101.097 E.00384 +G1 X123.813 Y101.097 F9000 +G1 X124.103 Y100.893 +G1 F900 +G1 X124.156 Y100.993 E.00374 +G1 X124.316 Y101.072 E.00589 +G1 X124.485 Y101.097 E.00564 +G1 X125.647 Y101.095 E.03837 +G1 X125.936 Y100.99 E.01015 +G1 X126.139 Y100.597 E.0146 +G1 X129.403 Y100.597 E.10777 +G1 X129.403 Y103.568 E.09809 +G1 X128.985 Y103.809 E.01593 +G1 X128.903 Y104.105 E.01014 +G1 X128.912 Y105.922 E.05999 +G1 X129.013 Y106.159 E.00851 +G1 X129.403 Y106.357 E.01444 +G1 X129.403 Y109.403 E.10057 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y104.229 E.05091 +G1 X121.014 Y103.931 E.01021 +G1 X120.597 Y103.692 E.01587 +G1 X120.597 Y100.597 E.10219 +G1 X123.949 Y100.597 E.11067 +G1 X124.075 Y100.84 E.00904 +G1 X124.075 Y100.84 F9000 +G1 X124.36 Y100.643 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X124.377 Y100.676 E.00116 +G1 X124.485 Y100.71 E.00355 +G1 X125.602 Y100.71 E.03502 +G1 X125.711 Y100.675 E.00359 +G1 X125.791 Y100.521 E.00544 +G1 X125.791 Y100.21 E.00975 +G1 X129.79 Y100.21 E.12539 +G1 X129.79 Y103.917 E.11623 +G1 X129.479 Y103.917 E.00975 +G1 X129.327 Y103.993 E.00533 +G1 X129.29 Y104.105 E.0037 +G1 X129.304 Y105.891 E.056 +G1 X129.479 Y106.009 E.00662 +G1 X129.79 Y106.009 E.00975 +G1 X129.79 Y106.32 E.00975 +G1 X129.79 Y109.79 E.1088 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y105 E.02417 +G1 X120.683 Y104.131 E.02726 +G1 X120.521 Y104.04 E.00583 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X124.297 Y100.21 E.12814 +G1 X124.297 Y100.521 E.00975 +G1 X124.332 Y100.59 E.00243 +G1 X124.332 Y100.59 F9000 +G1 X123.637 Y101.374 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X123.599 Y101.3 E.00275 +G1 X121.3 Y101.3 E.07591 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y103.207 E.06296 +G1 X128.462 Y103.327 E.0088 +G1 X128.357 Y103.507 E.00688 +;LAYER_CHANGE +;Z:5.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;5.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.462 Y103.327 E-.09898 +G1 X128.7 Y103.207 E-.12661 +G1 X128.7 Y101.3 E-.90583 +G1 X128.151 Y101.849 E-.36858 +;WIPE_END +G1 Z5.4 F9000 +;AFTER_LAYER_CHANGE +;5.4 +G1 Z5.4 +G1 X128.841 Y103.316 G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.703 Y103.384 E.00508 +G1 X128.53 Y103.758 E.01361 +G1 X128.506 Y104.375 E.02039 +G1 X128.506 Y105.959 E.0523 +G1 X128.58 Y106.332 E.01256 +G1 X128.699 Y106.541 E.00794 +G1 X129.006 Y106.694 E.01133 +G1 X129.006 Y109.006 E.07634 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y105 E.02546 +G1 X121.466 Y103.997 E.03313 +G1 X121.34 Y103.704 E.01053 +G1 X120.994 Y103.498 E.0133 +G1 X120.994 Y100.994 E.08268 +G1 X123.669 Y100.994 E.08832 +G1 X123.836 Y101.316 E.01198 +G1 X124.124 Y101.455 E.01056 +G1 X124.397 Y101.494 E.00911 +G1 X125.625 Y101.494 E.04055 +G1 X125.942 Y101.441 E.01061 +G1 X126.194 Y101.31 E.00938 +G1 X126.356 Y100.994 E.01172 +G1 X129.006 Y100.994 E.0875 +G1 X129.006 Y103.234 E.07396 +G1 X128.895 Y103.289 E.00409 +G1 X128.895 Y103.289 F9000 +G1 X129.103 Y103.583 +G1 F900 +G1 X129.02 Y103.624 E.00306 +G1 X128.917 Y103.845 E.00805 +G1 X128.903 Y104.375 E.01751 +G1 X128.912 Y106.062 E.0557 +G1 X129.017 Y106.303 E.00868 +G1 X129.403 Y106.496 E.01425 +G1 X129.403 Y109.403 E.09598 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y105 E.02546 +G1 X121.094 Y104.166 E.02754 +G1 X121.006 Y103.918 E.00869 +G1 X120.597 Y103.692 E.01543 +G1 X120.597 Y100.597 E.10219 +G1 X123.861 Y100.597 E.10777 +G1 X124.065 Y100.992 E.01468 +G1 X124.152 Y101.042 E.00331 +G1 X124.397 Y101.097 E.00829 +G1 X125.688 Y101.094 E.04263 +G1 X125.962 Y100.988 E.0097 +G1 X126.162 Y100.597 E.0145 +G1 X129.403 Y100.597 E.10701 +G1 X129.403 Y103.433 E.09364 +G1 X129.156 Y103.556 E.00911 +G1 X129.156 Y103.556 F9000 +G1 X129.356 Y103.844 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X124.041 Y105.793 E.00265 -G1 X125.114 Y105.783 E.03364 -G1 X124.331 Y104.804 E.03931 -;WIDTH:0.453973 -G1 X123.79 Y104.155 E.02888 -;WIDTH:0.480776 -G1 X123.754 Y104.091 E.00267 -;WIDTH:0.507579 -G1 X123.718 Y104.027 E.00284 -G1 X123.718 Y103.679 E.01345 -;WIDTH:0.524205 -G1 X123.727 Y103.591 E.00354 -G1 X123.783 Y103.572 E.00237 -;WIDTH:0.486019 -G1 X123.839 Y103.552 E.00219 -;WIDTH:0.447833 -G1 X123.895 Y103.533 E.00199 -;WIDTH:0.409647 -G1 X123.951 Y103.514 E.0018 -;WIDTH:0.37146 -G1 X126.049 Y103.514 E.05731 -;WIDTH:0.409647 -G1 X126.105 Y103.533 E.0018 -;WIDTH:0.447833 -G1 X126.161 Y103.552 E.00199 -;WIDTH:0.48602 -G1 X126.217 Y103.572 E.00219 -;WIDTH:0.524206 -G1 X126.273 Y103.591 E.00237 -G1 X126.28 Y104.069 E.01913 -;WIDTH:0.511099 -G1 X126.165 Y104.143 E.00532 -;WIDTH:0.465549 -G1 X126.049 Y104.217 E.00484 -;WIDTH:0.419999 -G1 X124.81 Y104.217 E.03885 -G1 X125.654 Y105.254 E.04192 -;WIDTH:0.433316 -G1 X126.158 Y105.862 E.02564 -;WIDTH:0.481089 -G1 X126.204 Y105.924 E.00281 -;WIDTH:0.528861 -G1 X126.25 Y105.986 E.00312 -;WIDTH:0.541746 -G1 X126.243 Y106.401 E.01722 -G1 X126.184 Y106.422 E.0026 -;WIDTH:0.499175 -G1 X126.125 Y106.443 E.00238 -;WIDTH:0.456603 -G1 X126.066 Y106.464 E.00215 -;WIDTH:0.414032 -G1 X126.007 Y106.486 E.00194 -;WIDTH:0.37146 -G1 X124.102 Y106.486 E.05204 -;WIDTH:0.419999 -G1 X123.916 Y106.354 E.00715 -G1 X123.913 Y105.972 E.01198 -G1 X123.957 Y105.911 E.00236 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X123.992 Y105.862 E-.0286 -G1 X124.041 Y105.793 E-.0402 -G1 X125.114 Y105.783 E-.5097 -G1 X124.331 Y104.804 E-.59546 -G1 X123.892 Y104.277 E-.32604 -;WIPE_END -G1 X125.681 Y104.631 F9000 +G1 X129.328 Y103.858 E.00098 +G1 X129.29 Y103.971 E.00374 +G1 X129.29 Y104.968 E.03126 +G1 X129.321 Y105.01 E.00164 +G1 X129.29 Y105.041 E.00137 +G1 X129.304 Y106.031 E.03104 +G1 X129.479 Y106.147 E.00658 +G1 X129.79 Y106.147 E.00975 +G1 X129.79 Y106.459 E.00978 +G1 X129.79 Y109.79 E.10444 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y105 E.02417 +G1 X120.695 Y104.156 E.02647 +G1 X120.521 Y104.04 E.00656 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X124.209 Y100.21 E.12539 +G1 X124.209 Y100.521 E.00975 +G1 X124.288 Y100.675 E.00543 +G1 X124.397 Y100.71 E.00359 +G1 X125.625 Y100.71 E.0385 +G1 X125.735 Y100.674 E.00363 +G1 X125.814 Y100.521 E.0054 +G1 X125.814 Y100.21 E.00975 +G1 X129.79 Y100.21 E.12466 +G1 X129.79 Y103.783 E.11203 +G1 X129.479 Y103.783 E.00975 +G1 X129.409 Y103.817 E.00244 +G1 X129.409 Y103.817 F9000 +G1 X128.305 Y103.497 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.471 Y103.181 E.01179 +G1 X128.7 Y103.067 E.00845 +G1 X128.7 Y101.3 E.05834 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X123.509 Y101.3 E.07294 +G1 X123.589 Y101.454 E.00573 +;LAYER_CHANGE +;Z:5.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;5.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.509 Y101.3 E-.08243 +G1 X121.3 Y101.3 E-1.04928 +G1 X121.848 Y101.848 E-.36829 +;WIPE_END +G1 Z5.6 F9000 +;AFTER_LAYER_CHANGE +;5.6 +G1 Z5.6 +G1 X129.82 Y105.012 G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F900 +G1 X129.465 Y105.012 E.01004 +G1 X129.465 Y105.012 F9000 +G1 X128.506 Y105.535 ;TYPE:Perimeter -;WIDTH:0.494061 -G1 F900 -G1 X126.123 Y104.626 E.01658 -G1 X126.376 Y104.386 E.01308 -G1 X126.376 Y105.485 E.04123 -G1 X125.719 Y104.677 E.03907 -G1 E-3.5 F3600 -;WIPE_START -G1 F7200 -G1 X125.681 Y104.631 E-.02834 -G1 X126.123 Y104.626 E-.20996 -G1 X126.376 Y104.386 E-.16564 -G1 X126.376 Y105.485 E-.52203 -G1 X125.719 Y104.677 E-.49466 -;WIPE_END -G1 E-.07937 F3600 -G1 X123.567 Y106.433 F9000 -G1 E5 F2400 -;WIDTH:0.3803 -G1 F900 -G1 X123.561 Y105.972 E.01293 -G1 X123.561 Y105.972 F9000 -G1 X123.766 Y105.64 -;WIDTH:0.445442 -G1 F900 -G1 X123.678 Y105.783 E.00562 -;WIDTH:0.407136 -G1 X123.561 Y105.972 E.00673 -G1 X123.581 Y105.713 E.00787 -;WIDTH:0.445442 -G1 X123.6 Y105.455 E.00866 -;WIDTH:0.483747 -G1 X123.619 Y105.197 E.00948 -G1 X123.619 Y104.568 E.02306 -G1 X124.263 Y105.374 E.03782 -G1 X123.91 Y105.406 E.01299 -G1 X123.797 Y105.589 E.00788 -G1 X123.797 Y105.589 F9000 -G1 X123.21 Y106.79 +;WIDTH:0.439999 +G1 F900 +G1 X128.506 Y106.097 E.01856 +G1 X128.546 Y106.374 E.00924 +G1 X128.708 Y106.69 E.01172 +G1 X129.006 Y106.837 E.01097 +G1 X129.006 Y109.006 E.07161 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.255 E.05005 +G1 X121.462 Y103.982 E.00908 +G1 X121.316 Y103.667 E.01146 +G1 X120.994 Y103.498 E.01201 +G1 X120.994 Y100.994 E.08268 +G1 X123.525 Y100.994 E.08357 +G1 X123.688 Y101.312 E.0118 +G1 X124.002 Y101.461 E.01148 +G1 X124.248 Y101.494 E.00819 +G1 X125.774 Y101.494 E.05038 +G1 X126.096 Y101.439 E.01079 +G1 X126.354 Y101.303 E.00963 +G1 X126.509 Y100.994 E.01141 +G1 X129.006 Y100.994 E.08244 +G1 X129.006 Y103.096 E.0694 +G1 X128.712 Y103.239 E.01079 +G1 X128.531 Y103.619 E.0139 +G1 X128.506 Y104.226 E.02006 +G1 X128.506 Y105.475 E.04124 +G1 X128.506 Y105.475 F9000 +G1 X128.904 Y104.789 +G1 F900 +G1 X128.904 Y104.894 E.00347 +G1 X128.959 Y105.012 E.0043 +G1 X128.915 Y105.051 E.00194 +G1 X128.903 Y105.5 E.01483 +G1 X128.927 Y106.261 E.02514 +G1 X129.022 Y106.448 E.00693 +G1 X129.403 Y106.636 E.01403 +G1 X129.403 Y109.403 E.09136 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.093 Y104.162 E.05312 +G1 X120.992 Y103.896 E.00939 +G1 X120.597 Y103.692 E.01468 +G1 X120.597 Y100.597 E.10219 +G1 X123.718 Y100.597 E.10305 +G1 X123.919 Y100.989 E.01455 +G1 X124.014 Y101.045 E.00364 +G1 X124.251 Y101.097 E.00801 +G1 X125.147 Y101.097 E.02958 +G1 X125.838 Y101.094 E.02282 +G1 X126.117 Y100.984 E.0099 +G1 X126.312 Y100.597 E.01431 +G1 X129.403 Y100.597 E.10206 +G1 X129.403 Y103.299 E.08921 +G1 X129.025 Y103.484 E.0139 +G1 X128.918 Y103.708 E.0082 +G1 X128.903 Y104.226 E.01711 +G1 X128.904 Y104.729 E.01661 +G1 X128.904 Y104.729 F9000 +G1 X129.387 Y104.944 ;TYPE:External perimeter ;WIDTH:0.419999 G1 F900 -G1 X123.21 Y105.197 E.04995 -;WIDTH:0.463789 -G1 X123.232 Y104.612 E.02049 -;WIDTH:0.507579 -G1 X123.254 Y104.027 E.02262 -;WIDTH:0.524205 -G1 X123.262 Y103.262 E.03062 -G1 X123.434 Y103.243 E.00693 -;WIDTH:0.486019 -G1 X123.606 Y103.224 E.00638 -;WIDTH:0.447833 -G1 X123.779 Y103.205 E.00586 -;WIDTH:0.409647 -G1 X123.951 Y103.186 E.00528 -;WIDTH:0.37146 -G1 X126.049 Y103.186 E.05731 -;WIDTH:0.409647 -G1 X126.161 Y103.205 E.00346 -;WIDTH:0.447833 -G1 X126.273 Y103.224 E.00382 -;WIDTH:0.48602 -G1 X126.385 Y103.243 E.00419 -;WIDTH:0.524206 -G1 X126.497 Y103.262 E.00455 -G1 X126.738 Y103.262 E.00964 -G1 X126.738 Y103.503 E.00964 -G1 X126.744 Y104.109 E.02425 -;WIDTH:0.511099 -G1 X126.767 Y104.48 E.01447 -;WIDTH:0.465549 -G1 X126.79 Y104.85 E.01303 -;WIDTH:0.433316 -G1 X126.783 Y105.64 E.02565 -;WIDTH:0.481089 -G1 X126.759 Y105.813 E.00636 -;WIDTH:0.528861 -G1 X126.736 Y105.986 E.00705 -;WIDTH:0.541746 -G1 X126.729 Y106.729 E.03082 -G1 X126.549 Y106.75 E.00752 -;WIDTH:0.499175 -G1 X126.368 Y106.772 E.00692 -;WIDTH:0.456603 -G1 X126.187 Y106.793 E.00627 -;WIDTH:0.414032 -G1 X126.007 Y106.814 E.00559 -;WIDTH:0.37146 -G1 X124.102 Y106.814 E.05204 -;WIDTH:0.419999 -G1 X123.27 Y106.792 E.0261 +G1 X129.465 Y105.012 E.00324 +G1 X129.294 Y105.131 E.00653 +G1 X129.29 Y106.097 E.03029 +G1 X129.329 Y106.212 E.00381 +G1 X129.479 Y106.286 E.00524 +G1 X129.79 Y106.286 E.00975 +G1 X129.79 Y106.597 E.00975 +G1 X129.79 Y109.79 E.10011 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.693 Y104.151 E.0508 +G1 X120.521 Y104.04 E.00642 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X124.066 Y100.21 E.1209 +G1 X124.066 Y100.521 E.00975 +G1 X124.145 Y100.675 E.00543 +G1 X124.253 Y100.71 E.00356 +G1 X125.774 Y100.71 E.04769 +G1 X125.886 Y100.673 E.0037 +G1 X125.963 Y100.521 E.00534 +G1 X125.963 Y100.21 E.00975 +G1 X129.79 Y100.21 E.11999 +G1 X129.79 Y103.649 E.10783 +G1 X129.479 Y103.649 E.00975 +G1 X129.316 Y103.743 E.0059 +G1 X129.29 Y104.226 E.01517 +G1 X129.29 Y104.86 E.01988 +G1 X129.342 Y104.905 E.00216 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.387 Y104.944 E-.02829 +G1 X129.465 Y105.012 E-.04915 +G1 X129.294 Y105.131 E-.09896 +G1 X129.29 Y106.097 E-.45885 +G1 X129.329 Y106.212 E-.05768 +G1 X129.479 Y106.286 E-.07945 +G1 X129.79 Y106.286 E-.14772 +G1 X129.79 Y106.597 E-.14773 +G1 X129.79 Y107.507 E-.43217 +;WIPE_END +G1 X126.476 Y101.581 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X126.553 Y101.541 E.00286 +G1 X126.674 Y101.3 E.0089 +G1 X128.7 Y101.3 E.06689 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.336 E.06722 +G1 X121.582 Y103.521 E.01114 +;LAYER_CHANGE +;Z:5.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;5.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y103.336 E-.1602 +G1 X121.3 Y101.3 E-.9671 +G1 X121.855 Y101.855 E-.3727 +;WIPE_END +G1 Z5.8 F9000 +;AFTER_LAYER_CHANGE +;5.8 +G1 Z5.8 +G1 X129.701 Y105.014 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.600836 +G1 F900 +G1 X129.454 Y105.014 E.01146 +G1 X129.454 Y105.014 F9000 +G1 X128.516 Y105.409 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.506 Y106.236 E.02731 +G1 X128.548 Y106.518 E.00941 +G1 X128.717 Y106.84 E.01201 +G1 X129.006 Y106.98 E.0106 +G1 X129.006 Y109.006 E.06689 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.456 Y103.958 E.00904 +G1 X121.309 Y103.657 E.01106 +G1 X120.994 Y103.497 E.01167 +G1 X120.994 Y100.994 E.08264 +G1 X123.379 Y100.994 E.07875 +G1 X123.536 Y101.305 E.0115 +G1 X123.723 Y101.413 E.00713 +G1 X124.112 Y101.494 E.01312 +G1 X125.923 Y101.494 E.05979 +G1 X126.251 Y101.437 E.01099 +G1 X126.514 Y101.294 E.00988 +G1 X126.662 Y100.994 E.01104 +G1 X129.006 Y100.994 E.07739 +G1 X129.006 Y102.958 E.06485 +G1 X128.721 Y103.094 E.01043 +G1 X128.532 Y103.479 E.01416 +G1 X128.506 Y104.077 E.01976 +G1 X128.522 Y104.914 E.02764 +G1 X128.517 Y105.349 E.01436 +G1 X128.517 Y105.349 F9000 +G1 X129.04 Y105.014 +G1 F900 +G1 X128.939 Y105.091 E.00419 +G1 X128.903 Y105.5 E.01356 +G1 X128.928 Y106.403 E.02983 +G1 X129.028 Y106.593 E.00709 +G1 X129.403 Y106.775 E.01376 +G1 X129.403 Y109.403 E.08677 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.091 Y104.145 E.05369 +G1 X120.987 Y103.89 E.00909 +G1 X120.597 Y103.691 E.01446 +G1 X120.597 Y100.597 E.10216 +G1 X123.575 Y100.597 E.09833 +G1 X123.771 Y100.985 E.01435 +G1 X123.882 Y101.049 E.00423 +G1 X124.112 Y101.097 E.00776 +G1 X125.987 Y101.094 E.06191 +G1 X126.273 Y100.979 E.01018 +G1 X126.461 Y100.597 E.01406 +G1 X129.403 Y100.597 E.09714 +G1 X129.403 Y103.165 E.08479 +G1 X129.03 Y103.343 E.01365 +G1 X128.986 Y103.407 E.00256 +G1 X128.903 Y103.704 E.01018 +G1 X128.903 Y104.736 E.03407 +G1 X128.939 Y104.937 E.00674 +G1 X128.992 Y104.977 E.00219 +G1 X128.992 Y104.977 F9000 +G1 X129.359 Y104.853 +;TYPE:External perimeter +;WIDTH:0.510418 +G1 F900 +G1 X129.372 Y104.875 E.00099 +;WIDTH:0.555627 +G1 X129.413 Y104.944 E.00342 +;WIDTH:0.600836 +G1 X129.454 Y105.014 E.00376 +G1 X129.413 Y105.083 E.00372 +;WIDTH:0.555627 +G1 X129.372 Y105.153 E.00346 +;WIDTH:0.510418 +G1 X129.331 Y105.222 E.00312 +;WIDTH:0.465209 +G1 X129.29 Y105.292 E.00285 +;WIDTH:0.419999 +G1 X129.29 Y106.236 E.0296 +G1 X129.331 Y106.353 E.00389 +G1 X129.479 Y106.424 E.00515 +G1 X129.79 Y106.424 E.00975 +G1 X129.79 Y106.736 E.00978 +G1 X129.79 Y109.79 E.09576 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.692 Y104.148 E.05089 +G1 X120.521 Y104.04 E.00634 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X123.924 Y100.21 E.11645 +G1 X123.924 Y100.521 E.00975 +G1 X124.001 Y100.673 E.00534 +G1 X124.112 Y100.71 E.00367 +G1 X125.923 Y100.71 E.05678 +G1 X126.038 Y100.671 E.00381 +G1 X126.112 Y100.521 E.00524 +G1 X126.112 Y100.21 E.00975 +G1 X129.79 Y100.21 E.11532 +G1 X129.79 Y103.515 E.10363 +G1 X129.479 Y103.515 E.00975 +G1 X129.332 Y103.586 E.00512 +G1 X129.29 Y103.704 E.00393 +G1 X129.29 Y104.736 E.03236 +;WIDTH:0.465209 +G1 X129.329 Y104.801 E.00266 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.359 Y104.853 E-.02852 +G1 X129.372 Y104.875 E-.01214 +G1 X129.413 Y104.944 E-.03812 +G1 X129.454 Y105.014 E-.03853 +G1 X129.413 Y105.083 E-.03812 +G1 X129.372 Y105.153 E-.03853 +G1 X129.331 Y105.222 E-.03812 +G1 X129.29 Y105.292 E-.03853 +G1 X129.29 Y106.236 E-.4484 +G1 X129.331 Y106.353 E-.05889 +G1 X129.479 Y106.424 E-.07797 +G1 X129.79 Y106.424 E-.14772 +G1 X129.79 Y106.736 E-.1482 +G1 X129.79 Y107.469 E-.34821 +;WIPE_END +G1 X126.491 Y101.652 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X126.716 Y101.529 E.00847 +G1 X126.829 Y101.3 E.00843 +G1 X128.7 Y101.3 E.06178 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.334 E.06716 +G1 X121.583 Y103.522 E.01122 +;LAYER_CHANGE +;Z:6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y103.334 E-.16138 +G1 X121.3 Y101.3 E-.96615 +G1 X121.854 Y101.854 E-.37247 +;WIPE_END +G1 Z6 F9000 +;AFTER_LAYER_CHANGE +;6 +G1 Z6 +G1 X125.046 Y100.072 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F900 +G1 X125.046 Y100.526 E.01284 +G1 X125.046 Y100.526 F9000 +G1 X125.678 Y101.494 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.072 Y101.494 E.01301 +G1 X126.406 Y101.435 E.0112 +G1 X126.675 Y101.285 E.01017 +G1 X126.816 Y100.994 E.01068 +G1 X129.006 Y100.994 E.07231 +G1 X129.006 Y102.819 E.06026 +G1 X128.73 Y102.949 E.01007 +G1 X128.534 Y103.34 E.01444 +G1 X128.506 Y103.928 E.01944 +G1 X128.508 Y104.68 E.02483 +G1 X128.598 Y105.015 E.01145 +G1 X128.539 Y105.163 E.00526 +G1 X128.506 Y105.416 E.00842 +G1 X128.506 Y106.374 E.03163 +G1 X128.55 Y106.662 E.00962 +G1 X128.726 Y106.991 E.01232 +G1 X129.006 Y107.123 E.01022 +G1 X129.006 Y109.006 E.06217 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y108.164 E.0278 +G1 X121.168 Y108.098 E.00614 +G1 X121.431 Y107.716 E.01531 +G1 X121.494 Y107.371 E.01158 +G1 X121.494 Y103.299 E.13445 +G1 X121.47 Y102.411 E.02933 +G1 X121.168 Y101.902 E.01954 +G1 X120.994 Y101.836 E.00614 +G1 X120.994 Y100.994 E.0278 +G1 X123.217 Y100.994 E.0734 +G1 X123.344 Y101.266 E.00991 +G1 X123.436 Y101.335 E.0038 +G1 X123.763 Y101.472 E.01171 +G1 X123.97 Y101.494 E.00687 +G1 X125.618 Y101.494 E.05441 +G1 X125.618 Y101.494 F9000 +G1 X126.465 Y100.9 +G1 F900 +G1 X126.611 Y100.597 E.01111 +G1 X129.403 Y100.597 E.09218 +G1 X129.403 Y103.03 E.08033 +G1 X129.036 Y103.202 E.01338 +G1 X128.99 Y103.265 E.00258 +G1 X128.903 Y103.57 E.01047 +G1 X128.903 Y104.614 E.03447 +G1 X128.972 Y104.887 E.0093 +G1 X129.166 Y105.015 E.00767 +G1 X128.972 Y105.143 E.00767 +G1 X128.923 Y105.266 E.00437 +G1 X128.903 Y105.5 E.00775 +G1 X128.929 Y106.545 E.03451 +G1 X129.033 Y106.739 E.00727 +G1 X129.403 Y106.914 E.01351 +G1 X129.403 Y109.403 E.08218 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y107.918 E.04903 +G1 X120.904 Y107.801 E.01085 +G1 X121.06 Y107.576 E.00904 +G1 X121.097 Y107.371 E.00688 +G1 X121.094 Y102.567 E.15861 +G1 X120.904 Y102.199 E.01367 +G1 X120.597 Y102.082 E.01085 +G1 X120.597 Y100.597 E.04903 +G1 X123.429 Y100.597 E.0935 +G1 X123.599 Y100.962 E.01329 +G1 X123.707 Y101.034 E.00429 +G1 X123.97 Y101.097 E.00893 +G1 X126.136 Y101.094 E.07152 +G1 X126.429 Y100.973 E.01047 +G1 X126.438 Y100.954 E.00069 +G1 X126.438 Y100.954 F9000 +G1 X126.201 Y100.645 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.261 Y100.521 E.00432 +G1 X126.261 Y100.21 E.00975 +G1 X129.79 Y100.21 E.11065 +G1 X129.79 Y103.382 E.09946 +G1 X129.479 Y103.382 E.00975 +G1 X129.319 Y103.47 E.00573 +G1 X129.29 Y103.928 E.01439 +G1 X129.296 Y104.663 E.02305 +;WIDTH:0.445393 +G1 X129.461 Y104.815 E.00751 +G1 X129.778 Y104.815 E.01061 +;WIDTH:0.443861 +G1 X129.778 Y105.216 E.01337 +G1 X129.479 Y105.216 E.00997 +G1 X129.313 Y105.327 E.00666 +;WIDTH:0.419999 +G1 X129.29 Y105.416 E.00288 +G1 X129.29 Y106.374 E.03004 +G1 X129.333 Y106.494 E.004 +G1 X129.479 Y106.563 E.00506 +G1 X129.79 Y106.563 E.00975 +G1 X129.79 Y106.874 E.00975 +G1 X129.79 Y109.79 E.09143 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y107.56 E.06992 +G1 X120.521 Y107.56 E.00975 +G1 X120.647 Y107.512 E.00423 +G1 X120.71 Y107.371 E.00484 +G1 X120.71 Y102.629 E.14868 +G1 X120.647 Y102.488 E.00484 +G1 X120.521 Y102.44 E.00423 +G1 X120.21 Y102.44 E.00975 +G1 X120.21 Y100.21 E.06992 +G1 X123.781 Y100.21 E.11197 +G1 X123.781 Y100.521 E.00975 +G1 X123.849 Y100.666 E.00502 +G1 X123.97 Y100.71 E.00404 +G1 X124.996 Y100.71 E.03217 +G1 X125.046 Y100.526 E.00598 +G1 X125.094 Y100.71 E.00596 +G1 X126.072 Y100.71 E.03066 +G1 X126.158 Y100.68 E.00286 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.201 Y100.645 E-.02634 +G1 X126.261 Y100.521 E-.06543 +G1 X126.261 Y100.21 E-.14772 +G1 X128.915 Y100.21 E-1.26051 +;WIPE_END +G1 X123.503 Y101.702 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X123.13 Y101.488 E.0142 +G1 X123.043 Y101.3 E.00684 +G1 X121.3 Y101.3 E.05755 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y102.646 E.04444 +G1 X128.507 Y102.736 E.00703 +G1 X128.391 Y102.897 E.00655 +G1 X128.236 Y103.265 E.01318 +G1 X128.209 Y103.489 E.00745 +;LAYER_CHANGE +;Z:6.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;6.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.236 Y103.265 E-.10717 +G1 X128.391 Y102.897 E-.18967 +G1 X128.507 Y102.736 E-.09426 +G1 X128.7 Y102.646 E-.10115 +G1 X128.7 Y101.3 E-.63935 +G1 X128.152 Y101.848 E-.3684 +;WIPE_END +G1 Z6.2 F9000 +;AFTER_LAYER_CHANGE +;6.2 +G1 Z6.2 +G1 X125.05 Y100.199 +G1 E5 F2400 +;TYPE:External perimeter +;WIDTH:0.39964 +G1 F900 +G1 X125.05 Y100.537 E.01003 +G1 X125.05 Y100.537 F9000 +G1 X125.554 Y101.494 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X126.221 Y101.494 E.02202 +G1 X126.561 Y101.433 E.01141 +G1 X126.837 Y101.274 E.01052 +G1 X126.97 Y100.994 E.01023 +G1 X129.006 Y100.994 E.06722 +G1 X129.006 Y102.681 E.0557 +G1 X128.741 Y102.802 E.00962 +G1 X128.535 Y103.2 E.0148 +G1 X128.506 Y103.779 E.01914 +G1 X128.521 Y104.659 E.02906 +G1 X128.664 Y105.016 E.0127 +G1 X128.56 Y105.222 E.00762 +G1 X128.506 Y105.543 E.01075 +G1 X128.506 Y106.513 E.03203 +G1 X128.552 Y106.807 E.00983 +G1 X128.737 Y107.142 E.01264 +G1 X129.006 Y107.266 E.00978 +G1 X129.006 Y109.006 E.05745 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y107.954 E.03473 +G1 X121.196 Y107.872 E.0072 +G1 X121.436 Y107.502 E.01456 +G1 X121.494 Y107.171 E.0111 +G1 X121.494 Y102.829 E.14336 +G1 X121.433 Y102.49 E.01137 +G1 X121.196 Y102.128 E.01429 +G1 X120.994 Y102.046 E.0072 +G1 X120.994 Y100.994 E.03473 +G1 X123.072 Y100.994 E.06861 +G1 X123.195 Y101.261 E.00971 +G1 X123.492 Y101.434 E.01135 +G1 X123.827 Y101.494 E.01124 +G1 X125.228 Y101.494 E.04626 +G1 X125.494 Y101.494 E.00878 +G1 X125.494 Y101.494 F9000 +G1 X124.849 Y101.097 +G1 F900 +G1 X124.873 Y101.097 E.00079 +G1 X124.941 Y101.093 E.00225 +G1 X125.05 Y101.031 E.00414 +G1 X125.094 Y101.081 E.0022 +G1 X126.285 Y101.094 E.03933 +G1 X126.586 Y100.967 E.01079 +G1 X126.761 Y100.597 E.01351 +G1 X129.403 Y100.597 E.08723 +G1 X129.403 Y102.896 E.07591 +G1 X129.042 Y103.061 E.01311 +G1 X128.92 Y103.296 E.00874 +G1 X128.903 Y103.779 E.01596 +G1 X128.904 Y104.529 E.02476 +G1 X129.004 Y104.816 E.01003 +G1 X129.348 Y105.016 E.01314 +G1 X129.004 Y105.217 E.01315 +G1 X128.935 Y105.353 E.00504 +G1 X128.903 Y105.543 E.00636 +G1 X128.93 Y106.687 E.03778 +G1 X129.04 Y106.885 E.00748 +G1 X129.403 Y107.053 E.01321 +G1 X129.403 Y109.403 E.07759 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y107.717 E.05567 +G1 X120.921 Y107.586 E.01154 +G1 X121.063 Y107.367 E.00862 +G1 X121.097 Y107.171 E.00657 +G1 X121.089 Y102.735 E.14646 +G1 X120.921 Y102.414 E.01196 +G1 X120.597 Y102.283 E.01154 +G1 X120.597 Y100.597 E.05567 +G1 X123.287 Y100.597 E.08882 +G1 X123.453 Y100.959 E.01315 +G1 X123.544 Y101.022 E.00365 +G1 X123.827 Y101.097 E.00967 +G1 X124.789 Y101.097 E.03176 +G1 X124.789 Y101.097 F9000 +G1 X124.97 Y100.618 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X125.05 Y100.537 E.00357 +G1 X125.184 Y100.705 E.00674 +G1 X125.228 Y100.71 E.00139 +G1 X126.221 Y100.71 E.03113 +G1 X126.341 Y100.667 E.004 +G1 X126.41 Y100.521 E.00506 +G1 X126.41 Y100.21 E.00975 +G1 X129.79 Y100.21 E.10598 +G1 X129.79 Y103.248 E.09525 +G1 X129.479 Y103.248 E.00975 +G1 X129.336 Y103.314 E.00494 +G1 X129.296 Y103.391 E.00272 +G1 X129.291 Y104.51 E.03509 +;WIDTH:0.457848 +G1 X129.332 Y104.571 E.00254 +;WIDTH:0.495697 +G1 X129.373 Y104.632 E.00277 +;WIDTH:0.533546 +G1 X129.414 Y104.692 E.00296 +;WIDTH:0.571394 +G1 X129.456 Y104.753 E.00325 +G1 X129.715 Y104.753 E.01138 +;WIDTH:0.570084 +G1 X129.715 Y105.279 E.02306 +G1 X129.479 Y105.279 E.01034 +G1 X129.44 Y105.318 E.00242 +;WIDTH:0.532563 +G1 X129.401 Y105.358 E.00227 +;WIDTH:0.495042 +G1 X129.362 Y105.397 E.00207 +;WIDTH:0.457521 +G1 X129.323 Y105.436 E.0019 +;WIDTH:0.419999 +G1 X129.29 Y105.541 E.00345 +G1 X129.29 Y106.513 E.03048 +G1 X129.335 Y106.635 E.00408 +G1 X129.479 Y106.701 E.00497 +G1 X129.79 Y106.701 E.00975 +G1 X129.79 Y107.013 E.00978 +G1 X129.79 Y109.79 E.08707 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y107.36 E.07619 +G1 X120.521 Y107.36 E.00975 +G1 X120.652 Y107.307 E.00443 +G1 X120.71 Y107.171 E.00464 +G1 X120.71 Y102.829 E.13614 +G1 X120.652 Y102.693 E.00464 +G1 X120.521 Y102.64 E.00443 +G1 X120.21 Y102.64 E.00975 +G1 X120.21 Y100.21 E.07619 +G1 X123.639 Y100.21 E.10751 +G1 X123.639 Y100.521 E.00975 +G1 X123.705 Y100.665 E.00497 +G1 X123.827 Y100.71 E.00408 +G1 X124.879 Y100.71 E.03298 +G1 X124.927 Y100.661 E.00215 +G1 X124.927 Y100.661 F9000 +G1 X123.496 Y101.743 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X123.197 Y101.634 E.01051 +G1 X122.981 Y101.483 E.0087 +G1 X122.897 Y101.3 E.00665 +G1 X121.3 Y101.3 E.05273 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y102.505 E.03979 +G1 X128.521 Y102.587 E.0065 +G1 X128.402 Y102.745 E.00653 +G1 X128.238 Y103.123 E.0136 +G1 X128.2 Y103.492 E.01225 +;LAYER_CHANGE +;Z:6.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;6.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.238 Y103.123 E-.1762 +G1 X128.402 Y102.745 E-.19572 +G1 X128.521 Y102.587 E-.09396 +G1 X128.7 Y102.505 E-.09352 +G1 X128.7 Y101.3 E-.57237 +G1 X128.152 Y101.848 E-.36823 +;WIPE_END +G1 Z6.4 F9000 +;AFTER_LAYER_CHANGE +;6.4 +G1 Z6.4 +G1 X128.825 Y102.623 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.753 Y102.655 E.0026 +G1 X128.537 Y103.059 E.01513 +G1 X128.506 Y103.63 E.01888 +G1 X128.528 Y104.569 E.03101 +G1 X128.695 Y104.939 E.0134 +G1 X128.809 Y105.017 E.00456 +G1 X128.695 Y105.096 E.00458 +G1 X128.578 Y105.304 E.00788 +G1 X128.506 Y105.672 E.01238 +G1 X128.506 Y106.651 E.03232 +G1 X128.554 Y106.952 E.01006 +G1 X128.749 Y107.294 E.013 +G1 X129.006 Y107.41 E.00931 +G1 X129.006 Y109.006 E.0527 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y107.744 E.04167 +G1 X121.22 Y107.649 E.00809 +G1 X121.44 Y107.29 E.0139 +G1 X121.494 Y106.971 E.01068 +G1 X121.494 Y103.685 E.10849 +G1 X121.472 Y102.823 E.02847 +G1 X121.383 Y102.577 E.00864 +G1 X121.22 Y102.351 E.0092 +G1 X120.994 Y102.256 E.00809 +G1 X120.994 Y100.994 E.04167 +G1 X122.929 Y100.994 E.06389 +G1 X123.049 Y101.257 E.00954 +G1 X123.267 Y101.4 E.00861 +G1 X123.685 Y101.494 E.01415 +G1 X124.74 Y101.494 E.03483 +G1 X125.055 Y101.437 E.01057 +G1 X125.37 Y101.494 E.01057 +G1 X126.37 Y101.494 E.03302 +G1 X126.717 Y101.43 E.01165 +G1 X127 Y101.263 E.01085 +G1 X127.124 Y100.994 E.00978 +G1 X129.006 Y100.994 E.06214 +G1 X129.006 Y102.542 E.05111 +G1 X128.879 Y102.599 E.0046 +G1 X128.879 Y102.599 F9000 +G1 X129.094 Y102.9 +G1 F900 +G1 X129.049 Y102.92 E.00163 +G1 X128.921 Y103.159 E.00895 +G1 X128.903 Y103.63 E.01556 +G1 X128.906 Y104.423 E.02618 +G1 X129.015 Y104.704 E.00995 +G1 X129.327 Y104.919 E.01251 +G1 X129.403 Y104.929 E.00253 +G1 X129.403 Y105.106 E.00584 +G1 X129.327 Y105.116 E.00253 +G1 X129.015 Y105.331 E.01251 +G1 X128.946 Y105.454 E.00466 +G1 X128.903 Y105.672 E.00734 +G1 X128.931 Y106.829 E.03821 +G1 X129.047 Y107.031 E.00769 +G1 X129.403 Y107.192 E.0129 +G1 X129.403 Y109.403 E.073 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y107.515 E.06234 +G1 X120.935 Y107.372 E.01212 +G1 X121.065 Y107.16 E.00821 +G1 X121.097 Y106.971 E.00633 +G1 X121.084 Y102.907 E.13418 +G1 X120.935 Y102.628 E.01044 +G1 X120.597 Y102.485 E.01212 +G1 X120.597 Y100.597 E.06234 +G1 X123.144 Y100.597 E.08409 +G1 X123.309 Y100.957 E.01308 +G1 X123.438 Y101.041 E.00508 +G1 X123.685 Y101.097 E.00836 +G1 X124.74 Y101.097 E.03483 +G1 X124.964 Y101.051 E.00755 +G1 X125.055 Y100.927 E.00508 +G1 X125.145 Y101.051 E.00506 +G1 X125.25 Y101.085 E.00364 +G1 X126.434 Y101.094 E.03909 +G1 X126.743 Y100.96 E.01112 +G1 X126.911 Y100.597 E.01321 +G1 X129.403 Y100.597 E.08228 +G1 X129.403 Y102.761 E.07145 +G1 X129.148 Y102.875 E.00922 +G1 X129.148 Y102.875 F9000 +G1 X129.353 Y103.171 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.338 Y103.177 E.00051 +G1 X129.296 Y103.256 E.00281 +G1 X129.29 Y103.63 E.01173 +G1 X129.297 Y104.415 E.02461 +G1 X129.429 Y104.545 E.00581 +G1 X129.79 Y104.552 E.01132 +G1 X129.79 Y105.483 E.02919 +G1 X129.479 Y105.483 E.00975 +G1 X129.327 Y105.56 E.00534 +G1 X129.29 Y105.672 E.0037 +G1 X129.29 Y106.651 E.0307 +G1 X129.337 Y106.776 E.00419 +G1 X129.479 Y106.84 E.00488 +G1 X129.79 Y106.84 E.00975 +G1 X129.79 Y107.151 E.00975 +G1 X129.79 Y109.79 E.08274 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y107.16 E.08246 +G1 X120.521 Y107.16 E.00975 +G1 X120.657 Y107.103 E.00462 +G1 X120.71 Y106.971 E.00446 +G1 X120.71 Y103.685 E.10303 +G1 X120.689 Y102.941 E.02334 +G1 X120.521 Y102.84 E.00615 +G1 X120.21 Y102.84 E.00975 +G1 X120.21 Y100.21 E.08246 +G1 X123.496 Y100.21 E.10303 +G1 X123.496 Y100.521 E.00975 +G1 X123.562 Y100.664 E.00494 +G1 X123.685 Y100.71 E.00412 +G1 X124.74 Y100.71 E.03308 +G1 X124.779 Y100.706 E.00123 +G1 X124.838 Y100.621 E.00324 +;WIDTH:0.389589 +G1 X124.897 Y100.535 E.00301 +;WIDTH:0.359179 +G1 X124.897 Y100.179 E.00936 +;WIDTH:0.357976 +G1 X125.212 Y100.179 E.00825 +G1 X125.212 Y100.521 E.00896 +;WIDTH:0.388988 +G1 X125.254 Y100.608 E.00278 +;WIDTH:0.419999 +G1 X125.296 Y100.695 E.00303 +G1 X125.37 Y100.71 E.00237 +G1 X126.37 Y100.71 E.03135 +G1 X126.492 Y100.665 E.00408 +G1 X126.559 Y100.521 E.00498 +G1 X126.559 Y100.21 E.00975 +G1 X129.79 Y100.21 E.10131 +G1 X129.79 Y103.114 E.09105 +G1 X129.479 Y103.114 E.00975 +G1 X129.408 Y103.146 E.00244 +G1 X129.408 Y103.146 F9000 +G1 X128.201 Y103.495 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.24 Y102.98 E.01705 +G1 X128.415 Y102.593 E.01402 +G1 X128.536 Y102.436 E.00654 +G1 X128.7 Y102.363 E.00593 +G1 X128.7 Y101.3 E.0351 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X122.752 Y101.3 E.04794 +G1 X122.835 Y101.48 E.00654 +G1 X123.134 Y101.676 E.0118 +G1 X123.492 Y101.778 E.01229 +;LAYER_CHANGE +;Z:6.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;6.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.134 Y101.676 E-.17682 +G1 X122.835 Y101.48 E-.16982 +G1 X122.752 Y101.3 E-.09415 +G1 X121.3 Y101.3 E-.6897 +G1 X121.85 Y101.85 E-.36951 +;WIPE_END +G1 Z6.6 F9000 +;AFTER_LAYER_CHANGE +;6.6 +G1 Z6.6 +G1 X122.867 Y101.173 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X122.905 Y101.256 E.00301 +G1 X123.193 Y101.429 E.01109 +G1 X123.543 Y101.494 E.01175 +G1 X124.7 Y101.489 E.0382 +G1 X125.06 Y101.373 E.01249 +G1 X125.231 Y101.451 E.00621 +G1 X125.518 Y101.494 E.00958 +G1 X126.519 Y101.494 E.03305 +G1 X126.873 Y101.428 E.01189 +G1 X127.163 Y101.251 E.01122 +G1 X127.279 Y100.994 E.00931 +G1 X129.006 Y100.994 E.05702 +G1 X129.006 Y102.403 E.04652 +G1 X128.765 Y102.508 E.00868 +G1 X128.539 Y102.918 E.01546 +G1 X128.506 Y103.169 E.00836 +G1 X128.506 Y104.233 E.03513 +G1 X128.536 Y104.472 E.00795 +G1 X128.701 Y104.816 E.0126 +G1 X128.949 Y105.018 E.01056 +G1 X128.701 Y105.219 E.01054 +G1 X128.554 Y105.501 E.0105 +G1 X128.506 Y105.803 E.0101 +G1 X128.506 Y106.789 E.03256 +G1 X128.556 Y107.098 E.01034 +G1 X128.761 Y107.447 E.01336 +G1 X129.006 Y107.554 E.00883 +G1 X129.006 Y109.006 E.04794 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y107.535 E.04857 +G1 X121.24 Y107.427 E.00887 +G1 X121.444 Y107.079 E.01332 +G1 X121.494 Y106.771 E.0103 +G1 X121.494 Y103.543 E.10658 +G1 X121.462 Y102.98 E.01862 +G1 X121.24 Y102.573 E.01531 +G1 X120.994 Y102.465 E.00887 +G1 X120.994 Y100.994 E.04857 +G1 X122.786 Y100.994 E.05917 +G1 X122.842 Y101.118 E.00449 +G1 X123.142 Y100.905 F9000 +G1 F900 +G1 X123.165 Y100.956 E.00185 +G1 X123.336 Y101.059 E.00659 +G1 X123.543 Y101.097 E.00695 +G1 X124.656 Y101.094 E.03675 +G1 X124.903 Y101.011 E.0086 +G1 X125.06 Y100.758 E.00983 +G1 X125.216 Y101.011 E.00981 +G1 X125.349 Y101.071 E.00482 +G1 X125.518 Y101.097 E.00565 +G1 X126.519 Y101.097 E.03305 +G1 X126.729 Y101.058 E.00705 +G1 X126.9 Y100.953 E.00663 +G1 X127.061 Y100.597 E.0129 +G1 X129.403 Y100.597 E.07733 +G1 X129.403 Y102.627 E.06703 +G1 X129.057 Y102.778 E.01246 +G1 X128.922 Y103.021 E.00918 +G1 X128.903 Y103.481 E.0152 +G1 X128.907 Y104.301 E.02707 +G1 X129.018 Y104.578 E.00985 +G1 X129.235 Y104.754 E.00923 +G1 X129.403 Y104.791 E.00568 +G1 X129.403 Y105.244 E.01496 +G1 X129.235 Y105.281 E.00568 +G1 X129.018 Y105.457 E.00923 +G1 X128.955 Y105.565 E.00413 +G1 X128.903 Y105.803 E.00804 +G1 X128.903 Y106.789 E.03256 +G1 X128.933 Y106.972 E.00612 +G1 X129.054 Y107.178 E.00789 +G1 X129.403 Y107.332 E.01259 +G1 X129.403 Y109.403 E.06838 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y107.314 E.06897 +G1 X120.947 Y107.159 E.01264 +G1 X121.068 Y106.953 E.00789 +G1 X121.097 Y106.771 E.00608 +G1 X121.097 Y103.543 E.10658 +G1 X121.078 Y103.082 E.01523 +G1 X120.947 Y102.841 E.00906 +G1 X120.597 Y102.686 E.01264 +G1 X120.597 Y100.597 E.06897 +G1 X123.001 Y100.597 E.07937 +G1 X123.117 Y100.85 E.00919 +G1 X123.117 Y100.85 F9000 +G1 X123.411 Y100.647 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.419 Y100.664 E.00059 +G1 X123.543 Y100.71 E.00415 +G1 X124.639 Y100.706 E.03436 +;WIDTH:0.461675 +G1 X124.735 Y100.624 E.0044 +;WIDTH:0.50335 +G1 X124.83 Y100.542 E.0048 +G1 X124.83 Y100.251 E.01114 +;WIDTH:0.501597 +G1 X125.289 Y100.251 E.01751 +G1 X125.289 Y100.521 E.0103 +G1 X125.354 Y100.602 E.00396 +;WIDTH:0.460798 +G1 X125.419 Y100.682 E.00358 +;WIDTH:0.419999 +G1 X125.518 Y100.71 E.00323 +G1 X126.519 Y100.71 E.03139 +G1 X126.644 Y100.663 E.00419 +G1 X126.708 Y100.521 E.00488 +G1 X126.708 Y100.21 E.00975 +G1 X129.79 Y100.21 E.09663 +G1 X129.79 Y102.981 E.08688 +G1 X129.479 Y102.981 E.00975 +G1 X129.34 Y103.041 E.00475 +G1 X129.296 Y103.12 E.00284 +G1 X129.29 Y103.481 E.01132 +G1 X129.293 Y104.264 E.02455 +G1 X129.399 Y104.403 E.00548 +G1 X129.79 Y104.421 E.01227 +G1 X129.79 Y105.614 E.03741 +G1 X129.479 Y105.614 E.00975 +G1 X129.328 Y105.69 E.0053 +G1 X129.29 Y105.803 E.00374 +G1 X129.29 Y106.789 E.03092 +G1 X129.34 Y106.917 E.00431 +G1 X129.479 Y106.978 E.00476 +G1 X129.79 Y106.978 E.00975 +G1 X129.79 Y107.289 E.00975 +G1 X129.79 Y109.79 E.07842 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y106.96 E.08873 +G1 X120.521 Y106.96 E.00975 +G1 X120.661 Y106.898 E.0048 +G1 X120.71 Y106.771 E.00427 +G1 X120.71 Y103.229 E.11106 +G1 X120.677 Y103.121 E.00354 +G1 X120.521 Y103.04 E.00551 +G1 X120.21 Y103.04 E.00975 +G1 X120.21 Y100.21 E.08873 +G1 X123.354 Y100.21 E.09858 +G1 X123.354 Y100.521 E.00975 +G1 X123.386 Y100.592 E.00244 +G1 X123.386 Y100.592 F9000 +G1 X123.492 Y101.793 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X123.08 Y101.715 E.01384 +G1 X122.691 Y101.481 E.01499 +G1 X122.609 Y101.3 E.00656 +G1 X121.3 Y101.3 E.04322 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y102.221 E.03041 +G1 X128.553 Y102.285 E.00529 +G1 X128.428 Y102.439 E.00655 +G1 X128.243 Y102.837 E.01449 +G1 X128.2 Y103.498 E.02187 +;LAYER_CHANGE +;Z:6.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;6.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.243 Y102.837 E-.31464 +G1 X128.428 Y102.439 E-.20848 +G1 X128.553 Y102.285 E-.09421 +G1 X128.7 Y102.221 E-.07616 +G1 X128.7 Y101.3 E-.43748 +G1 X128.151 Y101.849 E-.36903 +;WIPE_END +G1 Z6.8 F9000 +;AFTER_LAYER_CHANGE +;6.8 +G1 Z6.8 +G1 X128.817 Y102.343 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.779 Y102.359 E.00136 +G1 X128.541 Y102.777 E.01588 +G1 X128.506 Y103.332 E.01836 +G1 X128.533 Y104.33 E.03296 +G1 X128.699 Y104.683 E.01288 +G1 X129.006 Y104.935 E.01311 +G1 X129.006 Y105.101 E.00548 +G1 X128.699 Y105.354 E.01313 +G1 X128.565 Y105.601 E.00928 +G1 X128.506 Y105.935 E.0112 +G1 X128.506 Y106.928 E.03279 +G1 X128.559 Y107.244 E.01058 +G1 X128.775 Y107.6 E.01375 +G1 X129.006 Y107.699 E.0083 +G1 X129.006 Y109.006 E.04315 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y107.328 E.0554 +G1 X121.258 Y107.207 E.00959 +G1 X121.448 Y106.869 E.0128 +G1 X121.494 Y106.571 E.00996 +G1 X121.494 Y103.429 E.10374 +G1 X121.451 Y103.141 E.00961 +G1 X121.264 Y102.8 E.01284 +G1 X120.994 Y102.672 E.00987 +G1 X120.994 Y100.994 E.0554 +G1 X122.643 Y100.994 E.05445 +G1 X122.762 Y101.256 E.0095 +G1 X123.122 Y101.453 E.01355 +G1 X123.4 Y101.494 E.00928 +G1 X124.46 Y101.494 E.035 +G1 X124.651 Y101.475 E.00634 +G1 X125.028 Y101.311 E.01357 +G1 X125.065 Y101.254 E.00224 +G1 X125.101 Y101.311 E.00223 +G1 X125.319 Y101.429 E.00818 +G1 X125.67 Y101.494 E.01179 +G1 X126.668 Y101.494 E.03295 +G1 X127.03 Y101.425 E.01217 +G1 X127.327 Y101.237 E.01161 +G1 X127.434 Y100.994 E.00877 +G1 X129.006 Y100.994 E.0519 +G1 X129.006 Y102.263 E.0419 +G1 X128.872 Y102.32 E.00481 +G1 X128.872 Y102.32 F9000 +G1 X129.089 Y102.625 +G1 F900 +G1 X129.065 Y102.635 E.00086 +G1 X128.924 Y102.882 E.00939 +G1 X128.903 Y103.035 E.0051 +G1 X128.903 Y104.102 E.03523 +G1 X128.962 Y104.355 E.00858 +G1 X129.167 Y104.586 E.0102 +G1 X129.403 Y104.655 E.00812 +G1 X129.403 Y105.381 E.02397 +G1 X129.167 Y105.451 E.00813 +G1 X128.962 Y105.681 E.01017 +G1 X128.903 Y105.935 E.00861 +G1 X128.903 Y106.928 E.03279 +G1 X128.934 Y107.115 E.00626 +G1 X129.062 Y107.326 E.00815 +G1 X129.403 Y107.471 E.01223 +G1 X129.403 Y109.403 E.06379 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y107.112 E.07564 +G1 X120.957 Y106.947 E.01308 +G1 X121.07 Y106.747 E.00758 +G1 X121.097 Y106.571 E.00588 +G1 X121.097 Y103.429 E.10374 +G1 X121.071 Y103.258 E.00571 +G1 X120.961 Y103.056 E.00759 +G1 X120.597 Y102.887 E.01325 +G1 X120.597 Y100.597 E.07561 +G1 X122.859 Y100.597 E.07469 +G1 X123.023 Y100.956 E.01303 +G1 X123.235 Y101.073 E.00799 +G1 X123.4 Y101.097 E.00551 +G1 X124.534 Y101.092 E.03744 +G1 X124.796 Y100.989 E.00929 +G1 X125.065 Y100.597 E.0157 +G1 X125.333 Y100.989 E.01568 +G1 X125.462 Y101.058 E.00483 +G1 X125.67 Y101.097 E.00699 +G1 X126.668 Y101.097 E.03295 +G1 X126.882 Y101.056 E.00719 +G1 X127.058 Y100.945 E.00687 +G1 X127.211 Y100.597 E.01255 +G1 X129.403 Y100.597 E.07237 +G1 X129.403 Y102.492 E.06257 +G1 X129.145 Y102.601 E.00925 +G1 X129.145 Y102.601 F9000 +G1 X129.352 Y102.901 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.343 Y102.904 E.0003 +G1 X129.297 Y102.985 E.00292 +G1 X129.29 Y103.332 E.01088 +G1 X129.292 Y104.127 E.02493 +G1 X129.376 Y104.26 E.00493 +G1 X129.79 Y104.29 E.01301 +G1 X129.79 Y105.746 E.04565 +G1 X129.479 Y105.746 E.00975 +G1 X129.327 Y105.822 E.00533 +G1 X129.29 Y105.935 E.00373 +G1 X129.29 Y106.928 E.03113 +G1 X129.342 Y107.058 E.00439 +G1 X129.479 Y107.116 E.00466 +G1 X129.79 Y107.116 E.00975 +G1 X129.79 Y107.428 E.00978 +G1 X129.79 Y109.79 E.07406 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y106.76 E.095 +G1 X120.521 Y106.76 E.00975 +G1 X120.664 Y106.695 E.00493 +G1 X120.71 Y106.571 E.00415 +G1 X120.71 Y103.429 E.09852 +G1 X120.665 Y103.307 E.00408 +G1 X120.521 Y103.24 E.00498 +G1 X120.21 Y103.24 E.00975 +G1 X120.21 Y100.21 E.095 +G1 X123.212 Y100.21 E.09413 +G1 X123.212 Y100.521 E.00975 +G1 X123.276 Y100.664 E.00491 +G1 X123.4 Y100.71 E.00415 +G1 X124.528 Y100.697 E.03537 +G1 X124.645 Y100.557 E.00572 +G1 X124.648 Y100.21 E.01088 +G1 X125.481 Y100.21 E.02612 +G1 X125.481 Y100.521 E.00975 +G1 X125.56 Y100.674 E.0054 +G1 X125.67 Y100.71 E.00363 +G1 X126.668 Y100.71 E.03129 +G1 X126.796 Y100.66 E.00431 +G1 X126.857 Y100.521 E.00476 +G1 X126.857 Y100.21 E.00975 +G1 X129.79 Y100.21 E.09196 +G1 X129.79 Y102.847 E.08268 +G1 X129.479 Y102.847 E.00975 +G1 X129.407 Y102.877 E.00245 +G1 X129.407 Y102.877 F9000 +G1 X128.2 Y103.502 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.246 Y102.692 E.02679 +G1 X128.443 Y102.284 E.01496 +G1 X128.571 Y102.132 E.00656 +G1 X128.7 Y102.078 E.00462 +G1 X128.7 Y101.3 E.02569 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X122.466 Y101.3 E.0385 +G1 X122.549 Y101.483 E.00663 +G1 X123.03 Y101.747 E.01812 +G1 X123.493 Y101.799 E.01538 +;LAYER_CHANGE +;Z:7 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;7 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X123.03 Y101.747 E-.22131 +G1 X122.549 Y101.483 E-.26063 +G1 X122.466 Y101.3 E-.09545 +G1 X121.3 Y101.3 E-.55385 +G1 X121.849 Y101.849 E-.36876 +;WIPE_END +G1 Z7 F9000 +;AFTER_LAYER_CHANGE +;7 +G1 Z7 +G1 X122.576 Y101.177 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X122.605 Y101.243 E.00238 +G1 X123.018 Y101.464 E.01547 +G1 X123.629 Y101.494 E.0202 +G1 X124.549 Y101.466 E.03039 +G1 X124.901 Y101.301 E.01284 +G1 X125.07 Y101.084 E.00908 +G1 X125.239 Y101.301 E.00908 +G1 X125.529 Y101.449 E.01075 +G1 X125.821 Y101.494 E.00975 +G1 X126.817 Y101.494 E.03289 +G1 X127.187 Y101.421 E.01245 +G1 X127.483 Y101.231 E.01161 +G1 X127.589 Y100.994 E.00857 +G1 X129.006 Y100.994 E.04679 +G1 X129.006 Y102.123 E.03728 +G1 X128.795 Y102.21 E.00754 +G1 X128.707 Y102.309 E.00437 +G1 X128.543 Y102.634 E.01202 +G1 X128.506 Y103.183 E.01817 +G1 X128.517 Y104.117 E.03084 +G1 X128.645 Y104.473 E.01249 +G1 X128.867 Y104.727 E.01114 +G1 X129.006 Y104.776 E.00487 +G1 X129.006 Y105.261 E.01601 +G1 X128.867 Y105.31 E.00487 +G1 X128.645 Y105.565 E.01116 +G1 X128.575 Y105.707 E.00523 +G1 X128.506 Y106.066 E.01207 +G1 X128.506 Y107.066 E.03302 +G1 X128.562 Y107.391 E.01089 +G1 X128.791 Y107.754 E.01417 +G1 X129.006 Y107.843 E.00768 +G1 X129.006 Y109.006 E.0384 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y107.12 E.06227 +G1 X121.274 Y106.988 E.01022 +G1 X121.45 Y106.66 E.01229 +G1 X121.494 Y106.371 E.00965 +G1 X121.494 Y103.629 E.09053 +G1 X121.426 Y103.272 E.012 +G1 X121.262 Y102.997 E.01057 +G1 X120.994 Y102.874 E.00974 +G1 X120.994 Y100.994 E.06207 +G1 X122.495 Y100.994 E.04956 +G1 X122.552 Y101.122 E.00463 +G1 X122.552 Y101.122 F9000 +G1 X122.853 Y100.907 +G1 F900 +G1 X122.872 Y100.949 E.00152 +G1 X123.116 Y101.079 E.00913 +G1 X123.629 Y101.097 E.01695 +G1 X124.412 Y101.089 E.02585 +G1 X124.663 Y100.983 E.009 +G1 X124.849 Y100.744 E.01 +G1 X124.879 Y100.597 E.00495 +G1 X125.261 Y100.597 E.01261 +G1 X125.29 Y100.744 E.00495 +G1 X125.477 Y100.983 E.01002 +G1 X125.588 Y101.048 E.00425 +G1 X125.821 Y101.097 E.00786 +G1 X126.817 Y101.097 E.03289 +G1 X127.036 Y101.054 E.00737 +G1 X127.211 Y100.941 E.00688 +G1 X127.361 Y100.597 E.01239 +G1 X129.403 Y100.597 E.06742 +G1 X129.403 Y102.357 E.05811 +G1 X129.074 Y102.492 E.01174 +G1 X128.925 Y102.744 E.00967 +G1 X128.903 Y102.902 E.00527 +G1 X128.903 Y103.971 E.0353 +G1 X128.944 Y104.184 E.00716 +G1 X129.116 Y104.418 E.00959 +G1 X129.403 Y104.52 E.01006 +G1 X129.403 Y105.518 E.03295 +G1 X129.116 Y105.619 E.01005 +G1 X128.944 Y105.854 E.00962 +G1 X128.903 Y106.066 E.00713 +G1 X128.903 Y107.066 E.03302 +G1 X128.936 Y107.258 E.00643 +G1 X129.072 Y107.474 E.00843 +G1 X129.403 Y107.611 E.01183 +G1 X129.403 Y109.403 E.05917 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.911 E.08228 +G1 X120.967 Y106.736 E.01351 +G1 X121.071 Y106.542 E.00727 +G1 X121.097 Y106.371 E.00571 +G1 X121.092 Y103.556 E.09294 +G1 X120.959 Y103.255 E.01087 +G1 X120.597 Y103.088 E.01316 +G1 X120.597 Y100.597 E.08225 +G1 X122.716 Y100.597 E.06996 +G1 X122.829 Y100.852 E.00921 +G1 X122.829 Y100.852 F9000 +G1 X123.125 Y100.647 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X123.131 Y100.661 E.00048 +G1 X123.211 Y100.704 E.00285 +G1 X123.629 Y100.71 E.01311 +G1 X124.355 Y100.706 E.02276 +G1 X124.492 Y100.594 E.00555 +G1 X124.507 Y100.21 E.01205 +G1 X125.633 Y100.21 E.0353 +G1 X125.633 Y100.521 E.00975 +G1 X125.709 Y100.672 E.0053 +G1 X125.821 Y100.71 E.00371 +G1 X126.817 Y100.71 E.03123 +G1 X126.947 Y100.659 E.00438 +G1 X127.006 Y100.521 E.00471 +G1 X127.006 Y100.21 E.00975 +G1 X129.79 Y100.21 E.08729 +G1 X129.79 Y102.713 E.07848 +G1 X129.479 Y102.713 E.00975 +G1 X129.346 Y102.768 E.00451 +G1 X129.29 Y102.902 E.00455 +G1 X129.29 Y103.971 E.03352 +G1 X129.317 Y104.068 E.00316 +G1 X129.479 Y104.16 E.00584 +G1 X129.79 Y104.16 E.00975 +G1 X129.79 Y105.878 E.05387 +G1 X129.479 Y105.878 E.00975 +G1 X129.36 Y105.92 E.00396 +G1 X129.29 Y106.066 E.00508 +G1 X129.29 Y107.066 E.03135 +G1 X129.345 Y107.2 E.00454 +G1 X129.479 Y107.255 E.00454 +G1 X129.79 Y107.255 E.00975 +G1 X129.79 Y107.566 E.00975 +G1 X129.79 Y109.79 E.06973 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y106.56 E.10127 +G1 X120.521 Y106.56 E.00975 +G1 X120.667 Y106.491 E.00506 +G1 X120.71 Y106.371 E.004 +G1 X120.71 Y103.629 E.08597 +G1 X120.665 Y103.506 E.00411 +G1 X120.521 Y103.44 E.00497 +G1 X120.21 Y103.44 E.00975 +G1 X120.21 Y100.21 E.10127 +G1 X123.069 Y100.21 E.08964 +G1 X123.069 Y100.521 E.00975 +G1 X123.101 Y100.592 E.00244 +G1 X123.101 Y100.592 F9000 +G1 X123.495 Y101.8 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X122.941 Y101.761 E.01834 +G1 X122.561 Y101.595 E.01369 +G1 X122.385 Y101.459 E.00734 +G1 X122.315 Y101.3 E.00574 +G1 X121.3 Y101.3 E.03351 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.7 Y101.934 E.02093 +G1 X128.451 Y102.14 E.01067 +G1 X128.249 Y102.548 E.01503 +G1 X128.2 Y103.506 E.03167 +;LAYER_CHANGE +;Z:7.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;7.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.249 Y102.548 E-.45564 +G1 X128.451 Y102.14 E-.21625 +G1 X128.7 Y101.934 E-.1535 +G1 X128.7 Y101.3 E-.30115 +G1 X128.144 Y101.856 E-.37346 +;WIPE_END +G1 Z7.2 F9000 +;AFTER_LAYER_CHANGE +;7.2 +G1 Z7.2 +G1 X128.787 Y104.517 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.802 Y104.539 E.00088 +G1 X129.006 Y104.622 E.00727 +G1 X129.006 Y105.417 E.02625 +G1 X128.802 Y105.499 E.00726 +G1 X128.584 Y105.817 E.01273 +G1 X128.506 Y106.198 E.01284 +G1 X128.506 Y107.205 E.03325 +G1 X128.565 Y107.538 E.01117 +G1 X128.808 Y107.91 E.01467 +G1 X129.006 Y107.989 E.00704 +G1 X129.006 Y109.006 E.03358 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.914 E.06907 +G1 X121.288 Y106.771 E.01079 +G1 X121.453 Y106.451 E.01189 +G1 X121.494 Y106.171 E.00934 +G1 X121.494 Y104.177 E.06584 +G1 X121.466 Y103.597 E.01917 +G1 X121.268 Y103.205 E.0145 +G1 X120.994 Y103.077 E.00999 +G1 X120.994 Y100.994 E.06877 +G1 X122.346 Y100.994 E.04464 +G1 X122.448 Y101.229 E.00846 +G1 X122.687 Y101.395 E.00961 +G1 X123.115 Y101.494 E.0145 +G1 X123.829 Y101.494 E.02357 +G1 X124.4 Y101.468 E.01887 +G1 X124.757 Y101.302 E.013 +G1 X125.015 Y100.994 E.01327 +G1 X125.135 Y100.994 E.00396 +G1 X125.392 Y101.302 E.01324 +G1 X125.645 Y101.437 E.00947 +G1 X125.973 Y101.494 E.01099 +G1 X126.967 Y101.494 E.03282 +G1 X127.345 Y101.418 E.01273 +G1 X127.632 Y101.231 E.01131 +G1 X127.745 Y100.994 E.00867 +G1 X129.006 Y100.994 E.04163 +G1 X129.006 Y101.983 E.03265 +G1 X128.812 Y102.06 E.00689 +G1 X128.721 Y102.157 E.00439 +G1 X128.546 Y102.492 E.01248 +G1 X128.506 Y102.768 E.00921 +G1 X128.506 Y103.84 E.03539 +G1 X128.584 Y104.221 E.01284 +G1 X128.753 Y104.468 E.00988 +G1 X128.753 Y104.468 F9000 +G1 X129.068 Y104.236 +G1 F900 +G1 X129.078 Y104.254 E.00068 +G1 X129.403 Y104.385 E.01157 +G1 X129.403 Y105.653 E.04187 +G1 X129.078 Y105.784 E.01157 +G1 X128.949 Y105.973 E.00756 +G1 X128.903 Y106.198 E.00758 +G1 X128.903 Y107.205 E.03325 +G1 X128.938 Y107.402 E.00661 +G1 X129.082 Y107.622 E.00868 +G1 X129.403 Y107.75 E.01141 +G1 X129.403 Y109.403 E.05458 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.71 E.08892 +G1 X120.975 Y106.526 E.01388 +G1 X121.073 Y106.337 E.00703 +G1 X121.097 Y106.171 E.00554 +G1 X121.097 Y104.984 E.03919 +G1 X121.08 Y103.691 E.04269 +G1 X120.963 Y103.459 E.00858 +G1 X120.597 Y103.288 E.01334 +G1 X120.597 Y100.597 E.08885 +G1 X122.572 Y100.597 E.06521 +G1 X122.72 Y100.94 E.01233 +G1 X122.862 Y101.038 E.0057 +G1 X123.115 Y101.097 E.00858 +G1 X124.177 Y101.097 E.03506 +G1 X124.427 Y101.04 E.00847 +G1 X124.666 Y100.825 E.01061 +G1 X124.731 Y100.597 E.00783 +G1 X125.419 Y100.597 E.02272 +G1 X125.484 Y100.825 E.00783 +G1 X125.723 Y101.04 E.01061 +G1 X125.973 Y101.097 E.00847 +G1 X126.967 Y101.097 E.03282 +G1 X127.191 Y101.052 E.00754 +G1 X127.361 Y100.941 E.0067 +G1 X127.511 Y100.597 E.01239 +G1 X129.403 Y100.597 E.06247 +G1 X129.403 Y102.222 E.05365 +G1 X129.084 Y102.349 E.01134 +G1 X128.927 Y102.604 E.00989 +G1 X128.903 Y103.033 E.01419 +G1 X128.923 Y103.989 E.03157 +G1 X129.038 Y104.185 E.0075 +G1 X129.038 Y104.185 F9000 +G1 X129.36 Y103.962 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.479 Y104.029 E.00428 +G1 X129.79 Y104.029 E.00975 +G1 X129.79 Y106.01 E.06211 +G1 X129.479 Y106.01 E.00975 +G1 X129.347 Y106.063 E.00446 +G1 X129.29 Y106.198 E.00459 +G1 X129.29 Y107.205 E.03157 +G1 X129.349 Y107.341 E.00465 +G1 X129.479 Y107.393 E.00439 +G1 X129.79 Y107.393 E.00975 +G1 X129.79 Y107.705 E.00978 +G1 X129.79 Y109.79 E.06537 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y106.36 E.10755 +G1 X120.521 Y106.36 E.00975 +G1 X120.67 Y106.288 E.00519 +G1 X120.71 Y106.171 E.00388 +G1 X120.71 Y104.984 E.03722 +G1 X120.692 Y103.748 E.03876 +G1 X120.521 Y103.64 E.00634 +G1 X120.21 Y103.64 E.00975 +G1 X120.21 Y100.21 E.10755 +G1 X122.927 Y100.21 E.08519 +G1 X122.927 Y100.521 E.00975 +G1 X122.986 Y100.659 E.00471 +G1 X123.115 Y100.71 E.00435 +G1 X124.214 Y100.706 E.03446 +G1 X124.337 Y100.621 E.00469 +G1 X124.365 Y100.21 E.01292 +G1 X125.785 Y100.21 E.04452 +G1 X125.785 Y100.521 E.00975 +G1 X125.861 Y100.673 E.00533 +G1 X125.973 Y100.71 E.0037 +G1 X126.967 Y100.71 E.03117 +G1 X127.096 Y100.659 E.00435 +G1 X127.155 Y100.521 E.00471 +G1 X127.155 Y100.21 E.00975 +G1 X129.79 Y100.21 E.08262 +G1 X129.79 Y102.579 E.07428 +G1 X129.479 Y102.579 E.00975 +G1 X129.349 Y102.631 E.00439 +G1 X129.29 Y102.768 E.00468 +G1 X129.29 Y103.84 E.03361 +G1 X129.314 Y103.927 E.00283 +G1 X129.314 Y103.927 F9000 +G1 X128.201 Y103.51 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.252 Y102.402 E.03662 +G1 X128.451 Y102.005 E.01466 +G1 X128.7 Y101.79 E.01086 +G1 X128.7 Y101.3 E.01618 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X122.163 Y101.3 E.02849 +G1 X122.225 Y101.444 E.00518 +G1 X122.551 Y101.67 E.0131 +G1 X122.856 Y101.774 E.01064 +G1 X123.498 Y101.8 E.02121 +;LAYER_CHANGE +;Z:7.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;7.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X122.856 Y101.774 E-.3052 +G1 X122.551 Y101.67 E-.15307 +G1 X122.225 Y101.444 E-.18842 +G1 X122.163 Y101.3 E-.07447 +G1 X121.3 Y101.3 E-.40992 +G1 X121.849 Y101.849 E-.36892 +;WIPE_END +G1 Z7.4 F9000 +;AFTER_LAYER_CHANGE +;7.4 +G1 Z7.4 +G1 X122.277 Y101.185 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X122.289 Y101.214 E.00104 +G1 X122.655 Y101.441 E.01422 +G1 X122.973 Y101.494 E.01064 +G1 X124.029 Y101.494 E.03487 +G1 X124.258 Y101.468 E.00761 +G1 X124.536 Y101.355 E.00991 +G1 X124.793 Y101.132 E.01123 +G1 X124.841 Y100.994 E.00482 +G1 X125.319 Y100.994 E.01578 +G1 X125.367 Y101.132 E.00482 +G1 X125.624 Y101.355 E.01123 +G1 X125.767 Y101.426 E.00527 +G1 X126.125 Y101.494 E.01203 +G1 X127.116 Y101.494 E.03272 +G1 X127.504 Y101.413 E.01309 +G1 X127.781 Y101.231 E.01094 +G1 X127.901 Y100.994 E.00877 +G1 X129.006 Y100.994 E.03648 +G1 X129.006 Y101.842 E.028 +G1 X128.831 Y101.908 E.00618 +G1 X128.738 Y102.004 E.00441 +G1 X128.549 Y102.348 E.01296 +G1 X128.506 Y102.634 E.00955 +G1 X128.506 Y103.709 E.03549 +G1 X128.563 Y104.038 E.01102 +G1 X128.753 Y104.357 E.01226 +G1 X129.006 Y104.471 E.00916 +G1 X129.006 Y105.569 E.03625 +G1 X128.753 Y105.682 E.00915 +G1 X128.563 Y106.002 E.01229 +G1 X128.506 Y106.33 E.01099 +G1 X128.506 Y107.343 E.03345 +G1 X128.568 Y107.686 E.01151 +G1 X128.828 Y108.066 E.0152 +G1 X129.006 Y108.134 E.00629 +G1 X129.006 Y109.006 E.02879 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.708 E.07587 +G1 X121.3 Y106.555 E.0113 +G1 X121.419 Y106.345 E.00797 +G1 X121.494 Y105.971 E.01259 +G1 X121.494 Y104.035 E.06392 +G1 X121.435 Y103.694 E.01143 +G1 X121.275 Y103.414 E.01065 +G1 X120.994 Y103.28 E.01028 +G1 X120.994 Y100.994 E.07548 +G1 X122.198 Y100.994 E.03975 +G1 X122.254 Y101.13 E.00486 +G1 X122.254 Y101.13 F9000 +G1 X122.56 Y100.912 +G1 F900 +G1 X122.568 Y100.931 E.00068 +G1 X122.785 Y101.065 E.00842 +G1 X122.973 Y101.097 E.0063 +G1 X124.029 Y101.097 E.03487 +G1 X124.247 Y101.057 E.00732 +G1 X124.484 Y100.883 E.00971 +G1 X124.584 Y100.597 E.01 +G1 X125.576 Y100.597 E.03275 +G1 X125.676 Y100.883 E.01 +G1 X125.913 Y101.057 E.00971 +G1 X126.125 Y101.097 E.00712 +G1 X127.116 Y101.097 E.03272 +G1 X127.345 Y101.049 E.00773 +G1 X127.51 Y100.941 E.00651 +G1 X127.661 Y100.597 E.0124 +G1 X129.403 Y100.597 E.05752 +G1 X129.403 Y102.087 E.0492 +G1 X129.095 Y102.205 E.01089 +G1 X128.928 Y102.465 E.0102 +G1 X128.903 Y102.884 E.01386 +G1 X128.925 Y103.868 E.0325 +G1 X129.049 Y104.093 E.00848 +G1 X129.403 Y104.251 E.0128 +G1 X129.403 Y105.788 E.05075 +G1 X129.049 Y105.947 E.01281 +G1 X128.953 Y106.095 E.00582 +G1 X128.903 Y106.33 E.00793 +G1 X128.903 Y107.343 E.03345 +G1 X128.94 Y107.546 E.00681 +G1 X129.093 Y107.771 E.00898 +G1 X129.403 Y107.89 E.01096 +G1 X129.403 Y109.403 E.04996 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.509 E.09555 +G1 X120.982 Y106.317 E.0142 +G1 X121.053 Y106.193 E.00472 +G1 X121.097 Y105.971 E.00747 +G1 X121.097 Y104.035 E.06392 +G1 X121.062 Y103.831 E.00683 +G1 X120.968 Y103.665 E.0063 +G1 X120.597 Y103.489 E.01356 +G1 X120.597 Y100.597 E.09549 +G1 X122.429 Y100.597 E.06049 +G1 X122.537 Y100.856 E.00927 +G1 X122.537 Y100.856 F9000 +G1 X122.837 Y100.649 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X122.84 Y100.656 E.00024 +G1 X122.973 Y100.71 E.0045 +G1 X124.029 Y100.71 E.03311 +G1 X124.132 Y100.683 E.00334 +G1 X124.224 Y100.521 E.00584 +G1 X124.224 Y100.21 E.00975 +G1 X125.936 Y100.21 E.05368 +G1 X125.936 Y100.521 E.00975 +G1 X125.978 Y100.64 E.00396 +G1 X126.125 Y100.71 E.0051 +G1 X127.116 Y100.71 E.03107 +G1 X127.245 Y100.659 E.00435 +G1 X127.304 Y100.521 E.00471 +G1 X127.304 Y100.21 E.00975 +G1 X129.79 Y100.21 E.07795 +G1 X129.79 Y102.446 E.07011 +G1 X129.479 Y102.446 E.00975 +G1 X129.353 Y102.494 E.00423 +G1 X129.298 Y102.579 E.00317 +G1 X129.29 Y102.884 E.00957 +G1 X129.306 Y103.787 E.02832 +G1 X129.479 Y103.898 E.00644 +G1 X129.79 Y103.898 E.00975 +G1 X129.79 Y106.142 E.07036 +G1 X129.479 Y106.142 E.00975 +G1 X129.338 Y106.204 E.00483 +G1 X129.29 Y106.33 E.00423 +G1 X129.29 Y107.343 E.03176 +G1 X129.352 Y107.483 E.0048 +G1 X129.479 Y107.532 E.00427 +G1 X129.79 Y107.532 E.00975 +G1 X129.79 Y107.843 E.00975 +G1 X129.79 Y109.79 E.06105 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y106.16 E.11382 +G1 X120.521 Y106.16 E.00975 +G1 X120.672 Y106.084 E.0053 +G1 X120.71 Y105.971 E.00374 +G1 X120.71 Y104.959 E.03173 +G1 X120.687 Y103.938 E.03202 +G1 X120.521 Y103.84 E.00604 +G1 X120.21 Y103.84 E.00975 +G1 X120.21 Y100.21 E.11382 +G1 X122.784 Y100.21 E.08071 +G1 X122.784 Y100.521 E.00975 +G1 X122.814 Y100.593 E.00245 +G1 X122.814 Y100.593 F9000 +G1 X123.504 Y101.794 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X122.78 Y101.786 E.02391 +G1 X122.548 Y101.728 E.0079 +G1 X122.064 Y101.428 E.0188 +G1 X122.011 Y101.3 E.00457 +G1 X121.3 Y101.3 E.02348 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.094 Y101.3 E.02001 +G1 X127.985 Y101.459 E.00636 +G1 X127.628 Y101.694 E.01411 +G1 X127.263 Y101.792 E.01248 +G1 X126.489 Y101.8 E.02556 +;LAYER_CHANGE +;Z:7.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;7.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.263 Y101.792 E-.36767 +G1 X127.628 Y101.694 E-.17952 +G1 X127.985 Y101.459 E-.20302 +G1 X128.094 Y101.3 E-.09157 +G1 X128.7 Y101.3 E-.28785 +G1 X128.149 Y101.851 E-.37037 +;WIPE_END +G1 Z7.6 F9000 +;AFTER_LAYER_CHANGE +;7.6 +G1 Z7.6 +G1 X125.599 Y101.214 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X125.894 Y101.416 E.0118 +G1 X126.276 Y101.494 E.01287 +G1 X127.265 Y101.494 E.03265 +G1 X127.664 Y101.408 E.01348 +G1 X127.931 Y101.23 E.0106 +G1 X128.058 Y100.994 E.00885 +G1 X129.006 Y100.994 E.0313 +G1 X129.006 Y101.7 E.02331 +G1 X128.852 Y101.756 E.00541 +G1 X128.756 Y101.849 E.00441 +G1 X128.552 Y102.203 E.01349 +G1 X128.506 Y102.5 E.00992 +G1 X128.506 Y103.579 E.03563 +G1 X128.551 Y103.87 E.00972 +G1 X128.715 Y104.181 E.01161 +G1 X129.006 Y104.322 E.01068 +G1 X129.006 Y105.718 E.04609 +G1 X128.715 Y105.86 E.01069 +G1 X128.571 Y106.111 E.00955 +G1 X128.506 Y106.462 E.01179 +G1 X128.506 Y107.482 E.03368 +G1 X128.572 Y107.835 E.01186 +G1 X128.849 Y108.223 E.01574 +G1 X129.006 Y108.281 E.00553 +G1 X129.006 Y109.006 E.02394 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.502 E.08268 +G1 X121.311 Y106.34 E.01175 +G1 X121.424 Y106.135 E.00773 +G1 X121.494 Y105.771 E.01224 +G1 X121.494 Y104.229 E.05091 +G1 X121.394 Y103.8 E.01454 +G1 X121.285 Y103.625 E.00681 +G1 X120.994 Y103.485 E.01066 +G1 X120.994 Y100.994 E.08225 +G1 X122.048 Y100.994 E.0348 +G1 X122.13 Y101.196 E.0072 +G1 X122.625 Y101.472 E.01871 +G1 X122.83 Y101.494 E.00681 +G1 X123.894 Y101.494 E.03513 +G1 X124.276 Y101.416 E.01287 +G1 X124.588 Y101.203 E.01247 +G1 X124.673 Y100.994 E.00745 +G1 X125.497 Y100.994 E.02721 +G1 X125.567 Y101.166 E.00613 +G1 X125.567 Y101.166 F9000 +G1 X125.877 Y100.932 +G1 F900 +G1 X126.05 Y101.051 E.00693 +G1 X126.276 Y101.097 E.00761 +G1 X127.265 Y101.097 E.03265 +G1 X127.501 Y101.046 E.00797 +G1 X127.659 Y100.941 E.00626 +G1 X127.812 Y100.597 E.01243 +G1 X129.403 Y100.597 E.05253 +G1 X129.403 Y101.952 E.04474 +G1 X129.108 Y102.06 E.01037 +G1 X128.931 Y102.324 E.01049 +G1 X128.903 Y102.735 E.0136 +G1 X128.914 Y103.692 E.0316 +G1 X129.027 Y103.935 E.00885 +G1 X129.403 Y104.118 E.01381 +G1 X129.403 Y105.923 E.0596 +G1 X129.027 Y106.105 E.01379 +G1 X128.982 Y106.171 E.00264 +G1 X128.903 Y106.462 E.00996 +G1 X128.903 Y107.482 E.03368 +G1 X128.942 Y107.691 E.00702 +G1 X129.106 Y107.921 E.00933 +G1 X129.403 Y108.03 E.01045 +G1 X129.403 Y109.403 E.04533 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.308 E.10219 +G1 X120.989 Y106.108 E.01453 +G1 X121.055 Y105.987 E.00455 +G1 X121.097 Y105.771 E.00727 +G1 X121.097 Y104.927 E.02787 +G1 X121.095 Y104.174 E.02486 +G1 X120.973 Y103.872 E.01075 +G1 X120.597 Y103.69 E.01379 +G1 X120.597 Y100.597 E.10212 +G1 X122.285 Y100.597 E.05573 +G1 X122.416 Y100.921 E.01154 +G1 X122.709 Y101.084 E.01107 +G1 X122.83 Y101.097 E.00402 +G1 X123.894 Y101.097 E.03513 +G1 X124.044 Y101.077 E.005 +G1 X124.305 Y100.925 E.00997 +G1 X124.439 Y100.597 E.0117 +G1 X125.732 Y100.597 E.04269 +G1 X125.848 Y100.882 E.01016 +G1 X125.848 Y100.882 F9000 +G1 X126.149 Y100.657 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X126.276 Y100.71 E.00431 +G1 X127.265 Y100.71 E.03101 +G1 X127.394 Y100.659 E.00435 +G1 X127.453 Y100.521 E.00471 +G1 X127.453 Y100.21 E.00975 +G1 X129.79 Y100.21 E.07327 +G1 X129.79 Y102.312 E.06591 +G1 X129.479 Y102.312 E.00975 +G1 X129.357 Y102.356 E.00407 +G1 X129.299 Y102.443 E.00328 +G1 X129.29 Y102.735 E.00916 +G1 X129.308 Y103.658 E.02895 +G1 X129.479 Y103.767 E.00636 +G1 X129.79 Y103.767 E.00975 +G1 X129.79 Y106.273 E.07857 +G1 X129.479 Y106.273 E.00975 +G1 X129.33 Y106.345 E.00519 +G1 X129.29 Y106.462 E.00388 +G1 X129.29 Y107.482 E.03198 +G1 X129.357 Y107.626 E.00498 +G1 X129.479 Y107.67 E.00407 +G1 X129.79 Y107.67 E.00975 +G1 X129.79 Y107.982 E.00978 +G1 X129.79 Y109.79 E.05669 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.96 E.12009 +G1 X120.521 Y105.96 E.00975 +G1 X120.675 Y105.882 E.00541 +G1 X120.71 Y105.771 E.00365 +G1 X120.71 Y104.927 E.02646 +G1 X120.691 Y104.145 E.02453 +G1 X120.521 Y104.04 E.00626 +G1 X120.21 Y104.04 E.00975 +G1 X120.21 Y100.21 E.12009 +G1 X122.642 Y100.21 E.07625 +G1 X122.642 Y100.521 E.00975 +G1 X122.694 Y100.652 E.00442 +G1 X122.83 Y100.71 E.00464 +G1 X123.894 Y100.71 E.03336 +G1 X123.991 Y100.683 E.00316 +G1 X124.082 Y100.521 E.00583 +G1 X124.082 Y100.21 E.00975 +G1 X126.088 Y100.21 E.0629 +G1 X126.088 Y100.521 E.00975 +G1 X126.122 Y100.605 E.00284 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.149 Y100.657 E-.02783 +G1 X126.276 Y100.71 E-.06537 +G1 X127.265 Y100.71 E-.46978 +G1 X127.394 Y100.659 E-.06589 +G1 X127.453 Y100.521 E-.07129 +G1 X127.453 Y100.21 E-.14772 +G1 X128.826 Y100.21 E-.65212 +;WIPE_END +G1 X128.089 Y101.489 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X128.255 Y101.3 E.00831 +G1 X128.7 Y101.3 E.01469 +G1 X121.3 Y108.7 E.34553 +G1 X128.7 Y108.7 E.24433 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.315 E.06653 +G1 X121.515 Y103.419 E.00789 +G1 X121.583 Y103.528 E.00424 +;LAYER_CHANGE +;Z:7.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;7.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.515 Y103.419 E-.06102 +G1 X121.3 Y103.315 E-.11345 +G1 X121.3 Y101.3 E-.95713 +G1 X121.848 Y101.848 E-.3684 +;WIPE_END +G1 Z7.8 F9000 +;AFTER_LAYER_CHANGE +;7.8 +G1 Z7.8 +G1 X121.16 Y103.772 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y103.69 E.00611 +G1 X120.994 Y100.994 E.08901 +G1 X121.898 Y100.994 E.02985 +G1 X121.969 Y101.177 E.00648 +G1 X122.292 Y101.41 E.01315 +G1 X122.688 Y101.494 E.01337 +G1 X123.752 Y101.494 E.03513 +G1 X124.086 Y101.435 E.0112 +G1 X124.39 Y101.256 E.01165 +G1 X124.509 Y100.994 E.0095 +G1 X125.671 Y100.994 E.03837 +G1 X125.79 Y101.256 E.0095 +G1 X126.094 Y101.435 E.01165 +G1 X126.428 Y101.494 E.0112 +G1 X127.414 Y101.494 E.03256 +G1 X127.825 Y101.403 E.0139 +G1 X128.162 Y101.143 E.01405 +G1 X128.216 Y100.994 E.00523 +G1 X129.006 Y100.994 E.02608 +G1 X129.006 Y101.558 E.01862 +G1 X128.876 Y101.603 E.00454 +G1 X128.777 Y101.693 E.00442 +G1 X128.557 Y102.057 E.01404 +G1 X128.506 Y102.586 E.01755 +G1 X128.528 Y103.655 E.0353 +G1 X128.684 Y104.01 E.0128 +G1 X129.006 Y104.176 E.01196 +G1 X129.006 Y105.866 E.0558 +G1 X128.684 Y106.032 E.01196 +G1 X128.579 Y106.223 E.0072 +G1 X128.506 Y106.594 E.01248 +G1 X128.506 Y107.62 E.03388 +G1 X128.577 Y107.985 E.01228 +G1 X128.874 Y108.382 E.01637 +G1 X129.006 Y108.428 E.00462 +G1 X129.006 Y109.006 E.01908 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.297 E.08944 +G1 X121.321 Y106.126 E.01218 +G1 X121.427 Y105.926 E.00747 +G1 X121.494 Y105.571 E.01193 +G1 X121.494 Y104.89 E.02248 +G1 X121.477 Y104.246 E.02127 +G1 X121.295 Y103.838 E.01475 +G1 X121.213 Y103.798 E.00301 +G1 X121.213 Y103.798 F9000 +G1 X120.888 Y104.055 +G1 F900 +G1 X120.597 Y103.89 E.01105 +G1 X120.597 Y100.597 E.10873 +G1 X122.141 Y100.597 E.05098 +G1 X122.263 Y100.909 E.01106 +G1 X122.453 Y101.047 E.00775 +G1 X122.688 Y101.097 E.00793 +G1 X123.86 Y101.087 E.0387 +G1 X124.13 Y100.956 E.00991 +G1 X124.293 Y100.597 E.01302 +G1 X125.887 Y100.597 E.05263 +G1 X126.051 Y100.956 E.01303 +G1 X126.191 Y101.046 E.0055 +G1 X126.428 Y101.097 E.008 +G1 X127.414 Y101.097 E.03256 +G1 X127.657 Y101.043 E.00822 +G1 X127.808 Y100.941 E.00602 +G1 X127.962 Y100.597 E.01244 +G1 X129.403 Y100.597 E.04758 +G1 X129.403 Y101.817 E.04028 +G1 X129.063 Y101.968 E.01228 +G1 X128.933 Y102.183 E.0083 +G1 X128.903 Y102.586 E.01334 +G1 X128.916 Y103.57 E.03249 +G1 X129.009 Y103.78 E.00758 +G1 X129.403 Y103.984 E.01465 +G1 X129.403 Y106.057 E.06844 +G1 X129.009 Y106.261 E.01465 +G1 X128.979 Y106.308 E.00184 +G1 X128.903 Y106.594 E.00977 +G1 X128.903 Y107.62 E.03388 +G1 X128.945 Y107.836 E.00727 +G1 X129.121 Y108.071 E.00969 +G1 X129.403 Y108.169 E.00986 +G1 X129.403 Y109.403 E.04074 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y106.107 E.10882 +G1 X120.995 Y105.899 E.01483 +G1 X121.057 Y105.781 E.0044 +G1 X121.097 Y105.571 E.00706 +G1 X121.097 Y104.429 E.03771 +G1 X121.01 Y104.124 E.01047 +G1 X120.94 Y104.084 E.00266 +G1 X120.94 Y104.084 F9000 +G1 X120.639 Y104.305 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.521 Y104.24 E.00422 +G1 X120.21 Y104.24 E.00975 +G1 X120.21 Y100.21 E.12636 +G1 X122.499 Y100.21 E.07177 +G1 X122.499 Y100.521 E.00975 +G1 X122.549 Y100.649 E.00431 +G1 X122.688 Y100.71 E.00476 +G1 X123.752 Y100.71 E.03336 +G1 X123.83 Y100.693 E.0025 +G1 X123.941 Y100.521 E.00642 +G1 X123.941 Y100.21 E.00975 +G1 X126.24 Y100.21 E.07208 +G1 X126.24 Y100.521 E.00975 +G1 X126.304 Y100.664 E.00491 +G1 X126.428 Y100.71 E.00415 +G1 X127.414 Y100.71 E.03092 +G1 X127.543 Y100.659 E.00435 +G1 X127.602 Y100.521 E.00471 +G1 X127.602 Y100.21 E.00975 +G1 X129.79 Y100.21 E.0686 +G1 X129.79 Y102.178 E.06171 +G1 X129.479 Y102.178 E.00975 +G1 X129.362 Y102.219 E.00389 +G1 X129.3 Y102.307 E.00338 +G1 X129.29 Y102.586 E.00875 +G1 X129.301 Y103.512 E.02904 +G1 X129.479 Y103.636 E.0068 +G1 X129.79 Y103.636 E.00975 +G1 X129.79 Y106.405 E.08682 +G1 X129.479 Y106.405 E.00975 +G1 X129.325 Y106.485 E.00544 +G1 X129.29 Y106.594 E.00359 +G1 X129.29 Y107.62 E.03217 +G1 X129.361 Y107.768 E.00515 +G1 X129.479 Y107.809 E.00392 +G1 X129.79 Y107.809 E.00975 +G1 X129.79 Y108.12 E.00975 +G1 X129.79 Y109.79 E.05236 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.76 E.12636 +G1 X120.521 Y105.76 E.00975 +G1 X120.676 Y105.679 E.00548 +G1 X120.71 Y105.571 E.00355 +G1 X120.71 Y104.89 E.02135 +G1 X120.682 Y104.34 E.01727 +G1 X120.682 Y104.34 F9000 +G1 X121.443 Y103.594 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.3 Y103.523 E.00527 +G1 X121.3 Y101.3 E.0734 +G1 X128.7 Y108.7 E.34553 +G1 X121.3 Y108.7 E.24433 +G1 X128.7 Y101.3 E.34553 +G1 X128.418 Y101.3 E.00931 +G1 X128.276 Y101.464 E.00716 +;LAYER_CHANGE +;Z:8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.418 Y101.3 E-.10304 +G1 X128.7 Y101.3 E-.13395 +G1 X126.82 Y103.18 E-1.26301 +;WIPE_END +G1 Z8 F9000 +;AFTER_LAYER_CHANGE +;8 +G1 Z8 +G1 X128.294 Y101.246 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.558149 +G1 F900 +G1 X128.378 Y101.152 E.0054 +;WIDTH:0.556481 +G1 X128.412 Y101.052 E.00451 +G1 X128.948 Y101.052 E.02289 +G1 X128.948 Y101.376 E.01384 +G1 X128.869 Y101.402 E.00355 +;WIDTH:0.558149 +G1 X128.652 Y101.615 E.01303 +G1 X128.622 Y101.713 E.00439 +;WIDTH:0.518766 +G1 X128.592 Y101.811 E.00406 +;WIDTH:0.479383 +G1 X128.561 Y101.91 E.00376 +;WIDTH:0.439999 +G1 X128.506 Y102.437 E.01749 +G1 X128.516 Y103.458 E.03371 +G1 X128.605 Y103.744 E.00989 +G1 X128.838 Y104.049 E.01267 +G1 X129.006 Y104.112 E.00592 +G1 X129.006 Y105.93 E.06003 +G1 X128.838 Y105.993 E.00592 +G1 X128.605 Y106.298 E.01267 +G1 X128.506 Y106.725 E.01447 +G1 X128.506 Y107.759 E.03414 +G1 X128.582 Y108.136 E.0127 +G1 X128.901 Y108.542 E.01705 +G1 X129.006 Y108.576 E.00364 +G1 X129.006 Y109.006 E.0142 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y106.092 E.09621 +G1 X121.33 Y105.912 E.01259 +G1 X121.48 Y105.539 E.01327 +G1 X121.494 Y104.913 E.02067 +G1 X121.456 Y104.359 E.01833 +G1 X121.306 Y104.053 E.01125 +G1 X120.994 Y103.895 E.01155 +G1 X120.994 Y100.994 E.09578 +G1 X121.748 Y100.994 E.0249 +G1 X121.808 Y101.155 E.00567 +G1 X122.194 Y101.429 E.01563 +G1 X122.545 Y101.494 E.01179 +G1 X123.611 Y101.494 E.0352 +G1 X123.881 Y101.456 E.009 +G1 X124.199 Y101.296 E.01175 +G1 X124.349 Y100.994 E.01113 +G1 X125.842 Y100.994 E.04929 +G1 X125.991 Y101.296 E.01112 +G1 X126.277 Y101.446 E.01066 +G1 X126.683 Y101.494 E.0135 +G1 X127.672 Y101.488 E.03265 +;WIDTH:0.479383 +G1 X127.871 Y101.416 E.00768 +;WIDTH:0.518766 +G1 X128.07 Y101.345 E.00836 +;WIDTH:0.558149 +G1 X128.247 Y101.281 E.00806 +G1 X128.247 Y101.281 F9000 +G1 X127.959 Y100.921 +;WIDTH:0.439999 +G1 F900 +G1 X128.018 Y100.874 E.00249 +G1 X128.113 Y100.597 E.00967 +G1 X129.403 Y100.597 E.04259 +G1 X129.403 Y101.682 E.03582 +G1 X129.078 Y101.82 E.01166 +G1 X128.936 Y102.042 E.0087 +G1 X128.903 Y102.233 E.0064 +G1 X128.903 Y103.317 E.03579 +G1 X128.938 Y103.516 E.00667 +G1 X129.099 Y103.75 E.00938 +G1 X129.403 Y103.864 E.01072 +G1 X129.403 Y106.178 E.0764 +G1 X129.099 Y106.292 E.01072 +G1 X128.961 Y106.473 E.00751 +G1 X128.903 Y106.725 E.00854 +G1 X128.903 Y107.759 E.03414 +G1 X128.948 Y107.982 E.00751 +G1 X129.137 Y108.222 E.01009 +G1 X129.403 Y108.309 E.00924 +G1 X129.403 Y109.403 E.03612 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y105.907 E.11543 +G1 X121 Y105.692 E.01508 +G1 X121.059 Y105.578 E.00424 +G1 X121.097 Y105.371 E.00695 +G1 X121.093 Y104.559 E.02681 +G1 X120.986 Y104.288 E.00962 +G1 X120.597 Y104.091 E.0144 +G1 X120.597 Y100.597 E.11536 +G1 X121.998 Y100.597 E.04626 +G1 X122.109 Y100.897 E.01056 +G1 X122.338 Y101.058 E.00924 +G1 X122.545 Y101.097 E.00695 +G1 X123.703 Y101.09 E.03823 +G1 X123.959 Y100.98 E.0092 +G1 X124.149 Y100.597 E.01412 +G1 X126.042 Y100.597 E.0625 +G1 X126.232 Y100.98 E.01412 +G1 X126.334 Y101.042 E.00394 +G1 X126.58 Y101.097 E.00832 +G1 X127.563 Y101.097 E.03246 +G1 X127.814 Y101.039 E.00851 +G1 X127.913 Y100.959 E.0042 +G1 X127.913 Y100.959 F9000 +G1 X127.697 Y100.648 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X127.751 Y100.521 E.00433 +G1 X127.751 Y100.21 E.00975 +G1 X129.79 Y100.21 E.06393 +G1 X129.79 Y102.044 E.0575 +G1 X129.479 Y102.044 E.00975 +G1 X129.367 Y102.081 E.0037 +G1 X129.29 Y102.233 E.00534 +G1 X129.29 Y103.317 E.03399 +G1 X129.32 Y103.419 E.00333 +G1 X129.479 Y103.506 E.00568 +G1 X129.79 Y103.506 E.00975 +G1 X129.79 Y106.537 E.09503 +G1 X129.479 Y106.537 E.00975 +G1 X129.354 Y106.584 E.00419 +G1 X129.29 Y106.725 E.00486 +G1 X129.29 Y107.759 E.03242 +G1 X129.367 Y107.91 E.00531 +G1 X129.479 Y107.947 E.0037 +G1 X129.79 Y107.947 E.00975 +G1 X129.79 Y108.259 E.00978 +G1 X129.79 Y109.79 E.048 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.56 E.13263 +G1 X120.521 Y105.56 E.00975 +G1 X120.678 Y105.476 E.00558 +G1 X120.71 Y105.373 E.00338 +G1 X120.71 Y104.629 E.02333 +G1 X120.673 Y104.517 E.0037 +G1 X120.521 Y104.44 E.00534 +G1 X120.21 Y104.44 E.00975 +G1 X120.21 Y100.21 E.13263 +G1 X122.357 Y100.21 E.06732 +G1 X122.357 Y100.521 E.00975 +G1 X122.402 Y100.644 E.00411 +G1 X122.545 Y100.71 E.00494 +G1 X123.611 Y100.71 E.03342 +G1 X123.68 Y100.697 E.0022 +G1 X123.799 Y100.521 E.00666 +G1 X123.799 Y100.21 E.00975 +G1 X126.391 Y100.21 E.08127 +G1 X126.391 Y100.521 E.00975 +G1 X126.466 Y100.672 E.00529 +G1 X126.58 Y100.71 E.00377 +G1 X127.563 Y100.71 E.03082 +G1 X127.647 Y100.677 E.00283 +G1 X127.647 Y100.677 F9000 +G1 X126.065 Y101.686 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X126.575 Y101.8 E.01725 +G1 X127.71 Y101.792 E.03748 +G1 X128.31 Y101.614 E.02066 +G1 X128.293 Y101.707 E.00312 +G1 X121.3 Y108.7 E.32653 +G1 X128.646 Y108.7 E.24255 +G1 X121.3 Y101.3 E.34427 +G1 X121.3 Y103.682 E.07865 +;LAYER_CHANGE +;Z:8.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;8.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y101.3 E-1.13145 +G1 X121.847 Y101.851 E-.36855 +;WIPE_END +G1 Z8.2 F9000 +;AFTER_LAYER_CHANGE +;8.2 +G1 Z8.2 +G1 X121.152 Y104.182 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y104.101 E.00586 +G1 X120.994 Y100.994 E.10258 +G1 X121.597 Y100.994 E.01991 +G1 X121.645 Y101.131 E.00479 +G1 X122.04 Y101.424 E.01624 +G1 X122.403 Y101.494 E.01221 +G1 X123.678 Y101.472 E.0421 +G1 X124.014 Y101.327 E.01208 +G1 X124.191 Y100.994 E.01245 +G1 X126.01 Y100.994 E.06006 +G1 X126.186 Y101.327 E.01244 +G1 X126.385 Y101.43 E.0074 +G1 X126.731 Y101.494 E.01162 +G1 X127.712 Y101.494 E.03239 +;WIDTH:0.441764 +G1 X128.152 Y101.39 E.01499 +;WIDTH:0.457639 +G1 X128.384 Y101.237 E.00958 +G1 X128.498 Y101.109 E.00591 +;WIDTH:0.460292 +G1 X128.54 Y101.004 E.00392 +G1 X128.996 Y101.004 E.01583 +G1 X128.996 Y101.264 E.00902 +G1 X128.928 Y101.285 E.00247 +G1 X128.692 Y101.512 E.01136 +;WIDTH:0.457639 +G1 X128.566 Y101.76 E.00959 +;WIDTH:0.441764 +G1 X128.506 Y102.099 E.01142 +;WIDTH:0.439999 +G1 X128.506 Y103.186 E.03589 +G1 X128.574 Y103.543 E.012 +G1 X128.807 Y103.89 E.0138 +G1 X129.006 Y103.97 E.00708 +G1 X129.006 Y106.074 E.06947 +G1 X128.807 Y106.153 E.00707 +G1 X128.584 Y106.475 E.01293 +G1 X128.506 Y106.857 E.01287 +G1 X128.506 Y107.897 E.03434 +G1 X128.588 Y108.288 E.01319 +G1 X128.933 Y108.702 E.01779 +G1 X129.006 Y108.725 E.00253 +G1 X129.006 Y109.006 E.00928 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y105.887 E.10298 +G1 X121.338 Y105.7 E.01293 +G1 X121.455 Y105.443 E.00932 +G1 X121.494 Y104.888 E.01837 +G1 X121.428 Y104.476 E.01378 +G1 X121.316 Y104.268 E.0078 +G1 X121.205 Y104.21 E.00414 +G1 X120.893 Y104.446 F9000 +G1 F900 +G1 X120.597 Y104.292 E.01102 +G1 X120.597 Y100.597 E.122 +G1 X121.854 Y100.597 E.0415 +G1 X121.954 Y100.882 E.00997 +G1 X122.188 Y101.056 E.00963 +G1 X122.403 Y101.097 E.00723 +G1 X123.593 Y101.084 E.03929 +G1 X123.792 Y100.998 E.00716 +G1 X124.005 Y100.597 E.01499 +G1 X126.196 Y100.597 E.07234 +G1 X126.409 Y100.998 E.01499 +G1 X126.479 Y101.039 E.00268 +G1 X126.731 Y101.097 E.00854 +G1 X127.712 Y101.097 E.03239 +G1 X127.972 Y101.035 E.00883 +G1 X128.173 Y100.866 E.00867 +G1 X128.263 Y100.597 E.00937 +G1 X129.403 Y100.597 E.03764 +G1 X129.403 Y101.547 E.03137 +G1 X129.094 Y101.671 E.01099 +G1 X128.939 Y101.899 E.0091 +G1 X128.903 Y102.099 E.00671 +G1 X128.903 Y103.186 E.03589 +G1 X128.932 Y103.367 E.00605 +G1 X129.081 Y103.603 E.00922 +G1 X129.403 Y103.732 E.01145 +G1 X129.403 Y106.312 E.08518 +G1 X129.081 Y106.441 E.01145 +G1 X128.963 Y106.602 E.00659 +G1 X128.903 Y106.857 E.00865 +G1 X128.903 Y107.897 E.03434 +G1 X128.951 Y108.128 E.00779 +G1 X129.156 Y108.374 E.01057 +G1 X129.403 Y108.45 E.00853 +G1 X129.403 Y109.403 E.03147 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y105.706 E.12206 +G1 X121.005 Y105.484 E.01534 +G1 X121.074 Y105.332 E.00551 +G1 X121.09 Y104.74 E.01955 +G1 X120.992 Y104.497 E.00865 +G1 X120.947 Y104.473 E.00168 +G1 X120.947 Y104.473 F9000 +G1 X120.633 Y104.716 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.521 Y104.64 E.00424 +G1 X120.21 Y104.64 E.00975 +G1 X120.21 Y100.21 E.1389 +G1 X122.214 Y100.21 E.06283 +G1 X122.214 Y100.521 E.00975 +G1 X122.256 Y100.64 E.00396 +G1 X122.333 Y100.696 E.00299 +G1 X122.753 Y100.71 E.01318 +G1 X123.536 Y100.698 E.02455 +G1 X123.658 Y100.521 E.00674 +G1 X123.658 Y100.21 E.00975 +G1 X126.543 Y100.21 E.09046 +G1 X126.543 Y100.521 E.00975 +G1 X126.626 Y100.678 E.00557 +G1 X126.731 Y100.71 E.00344 +G1 X127.712 Y100.71 E.03076 +G1 X127.841 Y100.659 E.00435 +G1 X127.9 Y100.521 E.00471 +G1 X127.9 Y100.21 E.00975 +G1 X129.79 Y100.21 E.05926 +G1 X129.79 Y101.911 E.05333 +G1 X129.373 Y101.943 E.01311 +G1 X129.302 Y102.034 E.00362 +G1 X129.29 Y102.288 E.00797 +G1 X129.31 Y103.27 E.0308 +G1 X129.479 Y103.375 E.00624 +G1 X129.79 Y103.375 E.00975 +G1 X129.79 Y106.669 E.10328 +G1 X129.479 Y106.669 E.00975 +G1 X129.348 Y106.721 E.00442 +G1 X129.29 Y106.857 E.00464 +G1 X129.29 Y107.897 E.03261 +G1 X129.373 Y108.053 E.00554 +G1 X129.79 Y108.086 E.01312 +G1 X129.79 Y108.397 E.00975 +G1 X129.79 Y109.79 E.04368 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y105.36 E.1389 +G1 X120.521 Y105.36 E.00975 +G1 X120.68 Y105.274 E.00567 +G1 X120.702 Y105.224 E.00171 +G1 X120.697 Y104.76 E.01455 +G1 X120.682 Y104.75 E.00057 +G1 X120.682 Y104.75 F9000 +G1 X121.3 Y103.682 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.3 Y101.3 E.07865 +G1 X128.277 Y108.277 E.32578 +G1 X128.2 Y107.908 E.01245 +G1 X128.22 Y106.628 E.04227 +G1 X128.378 Y106.206 E.01488 +G1 X128.604 Y105.921 E.01201 +G1 X128.7 Y105.882 E.00342 +G1 X128.7 Y104.161 E.05682 +G1 X128.604 Y104.123 E.00341 +G1 X128.376 Y103.835 E.01213 +G1 X128.239 Y103.502 E.01189 +G1 X128.2 Y102.09 E.04664 +G1 X128.262 Y101.738 E.0118 +G1 X121.3 Y108.7 E.32508 +G1 X123.682 Y108.7 E.07865 +;LAYER_CHANGE +;Z:8.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;8.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y108.7 E-1.13145 +G1 X121.849 Y108.151 E-.36855 +;WIPE_END +G1 Z8.4 F9000 +;AFTER_LAYER_CHANGE +;8.4 +G1 Z8.4 +G1 X120.994 Y109.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X121.054 Y109.006 E.26255 +G1 X121.054 Y109.006 F9000 +G1 X120.597 Y109.403 +G1 F900 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.657 Y109.403 E.28877 +G1 X120.21 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.27 Y109.79 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X120.21 Y109.79 E-.0285 +G1 X120.21 Y106.692 E-1.4715 +;WIPE_END +G1 X123.682 Y108.7 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.3 Y108.7 E.07865 +G1 X128.159 Y101.841 E.32027 +G1 X128.117 Y102.101 E.0087 +G1 X128.153 Y103.756 E.05466 +G1 X128.352 Y104.21 E.01637 +G1 X128.7 Y104.431 E.01361 +G1 X128.7 Y108.7 E.14095 +G1 X121.3 Y101.3 E.34553 +G1 X121.3 Y103.682 E.07865 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y101.3 E-1.13145 +G1 X121.849 Y101.849 E-.36855 +;WIPE_END +G1 X126.172 Y101.371 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.625194 +G1 F900 +G1 X126.444 Y101.393 E.01321 +;WIDTH:0.62608 +G1 X127.681 Y101.393 E.05998 +G1 X127.853 Y101.373 E.0084 +;WIDTH:0.585704 +G1 X128.025 Y101.353 E.00782 +;WIDTH:0.545328 +G1 X128.197 Y101.332 E.00724 +;WIDTH:0.534986 +G1 X128.311 Y101.347 E.0047 +;WIDTH:0.56502 +G1 X128.424 Y101.362 E.00495 +;WIDTH:0.60995 +G1 X128.508 Y101.407 E.00449 +;WIDTH:0.65488 +G1 X128.593 Y101.453 E.00492 +G1 X128.617 Y101.589 E.00703 +;WIDTH:0.605734 +G1 X128.642 Y101.725 E.00647 +;WIDTH:0.590638 +G1 X128.625 Y101.921 E.00896 +;WIDTH:0.624688 +G1 X128.608 Y102.116 E.00947 +;WIDTH:0.62629 +G1 X128.617 Y103.614 E.07267 +;LAYER_CHANGE +;Z:8.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0 +;8.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.608 Y102.116 E-.71156 +G1 X128.625 Y101.921 E-.09298 +G1 X128.642 Y101.725 E-.09345 +G1 X128.617 Y101.589 E-.06568 +G1 X128.593 Y101.453 E-.0656 +G1 X128.508 Y101.407 E-.04591 +G1 X128.424 Y101.362 E-.04526 +G1 X128.311 Y101.347 E-.05415 +G1 X128.197 Y101.332 E-.05462 +G1 X128.025 Y101.353 E-.08231 +G1 X127.853 Y101.373 E-.08225 +G1 X127.681 Y101.393 E-.08225 +G1 X127.63 Y101.393 E-.02398 +;WIPE_END +G1 Z8.6 F9000 +;AFTER_LAYER_CHANGE +;8.6 +G1 Z8.6 +G1 X129.006 Y100.994 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F900 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 +;WIPE_END +G1 X128.7 Y104.794 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.440028 +G1 F900 +G1 X128.7 Y103.945 E.02803 +;WIDTH:0.486439 +G1 X128.677 Y103.757 E.00698 +;WIDTH:0.532849 +G1 X128.654 Y103.57 E.00768 +;WIDTH:0.57926 +G1 X128.63 Y103.383 E.00841 +;WIDTH:0.62567 +G1 X128.607 Y103.196 E.00913 +G1 X128.608 Y102.116 E.05233 +;WIDTH:0.62472 +G1 X128.625 Y101.876 E.01164 +;WIDTH:0.590673 +G1 X128.642 Y101.636 E.01096 +;WIDTH:0.598707 +G1 X128.621 Y101.565 E.00342 +;WIDTH:0.640788 +G1 X128.6 Y101.493 E.00373 +;WIDTH:0.682868 +G1 X128.579 Y101.421 E.00399 +G1 X128.487 Y101.399 E.00503 +;WIDTH:0.637693 +G1 X128.395 Y101.376 E.00469 +;WIDTH:0.592518 +G1 X128.303 Y101.354 E.00432 +;WIDTH:0.547343 +G1 X128.211 Y101.331 E.00398 +;WIDTH:0.543491 +G1 X128.034 Y101.352 E.00742 +;WIDTH:0.584814 +G1 X127.858 Y101.372 E.00798 +;WIDTH:0.626136 +G1 X127.681 Y101.393 E.00864 +G1 X126.6 Y101.393 E.05242 +;WIDTH:0.625798 +G1 X126.461 Y101.372 E.00681 +;WIDTH:0.584769 +G1 X126.322 Y101.352 E.00633 +;WIDTH:0.54374 +G1 X126.183 Y101.331 E.00585 +;WIDTH:0.502711 +G1 X126.045 Y101.311 E.00533 +;WIDTH:0.461682 +G1 X125.952 Y101.3 E.00326 +;WIDTH:0.440028 +G1 X121.498 Y101.3 E.14707 +;WIDTH:0.474095 +G1 X121.416 Y101.317 E.003 +;WIDTH:0.508162 +G1 X121.334 Y101.334 E.00324 +G1 X121.317 Y101.416 E.00324 +;WIDTH:0.474095 +G1 X121.3 Y101.498 E.003 +;WIDTH:0.440028 +G1 X121.3 Y102.37 E.02879 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.3 Y101.498 E-.4142 +G1 X121.317 Y101.416 E-.03978 +G1 X121.334 Y101.334 E-.03978 +G1 X121.416 Y101.317 E-.03978 +G1 X121.498 Y101.3 E-.03978 +G1 X123.449 Y101.3 E-.92668 +;WIPE_END +G1 X121.3 Y106.632 F9000 +G1 E5 F2400 +G1 F900 +G1 X121.3 Y105.555 E.03556 +G1 X121.3 Y105.555 F9000 +G1 X121.697 Y106.924 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.697 Y107.515 E.01951 +G1 X121.404 Y107.594 E.01002 +G1 X121.3 Y107.417 E.00678 +G1 X121.3 Y108.7 E.04236 +G1 X128.168 Y101.788 E.32172 +G1 X127.852 Y101.876 E.01083 +G1 X126.582 Y101.883 E.04193 +G1 X126.152 Y101.814 E.01438 +G1 X125.894 Y101.697 E.00935 +G1 X121.697 Y101.697 E.13857 +G1 X128.303 Y108.303 E.30846 +G1 X128.303 Y106.885 E.04682 +G1 X128.7 Y107.282 E.01854 +G1 X128.7 Y106.879 E.01331 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.7 Y107.282 E-.19142 +G1 X128.303 Y106.885 E-.26669 +G1 X128.303 Y108.303 E-.67355 +G1 X127.755 Y107.755 E-.36834 +;WIPE_END +G1 X122.964 Y108.7 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.440028 +G1 F900 +G1 X128.502 Y108.7 E.18286 +;WIDTH:0.474094 +G1 X128.584 Y108.683 E.003 +;WIDTH:0.50816 +G1 X128.666 Y108.666 E.00324 +G1 X128.683 Y108.584 E.00324 +;WIDTH:0.474094 +G1 X128.7 Y108.502 E.003 +;WIDTH:0.44003 +G1 X128.7 Y107.844 E.02173 +;LAYER_CHANGE +;Z:8.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;8.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.7 Y108.502 E-.31255 +G1 X128.683 Y108.584 E-.03978 +G1 X128.666 Y108.666 E-.03978 +G1 X128.584 Y108.683 E-.03978 +G1 X128.502 Y108.7 E-.03978 +G1 X126.337 Y108.7 E-1.02833 +;WIPE_END +G1 Z8.8 F9000 +;AFTER_LAYER_CHANGE +;8.8 +G1 Z8.8 +G1 X129.006 Y109.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 +;WIPE_END +G1 X123.179 Y108.877 F9000 +G1 E5 F2400 +M204 S250 +;TYPE:Bridge infill +;WIDTH:0.400345 +;HEIGHT:0.389872 +G1 F1500 +G1 X121.881 Y107.579 E.09607 +G1 X121.321 Y106.629 E.05771 +G1 X121.321 Y106.383 E.01287 +G1 X123.617 Y108.679 E.16993 +G1 X124.254 Y108.679 E.03334 +G1 X121.321 Y105.746 E.21708 +G1 X121.321 Y105.597 E.0078 +G1 X122.986 Y106.774 E.10671 +G1 X124.891 Y108.679 E.14099 +G1 X125.528 Y108.679 E.03334 +G1 X123.305 Y106.455 E.16457 +G1 X123.623 Y106.137 E.02354 +G1 X126.165 Y108.679 E.18814 +G1 X126.802 Y108.679 E.03334 +G1 X123.941 Y105.818 E.21175 +G1 X124.26 Y105.5 E.02357 +G1 X127.439 Y108.679 E.23529 +G1 X128.076 Y108.679 E.03334 +G1 X121.321 Y101.924 E.49996 +G1 X121.321 Y101.321 E.03156 +G1 X121.355 Y101.321 E.00178 +G1 X128.679 Y108.645 E.54207 +G1 X128.679 Y108.008 E.03334 +G1 X121.992 Y101.321 E.49492 +G1 X122.629 Y101.321 E.03334 +G1 X125.774 Y104.466 E.23277 +G1 X126.092 Y104.148 E.02354 +G1 X123.266 Y101.321 E.2092 +G1 X123.903 Y101.321 E.03334 +G1 X126.411 Y103.829 E.18562 +G1 X126.729 Y103.511 E.02354 +G1 X124.54 Y101.321 E.16205 +G1 X125.176 Y101.321 E.03329 +G1 X127.048 Y103.193 E.13855 +G1 X127.088 Y103.152 E.003 +G1 X128.679 Y104.742 E.11772 +G1 X128.679 Y104.187 E.02905 +G1 X125.813 Y101.321 E.21212 +G1 X126.45 Y101.321 E.03334 +G1 X128.679 Y103.55 E.16497 +G1 X128.679 Y102.913 E.03334 +G1 X127.087 Y101.321 E.11783 +G1 X127.724 Y101.321 E.03334 +G1 X128.679 Y102.276 E.07068 +G1 X128.679 Y101.639 E.03334 +G1 X128.163 Y101.123 E.03819 +M204 S500 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.679 Y101.639 E-.34662 +G1 X128.679 Y102.276 E-.30258 +G1 X127.724 Y101.321 E-.64152 +G1 X127.283 Y101.321 E-.20928 +;WIPE_END +G1 X121.851 Y108.149 F9000 +G1 E5 F2400 +;TYPE:Internal infill +;WIDTH:0.44 +;HEIGHT:0.2 +G1 F900 +G1 X121.3 Y108.7 E.02573 +;LAYER_CHANGE +;Z:9 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;9 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.851 Y108.149 E-.37014 +;WIPE_END +G1 E-1.12986 F3600 +G1 Z9 F9000 +;AFTER_LAYER_CHANGE +;9 +G1 Z9 +G1 X120.994 Y109.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X121.054 Y109.006 E.26255 +G1 X121.054 Y109.006 F9000 +G1 X120.597 Y109.403 +G1 F900 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.657 Y109.403 E.28877 +G1 X120.21 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.27 Y109.79 E.29849 +G1 X121.3 Y108.7 F9000 +;TYPE:Internal infill +;WIDTH:0.44 +G1 F900 +G1 X121.453 Y108.547 E.00714 +G1 X121.453 Y108.547 F9000 +G1 X121.822 Y107.514 +;TYPE:Solid infill +;WIDTH:0.439999 +G1 F900 +G1 X122.039 Y106.884 E.022 +G1 X122.465 Y106.564 E.01759 +G1 X122.815 Y106.497 E.01177 +G1 X123.671 Y106.497 E.02826 +G1 X123.844 Y106.388 E.00675 +G1 X126.156 Y108.7 E.10796 +G1 X121.983 Y108.7 E.13778 +G1 X121.837 Y108.417 E.01051 +G1 X121.825 Y107.712 E.02328 +G1 X122.223 Y107.775 E.0133 +G1 X122.233 Y108.303 E.01744 +G1 X125.197 Y108.303 E.09786 +G1 X123.743 Y106.848 E.06792 +G1 X123.671 Y106.894 E.00282 +G1 X122.853 Y106.894 E.02701 +G1 X122.63 Y106.937 E.0075 +G1 X122.375 Y107.128 E.01052 +G1 X122.22 Y107.577 E.01568 +G1 X122.676 Y107.851 E.01756 +;WIDTH:0.548982 +G1 X122.673 Y107.68 E.0072 +G1 X122.757 Y107.406 E.01206 +G1 X122.896 Y107.345 E.00639 +G1 X123.601 Y107.345 E.02967 +G1 X124.107 Y107.851 E.03011 +G1 X122.875 Y107.851 E.05184 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.107 Y107.851 E-.5852 +G1 X123.601 Y107.345 E-.33991 +G1 X122.896 Y107.345 E-.33487 +G1 X122.757 Y107.406 E-.0721 +G1 X122.673 Y107.68 E-.13613 +G1 X122.674 Y107.747 E-.03179 +;WIPE_END +G1 X126.718 Y108.7 F9000 +G1 E5 F2400 +;WIDTH:0.439999 +G1 F900 +G1 X123.573 Y105.555 E.14685 +G1 X123.322 Y105.275 E.01242 +;WIDTH:0.398128 +G1 X121.801 Y103.574 E.0674 +G1 X121.801 Y103.574 F9000 +G1 X121.199 Y103 +;WIDTH:0.410432 +G1 F900 +G1 X121.249 Y102.859 E.00457 +;WIDTH:0.437944 +G1 X121.299 Y102.718 E.00491 +;WIDTH:0.439999 +G1 X127.279 Y108.7 E.27927 +G1 X127.841 Y108.7 E.01856 +G1 X121.3 Y102.159 E.30542 +G1 X121.3 Y101.598 E.01852 +G1 X128.402 Y108.7 E.33162 +G1 X128.587 Y108.721 E.00615 +;WIDTH:0.41146 +G1 X128.772 Y108.742 E.00571 +;WIDTH:0.38292 +G1 X128.753 Y108.813 E.00208 +G1 X128.824 Y108.794 E.00208 +G1 X128.813 Y108.753 E.0012 +;WIDTH:0.439999 +G1 X128.7 Y108.436 E.01111 +G1 X121.564 Y101.3 E.3332 +G1 X121.406 Y101.244 E.00553 +;WIDTH:0.41146 +G1 X121.247 Y101.187 E.00518 +;WIDTH:0.38292 +G1 X121.258 Y101.228 E.0012 +G1 X121.187 Y101.247 E.00208 +G1 X121.206 Y101.176 E.00208 +G1 X121.206 Y101.176 F9000 +G1 X122.125 Y101.3 +;WIDTH:0.439999 +G1 F900 +G1 X128.7 Y107.875 E.30701 +G1 X128.779 Y107.93 E.00318 +;WIDTH:0.41146 +G1 X128.859 Y107.986 E.00299 +;WIDTH:0.38292 +G1 X128.84 Y108.057 E.00208 +G1 X128.911 Y108.038 E.00208 +G1 X128.9 Y107.997 E.0012 +;WIDTH:0.439999 +G1 X128.706 Y107.328 E.023 +;WIDTH:0.427474 +G1 X128.488 Y107.202 E.00805 +;WIDTH:0.38292 +G1 X128.358 Y107.04 E.00587 +;WIDTH:0.41146 +G1 X128.252 Y106.899 E.00541 +;WIDTH:0.439999 +G1 X128.146 Y106.759 E.0058 +G1 X122.687 Y101.3 E.2549 +G1 X123.248 Y101.3 E.01852 +G1 X125.975 Y104.045 E.12775 +;WIDTH:0.413836 +G1 X126.097 Y104.185 E.00573 +;WIDTH:0.387672 +G1 X127.651 Y105.912 E.0666 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.097 Y104.185 E-1.10354 +G1 X125.975 Y104.045 E-.08821 +G1 X125.518 Y103.585 E-.30825 +;WIPE_END +G1 X126.033 Y103.523 F9000 +G1 E5 F2400 +;WIDTH:0.439999 +G1 F900 +G1 X123.81 Y101.3 E.1038 +G1 X123.749 Y101.2 E.00387 +;WIDTH:0.41146 +G1 X123.688 Y101.1 E.00359 +;WIDTH:0.38292 +G1 X123.699 Y101.141 E.0012 +G1 X123.627 Y101.16 E.00211 +G1 X123.646 Y101.089 E.00208 +;WIDTH:0.439999 +G1 X124.371 Y101.3 E.02493 +G1 X128.7 Y101.3 E.14293 +G1 X128.7 Y102.899 E.05279 +G1 X128.554 Y102.94 E.00501 +G1 X128.082 Y103.358 E.02082 +G1 X127.697 Y103.493 E.01347 +G1 X126.458 Y103.5 E.04091 +G1 X126.455 Y103.383 E.00386 +G1 X124.512 Y101.44 E.09073 +G1 X124.512 Y101.44 F9000 +G1 X125.33 Y101.697 +G1 F900 +G1 X128.303 Y101.697 E.09816 +G1 X128.29 Y102.643 E.03124 +G1 X127.877 Y103.009 E.01822 +G1 X127.628 Y103.096 E.00871 +G1 X126.735 Y103.101 E.02948 +G1 X125.47 Y101.837 E.05904 +G1 X125.47 Y101.837 F9000 +G1 X126.416 Y102.147 +;WIDTH:0.545706 +G1 F900 +G1 X127.854 Y102.146 E.06012 +;WIDTH:0.544242 +G1 X127.854 Y102.429 E.0118 +G1 X127.681 Y102.582 E.00963 +G1 X127.509 Y102.648 E.00768 +;WIDTH:0.545706 +G1 X126.92 Y102.65 E.02462 +G1 X126.557 Y102.287 E.02146 +;LAYER_CHANGE +;Z:9.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;9.2 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X126.92 Y102.65 E-.24385 +G1 X127.509 Y102.648 E-.27978 +G1 X127.681 Y102.582 E-.08751 +G1 X127.854 Y102.429 E-.1097 +G1 X127.854 Y102.146 E-.13443 +G1 X126.497 Y102.147 E-.64473 +;WIPE_END +G1 Z9.2 F9000 +;AFTER_LAYER_CHANGE +;9.2 +G1 Z9.2 +G1 X129.006 Y100.994 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F900 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 +;WIPE_END +G1 X121.408 Y101.332 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.53634 +G1 F900 +G1 X121.364 Y101.289 E.00252 +G1 X121.304 Y101.304 E.00254 +G1 X121.289 Y101.364 E.00254 +G1 X121.332 Y101.408 E.00252 +G1 X121.392 Y101.392 E.00255 +;WIDTH:0.439999 +G1 X121.3 Y102.026 E.02115 +G1 X122.026 Y101.3 E.0339 +G1 X122.587 Y101.3 E.01852 +G1 X121.3 Y102.587 E.06009 +G1 X121.3 Y103.149 E.01856 +G1 X123.149 Y101.3 E.08634 +G1 X123.711 Y101.3 E.01856 +G1 X121.3 Y103.711 E.11258 +G1 X121.3 Y104.272 E.01852 +G1 X124.272 Y101.3 E.13877 +G1 X124.834 Y101.3 E.01856 +G1 X121.3 Y104.834 E.16501 +G1 X121.3 Y105.395 E.01852 +G1 X125.395 Y101.3 E.19121 +G1 X125.957 Y101.3 E.01856 +G1 X121.3 Y105.957 E.21745 +G1 X121.3 Y106.518 E.01852 +G1 X126.518 Y101.3 E.24365 +G1 X127.08 Y101.3 E.01856 +G1 X121.3 Y107.08 E.26989 +G1 X121.3 Y107.641 E.01852 +G1 X127.641 Y101.3 E.29608 +G1 X128.203 Y101.3 E.01856 +G1 X121.3 Y108.203 E.32232 +G1 X121.152 Y108.677 E.0164 +;WIDTH:0.38292 +G1 X121.162 Y108.838 E.00456 +;WIDTH:0.41146 +G1 X121.263 Y108.769 E.00375 +;WIDTH:0.439999 +G1 X121.364 Y108.7 E.00404 +G1 X128.7 Y101.364 E.34254 +G1 X128.769 Y101.263 E.00404 +;WIDTH:0.41146 +G1 X128.838 Y101.162 E.00375 +;WIDTH:0.38292 +G1 X128.677 Y101.152 E.00456 +;WIDTH:0.439999 +G1 X128.7 Y101.926 E.02557 +G1 X121.926 Y108.7 E.3163 +G1 X122.488 Y108.7 E.01856 +G1 X128.7 Y102.488 E.29006 +G1 X128.7 Y103.049 E.01852 +G1 X123.049 Y108.7 E.26386 +G1 X123.611 Y108.7 E.01856 +G1 X128.7 Y103.611 E.23762 +G1 X128.7 Y104.172 E.01852 +G1 X124.172 Y108.7 E.21143 +G1 X124.734 Y108.7 E.01856 +G1 X128.7 Y104.734 E.18519 +G1 X128.7 Y105.295 E.01852 +G1 X125.295 Y108.7 E.15899 +G1 X125.857 Y108.7 E.01856 +G1 X128.7 Y105.857 E.13275 +G1 X128.7 Y106.418 E.01852 +G1 X126.418 Y108.7 E.10655 +G1 X126.98 Y108.7 E.01856 +G1 X128.7 Y106.98 E.08031 +G1 X128.63 Y107.711 E.02425 +;WIDTH:0.580797 +G1 X128.63 Y108.63 E.0411 +G1 X127.711 Y108.63 E.0411 +G1 X128.489 Y107.852 E.04921 +;LAYER_CHANGE +;Z:9.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;9.4 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.711 Y108.63 E-.52262 +G1 X128.63 Y108.63 E-.43652 +G1 X128.63 Y107.711 E-.43652 +G1 X128.651 Y107.492 E-.10434 +;WIPE_END +G1 Z9.4 F9000 +;AFTER_LAYER_CHANGE +;9.4 +G1 Z9.4 +G1 X129.006 Y109.006 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X129.006 Y100.994 E.26453 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y108.946 E.13029 +G1 X129.006 Y108.946 F9000 +G1 X129.403 Y109.403 +G1 F900 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.403 Y100.597 E.29075 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.343 E.14339 +G1 X129.79 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.73 E.14831 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y109.79 E-.0285 +G1 X126.692 Y109.79 E-1.4715 +;WIPE_END +G1 X121.332 Y108.592 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.53634 +G1 F900 +G1 X121.289 Y108.636 E.00252 +G1 X121.304 Y108.696 E.00254 +G1 X121.364 Y108.712 E.00255 +G1 X121.408 Y108.668 E.00255 +G1 X121.392 Y108.608 E.00255 +;WIDTH:0.439999 +G1 X121.3 Y107.974 E.02115 +G1 X122.026 Y108.7 E.0339 +G1 X122.587 Y108.7 E.01852 +G1 X121.3 Y107.413 E.06009 +G1 X121.3 Y106.851 E.01856 +G1 X123.149 Y108.7 E.08634 +G1 X123.711 Y108.7 E.01856 +G1 X121.3 Y106.289 E.11258 +G1 X121.3 Y105.728 E.01852 +G1 X124.272 Y108.7 E.13877 +G1 X124.834 Y108.7 E.01856 +G1 X121.3 Y105.166 E.16501 +G1 X121.3 Y104.605 E.01852 +G1 X125.395 Y108.7 E.19121 +G1 X125.957 Y108.7 E.01856 +G1 X121.3 Y104.043 E.21745 +G1 X121.3 Y103.482 E.01852 +G1 X126.518 Y108.7 E.24365 +G1 X127.08 Y108.7 E.01856 +G1 X121.3 Y102.92 E.26989 +G1 X121.3 Y102.359 E.01852 +G1 X127.641 Y108.7 E.29608 +G1 X128.203 Y108.7 E.01856 +G1 X121.3 Y101.797 E.32232 +G1 X121.152 Y101.323 E.0164 +;WIDTH:0.38292 +G1 X121.162 Y101.162 E.00456 +;WIDTH:0.41146 +G1 X121.263 Y101.231 E.00375 +;WIDTH:0.439999 +G1 X121.364 Y101.3 E.00404 +G1 X128.7 Y108.636 E.34254 +G1 X128.689 Y108.742 E.00352 +;WIDTH:0.41146 +G1 X128.677 Y108.848 E.00327 +;WIDTH:0.38292 +G1 X128.838 Y108.838 E.00456 +;WIDTH:0.439999 +G1 X128.7 Y108.074 E.02563 +G1 X121.926 Y101.3 E.3163 +G1 X122.488 Y101.3 E.01856 +G1 X128.7 Y107.512 E.29006 +G1 X128.7 Y106.951 E.01852 +G1 X123.049 Y101.3 E.26386 +G1 X123.611 Y101.3 E.01856 +G1 X128.7 Y106.389 E.23762 +G1 X128.7 Y105.828 E.01852 +G1 X124.172 Y101.3 E.21143 +G1 X124.734 Y101.3 E.01856 +G1 X128.7 Y105.266 E.18519 +G1 X128.7 Y104.705 E.01852 +G1 X125.295 Y101.3 E.15899 +G1 X125.857 Y101.3 E.01856 +G1 X128.7 Y104.143 E.13275 +G1 X128.7 Y103.582 E.01852 +G1 X126.418 Y101.3 E.10655 +G1 X126.817 Y101.089 E.0149 +;WIDTH:0.38292 +G1 X126.797 Y101.16 E.00209 +G1 X126.869 Y101.141 E.00211 +G1 X126.858 Y101.1 E.0012 +;WIDTH:0.41146 +G1 X126.919 Y101.2 E.00359 +;WIDTH:0.439999 +G1 X126.98 Y101.3 E.00387 +G1 X128.7 Y103.02 E.08031 +G1 X128.8 Y103.081 E.00387 +;WIDTH:0.41146 +G1 X128.9 Y103.142 E.00359 +;WIDTH:0.38292 +G1 X128.911 Y103.183 E.0012 +G1 X128.84 Y103.203 E.00209 +G1 X128.859 Y103.131 E.00211 +G1 X128.859 Y103.131 F9000 +G1 X127.852 Y101.511 +;WIDTH:0.580797 +G1 F900 +G1 X128.63 Y102.289 E.04921 +G1 X128.63 Y101.37 E.0411 +G1 X127.711 Y101.37 E.0411 +;LAYER_CHANGE +;Z:9.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0 +;9.6 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.63 Y101.37 E-.43652 +G1 X128.63 Y102.289 E-.43652 +G1 X127.852 Y101.511 E-.52262 +;WIPE_END +G1 E-.10434 F3600 +G1 Z9.6 F9000 +;AFTER_LAYER_CHANGE +;9.6 +G1 Z9.6 +G1 X129.006 Y100.994 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F909 +G1 X129.006 Y105 E.13227 +G1 X129.006 Y109.006 E.13227 +G1 X120.994 Y109.006 E.26453 +G1 X120.994 Y100.994 E.26453 +G1 X128.946 Y100.994 E.26255 +G1 X128.946 Y100.994 F9000 +G1 X129.403 Y100.597 +G1 F909 +G1 X129.403 Y105 E.14537 +G1 X129.403 Y109.403 E.14537 +G1 X120.597 Y109.403 E.29075 +G1 X120.597 Y100.597 E.29075 +G1 X129.343 Y100.597 E.28877 +G1 X129.79 Y100.21 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F909 +G1 X129.79 Y105 E.15019 +G1 X129.79 Y109.79 E.15019 +G1 X120.21 Y109.79 E.30037 +G1 X120.21 Y100.21 E.30037 +G1 X129.73 Y100.21 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X129.79 Y100.21 E-.0285 +G1 X129.79 Y103.308 E-1.4715 +;WIPE_END +G1 X128.655 Y104.407 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.52971 +G1 F909 +G1 X128.655 Y105.464 E.04278 +G1 X127.997 Y104.65 E.04237 +G1 X128.152 Y104.618 E.00641 +G1 X128.472 Y104.483 E.01406 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.152 Y104.618 E-.16497 +G1 X127.997 Y104.65 E-.07518 +G1 X128.655 Y105.464 E-.49718 +G1 X128.655 Y104.407 E-.50208 +;WIPE_END +G1 E-.26059 F3600 +G1 X128.881 Y108.137 F9000 +G1 E5 F2400 +;TYPE:Top solid infill +;WIDTH:0.410487 +G1 F909 +G1 X128.297 Y108.72 E.02522 +G1 X127.778 Y108.72 E.01586 +G1 X128.72 Y107.778 E.04072 +G1 X128.72 Y107.258 E.01589 +G1 X127.258 Y108.72 E.06319 +G1 X126.738 Y108.72 E.01589 +G1 X128.72 Y106.738 E.08567 +G1 X128.72 Y106.218 E.01589 +G1 X126.218 Y108.72 E.10814 +G1 X125.698 Y108.72 E.01589 +G1 X128.489 Y105.929 E.12064 +G1 X128.256 Y105.642 E.0113 +G1 X125.178 Y108.72 E.13304 +G1 X124.659 Y108.72 E.01586 +G1 X128.024 Y105.355 E.14545 +G1 X127.791 Y105.067 E.01132 +G1 X124.139 Y108.72 E.15787 +G1 X123.619 Y108.72 E.01589 +G1 X127.673 Y104.666 E.17523 +G1 X127.673 Y104.666 F9000 +G1 X127.933 Y104.406 +G1 F909 +G1 X128.72 Y103.619 E.03402 +G1 X128.72 Y103.099 E.01589 +G1 X123.099 Y108.72 E.24296 +G1 X122.579 Y108.72 E.01589 +G1 X128.72 Y102.579 E.26543 +G1 X128.72 Y102.06 E.01586 +G1 X122.06 Y108.72 E.28787 +G1 X121.54 Y108.72 E.01589 +G1 X128.72 Y101.54 E.31034 +G1 X128.72 Y101.28 E.00795 +G1 X128.46 Y101.28 E.00795 +G1 X121.28 Y108.46 E.31034 +G1 X121.28 Y107.94 E.01589 +G1 X127.94 Y101.28 E.28787 +G1 X127.421 Y101.28 E.01586 +G1 X121.28 Y107.42 E.26541 +G1 X121.28 Y106.901 E.01586 +G1 X126.901 Y101.28 E.24296 +G1 X126.381 Y101.28 E.01589 +G1 X122.36 Y105.301 E.1738 +G1 X122.129 Y105.012 E.01131 +G1 X125.861 Y101.28 E.16131 +G1 X125.341 Y101.28 E.01589 +G1 X121.898 Y104.723 E.14882 +G1 X121.667 Y104.435 E.01128 +G1 X124.821 Y101.28 E.13635 +G1 X124.302 Y101.28 E.01586 +G1 X121.436 Y104.146 E.12388 +G1 X121.28 Y103.951 E.00763 +G1 X121.28 Y103.782 E.00517 +G1 X123.782 Y101.28 E.10814 +G1 X123.262 Y101.28 E.01589 +G1 X121.28 Y103.262 E.08567 +G1 X121.28 Y102.742 E.01589 +G1 X122.742 Y101.28 E.06319 +G1 X122.222 Y101.28 E.01589 +G1 X121.28 Y102.222 E.04072 +G1 X121.28 Y101.703 E.01586 +G1 X121.863 Y101.119 E.02522 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.28 Y101.703 E-.39197 +G1 X121.28 Y102.222 E-.24653 +G1 X122.222 Y101.28 E-.63279 +G1 X122.704 Y101.28 E-.22871 +;WIPE_END +G1 X121.344 Y104.704 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.527308 +G1 F909 +G1 X121.967 Y105.484 E.04021 +G1 X121.773 Y105.566 E.00848 +G1 X121.344 Y105.856 E.02086 +G1 X121.344 Y104.903 E.03838 +;LAYER_CHANGE +;Z:9.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;9.8 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.344 Y105.856 E-.45268 +G1 X121.773 Y105.566 E-.24597 +G1 X121.967 Y105.484 E-.10004 +G1 X121.344 Y104.704 E-.47417 +;WIPE_END +G1 E-.22714 F3600 +G1 Z9.8 F9000 +;AFTER_LAYER_CHANGE +;9.8 +G1 Z9.8 +G1 X121.897 Y102.735 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X121.834 Y102.556 E.00627 +G1 X121.834 Y101.729 E.02731 +G1 X121.876 Y101.511 E.00733 +;WIDTH:0.482924 +G1 X122.089 Y101.315 E.01059 +;WIDTH:0.525848 +G1 X122.302 Y101.119 E.01162 +G1 X122.409 Y101.113 E.0043 +;WIDTH:0.519381 +G1 X127.591 Y101.113 E.2053 +G1 X127.857 Y101.312 E.01316 +;WIDTH:0.47969 +G1 X128.124 Y101.511 E.01209 +;WIDTH:0.439999 +G1 X128.166 Y101.729 E.00733 +G1 X128.166 Y102.539 E.02674 +G1 X128.077 Y102.847 E.01059 +G1 X127.876 Y103.039 E.00918 +G1 X127.591 Y103.114 E.00973 +G1 X124.696 Y103.114 E.09558 +G1 X127.932 Y107.113 E.16985 +G1 X128.06 Y107.475 E.01268 +G1 X128.06 Y108.271 E.02628 +G1 X128.028 Y108.462 E.00639 +G1 X127.847 Y108.718 E.01035 +;WIDTH:0.482924 +G1 X127.719 Y108.8 E.00556 +;WIDTH:0.525848 +G1 X127.591 Y108.881 E.00608 +G1 X127.484 Y108.887 E.0043 +;WIDTH:0.519381 +G1 X122.786 Y108.887 E.18612 +G1 X122.588 Y108.788 E.00877 +;WIDTH:0.47969 +G1 X122.389 Y108.688 E.00809 +;WIDTH:0.439999 +G1 X122.219 Y108.371 E.01188 +G1 X122.21 Y107.461 E.03005 +G1 X122.339 Y107.099 E.01269 +G1 X122.596 Y106.918 E.01038 +G1 X122.786 Y106.886 E.00636 +G1 X125.135 Y106.886 E.07756 +G1 X121.96 Y102.915 E.16787 +G1 X121.916 Y102.792 E.00431 +G1 X121.916 Y102.792 F9000 +G1 X122.241 Y102.614 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X122.221 Y102.556 E.00192 +G1 X122.221 Y101.729 E.02593 +G1 X122.235 Y101.657 E.0023 +G1 X122.409 Y101.54 E.00657 +G1 X127.591 Y101.54 E.16248 +G1 X127.765 Y101.657 E.00657 +G1 X127.779 Y102.539 E.02766 +G1 X127.75 Y102.64 E.00329 +G1 X127.617 Y102.725 E.00495 +G1 X123.878 Y102.727 E.11723 +G1 X127.631 Y107.357 E.18687 +G1 X127.673 Y107.475 E.00393 +G1 X127.673 Y108.271 E.02496 +G1 X127.603 Y108.418 E.0051 +G1 X127.484 Y108.46 E.00396 +G1 X122.786 Y108.46 E.1473 +G1 X122.656 Y108.408 E.00439 +G1 X122.597 Y108.271 E.00468 +G1 X122.597 Y107.461 E.0254 +G1 X122.64 Y107.343 E.00394 +G1 X122.786 Y107.273 E.00508 +G1 X125.972 Y107.273 E.09989 +G1 X125.597 Y106.843 E.01789 +G1 X122.262 Y102.673 E.16742 +G1 X122.261 Y102.671 E.00007 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X122.241 Y102.614 E-.02869 +G1 X122.221 Y102.556 E-.02914 +G1 X122.221 Y101.729 E-.39282 +G1 X122.235 Y101.657 E-.03484 +G1 X122.409 Y101.54 E-.0996 +G1 X124.335 Y101.54 E-.91491 +;WIPE_END +G1 X125.528 Y103.511 F9000 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X127.723 Y103.502 E.07247 +G1 X128.072 Y103.384 E.01216 +G1 X128.412 Y103.06 E.01551 +;WIDTH:0.471644 +G1 X128.565 Y102.709 E.01365 +G1 X128.575 Y102.539 E.00607 +;WIDTH:0.462599 +G1 X128.575 Y101.729 E.02827 +;WIDTH:0.508363 +G1 X128.523 Y101.349 E.01484 +G1 X128.047 Y101.028 E.02222 +G1 X128.972 Y101.028 E.0358 +G1 X128.972 Y101.261 E.00902 +G1 X128.995 Y101.729 E.01813 +;WIDTH:0.462599 +G1 X129.006 Y103.232 E.05245 +;WIDTH:0.439999 +G1 X129.006 Y106.593 E.11097 +;WIDTH:0.477902 +G1 X128.987 Y107.034 E.01597 +;WIDTH:0.515804 +G1 X128.968 Y108.271 E.04865 +;WIDTH:0.551845 +G1 X128.95 Y108.95 E.02874 +G1 X128.217 Y108.95 E.03102 +G1 X128.455 Y108.611 E.01753 +G1 X128.495 Y108.271 E.01449 +;WIDTH:0.515804 +G1 X128.495 Y107.475 E.0313 +G1 X128.368 Y107.169 E.01303 +;WIDTH:0.477902 +G1 X128.241 Y106.863 E.01198 +;WIDTH:0.439999 +G1 X125.566 Y103.558 E.14039 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X125.528 Y103.511 E-.02871 +G1 X127.723 Y103.502 E-1.04263 +G1 X128.072 Y103.384 E-.17499 +G1 X128.412 Y103.06 E-.22309 +G1 X128.438 Y103.001 E-.03058 +;WIPE_END +G1 X124.309 Y106.489 F9000 +G1 E5 F2400 +G1 F900 +G1 X122.786 Y106.489 E.05029 +G1 X122.465 Y106.543 E.01075 +G1 X122.031 Y106.848 E.01751 +G1 X121.813 Y107.461 E.02148 +G1 X121.837 Y108.485 E.03382 +G1 X122.115 Y108.976 E.01863 +G1 X122.19 Y109.006 E.00267 +G1 X120.994 Y109.006 E.03949 +G1 X120.994 Y103.393 E.18533 +;WIDTH:0.462599 +G1 X121.005 Y101.729 E.05807 +;WIDTH:0.508362 +G1 X121.028 Y101.028 E.02714 +G1 X121.874 Y101.028 E.03274 +G1 X121.477 Y101.349 E.01976 +G1 X121.425 Y101.729 E.01484 +;WIDTH:0.471645 +G1 X121.435 Y102.726 E.03554 +G1 X121.65 Y103.163 E.01736 +;WIDTH:0.439999 +G1 X124.272 Y106.442 E.13862 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.309 Y106.489 E-.02841 +G1 X122.786 Y106.489 E-.72342 +G1 X122.465 Y106.543 E-.15462 +G1 X122.031 Y106.848 E-.25197 +G1 X121.813 Y107.461 E-.30904 +G1 X121.815 Y107.53 E-.03254 +;WIPE_END +G1 X120.597 Y109.403 F9000 +G1 E5 F2400 +G1 F900 +G1 X120.597 Y100.597 E.29075 +G1 X121.261 Y100.597 E.02192 +;WIDTH:0.482924 +G1 X121.761 Y100.619 E.01831 +;WIDTH:0.525848 +G1 X122.409 Y100.637 E.02603 +;WIDTH:0.519381 +G1 X127.591 Y100.637 E.2053 +G1 X128.165 Y100.617 E.02275 +;WIDTH:0.47969 +G1 X128.739 Y100.597 E.02086 +;WIDTH:0.439999 +G1 X129.403 Y100.597 E.02192 +G1 X129.403 Y101.261 E.02192 +G1 X129.403 Y109.403 E.26883 +G1 X128.09 Y109.403 E.04335 +;WIDTH:0.47969 +G1 X127.787 Y109.383 E.01103 +;WIDTH:0.519381 +G1 X127.484 Y109.363 E.01203 +G1 X122.786 Y109.363 E.18612 +G1 X122.445 Y109.383 E.01353 +;WIDTH:0.47969 +G1 X122.103 Y109.403 E.01244 +;WIDTH:0.439999 +G1 X120.657 Y109.403 E.04774 +G1 X120.21 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y101.261 E.03295 +G1 X129.79 Y109.79 E.26742 +G1 X120.27 Y109.79 E.29849 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X120.21 Y109.79 E-.0285 +G1 X120.21 Y106.692 E-1.4715 +;WIPE_END +G1 X121.413 Y107.373 F9000 +G1 E5 F2400 +;TYPE:Solid infill +;WIDTH:0.665616 +G1 F900 +G1 X121.404 Y107.477 E.0054 +;WIDTH:0.682426 +G1 X121.421 Y108.579 E.05861 +G1 X121.421 Y108.579 F9000 +G1 X121.33 Y106.83 +;WIDTH:0.541354 +G1 F900 +G1 X121.351 Y106.966 E.0057 +;WIDTH:0.582775 +G1 X121.371 Y107.102 E.00617 +;WIDTH:0.624196 +G1 X121.392 Y107.237 E.0066 +;WIDTH:0.665616 +G1 X121.413 Y107.373 E.00713 +G1 X121.483 Y107.241 E.00774 +;WIDTH:0.620493 +G1 X121.554 Y107.11 E.00716 +;WIDTH:0.57537 +G1 X121.625 Y106.978 E.00664 +;WIDTH:0.530246 +G1 X121.695 Y106.846 E.00605 +;WIDTH:0.485123 +G1 X121.766 Y106.715 E.00548 +;WIDTH:0.439999 +G1 X121.839 Y106.618 E.00401 +G1 X122.321 Y106.28 E.01944 +G1 X122.794 Y106.183 E.01594 +G1 X123.673 Y106.183 E.02902 +G1 X121.414 Y103.358 E.11943 +G1 X121.332 Y103.206 E.0057 +G1 X121.3 Y103.402 E.00656 +G1 X121.3 Y106.634 E.10671 +G1 X121.697 Y106.233 E.01863 +G1 X121.697 Y104.348 E.06224 +G1 X122.847 Y105.786 E.06079 +G1 X122.236 Y105.881 E.02042 +G1 X121.863 Y106.125 E.01472 +G1 X122.088 Y105.45 E.02349 +;WIDTH:0.45414 +G1 X122.051 Y105.487 E.00179 +G1 X122.065 Y105.536 E.00174 +G1 X122.114 Y105.55 E.00174 +G1 X122.151 Y105.513 E.00179 +G1 X122.137 Y105.464 E.00174 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X122.151 Y105.513 E-.02421 +G1 X122.114 Y105.55 E-.02485 +G1 X122.065 Y105.536 E-.02421 +G1 X122.051 Y105.487 E-.02421 +G1 X122.088 Y105.45 E-.02485 +G1 X121.863 Y106.125 E-.33797 +G1 X122.236 Y105.881 E-.21172 +G1 X122.847 Y105.786 E-.29371 +G1 X122.144 Y104.907 E-.53427 +;WIPE_END +G1 X126.167 Y103.816 F9000 +G1 E5 F2400 +;WIDTH:0.439999 +G1 F900 +G1 X127.763 Y103.804 E.0527 +G1 X128.259 Y103.62 E.01747 +G1 X128.699 Y103.218 E.01968 +G1 X128.7 Y106.581 E.11104 +G1 X128.663 Y107.015 E.01438 +G1 X128.472 Y106.663 E.01322 +G1 X126.292 Y103.97 E.1144 +G1 X126.996 Y104.208 E.02454 +G1 X127.818 Y104.197 E.02714 +G1 X128.303 Y104.044 E.01679 +G1 X128.303 Y105.823 E.05874 +G1 X127.12 Y104.362 E.06207 +G1 X127.862 Y104.564 E.02539 +;WIDTH:0.498078 +G1 X127.822 Y104.605 E.00217 +G1 X127.837 Y104.66 E.00216 +G1 X127.892 Y104.674 E.00215 +G1 X127.932 Y104.634 E.00214 +G1 X127.917 Y104.579 E.00216 +;LAYER_CHANGE +;Z:10 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0 +;10 + + +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X127.932 Y104.634 E-.02708 +G1 X127.892 Y104.674 E-.02687 +G1 X127.837 Y104.66 E-.02696 +G1 X127.822 Y104.605 E-.02708 +G1 X127.862 Y104.564 E-.02721 +G1 X127.12 Y104.362 E-.36528 +G1 X128.303 Y105.823 E-.89295 +G1 X128.303 Y105.599 E-.10657 +;WIPE_END +G1 Z10 F9000 +;AFTER_LAYER_CHANGE +;10 +G1 Z10 +G1 X127.986 Y102.934 +G1 E5 F2400 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X127.876 Y103.039 E.00502 +G1 X127.591 Y103.114 E.00973 +G1 X124.696 Y103.114 E.09558 +G1 X127.932 Y107.113 E.16985 +G1 X128.06 Y107.475 E.01268 +G1 X128.06 Y108.271 E.02628 +G1 X128.028 Y108.462 E.00639 +G1 X127.847 Y108.718 E.01035 +;WIDTH:0.482924 +G1 X127.719 Y108.8 E.00556 +;WIDTH:0.525848 +G1 X127.591 Y108.881 E.00608 +G1 X127.484 Y108.887 E.0043 +;WIDTH:0.519381 +G1 X122.786 Y108.887 E.18612 +G1 X122.588 Y108.788 E.00877 +;WIDTH:0.47969 +G1 X122.389 Y108.688 E.00809 +;WIDTH:0.439999 +G1 X122.219 Y108.371 E.01188 +G1 X122.21 Y107.461 E.03005 +G1 X122.339 Y107.099 E.01269 +G1 X122.596 Y106.918 E.01038 +G1 X122.786 Y106.886 E.00636 +G1 X125.135 Y106.886 E.07756 +G1 X121.96 Y102.915 E.16787 +G1 X121.834 Y102.556 E.01256 +G1 X121.834 Y101.729 E.02731 +G1 X121.876 Y101.511 E.00733 +;WIDTH:0.482924 +G1 X122.089 Y101.315 E.01059 +;WIDTH:0.525848 +G1 X122.302 Y101.119 E.01162 +G1 X122.409 Y101.113 E.0043 +;WIDTH:0.519381 +G1 X127.591 Y101.113 E.2053 +G1 X127.857 Y101.312 E.01316 +;WIDTH:0.47969 +G1 X128.124 Y101.511 E.01209 +;WIDTH:0.439999 +G1 X128.166 Y101.729 E.00733 +G1 X128.166 Y102.539 E.02674 +G1 X128.077 Y102.847 E.01059 +G1 X128.029 Y102.892 E.00217 +G1 X128.029 Y102.892 F9000 +G1 X127.714 Y102.663 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X127.617 Y102.725 E.00361 +G1 X123.878 Y102.727 E.11723 +G1 X127.631 Y107.357 E.18687 +G1 X127.673 Y107.475 E.00393 +G1 X127.673 Y108.271 E.02496 +G1 X127.603 Y108.418 E.0051 +G1 X127.484 Y108.46 E.00396 +G1 X122.786 Y108.46 E.1473 +G1 X122.656 Y108.408 E.00439 +G1 X122.597 Y108.271 E.00468 +G1 X122.597 Y107.461 E.0254 +G1 X122.64 Y107.343 E.00394 +G1 X122.786 Y107.273 E.00508 +G1 X125.972 Y107.273 E.09989 +G1 X125.597 Y106.843 E.01789 +G1 X122.262 Y102.673 E.16742 +G1 X122.221 Y102.556 E.00389 +G1 X122.221 Y101.729 E.02593 +G1 X122.235 Y101.657 E.0023 +G1 X122.409 Y101.54 E.00657 +G1 X127.591 Y101.54 E.16248 +G1 X127.765 Y101.657 E.00657 +G1 X127.779 Y102.539 E.02766 +G1 X127.755 Y102.623 E.00274 +G1 X127.755 Y102.623 F9000 +G1 X128.259 Y103.206 +;TYPE:Perimeter +;WIDTH:0.439999 +G1 F900 +G1 X128.412 Y103.06 E.00698 +;WIDTH:0.471644 +G1 X128.565 Y102.709 E.01365 +G1 X128.575 Y102.539 E.00607 +;WIDTH:0.462599 +G1 X128.575 Y101.729 E.02827 +;WIDTH:0.508363 +G1 X128.523 Y101.349 E.01484 +G1 X128.047 Y101.028 E.02222 +G1 X128.972 Y101.028 E.0358 +G1 X128.972 Y101.261 E.00902 +G1 X128.995 Y101.729 E.01813 +;WIDTH:0.462599 +G1 X129.006 Y103.232 E.05245 +;WIDTH:0.439999 +G1 X129.006 Y106.593 E.11097 +;WIDTH:0.477902 +G1 X128.987 Y107.034 E.01597 +;WIDTH:0.515804 +G1 X128.968 Y108.271 E.04865 +;WIDTH:0.551845 +G1 X128.95 Y108.95 E.02874 +G1 X128.217 Y108.95 E.03102 +G1 X128.455 Y108.611 E.01753 +G1 X128.495 Y108.271 E.01449 +;WIDTH:0.515804 +G1 X128.495 Y107.475 E.0313 +G1 X128.368 Y107.169 E.01303 +;WIDTH:0.477902 +G1 X128.241 Y106.863 E.01198 +;WIDTH:0.439999 +G1 X125.528 Y103.511 E.14238 +G1 X127.723 Y103.502 E.07247 +G1 X128.072 Y103.384 E.01216 +G1 X128.215 Y103.248 E.00652 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X128.259 Y103.206 E-.02889 +G1 X128.412 Y103.06 E-.10045 +G1 X128.565 Y102.709 E-.18188 +G1 X128.575 Y102.539 E-.08089 +G1 X128.575 Y101.729 E-.38475 +G1 X128.523 Y101.349 E-.18218 +G1 X128.047 Y101.028 E-.27271 +G1 X128.612 Y101.028 E-.26825 +;WIPE_END +G1 X124.309 Y106.489 F9000 +G1 E5 F2400 +G1 F900 +G1 X122.786 Y106.489 E.05029 +G1 X122.465 Y106.543 E.01075 +G1 X122.031 Y106.848 E.01751 +G1 X121.813 Y107.461 E.02148 +G1 X121.837 Y108.485 E.03382 +G1 X122.115 Y108.976 E.01863 +G1 X122.19 Y109.006 E.00267 +G1 X120.994 Y109.006 E.03949 +G1 X120.994 Y103.393 E.18533 +;WIDTH:0.462599 +G1 X121.005 Y101.729 E.05807 +;WIDTH:0.508362 +G1 X121.028 Y101.028 E.02714 +G1 X121.874 Y101.028 E.03274 +G1 X121.477 Y101.349 E.01976 +G1 X121.425 Y101.729 E.01484 +;WIDTH:0.471645 +G1 X121.435 Y102.726 E.03554 +G1 X121.65 Y103.163 E.01736 +;WIDTH:0.439999 +G1 X124.272 Y106.442 E.13862 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X124.309 Y106.489 E-.02841 +G1 X122.786 Y106.489 E-.72342 +G1 X122.465 Y106.543 E-.15462 +G1 X122.031 Y106.848 E-.25197 +G1 X121.813 Y107.461 E-.30904 +G1 X121.815 Y107.53 E-.03254 +;WIPE_END +G1 X120.597 Y109.403 F9000 +G1 E5 F2400 +G1 F900 +G1 X120.597 Y100.597 E.29075 +G1 X121.261 Y100.597 E.02192 +;WIDTH:0.482924 +G1 X121.761 Y100.619 E.01831 +;WIDTH:0.525848 +G1 X122.409 Y100.637 E.02603 +;WIDTH:0.519381 +G1 X127.591 Y100.637 E.2053 +G1 X128.165 Y100.617 E.02275 +;WIDTH:0.47969 +G1 X128.739 Y100.597 E.02086 +;WIDTH:0.439999 +G1 X129.403 Y100.597 E.02192 +G1 X129.403 Y101.261 E.02192 +G1 X129.403 Y109.403 E.26883 +G1 X128.09 Y109.403 E.04335 +;WIDTH:0.47969 +G1 X127.787 Y109.383 E.01103 +;WIDTH:0.519381 +G1 X127.484 Y109.363 E.01203 +G1 X122.786 Y109.363 E.18612 +G1 X122.445 Y109.383 E.01353 +;WIDTH:0.47969 +G1 X122.103 Y109.403 E.01244 +;WIDTH:0.439999 +G1 X120.657 Y109.403 E.04774 +G1 X120.21 Y109.79 F9000 +;TYPE:External perimeter +;WIDTH:0.419999 +G1 F900 +G1 X120.21 Y100.21 E.30037 +G1 X129.79 Y100.21 E.30037 +G1 X129.79 Y101.261 E.03295 +G1 X129.79 Y109.79 E.26742 +G1 X120.27 Y109.79 E.29849 +G1 X121.203 Y108.881 F9000 +;TYPE:Top solid infill +;WIDTH:0.407261 +G1 F900 +G1 X121.555 Y108.529 E.01508 +G1 X121.528 Y108.275 E.00774 +G1 X121.528 Y108.041 E.00709 +G1 X121.28 Y108.289 E.01063 +G1 X121.28 Y107.773 E.01563 +G1 X121.748 Y107.305 E.02005 +G1 X121.748 Y107.305 F9000 +G1 X123.205 Y106.364 +G1 F900 +G1 X123.56 Y106.009 E.01521 +G1 X123.331 Y105.723 E.0111 +G1 X122.669 Y106.385 E.02836 +G1 X122.669 Y106.385 F9000 +G1 X123.216 Y105.322 +G1 F900 +G1 X121.28 Y107.258 E.08295 +G1 X121.28 Y106.743 E.0156 +G1 X122.873 Y105.15 E.06825 +G1 X122.644 Y104.864 E.0111 +G1 X121.28 Y106.228 E.05844 +G1 X121.28 Y105.712 E.01563 +G1 X122.415 Y104.577 E.04863 +G1 X122.186 Y104.291 E.0111 +G1 X121.28 Y105.197 E.03882 +G1 X121.28 Y104.682 E.0156 +G1 X121.957 Y104.005 E.02901 +G1 X121.728 Y103.719 E.0111 +G1 X121.28 Y104.167 E.01919 +G1 X121.28 Y103.651 E.01563 +G1 X121.613 Y103.318 E.01427 +G1 E-3.5 F3600 +;WIPE_START +G1 F7200 +G1 X121.28 Y103.651 E-.22369 +G1 X121.28 Y104.167 E-.2451 +G1 X121.728 Y103.719 E-.30094 +G1 X121.957 Y104.005 E-.17403 +G1 X121.28 Y104.682 E-.45478 +G1 X121.28 Y104.896 E-.10146 +;WIPE_END +G1 X128.881 Y106.391 F9000 +G1 E5 F2400 +;WIDTH:0.405603 +G1 F900 +G1 X128.504 Y106.767 E.01606 +G1 X128.47 Y106.692 E.00248 +G1 X128.289 Y106.469 E.00866 +G1 X128.72 Y106.038 E.01838 +G1 X128.72 Y105.525 E.01547 +G1 X128.06 Y106.186 E.02817 +G1 X127.83 Y105.902 E.01102 +G1 X128.72 Y105.013 E.03794 +G1 X128.72 Y104.5 E.01547 +G1 X127.601 Y105.619 E.04772 +G1 X127.372 Y105.335 E.011 +G1 X128.72 Y103.987 E.05749 +G1 X128.72 Y103.474 E.01547 +G1 X127.142 Y105.052 E.0673 +G1 X126.913 Y104.768 E.011 +G1 X127.962 Y103.719 E.04474 +G1 X127.765 Y103.786 E.00628 +G1 X127.38 Y103.788 E.01161 +G1 X126.683 Y104.485 E.02973 +G1 X126.454 Y104.201 E.011 +G1 X126.864 Y103.791 E.01749 +G1 X126.348 Y103.795 E.01556 +G1 X126.11 Y104.032 E.01013 ; stop printing object xyz-cali-cube-by-r3d_v1.stl id:0 copy 0 G1 E-3.5 F3600 ;WIPE_START G1 F7200;_WIPE -G1 X123.21 Y106.79 E-.02852 -G1 X123.21 Y105.197 E-.75668 -G1 X123.232 Y104.612 E-.27807 -G1 X123.254 Y104.027 E-.27807 -G1 X123.258 Y103.693 E-.15866 +G1 X126.348 Y103.795 E-.15954 +G1 X126.864 Y103.791 E-.24511 +G1 X126.454 Y104.201 E-.27542 +G1 X126.683 Y104.485 E-.17329 +G1 X127.38 Y103.788 E-.46821 +G1 X127.756 Y103.786 E-.17843 ;WIPE_END M107 ;TYPE:Custom ; Filament-specific end gcode ;END gcode for filament -G1 Z6 F600 ; Move print head up -G1 X5 Y193.8 F9000 ; present print -G1 Z74 F600 ; Move print head further up -G1 Z150 F600 ; Move print head further up +G1 Z12 F600 ; Move print head up +G1 X5 Y193.8 E-.5 F9000 ; present print M140 S0 ; turn off heatbed M104 S0 ; turn off temperature M107 ; turn off fan -M84 X Y E ; disable motors -; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl id:0 copy 0","polygon":[[127.000,107.000],[123.000,107.000],[123.000,103.000],[127.000,103.000]]}]} -; filament used [mm] = 67.36 -; filament used [cm3] = 0.16 -; filament used [g] = 0.20 -; filament cost = 0.00 -; total filament used [g] = 0.20 -; total filament cost = 0.00 +M84 ; disable motors +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl id:0 copy 0","polygon":[[130.000,110.000],[120.000,110.000],[120.000,100.000],[130.000,100.000]]}]} +; filament used [mm] = 331.04 +; filament used [cm3] = 0.80 +; filament used [g] = 0.99 +; filament cost = 0.02 +; total filament used [g] = 0.99 +; total filament cost = 0.02 ; total filament used for wipe tower [g] = 0.00 -; estimated printing time (normal mode) = 2m 49s -; estimated first layer printing time (normal mode) = 1m 9s +; estimated printing time (normal mode) = 12m 9s +; estimated first layer printing time (normal mode) = 1m 46s ; prusaslicer_config = begin ; arc_fitting = disabled @@ -2766,7 +9638,7 @@ M84 X Y E ; disable motors ; enable_dynamic_fan_speeds = 0 ; enable_dynamic_overhang_speeds = 0 ; end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n" -; end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors +; end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.85} E-.5 F{travel_speed*60} ; present print\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 ; disable motors ; external_perimeter_acceleration = 0 ; external_perimeter_extrusion_width = 0.42 ; external_perimeter_speed = 25 From b8eba3567ace93ea385e2b5c4d7072400ad5270d Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 6 Nov 2024 18:10:34 -0500 Subject: [PATCH 039/194] Logging completed for normal use of fabricators. --- Tests/parallel_test_runner.py | 4 +- Tests/test_runner.py | 119 ++++++++--- server/Classes/Fabricators/Device.py | 4 + server/Classes/Fabricators/Fabricator.py | 32 ++- .../Fabricators/Printers/Prusa/PrusaMK4.py | 2 +- server/Classes/Logger.py | 60 +++--- server/Mixins/usesMarlinGcode.py | 85 +++++--- server/pauseAndResumeTest.gcode | 201 ++++++++++++++++++ server/test.py | 194 +++++++++-------- 9 files changed, 508 insertions(+), 193 deletions(-) create mode 100644 server/pauseAndResumeTest.gcode diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index a423e4e3..0de6eaf5 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -41,10 +41,10 @@ def printColor(color, message): print(color + message + reset) # Function to run pytest for a specific port -def run_tests_for_port(port): +def run_tests_for_port(comm_port): env = os.environ.copy() verbosity = 1 - env["PORT"] = port + env["PORT"] = comm_port showAnything = False verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" subprocess.Popen(["pytest", "test_runner.py", verbosityCommand, f"--myVerbose={verbosity}"], env=env).wait() diff --git a/Tests/test_runner.py b/Tests/test_runner.py index 308c53f1..9766c5e1 100644 --- a/Tests/test_runner.py +++ b/Tests/test_runner.py @@ -6,23 +6,28 @@ from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S from Classes.Ports import Ports +from Classes.Jobs import Job from Classes.Fabricators.Fabricator import Fabricator from Classes.Vector3 import Vector3 from Mixins.canPause import canPause +def fabricator_setup(port): + if not port: return None + return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False) + class TestFabricator: - testLevelToRun = 5 + testLevelToRun = 1 shortTest = True + def __repr__(self): + return f"TestFabricator Class running on port {Ports.getPortByName(os.getenv('PORT'))}" + @classmethod @pytest.fixture(scope="session", autouse=True) def function_setup(cls, request): - port = os.getenv("PORT") - if not port: + fabricator_instance = fabricator_setup(request.session.config.port) + if fabricator_instance is None: pytest.skip("No port specified") - - fabricator_instance = Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False) - yield fabricator_instance fabricator_instance.device.disconnect() @@ -37,7 +42,13 @@ def test_connection(self, function_setup): @pytest.mark.order(2) def test_home(self, function_setup): fabricator = function_setup - assert fabricator.device.home(isVerbose=False), f"Failed to home {fabricator.getDescription()}" + assert fabricator.device.home(), f"Failed to home {fabricator.getDescription()}" + + @pytest.mark.xfail(reason="Expected to fail") + @pytest.mark.order(2) + def test_xfail(self, function_setup): + fabricator = function_setup + assert False, f"Expected to fail on {fabricator.getDescription()}" @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") @pytest.mark.order(3) @@ -46,7 +57,7 @@ def test_square(self, function_setup): squarePasses = 0 for point in [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), Vector3(50.0, 150.0, 2.0)]: - squarePasses += 1 if fabricator.device.goTo(point) else 0 + squarePasses += 1 if fabricator.device.goTo(point, isVerbose=True) else 0 assert squarePasses == 4, f"Failed to draw square on {fabricator.getDescription()}" @@ -69,45 +80,63 @@ def test_center(self, function_setup): assert fabricator.device.goTo( Vector3(125.0, 100.0, 2.0)), f"Failed to go to center on {fabricator.getDescription()}" - @pytest.mark.skip(reason="Pause isn't implemented yet") - @pytest.mark.order(6) - # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") - def test_pause(self, function_setup): + # @pytest.mark.skip(reason="Not doing heat test") + # @pytest.mark.order(6) + # def test_parse_gcode(self, function_setup): + # fabricator = function_setup + # assert fabricator.device.parseGcode( + # "../server/heatTest.gcode"), f"Failed to parse Gcode on {fabricator.getDescription()}" + + @pytest.mark.order(7) + @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + def test_pause_and_resume(self, function_setup): fabricator = function_setup if not isinstance(fabricator.device, canPause): pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") - assert fabricator.device.pause(), f"Failed to pause on {fabricator.getDescription()}" + from time import sleep + import threading - @pytest.mark.skip(reason="Resume isn't implemented yet") - @pytest.mark.order(7) - # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") - def test_resume(self, function_setup): - fabricator = function_setup - if not isinstance(fabricator.device, canPause): - pytest.skip(f"{fabricator.getDescription()} doesn't support resuming") - assert fabricator.device.resume(), f"Failed to resume on {fabricator.getDescription()}" + def parse_gcode(): + fabricator.queue.addToFront(Job("../server/pauseAndResumeTest.gcode", "pauseAndResumeTest", fabricator.dbID, "ready", "../server/pauseAndResumeTest.gcode", False, 1, fabricator.name),fabricator.dbID) + fabricator.begin() + assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" + def pause_and_resume_fabricator(): + sleep(2) + assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(20) + assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(1) + assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(20) + fabricator.resetToIdle() + assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - @pytest.mark.skip(reason="Cancel isn't implemented yet") - @pytest.mark.order(8) - # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") - def test_cancel(self, function_setup): - fabricator = function_setup - assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}" + parse_thread = threading.Thread(target=parse_gcode) + pause_thread = threading.Thread(target=pause_and_resume_fabricator) + parse_thread.start() + pause_thread.start() - @pytest.mark.skip(reason="getStatus isn't implemented yet") - @pytest.mark.order(9) - # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + parse_thread.join() + pause_thread.join() + + + @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + @pytest.mark.order(8) def test_status(self, function_setup): fabricator = function_setup assert fabricator.getStatus() is not None, f"Failed to get status on {fabricator.getDescription()}" - + assert fabricator.device.status is not None, f"Failed to get status on device of {fabricator.getDescription()}" + assert fabricator.getStatus() == fabricator.device.status, f"Internal status mismatch: fabricator: {fabricator.getDescription()}, device: {fabricator.device.status}" + assert fabricator.getStatus() == "idle", f"Status incorrect at {fabricator.getDescription()}, expected idle, got {fabricator.getStatus()}" @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") @pytest.mark.order(10) - def test_gcode(self, function_setup): + def test_gcode_print_time(self, function_setup): fabricator = function_setup expectedTime = 3 * 60 file = "../server/xyz-cali-cube" @@ -115,7 +144,7 @@ def test_gcode(self, function_setup): file = file + "-mini" if isinstance(fabricator.device, Ender3): file = file + "_ENDER3.gcode" - expectedTime = 4 * 60 + 15 if self.shortTest else 28 * 60 + expectedTime = 14 * 60 + 15 if self.shortTest else 28 * 60 elif isinstance(fabricator.device, PrusaMK4S): file = file + "_MK4S.gcode" expectedTime = 180 if self.shortTest else 1800 @@ -125,7 +154,9 @@ def test_gcode(self, function_setup): # expectedTime = 2040 # for my personal home test, 1072 expectedMinutes, expectedSeconds = divmod(expectedTime, 60) time = datetime.now() - assert fabricator.device.parseGcode(file), f"Failed to parse Gcode on {fabricator.getDescription()}" + with open(file, "r") as f: + fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name),fabricator.dbID) + fabricator.begin() time = datetime.now() - time minutes, seconds = divmod(time.seconds, 60) fabricator.device.serialConnection.write(b"M31\n") @@ -138,3 +169,23 @@ def test_gcode(self, function_setup): timeBoundary = max(120, expectedTime // 5) assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" + + # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + # @pytest.mark.order(9) + # def test_add_job(self, function_setup): + # fabricator = function_setup + # file = "../server/xyz-cali-cube" + # if self.shortTest: + # file = file + "-mini" + # if isinstance(fabricator.device, Ender3): + # file = file + "_ENDER3.gcode" + # elif isinstance(fabricator.device, PrusaMK4S): + # file = file + "_MK4S.gcode" + # elif isinstance(fabricator.device, PrusaMK4): + # file = file + "_MK4.gcode" + # with open(file, "r") as f: + # assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name),3), f"Failed to add job on {fabricator.getDescription()}" + # for job in fabricator.queue.getQueue(): + # assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" + # fabricator.queue.removeJob() + # assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" \ No newline at end of file diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 9d82ea24..381af993 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -5,6 +5,7 @@ from typing_extensions import Buffer from Classes.Vector3 import Vector3 +from Classes.Logger import Logger import serial import serial.tools.list_ports @@ -28,6 +29,9 @@ class Device(ABC): def __init__(self, serialPort: ListPortInfo | SysFS): self.serialPort = serialPort self.serialID = serialPort.serial_number + self.logger = Logger(self.serialPort.device, self.DESCRIPTION) + self.status = "idle" + self.verdict = "" def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 31d293fc..4c9926ed 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -56,7 +56,7 @@ def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = F db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, {self.description}, {self.hwid}, {self.devicePort}, {self.status}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}" @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: """returns the model of the printer based on the response to M997""" @@ -153,18 +153,30 @@ def resume(self): def cancel(self): """cancels the fabrication process""" - if self.status != "printing" and self.status != "paused": - print("status:", self.status) - return #TODO: return error message - self.setStatus("cancelled") - assert isinstance(self.device, Device) - return self.status == self.device.status == "cancelled" + try: + assert self.job is not None + assert self.device is not None + if self.status != "printing" and self.status != "paused": + self.device.logger.error(f"Fabricator isn't in the middle of a job: current status: {self.status}") + return #TODO: return error message + self.setStatus("cancelled") + return self.status == self.device.status == "cancelled" + except Exception as e: + if self.device is None: + print(e) + else: + self.device.logger.error("Error cancelling job:") + self.device.logger.error(e) + return False def getStatus(self): return self.status def setStatus(self, newStatus): try: + assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint"] + assert self.device is not None + if self.status == "error" and newStatus!= "error": self.device.hardReset(newStatus) self.status = newStatus @@ -178,7 +190,11 @@ def setStatus(self, newStatus): # ) return True except Exception as e: - print("Error setting status:", e) + if self.device is None: + print(e) + else: + self.device.logger.error("Error setting status:") + self.device.logger.error(e) return False def resetToIdle(self): diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index 7f2f99f5..bf78e4b5 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -9,11 +9,11 @@ class PrusaMK4(PrusaPrinter): homePosition = Vector3(14.0, -4.0, 2.0) def endSequence(self): - # self.gcodeEnding("{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}") self.sendGcode(b"M104 S0\n") # ; turn off temperature self.sendGcode(b"M140 S0\n") # ; turn off heatbed self.sendGcode(b"M107\n") # ; turn off fan self.sendGcode(b"G1 X241 Y170 F3600") + self.sendGcode(b"M84\n") def getPrintTime(self): pass diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 2b04a534..cc10f553 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -2,7 +2,6 @@ import os import sys import traceback - from _pytest._code.code import ExceptionChainRepr @@ -13,23 +12,32 @@ class Logger(logging.Logger): ERROR = logging.ERROR CRITICAL = logging.CRITICAL - def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None): + def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): super().__init__(f"Logger_{port}_{deviceName}") - console_handler = logging.StreamHandler(consoleLogger) - console_handler.setFormatter(CustomFormatter("%(asctime)s - %(message)s")) - self.addHandler(console_handler) - self.setLevel(logging.INFO) # Adjust this as needed + info = [] + if showDate: + info.append("%(asctime)s") + if showLevel: + info.append("%(levelname)s") + if showFile: + info.append("%(module)s.%(funcName)s:%(lineno)d") + formatString = " - ".join(info + ["%(message)s"]).lstrip(" - ") + if consoleLogger is not None: + console_handler = logging.StreamHandler(consoleLogger) + console_handler.setFormatter(CustomFormatter(formatString)) + self.addHandler(console_handler) if fileLogger is None: log_folder = "./server/logs" os.makedirs(log_folder, exist_ok=True) - subfolder = os.path.join(log_folder, port) + subfolder = os.path.join(log_folder, deviceName) os.makedirs(subfolder, exist_ok=True) - fileLogger = logging.FileHandler(os.path.join(subfolder, f"test_{port}.log")) + from datetime import datetime + fileLogger = logging.FileHandler(os.path.join(subfolder, f"{datetime.now().strftime('%m-%d-%Y__%H-%M-%S')}.log")) else: fileLogger = logging.FileHandler(fileLogger) - fileLogger.setFormatter(logging.Formatter("%(message)s")) + fileLogger.setFormatter(logging.Formatter(formatString)) self.addHandler(fileLogger) - self.setLevel(logging.INFO) # Adjust this as needed + self.setLevel(loggingLevel) def formatLog(self, msg): if isinstance(msg, str): @@ -47,32 +55,32 @@ def formatLog(self, msg): msg = str(msg) return msg - def info(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + def info(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): """Log a message with level INFO and append `end` after the message.""" msg = self.formatLog(msg) - super().info(msg + end, *args, **kwargs) + super().info(msg + end, *args, **kwargs, stacklevel=stacklevel) - def debug(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + def debug(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): """Log a message with level DEBUG and append `end` after the message.""" msg = self.formatLog(msg) - super().debug(msg + end, *args, **kwargs) + super().debug(msg + end, *args, **kwargs, stacklevel=stacklevel) - def warning(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + def warning(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): """Log a message with level WARNING and append `end` after the message.""" msg = self.formatLog(msg) - super().warning(msg + end, *args, **kwargs) + super().warning(msg + end, *args, **kwargs, stacklevel=stacklevel) - def error(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + def error(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): """Log a message with level ERROR and append `end` after the message.""" msg = self.formatLog(msg) - super().error(msg + end, *args, **kwargs) + super().error(msg + end, *args, **kwargs, stacklevel=stacklevel) - def critical(self, msg: str | Exception | ExceptionChainRepr, *args, end='', **kwargs): + def critical(self, msg: str | Exception | ExceptionChainRepr, end='',stacklevel: int = 2, *args, **kwargs): """Log a message with level CRITICAL and append `end` after the message.""" msg = self.formatLog(msg) - super().critical(msg + end, *args, **kwargs) + super().critical(msg + end, *args, **kwargs, stacklevel=stacklevel) - def logMessageOnly(self, msg: str, logLevel: int = None, *args, **kwargs): + def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *args, **kwargs): if logLevel is None: logLevel = self.level """Log a message without any additional formatting.""" @@ -80,15 +88,15 @@ def logMessageOnly(self, msg: str, logLevel: int = None, *args, **kwargs): for handler in self.handlers: handler.setFormatter(CustomFormatter("%(message)s")) if logLevel == self.DEBUG: - self.debug(msg, *args, **kwargs) + self.debug(msg, stacklevel=stacklevel, *args, **kwargs) elif logLevel == self.INFO: - self.info(msg, *args, **kwargs) + self.info(msg, stacklevel=stacklevel, *args, **kwargs) elif logLevel == self.WARNING: - self.warning(msg, *args, **kwargs) + self.warning(msg, stacklevel=stacklevel, *args, **kwargs) elif logLevel == self.ERROR: - self.error(msg, *args, **kwargs) + self.error(msg, stacklevel=stacklevel, *args, **kwargs) elif logLevel == self.CRITICAL: - self.critical(msg, *args, **kwargs) + self.critical(msg, stacklevel=stacklevel, *args, **kwargs) for handler, formatter in zip(self.handlers, oldFormatters): handler.setFormatter(formatter) diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index 16c00c57..d277d230 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -1,4 +1,3 @@ -import logging from abc import ABCMeta from time import sleep from typing_extensions import Buffer @@ -26,7 +25,8 @@ class usesMarlinGcode(canPause, hasResponsecodes, metaclass=ABCMeta): "G0": [alwaysTrue, checkOK], # Move "G1": [alwaysTrue, checkOK], # Move "G28": [alwaysTrue, checkXYZ], # Home - "G29": [checkXYZ, checkXYZ], # Auto bed leveling + "G29 P1": [alwaysTrue, checkOK], # Auto bed leveling + "G29 P9": [checkOK, checkOK], # Auto bed leveling "M73": [alwaysTrue, checkOK], # Set build percentage "M104": [alwaysTrue, alwaysTrue], # Set hotend temp "M109": [alwaysTrue, checkExtruderTemp], # Wait for hotend to reach target temp @@ -53,6 +53,7 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): for line in f: if line.startswith(";") or line == "\n": continue + if isVerbose: self.logger.debug(line.strip("\n")) if self.status == "paused": self.pause() while self.status == "paused": @@ -69,13 +70,15 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): self.endSequence() self.verdict = "cancelled" return True - if isVerbose: - print(line, end="") self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" return True except Exception as e: - print(e) + if self.logger is None: + print(e) + else: + self.logger.error("Error cancelling job:") + self.logger.error(e) self.verdict = "error" return True @@ -84,6 +87,15 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): assert self.serialConnection.is_open assert isinstance(gcode, bytes) self.serialConnection.write(gcode) + hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] + if hashIndex == "G29": + g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] + if g29addon == "P9": + hashIndex += " " + g29addon + else: + hashIndex += " P1" + elif hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) if callables[0] != alwaysTrue: while not callables[0](self.serialConnection.readline()): @@ -94,10 +106,12 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): line = self.serialConnection.readline() if "processing" in line.decode("utf-8"): continue - if isVerbose: print(line) + if isVerbose: self.logger.debug(line) if callables[1](line): + self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) return True except Exception as e: + self.logger.error(e) return False @@ -106,43 +120,60 @@ def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") - if isVerbose: print(response) + if isVerbose: self.logger.info(response) loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) def home(self, isVerbose: bool = False): - self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) - assert isinstance(self, Device) - return self.getHomePosition() == self.getToolHeadLocation() + try: + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) + return self.getHomePosition() == self.getToolHeadLocation() + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error homing:") + self.logger.error(e) + return def pause(self): - assert isinstance(self, canPause) - assert isinstance(self, Device) - assert self.serialConnection is not None - assert self.serialConnection.is_open - assert self.status == "printing" try: + assert isinstance(self, canPause) + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + assert self.status == "printing" self.sendGcode(usesMarlinGcode.keepAliveCMD) self.sendGcode(usesMarlinGcode.pauseCMD) return True except Exception as e: - print(e) + if self.logger is None: + print(e) + else: + self.logger.error("Error pausing job:") + self.logger.error(e) return False def resume(self): - assert isinstance(self, canPause) - assert isinstance(self, Device) - assert self.serialConnection is not None - assert self.serialConnection.is_open - assert self.status == "paused" try: + assert isinstance(self, canPause) + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + assert self.status == "paused" self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) self.sendGcode(usesMarlinGcode.resumeCMD) return True except Exception as e: - print(e) + if self.logger is None: + print(e) + else: + self.logger.error("Error resuming job:") + self.logger.error(e) return False @@ -150,14 +181,20 @@ def connect(self: Device): Device.connect(self) try: if self.serialConnection: - #self.serialConnection.write(b"M155 S1\n") + self.serialConnection.write(b"M155 S1\n") return True except Exception as e: - return e + if self.logger is None: + print(e) + else: + self.logger.error("Error connecting:") + self.logger.error(e) + return False def disconnect(self: Device): if self.serialConnection and self.serialConnection.is_open: + self.sendGcode(b"M155 S100\n") self.sendGcode(b"M155 S0\n") self.sendGcode(b"M104 S0\n") self.sendGcode(b"M140 S0\n") diff --git a/server/pauseAndResumeTest.gcode b/server/pauseAndResumeTest.gcode new file mode 100644 index 00000000..f673d91a --- /dev/null +++ b/server/pauseAndResumeTest.gcode @@ -0,0 +1,201 @@ +G28 ; home all axes + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point + +G1 X50 Y50 Z2 F36000 ; move to a point +G1 X200 Y50 Z2 F36000 ; move to a point +G1 X200 Y150 Z2 F36000 ; move to a point +G1 X50 Y150 Z2 F36000 ; move to a point \ No newline at end of file diff --git a/server/test.py b/server/test.py index 210376ae..ed0b32b5 100644 --- a/server/test.py +++ b/server/test.py @@ -1,10 +1,11 @@ -import re +#import re from datetime import datetime -from Classes.FabricatorList import FabricatorList +#from Classes.FabricatorList import FabricatorList from Classes.Fabricators.Fabricator import Fabricator +from Classes.Jobs import Job from Classes.Vector3 import Vector3 from Classes.Ports import Ports -from app import app +#from app import app red = '\033[31m' green = '\033[32m' @@ -14,151 +15,148 @@ cyan = '\033[36m' reset = '\033[0m' -def printColor(color, message): - print(color + message + reset) - + +def color_string(color, message): + return color + message + reset + + def test(testToRun: bool): if testToRun: - printColor(green, "Pass") - return 1 + return color_string(green, "Pass") else: - printColor(red, "Fail") - return 0 + color_string(red, "Fail") + def runTests(fabricator: Fabricator, isVerbose=False): # setup Tests tests = 0 passes = 0 - print(f"Testing {fabricator.getDescription()}") + logger = fabricator.device.logger + logger.info(f"Testing {fabricator.getDescription()}") # run tests tests += 1 - print(f"test {tests}: Connect", end=" ") - passes += test(fabricator.device is not None) + result = test(fabricator.device is not None) + passes += 1 if result else 0 + logger.info(f"test {tests}: Connect {result}", end=" ") + # tests += 1 - # print(f"test {tests}: Home", end=" ") + # logger.info(f"test {tests}: Home", end=" ") # passes += test(fabricator.device.home(isVerbose=isVerbose)) # # tests += 1 - # print(f"test {tests}: Square", end=" ") + # logger.info(f"test {tests}: Square", end=" ") # squarePasses = 0 # for point in patterns[0]: # squarePasses += 1 if fabricator.device.goTo(point) else 0 # passes += test(squarePasses == len(patterns[0])) # # tests += 1 - # print(f"test {tests}: Octagon", end=" ") + # logger.info(f"test {tests}: Octagon", end=" ") # squarePasses = 0 # for point in patterns[1]: # squarePasses += 1 if fabricator.device.goTo(point) else 0 # passes += test(squarePasses == len(patterns[1])) # tests += 1 - # print(f"test {tests}: Center", end=" ") + # logger.info(f"test {tests}: Center", end=" ") # passes += test(fabricator.device.goTo(endLocation)) tests += 1 - print(f"test {tests}: Parse Gcode", end=" ") - expectedTime = 3 * 60 + expectedTime = 9 * 60 + 15 expectedMinutes, expectedSeconds = divmod(expectedTime, 60) time = datetime.now() - passes += test(fabricator.device.parseGcode("server/xyz-cali-cube-mini_MK4.gcode", isVerbose=True)) + file = "server/xyz-cali-cube-mini_MK4.gcode" + with open(file, "r") as f: + fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name), fabricator.dbID) + fabricator.begin() time = datetime.now() - time minutes, seconds = divmod(time.seconds, 60) - print(f"\texpect print time: {int(expectedMinutes):02}:{int(expectedSeconds):02}") - print(f"\tactual print time: {int(minutes):02}:{int(seconds):02}") + logger.info(f"\texpect print time: {int(expectedMinutes):02}:{int(expectedSeconds):02}") + logger.info(f"\tactual print time: {int(minutes):02}:{int(seconds):02}") + result = test(fabricator.status == "complete") + passes += 1 if result else 0 + logger.info(f"test {tests}: Parse Gcode {result}", end=" ") # tests += 1 - # print(f"test {tests}: Pause", end=" ") + # logger.info(f"test {tests}: Pause", end=" ") # passes += test(fabricator.device.pause()) # # tests += 1 - # print(f"test {tests}: Resume", end=" ") + # logger.info(f"test {tests}: Resume", end=" ") # passes += test(fabricator.device.resume()) # # tests += 1 - # print(f"test {tests}: Cancel", end=" ") + # logger.info(f"test {tests}: Cancel", end=" ") # passes += test(fabricator.device.cancel()) # # # tests += 1 - # print(f"test {tests}: Status", end=" ") + # logger.info(f"test {tests}: Status", end=" ") # passes += test(fabricator.device.status()) # teardown tests fabricator.device.disconnect() if passes == tests: - printColor(green, f"All tests passed ({passes}/{tests})") + logger.info(color_string(green, f"All tests passed ({passes}/{tests})")) else: - printColor(red, f"{passes}/{tests} tests passed") - - -endLocation = Vector3(125.0,100.0,2.0) -patterns = [[Vector3(50.0,50.0,2.0), Vector3(200.0,50.0,2.0), Vector3(200.0,150.0,2.0), Vector3(50.0,150.0,2.0)], #square - [Vector3(50.0,100.0,2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0,50.0,2.0), Vector3(200.0, 100.0, 2), Vector3(200.0,150.0,2.0), Vector3(150.0, 200.0, 2), Vector3(100.0,200.0,2.0), Vector3(50.0, 150.0, 2.0)], # octagon - ] -with app.app_context(): - PrusaMK4S = None - PrusaMK4 = None - Ender3 = None - MakerBot = None - EnderPro = None - - # FabricatorList.init() - # for printer in FabricatorList.fabricators: - # runTests(printer) - - # ari's MK4S - if Ports.getPortByName("COM5") is not None: - PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S", addToDB=False) - runTests(PrusaMK4S) - - # nate's Ender 3 Pro - if Ports.getPortByName("COM6") is not None: - EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro", addToDB=False) - runTests(EnderPro) - - # school prusa mk4 - if Ports.getPortByName("COM3") is not None: - PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4", addToDB=False) - PrusaMK4.device.serialConnection.write(b"M31\n") - line = "" - while True: - try: - line = PrusaMK4.device.serialConnection.readline().decode("utf-8") - if re.search(r"\d+m \d+s", line): - printColor(green, line) - break - print(line, end="") - except KeyboardInterrupt: - break - min, sec = map(int, re.findall(r"\d+", line)) - printTime = min * 60 + sec - print(f"Print time: {min:02}:{sec:02}") - #runTests(PrusaMK4) - - # school Ender 3 - if Ports.getPortByName("COM8") is not None: - Ender3 = Fabricator(Ports.getPortByName("COM8"), "Ender 3", addToDB=False) - #runTests(Ender3) - #Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) - - # school Makerbot - if Ports.getPortByName("COM7") is not None: - MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot", addToDB=False) - runTests(MakerBot) - - if PrusaMK4 is not None and PrusaMK4.device.serialConnection.is_open: - PrusaMK4.device.disconnect() - - if Ender3 is not None and Ender3.device.serialConnection.is_open: - Ender3.device.disconnect() - - if MakerBot is not None and MakerBot.device.serialConnection.is_open: - MakerBot.device.disconnect() - - if EnderPro is not None and EnderPro.device.serialConnection.is_open: - EnderPro.device.disconnect() - - + logger.info(color_string(red, f"{passes}/{tests} tests passed")) + + +endLocation = Vector3(125.0, 100.0, 2.0) +patterns = [ + [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), Vector3(50.0, 150.0, 2.0)], + # square + [Vector3(50.0, 100.0, 2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0, 50.0, 2.0), Vector3(200.0, 100.0, 2), + Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)], + # octagon + ] +# with app.app_context(): +PrusaMK4S = None +PrusaMK4 = None +Ender3 = None +MakerBot = None +EnderPro = None + +# FabricatorList.init() +# for printer in FabricatorList.fabricators: +# runTests(printer) + +# ari's MK4S +if Ports.getPortByName("COM5") is not None: + PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S", addToDB=False) + #runTests(PrusaMK4S) + +# nate's Ender 3 Pro +if Ports.getPortByName("COM6") is not None: + EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro", addToDB=False) + #runTests(EnderPro) + +# school prusa mk4 +if Ports.getPortByName("COM3") is not None: + PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4", addToDB=False) + runTests(PrusaMK4) + +# school Ender 3 +if Ports.getPortByName("COM4") is not None: + Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3", addToDB=False) + # runTests(Ender3) + # Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) + +# school Makerbot +if Ports.getPortByName("COM7") is not None: + MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot", addToDB=False) + #runTests(MakerBot) + +if PrusaMK4 is not None and PrusaMK4.device.serialConnection.is_open: + PrusaMK4.device.disconnect() + +if Ender3 is not None and Ender3.device.serialConnection.is_open: + Ender3.device.disconnect() + +if MakerBot is not None and MakerBot.device.serialConnection.is_open: + MakerBot.device.disconnect() + +if EnderPro is not None and EnderPro.device.serialConnection.is_open: + EnderPro.device.disconnect() From a240aca96ae0a5e92570f487096ddacbe74be2e4 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 6 Nov 2024 20:00:15 -0500 Subject: [PATCH 040/194] added new requirement to make tests skip if something they are dependent on fail. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 5c0c93d0..bd3566dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ pluggy~=1.5.0 pyserial==3.5 pytest==8.3.3 pytest-order==1.3.0 +pytest-dependency~=0.6.0 python-dotenv==1.0.1 Requests==2.32.3 SQLAlchemy~=2.0.34 From 0d9a72eec85a0e53aa0d610aeb1543f6a1b3e4d4 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 6 Nov 2024 20:02:03 -0500 Subject: [PATCH 041/194] added more logging lines to keep track of what is happening. --- server/Mixins/usesMarlinGcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index d277d230..74d8fe81 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -50,6 +50,7 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): assert isinstance(self, Device) try: with open(file, "r") as f: + self.logger.info(f"Printing {file}") for line in f: if line.startswith(";") or line == "\n": continue @@ -62,6 +63,7 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" + self.logger.info("Job cancelled") return True elif self.status == "printing": self.resume() @@ -69,9 +71,11 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" + self.logger.info("Job cancelled") return True self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" + self.logger.info("Job complete") return True except Exception as e: if self.logger is None: @@ -149,6 +153,7 @@ def pause(self): assert self.status == "printing" self.sendGcode(usesMarlinGcode.keepAliveCMD) self.sendGcode(usesMarlinGcode.pauseCMD) + self.logger.info("Job Paused") return True except Exception as e: if self.logger is None: @@ -167,6 +172,7 @@ def resume(self): assert self.status == "paused" self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) self.sendGcode(usesMarlinGcode.resumeCMD) + self.logger.info("Job Resumed") return True except Exception as e: if self.logger is None: From 3302330bfea595dd82ed58971f17a2a0b8dc7b87 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 6 Nov 2024 20:02:26 -0500 Subject: [PATCH 042/194] test logging completed --- Tests/conftest.py | 150 ++++++++++++++++++++-------------- Tests/parallel_test_runner.py | 35 ++++---- Tests/test_runner.py | 75 ++++++++++------- server/Classes/Logger.py | 11 ++- 4 files changed, 159 insertions(+), 112 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 5c84aa41..1a6d75d6 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -14,6 +14,12 @@ def pytest_addoption(parser): default=1, help="my verbose level" ) + parser.addoption( + "--port", + action="store", + default=None, + help="port to test" + ) def line_separator(interrupter: str, symbol: str = "-", length: int = 80) -> str: if not interrupter: @@ -33,59 +39,58 @@ def setup_logger(port): log_file_path = os.path.join(subfolder, f"test_{port}.log") from Classes.Logger import Logger - return Logger(port, "Test Printer", consoleLogger=sys.stdout, fileLogger=log_file_path) + return Logger(port, "Test Printer", consoleLogger=sys.stdout, fileLogger=log_file_path, showFile=False, showLevel=False) @pytest.hookimpl(tryfirst=True) def pytest_sessionstart(session) -> None: - session.config.port = os.getenv("PORT") session.config.start_time = time.time() session.config.passed_count = 0 session.config.failed_count = 0 session.config.skipped_count = 0 + session.config.xfailed_count = 0 + session.config.xpassed_count = 0 session.config.failNames = [] session.config.fails = {} session.config.logger = setup_logger(session.config.port) - logger = session.config.logger - logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") - verinfo = platform.python_version() - msg = f"platform {sys.platform} -- Python {verinfo}" - pypy_version_info = getattr(sys, "pypy_version_info", None) - if pypy_version_info: - verinfo = ".".join(map(str, pypy_version_info[:3])) - msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" - msg += f", pytest-{pytest.__version__}, pluggy-{pluggy.__version__}" - logger.logMessageOnly(msg) - logger.logMessageOnly(f"rootdir: {session.config.rootdir}") - logger.logMessageOnly(f"verbosity: {session.config.verbosity}") + if session.config.verbosity > 0: + logger = session.config.logger + logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") + verinfo = platform.python_version() + msg = f"platform {sys.platform} -- Python {verinfo}" + pypy_version_info = getattr(sys, "pypy_version_info", None) + if pypy_version_info: + verinfo = ".".join(map(str, pypy_version_info[:3])) + msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" + msg += f", pytest-{pytest.__version__}, pluggy-{pluggy.__version__}" + logger.logMessageOnly(msg) + logger.logMessageOnly(f"rootdir: {session.config.rootdir}") - - session.config.logger = logger +def pytest_collection_modifyitems(session, config, items): + session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...", end="\n") def pytest_sessionfinish(session, exitstatus) -> None: session_duration = time.time() - session.config.start_time - passes = session.config.passed_count - fails = session.config.failed_count - skips = session.config.skipped_count - logger = session.config.logger - - summary = f"" - if passes == 0 and fails == 0 and skips == 0: - pass - elif passes > 0 and fails == 0 and skips == 0: - summary += f"\033[32m\033[1m{passes} passed\033[0m" - elif passes == 0 and fails > 0 and skips == 0: - summary += f"\033[31m\033[1m{fails} failed\033[0m" - elif passes == 0 and fails == 0 and skips > 0: - summary += f"\033[33m{skips} skipped\033[0m" - elif passes > 0 and fails > 0 and skips == 0: - summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[31m\033[1m{fails} failed\033[0m" - elif passes > 0 and fails == 0 and skips > 0: - summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[33m{skips} skipped\033[0m" - elif passes == 0 and fails > 0 and skips > 0: - summary += f"\033[31m\033[1m{fails} failed,\033[0m \033[33m{skips} skipped\033[0m" - elif passes > 0 and fails > 0 and skips > 0: - summary += f"\033[32m\033[1m{passes} passed,\033[0m \033[31m\033[1m{fails} failed,\033[0m \033[33m{skips} skipped\033[0m" + passes = session.config.passed_count + fails = session.config.failed_count + skips = session.config.skipped_count + xfails = session.config.xfailed_count + xpasses = session.config.xpassed_count + logger = session.config.logger + + summary = "" + stats = [] + if passes > 0: stats.append(f"\033[32m\033[1m{passes} passed") + if fails > 0: stats.append(f"\033[31m\033[1m{fails} failed") + if skips > 0: stats.append(f"\033[33m{skips} skipped") + if xfails > 0: stats.append(f"\033[33m{xfails} xfailed") + if xpasses > 0: stats.append(f"\031[33m{xpasses} xpassed") + + if len(stats) > 0: + summary = ", ".join(stats) + else: + summary = "\033[33mno tests ran" + summary += f"\033[32m in {session_duration:.2f}s" if session_duration > 3600: summary += f" ({session_duration // 3600:.0f}:{session_duration % 3600 // 60:.0f}:{session_duration % 60:.2f})" @@ -98,8 +103,10 @@ def pytest_sessionfinish(session, exitstatus) -> None: for failTest in session.config.failNames: logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) #todo: break this out into something that can be called anywhere for the Logger class - args = list(session.config.fails[failTest].reprtraceback.reprentries[0].reprfuncargs.args)[0] - logger.logMessageOnly(f"{args[0]} = {args[1]}\n") + things = list(session.config.fails[failTest].reprtraceback.reprentries[0].reprfuncargs.args) + for args in things: + logger.logMessageOnly(f"{args[0]} = {args[1]}") + logger.logMessageOnly("") for line in list(session.config.fails[failTest].reprtraceback.reprentries[0].lines): if line.startswith("E") or line.startswith(">"): logger.logMessageOnly(line.__str__(), logLevel=logger.ERROR) @@ -111,15 +118,13 @@ def pytest_sessionfinish(session, exitstatus) -> None: logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) def pytest_configure(config): - port = os.getenv("PORT") - config.port = port - cli_args = config.invocation_params.args - for arg in cli_args: - if arg.startswith("--myVerbose="): + for arg in config.invocation_params.args: + if not hasattr(config, "verbosity") and arg.startswith("--myVerbose="): config.verbosity = int(arg.split("=")[1]) - break - if not hasattr(config, "verbosity"): - config.verbosity = 1 + elif not hasattr(config, "port") and arg.startswith("--port="): + config.port = arg.split("=")[1] + elif not hasattr(config, "testLevel") and arg.startswith("--testLevel="): + config.testLevel = int(arg.split("=")[1]) if config.verbosity > 2: config.verbosity = 2 @@ -128,16 +133,27 @@ def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() # Retrieve the TestReport object # Only check the outcome after the "call" phase (i.e., after the test ran) + if (report.when == "setup" and report.skipped) or (report.when == "call"): if report.passed: - item.config.passed_count += 1 + if hasattr(report, "wasxfail"): + report.outcome = "xpassed" + report.xpassed = True + item.config.xpassed_count += 1 + else: + item.config.passed_count += 1 elif report.failed: item.config.failed_count += 1 failName = report.nodeid.split("::")[1] + "." + item.name item.config.failNames.append(failName) item.config.fails[failName] = report.longrepr elif report.skipped: - item.config.skipped_count += 1 + if hasattr(report, "wasxfail"): + report.outcome = "xfailed" + report.xfailed = True + item.config.xfailed_count += 1 + else: + item.config.skipped_count += 1 report.port = item.config.port report.verbosity = item.config.verbosity report.logger = item.config.logger @@ -147,31 +163,43 @@ def pytest_runtest_logreport(report): verbosity = report.verbosity yield logger = report.logger + port = report.port if (report.when == "setup" and report.skipped) or (report.when == "call"): - port = report.port if port is None and "test_runner.py::TestFabricator::" in report.nodeid: # Retrieve port from the test function if it's set as an attribute from test_runner import fabricator_setup port = fabricator_setup(os.getenv("PORT")).devicePort - if verbosity <= 0: + + if verbosity == 0: if report.passed: - logger.info(f"\033[32m.\033[0m", end="") + logger.info("\033[32m.\033[0m") elif report.failed: - logger.info(f"\033[31mF\033[0m", end="") + logger.info("\033[31mF\033[0m") elif report.skipped: - logger.info(f"\033[33ms\033[0m", end="") + logger.info("\033[33ms\033[0m") + elif hasattr(report, "xfailed") and report.xfailed: + logger.info("\033[33mX\033[0m") + elif hasattr(report, "xpassed") and report.xpassed: + logger.info("\033[31mx\033[0m") elif verbosity == 1: loc = report.nodeid.split("::")[-1] + testString = f"{loc}[{port}]{' ' * (27 - len(loc) - len(str(port)) - 2)}" if report.passed: - logger.info(f"{loc}[{port}] \033[32mPASSED\033[0m") + logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: - logger.info(f"{loc}[{port}] \033[31mFAILED\033[0m") + logger.info(f"{testString} \033[31mFAILED\033[0m") elif report.skipped: - logger.info(f"{loc}[{port}] \033[33mSKIPPED\033[0m") + logger.info(f"{testString} \033[33mSKIPPED\033[0m") + elif hasattr(report, "xfailed") and report.xfailed: + logger.info(f"{testString} \033[33mXFAILED\033[0m") + elif hasattr(report, "xpassed") and report.xpassed: + logger.info(f"{testString} \033[31mXPASSED\033[0m") elif verbosity >= 2: + loc = report.nodeid + testString = f"{loc}[{port}]{' ' * (59 - len(loc) - len(str(port)) - 2)}" if report.passed: - logger.info(f"{report.nodeid}[{port}] \033[32mPASSED\033[0m") + logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: - logger.info(f"{report.nodeid}[{port}] \033[31mFAILED\033[0m:\n\n {report.longrepr}") + logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n {report.longrepr}") elif report.skipped: - logger.info(f"{report.nodeid}[{port}] \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") \ No newline at end of file + logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") \ No newline at end of file diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 0de6eaf5..62f1860a 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -1,4 +1,3 @@ -import os import re import subprocess import threading @@ -41,23 +40,23 @@ def printColor(color, message): print(color + message + reset) # Function to run pytest for a specific port +testLevel = 10 +verbosity = 2 +showAnything = False +verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" def run_tests_for_port(comm_port): - env = os.environ.copy() - verbosity = 1 - env["PORT"] = comm_port - showAnything = False - verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" - subprocess.Popen(["pytest", "test_runner.py", verbosityCommand, f"--myVerbose={verbosity}"], env=env).wait() + subprocess.Popen(["pytest", "test_runner.py", verbosityCommand, f"--myVerbose={verbosity}", f"--port={comm_port}"]).wait() -# Create and start a thread for each port -threads = [] -for port in PORTS: - if Ports.getPortByName(port) is None: - continue - thread = threading.Thread(target=run_tests_for_port, args=(port,)) - thread.start() - threads.append(thread) +if __name__ == "__main__": + # Create and start a thread for each port + threads = [] + for port in PORTS: + if Ports.getPortByName(port) is None: + continue + thread = threading.Thread(target=run_tests_for_port, args=(port,)) + thread.start() + threads.append(thread) -# Wait for all threads to complete -for thread in threads: - thread.join() + # Wait for all threads to complete + for thread in threads: + thread.join() \ No newline at end of file diff --git a/Tests/test_runner.py b/Tests/test_runner.py index 9766c5e1..00114347 100644 --- a/Tests/test_runner.py +++ b/Tests/test_runner.py @@ -1,4 +1,5 @@ import os +import sys from datetime import datetime import re import pytest @@ -13,10 +14,11 @@ def fabricator_setup(port): if not port: return None - return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False) + return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) class TestFabricator: - testLevelToRun = 1 + from parallel_test_runner import testLevel + testLevelToRun = testLevel shortTest = True def __repr__(self): @@ -32,25 +34,28 @@ def function_setup(cls, request): fabricator_instance.device.disconnect() + @pytest.mark.dependency(name="test_connection") @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") @pytest.mark.order(1) def test_connection(self, function_setup): fabricator = function_setup assert fabricator.device is not None, f"No printer connected on {fabricator.getDescription()}" + @pytest.mark.dependency(name="test_home", depends=["test_connection"]) @pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") @pytest.mark.order(2) def test_home(self, function_setup): fabricator = function_setup assert fabricator.device.home(), f"Failed to home {fabricator.getDescription()}" - @pytest.mark.xfail(reason="Expected to fail") - @pytest.mark.order(2) - def test_xfail(self, function_setup): - fabricator = function_setup - assert False, f"Expected to fail on {fabricator.getDescription()}" + # @pytest.mark.xfail(reason="Expected to fail") + # @pytest.mark.order(2) + # def test_xfail(self, function_setup): + # fabricator = function_setup + # assert False, f"Expected to fail on {fabricator.getDescription()}" @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.order(3) def test_square(self, function_setup): fabricator = function_setup @@ -62,6 +67,7 @@ def test_square(self, function_setup): @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.order(4) def test_octagon(self, function_setup): fabricator = function_setup @@ -74,6 +80,7 @@ def test_octagon(self, function_setup): @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.order(5) def test_center(self, function_setup): fabricator = function_setup @@ -87,8 +94,9 @@ def test_center(self, function_setup): # assert fabricator.device.parseGcode( # "../server/heatTest.gcode"), f"Failed to parse Gcode on {fabricator.getDescription()}" - @pytest.mark.order(7) - @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + @pytest.mark.order(8) + @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") + @pytest.mark.dependency(depends=["test_home", "test_add_job"]) def test_pause_and_resume(self, function_setup): fabricator = function_setup if not isinstance(fabricator.device, canPause): @@ -125,7 +133,8 @@ def pause_and_resume_fabricator(): pause_thread.join() - @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") + @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.dependency(depends=["test_connection"]) @pytest.mark.order(8) def test_status(self, function_setup): fabricator = function_setup @@ -134,7 +143,31 @@ def test_status(self, function_setup): assert fabricator.getStatus() == fabricator.device.status, f"Internal status mismatch: fabricator: {fabricator.getDescription()}, device: {fabricator.device.status}" assert fabricator.getStatus() == "idle", f"Status incorrect at {fabricator.getDescription()}, expected idle, got {fabricator.getStatus()}" + @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") + @pytest.mark.dependency(name="test_add_job", depends=["test_connection"]) + @pytest.mark.order(7) + def test_add_job(self, function_setup): + fabricator = function_setup + file = "../server/xyz-cali-cube" + if self.shortTest: + file = file + "-mini" + if isinstance(fabricator.device, Ender3): + file = file + "_ENDER3.gcode" + elif isinstance(fabricator.device, PrusaMK4S): + file = file + "_MK4S.gcode" + elif isinstance(fabricator.device, PrusaMK4): + file = file + "_MK4.gcode" + with open(file, "r") as f: + assert fabricator.queue.addToFront( + Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name), + 3), f"Failed to add job on {fabricator.getDescription()}" + for job in fabricator.queue.getQueue(): + assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" + fabricator.queue.removeJob() + assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" + @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") + @pytest.mark.dependency(depends=["test_home", "test_add_job"]) @pytest.mark.order(10) def test_gcode_print_time(self, function_setup): fabricator = function_setup @@ -168,24 +201,4 @@ def test_gcode_print_time(self, function_setup): timeBoundary = max(120, expectedTime // 5) assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" - assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" - - # @pytest.mark.skipif(condition=testLevelToRun < 10, reason="Not doing lvl 10 tests") - # @pytest.mark.order(9) - # def test_add_job(self, function_setup): - # fabricator = function_setup - # file = "../server/xyz-cali-cube" - # if self.shortTest: - # file = file + "-mini" - # if isinstance(fabricator.device, Ender3): - # file = file + "_ENDER3.gcode" - # elif isinstance(fabricator.device, PrusaMK4S): - # file = file + "_MK4S.gcode" - # elif isinstance(fabricator.device, PrusaMK4): - # file = file + "_MK4.gcode" - # with open(file, "r") as f: - # assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name),3), f"Failed to add job on {fabricator.getDescription()}" - # for job in fabricator.queue.getQueue(): - # assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" - # fabricator.queue.removeJob() - # assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" \ No newline at end of file + assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" \ No newline at end of file diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index cc10f553..0f629bd4 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -12,8 +12,9 @@ class Logger(logging.Logger): ERROR = logging.ERROR CRITICAL = logging.CRITICAL - def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): + def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): super().__init__(f"Logger_{port}_{deviceName}") + self.setLevel(loggingLevel) info = [] if showDate: info.append("%(asctime)s") @@ -25,6 +26,12 @@ def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None, if consoleLogger is not None: console_handler = logging.StreamHandler(consoleLogger) console_handler.setFormatter(CustomFormatter(formatString)) + console_handler.setLevel(loggingLevel) + self.addHandler(console_handler) + else: + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(CustomFormatter(formatString)) + console_handler.setLevel(self.ERROR) self.addHandler(console_handler) if fileLogger is None: log_folder = "./server/logs" @@ -36,8 +43,8 @@ def __init__(self, port, deviceName, consoleLogger=sys.stdout, fileLogger=None, else: fileLogger = logging.FileHandler(fileLogger) fileLogger.setFormatter(logging.Formatter(formatString)) + fileLogger.setLevel(loggingLevel) self.addHandler(fileLogger) - self.setLevel(loggingLevel) def formatLog(self, msg): if isinstance(msg, str): From 12d56578048b20f82f7ddc5309e22f727072290f Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 7 Nov 2024 19:22:52 -0500 Subject: [PATCH 043/194] feat: switch to websockets and add emulator test server --- .gitignore | 4 +- printeremu/go.mod | 7 + printeremu/go.sum | 65 +++ printeremu/src/emulator.go | 97 ++--- printeremu/src/gcode_commands.go | 69 ++-- printeremu/src/printer.go | 93 +++-- printeremu/src/socketio_client.go | 190 +++------ printeremu/testserver/package-lock.json | 508 ++++++++++++++++++++++++ printeremu/testserver/package.json | 20 + printeremu/testserver/src/index.ts | 30 ++ printeremu/testserver/tsconfig.json | 13 + 11 files changed, 808 insertions(+), 288 deletions(-) create mode 100644 printeremu/testserver/package-lock.json create mode 100644 printeremu/testserver/package.json create mode 100644 printeremu/testserver/src/index.ts create mode 100644 printeremu/testserver/tsconfig.json diff --git a/.gitignore b/.gitignore index 3f190c95..9294921f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ migrations/ *.db server/qview3dserver.egg-info/ -server/dist \ No newline at end of file +server/dist + +printeremu/testserver/node_modules/ \ No newline at end of file diff --git a/printeremu/go.mod b/printeremu/go.mod index bf706a19..907810d2 100644 --- a/printeremu/go.mod +++ b/printeremu/go.mod @@ -3,3 +3,10 @@ module printeremu go 1.23.2 require github.com/gorilla/websocket v1.5.0 + +require ( + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gomodule/redigo v1.8.9 // indirect + github.com/googollee/go-socket.io v1.8.0-rc.1 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect +) diff --git a/printeremu/go.sum b/printeremu/go.sum index e5a03d4d..4ee0d513 100644 --- a/printeremu/go.sum +++ b/printeremu/go.sum @@ -1,2 +1,67 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= +github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/googollee/go-socket.io v1.6.0 h1:zbz0kEERgeYL/yEu9pBXSIyZEBluiNc2AJaMFmFjIOY= +github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= +github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8= +github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= +github.com/googollee/go-socket.io v1.8.0-rc.1 h1:Y5DV+pKDw2KFBtdEyxBp8mSuuU4XS3eHGNb2E3bLACI= +github.com/googollee/go-socket.io v1.8.0-rc.1/go.mod h1:oZhC7XylbziHxXhVdXvz6qQB0/jmNc4V3d9jgaEBKHI= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index f03e593a..dd6ff775 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -9,14 +9,15 @@ import ( "os" "strings" "time" + + "github.com/gorilla/websocket" ) -type PrintJob struct { - Command string +type Event struct { + Event string `json:"event"` + Data interface{} `json:"data"` } -var jobQueue = make(chan PrintJob) - // Main function to initialize and run the printer emulator func main() { extruder, printer, err := Init(1, "Device1", "Test Printer", "HWID1234", "Printer1", "Active") @@ -47,81 +48,43 @@ func Init(id int, device string, description string, hwid string, name string, s } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { - client := NewClient() - defer client.Close() - - client.On("connection", func(data interface{}) { - fmt.Println("Connected to backend!") - - RegisterPrinter(client, printer) - }) - - client.On("disconnect", func(data interface{}) { - log.Println("Disconnected from backend") - close(jobQueue) - }) - - client.On("error", func(data interface{}) { - if err, ok := data.(error); ok { - log.Println("Socket error:", err) - } - }) + // Connect to the WebSocket server + serverURL := "ws://127.0.0.1:8000" + conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) + if err != nil { + log.Fatal("Failed to connect to server:", err) + } + defer conn.Close() - log.Println("Attempting to connect...") - err := client.Connect("http://127.0.0.1:8000") - if err != nil { - log.Printf("Connection error: %v\n", err) - return - } + fmt.Println("Connected to WebSocket server") - go ProcessJobs(ctx, printer, client) + // Send a message to the server + err = conn.WriteMessage(websocket.TextMessage, []byte("Hello from Go client!")) + if err != nil { + log.Fatal("Failed to send message:", err) + } - <-ctx.Done() - log.Println("Connection stopping...") + // Read the server's response + _, message, err := conn.ReadMessage() + if err != nil { + log.Fatal("Failed to read message:", err) + } + + fmt.Println("Received from server:", string(message)) + + // Keep the connection alive + time.Sleep(5 * time.Second) } func RegisterPrinter(c *SocketIOClient, printer *Printer) { jsonPrinter, err := json.Marshal(printer) - if err != nil { log.Println("Failed to marshal printer object:", err) return } - log.Println("Emitting emuprintconnect event with data:", string(jsonPrinter)) - c.Emit("emuprintconnect", jsonPrinter) -} - -func ProcessJobs(ctx context.Context, printer *Printer, c *SocketIOClient) { - for { - select { - case job, ok := <-jobQueue: - if !ok { - log.Println("Job queue closed, exiting ProcessJobs.") - return - } - - response := CommandHandler(job.Command, printer) - log.Printf("Processing job: %s, response: %s\n", job.Command, response) - - // todo: handle weird edge case responses - /* if job.Command == "G29" { - - } else if job.Command == "G92" { - - } - */ - if !strings.Contains(response, "Unknown command") { - c.Emit("job_response", "ok\n") - } - case <-c.close: - log.Println("Client connection closed.") - return - case <-ctx.Done(): - log.Println("Context done, exiting ProcessJobs.") - return - } - } + log.Println("Emitting emuprintconnect event with JSON data:", string(jsonPrinter)) + //c.Emit("emuprintconnect", string(jsonPrinter)) } // Run function for G-code command input and processing diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index ca6c0bfa..ac6098c8 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -47,7 +47,7 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { - printer.extruder.Position = Vector3{X: 0, Y: 0, Z: 0} + printer.Extruder.Position = Vector3{X: 0, Y: 0, Z: 0} return "Homing completed\n" } @@ -57,15 +57,15 @@ type G0G1Command struct { } func NewG0G1Command(command string, printer *Printer) *G0G1Command { - target, feedRate := parseMoveCommand(command, printer.extruder.Position) + target, feedRate := parseMoveCommand(command, printer.Extruder.Position) return &G0G1Command{target: target, feedRate: feedRate} } func (cmd *G0G1Command) Execute(printer *Printer) string { - if printer.paused { + if printer.Paused { return "Printer is paused\n" } - printer.extruder.Position = cmd.target + printer.Extruder.Position = cmd.target return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } @@ -76,15 +76,15 @@ type G2G3Command struct { } func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Command { - target, radius := parseArcCommand(command, printer.extruder.Position) + target, radius := parseArcCommand(command, printer.Extruder.Position) return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } func (cmd *G2G3Command) Execute(printer *Printer) string { - if printer.paused { + if printer.Paused { return "Printer is paused\n" } - currentPos := printer.extruder.Position + currentPos := printer.Extruder.Position arcAngle := 180.0 angleRad := arcAngle * math.Pi / 180.0 @@ -95,7 +95,7 @@ func (cmd *G2G3Command) Execute(printer *Printer) string { cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) } - printer.extruder.Position = cmd.target + printer.Extruder.Position = cmd.target return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } @@ -116,14 +116,14 @@ func (cmd *G4Command) Execute(printer *Printer) string { type G90Command struct{} func (cmd *G90Command) Execute(printer *Printer) string { - printer.extruder.AbsolutePositioning = true + printer.Extruder.AbsolutePositioning = true return "Set to Absolute Positioning\n" } type G91Command struct{} func (cmd *G91Command) Execute(printer *Printer) string { - printer.extruder.AbsolutePositioning = false + printer.Extruder.AbsolutePositioning = false return "Set to Relative Positioning\n" } @@ -137,7 +137,7 @@ func NewG92Command(command string) *G92Command { } func (cmd *G92Command) Execute(printer *Printer) string { - printer.extruder.Position = cmd.position + printer.Extruder.Position = cmd.position return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) } @@ -146,14 +146,14 @@ func (cmd *G92Command) Execute(printer *Printer) string { type G20Command struct{} func (cmd *G20Command) Execute(printer *Printer) string { - printer.units = "inches" + printer.Units = "inches" return "Units set to inches\n" } type G21Command struct{} func (cmd *G21Command) Execute(printer *Printer) string { - printer.units = "mm" + printer.Units = "mm" return "Units set to millimeters\n" } @@ -169,7 +169,7 @@ func NewM104Command(command string) *M104Command { } func (cmd *M104Command) Execute(printer *Printer) string { - printer.extruder.TargetTemp = cmd.temperature + printer.Extruder.TargetTemp = cmd.temperature return fmt.Sprintf("Extruder temperature set to %.2f\n", cmd.temperature) } @@ -183,14 +183,14 @@ func NewM106Command(command string) *M106Command { } func (cmd *M106Command) Execute(printer *Printer) string { - printer.extruder.FanSpeed = cmd.fanSpeed + printer.Extruder.FanSpeed = cmd.fanSpeed return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) } type M107Command struct{} func (cmd *M107Command) Execute(printer *Printer) string { - printer.extruder.FanSpeed = 0.0 + printer.Extruder.FanSpeed = 0.0 return "Fan turned off\n" } @@ -205,8 +205,8 @@ func NewM140Command(command string) *M140Command { func (cmd *M140Command) Execute(printer *Printer) string { // Set the target temperature of the bed, but do not block - printer.heatbed.TargetTemp = cmd.temperature - printer.heatbed.Heating = true // Start heating + printer.Heatbed.TargetTemp = cmd.temperature + printer.Heatbed.Heating = true // Start heating return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) } @@ -222,19 +222,18 @@ func NewM190Command(command string) *M190Command { func (cmd *M190Command) Execute(printer *Printer) string { // Set the target temperature and check if the bed is heated - printer.heatbed.TargetTemp = cmd.temperature + printer.Heatbed.TargetTemp = cmd.temperature // Check if the current temperature is below the target - if printer.heatbed.Temp < printer.heatbed.TargetTemp { + if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { return "Waiting for bed to reach target temperature...\n" } // If target temperature reached, continue - printer.heatbed.Heating = false + printer.Heatbed.Heating = false return fmt.Sprintf("Bed temperature reached %.2f\n", cmd.temperature) } - type M113Command struct{} func NewM113Command(command string) *M113Command { @@ -242,7 +241,7 @@ func NewM113Command(command string) *M113Command { } func (cmd *M113Command) Execute(printer *Printer) string { - printer.keepAliveTime = time.Now() + printer.KeepAliveTime = time.Now() return "Keepalive signal sent\n" } @@ -256,7 +255,7 @@ func NewM204Command(command string) *M204Command { } func (cmd *M204Command) Execute(printer *Printer) string { - printer.acceleration = cmd.acceleration + printer.Acceleration = cmd.acceleration return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) } @@ -270,7 +269,7 @@ func NewM73Command(command string) *M73Command { } func (cmd *M73Command) Execute(printer *Printer) string { - printer.progress = cmd.progress + printer.Progress = cmd.progress return fmt.Sprintf("Progress set to %d%%\n", cmd.progress) } @@ -279,40 +278,40 @@ func (cmd *M73Command) Execute(printer *Printer) string { type CancelCommand struct{} func (cmd *CancelCommand) Execute(printer *Printer) string { - printer.paused = true + printer.Paused = true return "Emergency stop activated, printer paused\n" } type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) string { - position := printer.extruder.Position + position := printer.Extruder.Position return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) } type M997Command struct{} func (cmd *M997Command) Execute(printer *Printer) string { - return fmt.Sprintf("Machine name: %s", printer.device) + return fmt.Sprintf("Machine name: %s", printer.Device) } type M601Command struct{} func (cmd *M601Command) Execute(printer *Printer) string { - if printer.paused { + if printer.Paused { return "Printer is already paused\n" } - printer.paused = true + printer.Paused = true return "Machine is paused\n" } type M602Command struct{} func (cmd *M602Command) Execute(printer *Printer) string { - if !printer.paused { + if !printer.Paused { return "Printer is not paused\n" } - printer.paused = false + printer.Paused = false return "Machine is no longer paused\n" } @@ -326,7 +325,7 @@ func NewM900Command(command string) *M900Command { } func (cmd *M900Command) Execute(printer *Printer) string { - printer.linearAdvanceFactor = cmd.kFactor + printer.LinearAdvanceFactor = cmd.kFactor return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) } @@ -340,7 +339,7 @@ func NewM142Command(command string) *M142Command { } func (cmd *M142Command) Execute(printer *Printer) string { - printer.heatbreakTemp = cmd.temperature + printer.HeatbreakTemp = cmd.temperature return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) } @@ -493,5 +492,3 @@ var commandRegistry = map[string]func(string, *Printer) Command{ "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, } - - diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index bd0b1e4c..52952f69 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -7,103 +7,102 @@ import ( // Printer struct with associated components as pointers for easy modification type Printer struct { - id int - device string - description string - hwid string - name string - status string - date string - extruder *Extruder - heatbed *Heatbed - paused bool - units string - keepAliveTime time.Time - acceleration float64 - progress int - linearAdvanceFactor float64 - heatbreakTemp float64 - enabledMotors map[string]bool + Id int + Device string + Description string + Hwid string + Name string + Status string + Date string + Extruder *Extruder + Heatbed *Heatbed + Paused bool + Units string + KeepAliveTime time.Time + Acceleration float64 + Progress int + LinearAdvanceFactor float64 + HeatbreakTemp float64 + EnabledMotors map[string]bool } func (printer *Printer) DisableMotor(axis string) { - if printer.enabledMotors == nil { - printer.enabledMotors = make(map[string]bool) + if printer.EnabledMotors == nil { + printer.EnabledMotors = make(map[string]bool) } - printer.enabledMotors[axis] = false + printer.EnabledMotors[axis] = false } - // NewPrinter initializes a Printer with given specifications func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) *Printer { return &Printer{ - id: id, - device: device, - description: description, - hwid: hwid, - name: name, - status: status, - date: date, - extruder: extruder, - heatbed: heatbed, - paused: false, - units: "mm", // Default units - keepAliveTime: time.Now(), // Initialize with current time - acceleration: 0.0, // Default acceleration - progress: 0, // Default progress percentage + Id: id, + Device: device, + Description: description, + Hwid: hwid, + Name: name, + Status: status, + Date: date, + Extruder: extruder, + Heatbed: heatbed, + Paused: false, + Units: "mm", // Default units + KeepAliveTime: time.Now(), // Initialize with current time + Acceleration: 0.0, // Default acceleration + Progress: 0, // Default progress percentage } } // SetExtruderTemp sets the extruder temperature func (printer *Printer) SetExtruderTemp(temp float64) { - printer.extruder.SetExtruderTemp(temp) + printer.Extruder.SetExtruderTemp(temp) } // GetExtruderTemp gets the extruder temperature func (printer *Printer) GetExtruderTemp() float64 { - return printer.extruder.ExtruderTemp + return printer.Extruder.ExtruderTemp } // SetBedTemp sets the target bed temperature and begins heating func (printer *Printer) SetBedTemp(temp float64) { - printer.heatbed.SetBedTemp(temp) + printer.Heatbed.SetBedTemp(temp) } // GetBedTemp gets the current bed temperature func (printer *Printer) GetBedTemp() float64 { - return printer.heatbed.Temp + return printer.Heatbed.Temp } // UpdateBedTemperature updates the current temperature of the bed func (printer *Printer) UpdateBedTemperature(currentTemp float64) { - printer.heatbed.Temp = currentTemp - if printer.heatbed.Temp >= printer.heatbed.TargetTemp { - printer.heatbed.Heating = false + printer.Heatbed.Temp = currentTemp + if printer.Heatbed.Temp >= printer.Heatbed.TargetTemp { + printer.Heatbed.Heating = false } } // SetFanSpeed sets the fan speed on the extruder func (printer *Printer) SetFanSpeed(speed float64) { - printer.extruder.SetFanSpeed(speed) + printer.Extruder.SetFanSpeed(speed) } // GetFanSpeed gets the current fan speed on the extruder func (printer *Printer) GetFanSpeed() float64 { - return printer.extruder.FanSpeed + return printer.Extruder.FanSpeed } // Pause pauses the printer func (printer *Printer) Pause() { - printer.paused = true + printer.Paused = true } // Resume resumes the printer operation func (printer *Printer) Resume() { - printer.paused = false + printer.Paused = false } // String provides a formatted string representation of the Printer state func (printer *Printer) String() string { return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, Progress: %d%%, KeepAliveTime: %v}", - printer.id, printer.device, printer.description, printer.hwid, printer.name, printer.status, printer.date, printer.extruder.String(), printer.heatbed.String(), printer.acceleration, printer.progress, printer.keepAliveTime) + printer.Id, printer.Device, printer.Description, printer.Hwid, printer.Name, printer.Status, printer.Date, printer.Extruder.String(), printer.Heatbed.String(), printer.Acceleration, printer.Progress, printer.KeepAliveTime) } diff --git a/printeremu/src/socketio_client.go b/printeremu/src/socketio_client.go index 5257cf78..3eebc6d5 100644 --- a/printeremu/src/socketio_client.go +++ b/printeremu/src/socketio_client.go @@ -2,179 +2,95 @@ package src import ( "encoding/json" + "fmt" "log" - "net/url" - "time" - - "sync" "github.com/gorilla/websocket" ) -type SocketIOMessage struct { - Type int `json:"type"` - Event string `json:"event,omitempty"` - Data interface{} `json:"data,omitempty"` - Nsp string `json:"nsp,omitempty"` -} - +// SocketIOClient represents a simple client for interacting with Socket.IO type SocketIOClient struct { conn *websocket.Conn - send chan []byte - close chan struct{} - handlers map[string]func(interface{}) - wg sync.WaitGroup + sid string } -func NewClient() *SocketIOClient { - return &SocketIOClient{ - send: make(chan []byte), - handlers: make(map[string]func(interface{})), - close: make(chan struct{}), - } +// SocketIOMessage represents a message sent/received with Socket.IO +type SocketIOMessage struct { + Event string `json:"event"` + Data interface{} `json:"data"` } -func (c *SocketIOClient) Connect(uri string) error { - c.handlers = make(map[string]func(interface{})) - - u, err := url.Parse(uri) - if err != nil { - return err - } - - switch u.Scheme { - case "http", "ws": - u.Scheme = "ws" - case "https", "wss": - u.Scheme = "wss" - } - - u.Path = "/socket.io/" - u.RawQuery = "EIO=4&transport=websocket" - - conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) +// NewSocketIOClient initializes and returns a new client instance. +func NewSocketIOClient() *SocketIOClient { + return &SocketIOClient{} +} +// Connect establishes a WebSocket connection to the Socket.IO server. +func (c *SocketIOClient) Connect(serverURL string) error { + // Open the connection + conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) if err != nil { - return err + return fmt.Errorf("failed to connect to server: %v", err) } - c.conn = conn - c.wg.Add(2) - - go c.readPump() - go c.StartWritePump() - - c.send <- []byte("40") - + log.Printf("Connected to %s", serverURL) return nil } -func (c *SocketIOClient) readPump() { - defer func() { - close(c.close) - c.wg.Done() - c.conn.Close() - }() - - for { - _, message, err := c.conn.ReadMessage() - - if err != nil { - log.Printf("read error: %v", err) - return - } - - if len(message) > 0 { - switch message[0] { - case '0': // connection - log.Println("Connnected to server") - case '1': // keepalive? - c.send <- []byte("3") - case '4': // message - if len(message) > 1 { - c.handleMessage(message[1:]) - } - } - } +// Send sends a message to the WebSocket server. +func (c *SocketIOClient) Send(message []byte) error { + err := c.conn.WriteMessage(websocket.TextMessage, message) + if err != nil { + return fmt.Errorf("failed to send message: %v", err) } + log.Printf("Sent: %s", string(message)) + return nil } -func (c *SocketIOClient) StartWritePump() { - defer func() { - c.wg.Done() - c.conn.Close() - }() - - - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - - for { - select { - case message := <-c.send: - err := c.conn.WriteMessage(websocket.TextMessage, message) - if err != nil { - log.Println("write error:", err) - return - } - case <-ticker.C: - err := c.conn.WriteMessage(websocket.TextMessage, []byte("2")) - if err != nil { - log.Println("ping error:", err) - return - } - case <-c.close: - return - } +// Receive listens for messages from the WebSocket server. +func (c *SocketIOClient) Receive() (string, error) { + _, message, err := c.conn.ReadMessage() + if err != nil { + return "", fmt.Errorf("failed to read message: %v", err) } + return string(message), nil } - -func (c *SocketIOClient) On(event string, handler func(interface{})) { - c.handlers[event] = handler +// HandleSocketIOHandshake handles the initial handshake response from the Socket.IO server. +func (c *SocketIOClient) HandleSocketIOHandshake(response string) error { + if response[0] == '0' { // This is the Engine.IO handshake response + log.Println("Received handshake response from the server.") + // Send the "2" message to acknowledge and connect + return c.Send([]byte("2")) + } + return fmt.Errorf("unexpected handshake response: %s", response) } +// Emit sends an event with data (a JSON object) to the Socket.IO server. func (c *SocketIOClient) Emit(event string, data interface{}) error { msg := SocketIOMessage{ - Type: 4, Event: event, - Data: data, - Nsp: "/", + Data: data, } payload, err := json.Marshal(msg) - if err != nil { - log.Println("Failed to marshal message:", err) - return err + return fmt.Errorf("failed to marshal message: %v", err) } - c.send <- payload - - return nil + // Socket.IO "42" prefix for events + fullPayload := append([]byte("42"), payload...) + return c.Send(fullPayload) } -func (c *SocketIOClient) handleMessage(message []byte) { - if len(message) > 1 && message[0] == '0' { - message = message[1:] - } - - var msg SocketIOMessage - - if err := json.Unmarshal(message, &msg); err != nil { - log.Println("Failed to unmarshal message:", err) - //log.Println("Raw message:", string(message)) - return - } - - if handler, ok := c.handlers[msg.Event]; ok { - handler(msg.Data) - } else { - //log.Println("No handler registered for event:", msg.Event) +// Close closes the WebSocket connection. +func (c *SocketIOClient) Close() error { + if c.conn != nil { + err := c.conn.Close() + if err != nil { + return fmt.Errorf("failed to close connection: %v", err) + } + log.Println("Connection closed.") } + return nil } - -func (c *SocketIOClient) Close() { - close(c.close) - c.wg.Wait() -} \ No newline at end of file diff --git a/printeremu/testserver/package-lock.json b/printeremu/testserver/package-lock.json new file mode 100644 index 00000000..d0db98ac --- /dev/null +++ b/printeremu/testserver/package-lock.json @@ -0,0 +1,508 @@ +{ + "name": "testserver", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testserver", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "socket.io": "^4.8.1", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/node": "^22.9.0", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/printeremu/testserver/package.json b/printeremu/testserver/package.json new file mode 100644 index 00000000..5a26d75f --- /dev/null +++ b/printeremu/testserver/package.json @@ -0,0 +1,20 @@ +{ + "name": "testserver", + "version": "1.0.0", + "main": "src/index.ts", + "scripts": { + "dev": "ts-node src/index.ts" + }, + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@types/node": "^22.9.0", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + }, + "dependencies": { + "socket.io": "^4.8.1", + "ws": "^8.18.0" + } +} diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts new file mode 100644 index 00000000..2c72f5ce --- /dev/null +++ b/printeremu/testserver/src/index.ts @@ -0,0 +1,30 @@ +import { createServer } from "http"; +import WebSocket from "ws"; // Import the WebSocket library + +// Create an HTTP server +const httpServer = createServer(); + +// Create a WebSocket server (using the existing HTTP server) +const wss = new WebSocket.Server({ server: httpServer }); + +// Handle WebSocket client connections +wss.on('connection', (ws) => { + console.log('WebSocket client connected'); + + // Listen for incoming messages from WebSocket clients + ws.on('message', (message) => { + console.log('Received from WebSocket client:', message); + // Echo the message back to the client + ws.send('Hello from WebSocket server!'); + }); + + // Handle WebSocket client disconnection + ws.on('close', () => { + console.log('WebSocket client disconnected'); + }); +}); + +// Start the HTTP server (which is used by the WebSocket server) +httpServer.listen(8000, () => { + console.log("WebSocket server is running on http://localhost:8000"); +}); diff --git a/printeremu/testserver/tsconfig.json b/printeremu/testserver/tsconfig.json new file mode 100644 index 00000000..3a93d05d --- /dev/null +++ b/printeremu/testserver/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist" + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file From 934657ac8a4fa02335adf460da286676792e57f5 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 7 Nov 2024 19:45:33 -0500 Subject: [PATCH 044/194] test refactor to include dependencies. --- Tests/conftest.py | 59 ++--- Tests/parallel_test_runner.py | 11 - Tests/test_device.py | 68 ++++++ Tests/test_fabricator.py | 139 ++++++++++++ Tests/test_runner.py | 204 ------------------ requirements.txt | 22 +- server/Classes/Fabricators/Device.py | 4 +- server/Classes/Fabricators/Fabricator.py | 21 +- .../Printers/Prusa/PrusaPrinter.py | 2 - server/Classes/Logger.py | 8 +- server/Mixins/usesMarlinGcode.py | 2 + 11 files changed, 271 insertions(+), 269 deletions(-) create mode 100644 Tests/test_device.py create mode 100644 Tests/test_fabricator.py delete mode 100644 Tests/test_runner.py diff --git a/Tests/conftest.py b/Tests/conftest.py index 1a6d75d6..01473789 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -21,7 +21,7 @@ def pytest_addoption(parser): help="port to test" ) -def line_separator(interrupter: str, symbol: str = "-", length: int = 80) -> str: +def line_separator(interrupter: str, symbol: str = "-", length: int = 104) -> str: if not interrupter: return symbol * length interrupterNoColor = re.sub(r'\033\[[0-9;]*m', '', interrupter) @@ -43,6 +43,16 @@ def setup_logger(port): @pytest.hookimpl(tryfirst=True) def pytest_sessionstart(session) -> None: + for arg in session.config.invocation_params.args: + if not hasattr(session.config, "verbosity") and arg.startswith("--myVerbose="): + session.config.verbosity = int(arg.split("=")[1]) + elif not hasattr(session.config, "port") and arg.startswith("--port="): + session.config.port = arg.split("=")[1] + elif not hasattr(session.config, "testLevel") and arg.startswith("--testLevel="): + session.config.testLevel = int(arg.split("=")[1]) + if session.config.verbosity > 2: + session.config.verbosity = 2 + session.config.start_time = time.time() session.config.passed_count = 0 session.config.failed_count = 0 @@ -53,7 +63,8 @@ def pytest_sessionstart(session) -> None: session.config.fails = {} session.config.logger = setup_logger(session.config.port) - if session.config.verbosity > 0: + + if session.config.verbosity >= 0: logger = session.config.logger logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") verinfo = platform.python_version() @@ -67,7 +78,7 @@ def pytest_sessionstart(session) -> None: logger.logMessageOnly(f"rootdir: {session.config.rootdir}") def pytest_collection_modifyitems(session, config, items): - session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...", end="\n") + session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...") def pytest_sessionfinish(session, exitstatus) -> None: session_duration = time.time() - session.config.start_time @@ -78,7 +89,6 @@ def pytest_sessionfinish(session, exitstatus) -> None: xpasses = session.config.xpassed_count logger = session.config.logger - summary = "" stats = [] if passes > 0: stats.append(f"\033[32m\033[1m{passes} passed") if fails > 0: stats.append(f"\033[31m\033[1m{fails} failed") @@ -86,10 +96,8 @@ def pytest_sessionfinish(session, exitstatus) -> None: if xfails > 0: stats.append(f"\033[33m{xfails} xfailed") if xpasses > 0: stats.append(f"\031[33m{xpasses} xpassed") - if len(stats) > 0: - summary = ", ".join(stats) - else: - summary = "\033[33mno tests ran" + if len(stats) > 0: summary = ", ".join(stats) + else: summary = "\033[33mno tests ran" summary += f"\033[32m in {session_duration:.2f}s" if session_duration > 3600: @@ -117,24 +125,14 @@ def pytest_sessionfinish(session, exitstatus) -> None: logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) -def pytest_configure(config): - for arg in config.invocation_params.args: - if not hasattr(config, "verbosity") and arg.startswith("--myVerbose="): - config.verbosity = int(arg.split("=")[1]) - elif not hasattr(config, "port") and arg.startswith("--port="): - config.port = arg.split("=")[1] - elif not hasattr(config, "testLevel") and arg.startswith("--testLevel="): - config.testLevel = int(arg.split("=")[1]) - if config.verbosity > 2: - config.verbosity = 2 +visited_modules = set() @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() # Retrieve the TestReport object # Only check the outcome after the "call" phase (i.e., after the test ran) - - if (report.when == "setup" and report.skipped) or (report.when == "call"): + if (report.when == "setup" and not report.passed) or (report.when == "call"): if report.passed: if hasattr(report, "wasxfail"): report.outcome = "xpassed" @@ -157,6 +155,10 @@ def pytest_runtest_makereport(item, call): report.port = item.config.port report.verbosity = item.config.verbosity report.logger = item.config.logger + module_name = item.module.__name__ + if module_name not in visited_modules: + visited_modules.add(module_name) + report.logger.logMessageOnly("\n" + line_separator(item.module.__desc__(), symbol="-")) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_logreport(report): @@ -164,11 +166,10 @@ def pytest_runtest_logreport(report): yield logger = report.logger port = report.port - if (report.when == "setup" and report.skipped) or (report.when == "call"): - if port is None and "test_runner.py::TestFabricator::" in report.nodeid: + if (report.when == "setup" and not report.passed) or (report.when == "call"): + if port is None: # Retrieve port from the test function if it's set as an attribute - from test_runner import fabricator_setup - port = fabricator_setup(os.getenv("PORT")).devicePort + port = os.getenv("PORT") if verbosity == 0: if report.passed: @@ -181,6 +182,8 @@ def pytest_runtest_logreport(report): logger.info("\033[33mX\033[0m") elif hasattr(report, "xpassed") and report.xpassed: logger.info("\033[31mx\033[0m") + else: + logger.info(f"IDK what happened!?!?: {report}") elif verbosity == 1: loc = report.nodeid.split("::")[-1] testString = f"{loc}[{port}]{' ' * (27 - len(loc) - len(str(port)) - 2)}" @@ -194,12 +197,16 @@ def pytest_runtest_logreport(report): logger.info(f"{testString} \033[33mXFAILED\033[0m") elif hasattr(report, "xpassed") and report.xpassed: logger.info(f"{testString} \033[31mXPASSED\033[0m") + else: + logger.info(f"{testString} IDK what happened!?!?: {report}") elif verbosity >= 2: loc = report.nodeid - testString = f"{loc}[{port}]{' ' * (59 - len(loc) - len(str(port)) - 2)}" + testString = f"{loc}[{port}]{' ' * (47 - len(loc) - len(str(port)) - 2)}" if report.passed: logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n {report.longrepr}") elif report.skipped: - logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") \ No newline at end of file + logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") + else: + logger.info(f"{testString} IDK what happened!?!?: {report}") \ No newline at end of file diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 62f1860a..e68b8560 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -5,13 +5,6 @@ from server.Classes.Ports import Ports -red = '\033[31m' -green = '\033[32m' -yellow = '\033[33m' -blue = '\033[34m' -magenta = '\033[35m' -cyan = '\033[36m' -reset = '\033[0m' PORTS = [] # List of available ports for testing if platform.system() == "Windows": @@ -28,7 +21,6 @@ break except FileNotFoundError: pass - elif platform.system() == "Darwin": import glob PORTS = glob.glob("/dev/tty.*") @@ -36,9 +28,6 @@ import glob PORTS = glob.glob("/dev/tty[A-Za-z]*") -def printColor(color, message): - print(color + message + reset) - # Function to run pytest for a specific port testLevel = 10 verbosity = 2 diff --git a/Tests/test_device.py b/Tests/test_device.py new file mode 100644 index 00000000..48d9dfc4 --- /dev/null +++ b/Tests/test_device.py @@ -0,0 +1,68 @@ +import os +import sys +import pytest + +from Classes.Ports import Ports +from Classes.Fabricators.Fabricator import Fabricator +from Classes.Vector3 import Vector3 + +def device_setup(port): + if not port: return None + return Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) + + +from parallel_test_runner import testLevel +testLevelToRun = testLevel +shortTest = True +device = Fabricator.createDevice(None, consoleLogger=sys.stdout) + +def __desc__(): + return "Device Tests" + +def __repr__(): + return f"test_device.py running on port {Ports.getPortByName(os.getenv('PORT'))}" + +@pytest.fixture(scope="module", autouse=True) +def function_setup(request): + global device + device = device_setup(request.session.config.port) + if device is None: + pytest.skip("No port specified") + device.connect() + yield + device.disconnect() + +@pytest.mark.dependency() +@pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") +def test_connection(): + assert device is not None, f"No printer connected on {device.DESCRIPTION}" + + +@pytest.mark.dependency(depends=["test_connection"]) +@pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") +def test_home(): + assert device.home(), f"Failed to home {device.DESCRIPTION}" + +@pytest.mark.dependency(depends=["test_home"]) +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_square(): + squarePasses = 0 + for point in [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), + Vector3(50.0, 150.0, 2.0)]: + squarePasses += 1 if device.goTo(point, isVerbose=True) else 0 + assert squarePasses == 4, f"Failed to draw square on {device.DESCRIPTION}" + +@pytest.mark.dependency(depends=["test_home"]) +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_octagon(): + octagonPasses = 0 + for point in [Vector3(50.0, 100.0, 2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0, 50.0, 2.0), + Vector3(200.0, 100.0, 2), Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), + Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)]: + octagonPasses += 1 if device.goTo(point) else 0 + assert octagonPasses == 8, f"Failed to draw octagon on {device.DESCRIPTION}" + +@pytest.mark.dependency(depends=["test_home"]) +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_center(): + assert device.goTo(Vector3(125.0, 100.0, 2.0)), f"Failed to go to location on {device.DESCRIPTION}" \ No newline at end of file diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py new file mode 100644 index 00000000..88cc79d0 --- /dev/null +++ b/Tests/test_fabricator.py @@ -0,0 +1,139 @@ +import os +import sys +from datetime import datetime +import re +import pytest + +from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 +from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 +from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S +from Classes.Ports import Ports +from Classes.Jobs import Job +from Classes.Fabricators.Fabricator import Fabricator +from Mixins.canPause import canPause +from parallel_test_runner import testLevel + +testLevelToRun = testLevel +shortTest = True +fabricator = Fabricator(None, "Test Printer", addToDB=False, consoleLogger=sys.stdout) + +def __desc__(): return "Fabricator Tests" +def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" +def fabricator_setup(port): + if not port: return None + return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) + +@pytest.fixture(scope="module", autouse=True) +def function_setup(request): + global fabricator + fabricator = fabricator_setup(request.session.config.port) + if fabricator is None: + pytest.skip("No port specified") + yield + fabricator.device.disconnect() + fabricator = None + +@pytest.mark.dependency(depends=["test_device.py::test_connection"], scope="session") +@pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") +def test_status(): + assert fabricator.getStatus() is not None, f"Failed to get status on {fabricator.getDescription()}" + assert fabricator.device.status is not None, f"Failed to get status on device of {fabricator.getDescription()}" + assert fabricator.getStatus() == fabricator.device.status, f"Internal status mismatch: fabricator: {fabricator.getDescription()}, device: {fabricator.device.status}" + assert fabricator.getStatus() == "idle", f"Status incorrect at {fabricator.getDescription()}, expected idle, got {fabricator.getStatus()}" + +@pytest.mark.dependency(depends=["test_device.py::test_connection"], scope="session") +@pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") +def test_add_job(): + file = "../server/xyz-cali-cube" + if shortTest: + file = file + "-mini" + if isinstance(fabricator.device, Ender3): + file = file + "_ENDER3.gcode" + elif isinstance(fabricator.device, PrusaMK4S): + file = file + "_MK4S.gcode" + elif isinstance(fabricator.device, PrusaMK4): + file = file + "_MK4.gcode" + with open(file, "r") as f: + assert fabricator.queue.addToFront( + Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name), + 3), f"Failed to add job on {fabricator.getDescription()}" + for job in fabricator.queue.getQueue(): + assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" + fabricator.queue.removeJob() + assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" + +@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") +@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +def test_pause_and_resume(): + if not isinstance(fabricator.device, canPause): + pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") + + from time import sleep + import threading + + def parse_gcode(): + fabricator.queue.addToFront \ + (Job("../server/pauseAndResumeTest.gcode", "pauseAndResumeTest", fabricator.dbID, "ready", + "../server/pauseAndResumeTest.gcode", False, 1, fabricator.name), fabricator.dbID) + fabricator.begin() + assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" + + def pause_and_resume_fabricator(): + sleep(2) + assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(20) + assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(1) + assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(20) + + fabricator.resetToIdle() + assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + + parse_thread = threading.Thread(target=parse_gcode) + pause_thread = threading.Thread(target=pause_and_resume_fabricator) + + parse_thread.start() + pause_thread.start() + + parse_thread.join() + pause_thread.join() + +@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") +@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +def test_gcode_print_time(): + expectedTime = 3 * 60 + file = "../server/xyz-cali-cube" + if shortTest: + file = file + "-mini" + if isinstance(fabricator.device, Ender3): + file = file + "_ENDER3.gcode" + expectedTime = 14 * 60 + 15 if shortTest else 28 * 60 + elif isinstance(fabricator.device, PrusaMK4S): + file = file + "_MK4S.gcode" + expectedTime = 180 if shortTest else 1800 + elif isinstance(fabricator.device, PrusaMK4): + file = file + "_MK4.gcode" + expectedTime = 10 * 60 if shortTest else 1800 + # expectedTime = 2040 # for my personal home test, 1072 + expectedMinutes, expectedSeconds = divmod(expectedTime, 60) + time = datetime.now() + with open(file, "r") as f: + fabricator.queue.addToFront \ + (Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name) + , fabricator.dbID) + fabricator.begin() + time = datetime.now() - time + minutes, seconds = divmod(time.seconds, 60) + fabricator.device.serialConnection.write(b"M31\n") + line = "" + while not re.search(r"\d+m \d+s", line): + line = fabricator.device.serialConnection.readline().decode("utf-8") + printMinutes, printSeconds = map(int, re.findall(r"\d+", line)) + printTime = printMinutes * 60 + printSeconds + + timeBoundary = max(120, expectedTime // 5) + assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" + assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" \ No newline at end of file diff --git a/Tests/test_runner.py b/Tests/test_runner.py deleted file mode 100644 index 00114347..00000000 --- a/Tests/test_runner.py +++ /dev/null @@ -1,204 +0,0 @@ -import os -import sys -from datetime import datetime -import re -import pytest -from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 -from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 -from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S -from Classes.Ports import Ports -from Classes.Jobs import Job -from Classes.Fabricators.Fabricator import Fabricator -from Classes.Vector3 import Vector3 -from Mixins.canPause import canPause - -def fabricator_setup(port): - if not port: return None - return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) - -class TestFabricator: - from parallel_test_runner import testLevel - testLevelToRun = testLevel - shortTest = True - - def __repr__(self): - return f"TestFabricator Class running on port {Ports.getPortByName(os.getenv('PORT'))}" - - @classmethod - @pytest.fixture(scope="session", autouse=True) - def function_setup(cls, request): - fabricator_instance = fabricator_setup(request.session.config.port) - if fabricator_instance is None: - pytest.skip("No port specified") - yield fabricator_instance - - fabricator_instance.device.disconnect() - - @pytest.mark.dependency(name="test_connection") - @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") - @pytest.mark.order(1) - def test_connection(self, function_setup): - fabricator = function_setup - assert fabricator.device is not None, f"No printer connected on {fabricator.getDescription()}" - - @pytest.mark.dependency(name="test_home", depends=["test_connection"]) - @pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") - @pytest.mark.order(2) - def test_home(self, function_setup): - fabricator = function_setup - assert fabricator.device.home(), f"Failed to home {fabricator.getDescription()}" - - # @pytest.mark.xfail(reason="Expected to fail") - # @pytest.mark.order(2) - # def test_xfail(self, function_setup): - # fabricator = function_setup - # assert False, f"Expected to fail on {fabricator.getDescription()}" - - @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") - @pytest.mark.dependency(depends=["test_home"]) - @pytest.mark.order(3) - def test_square(self, function_setup): - fabricator = function_setup - squarePasses = 0 - for point in [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), - Vector3(50.0, 150.0, 2.0)]: - squarePasses += 1 if fabricator.device.goTo(point, isVerbose=True) else 0 - assert squarePasses == 4, f"Failed to draw square on {fabricator.getDescription()}" - - - @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") - @pytest.mark.dependency(depends=["test_home"]) - @pytest.mark.order(4) - def test_octagon(self, function_setup): - fabricator = function_setup - octagonPasses = 0 - for point in [Vector3(50.0, 100.0, 2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0, 50.0, 2.0), - Vector3(200.0, 100.0, 2), Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), - Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)]: - octagonPasses += 1 if fabricator.device.goTo(point) else 0 - assert octagonPasses == 8, f"Failed to draw octagon on {fabricator.getDescription()}" - - - @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") - @pytest.mark.dependency(depends=["test_home"]) - @pytest.mark.order(5) - def test_center(self, function_setup): - fabricator = function_setup - assert fabricator.device.goTo( - Vector3(125.0, 100.0, 2.0)), f"Failed to go to center on {fabricator.getDescription()}" - - # @pytest.mark.skip(reason="Not doing heat test") - # @pytest.mark.order(6) - # def test_parse_gcode(self, function_setup): - # fabricator = function_setup - # assert fabricator.device.parseGcode( - # "../server/heatTest.gcode"), f"Failed to parse Gcode on {fabricator.getDescription()}" - - @pytest.mark.order(8) - @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") - @pytest.mark.dependency(depends=["test_home", "test_add_job"]) - def test_pause_and_resume(self, function_setup): - fabricator = function_setup - if not isinstance(fabricator.device, canPause): - pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") - - from time import sleep - import threading - - def parse_gcode(): - fabricator.queue.addToFront(Job("../server/pauseAndResumeTest.gcode", "pauseAndResumeTest", fabricator.dbID, "ready", "../server/pauseAndResumeTest.gcode", False, 1, fabricator.name),fabricator.dbID) - fabricator.begin() - assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" - def pause_and_resume_fabricator(): - sleep(2) - assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(20) - assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(1) - assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(20) - - fabricator.resetToIdle() - assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - - parse_thread = threading.Thread(target=parse_gcode) - pause_thread = threading.Thread(target=pause_and_resume_fabricator) - - parse_thread.start() - pause_thread.start() - - parse_thread.join() - pause_thread.join() - - - @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") - @pytest.mark.dependency(depends=["test_connection"]) - @pytest.mark.order(8) - def test_status(self, function_setup): - fabricator = function_setup - assert fabricator.getStatus() is not None, f"Failed to get status on {fabricator.getDescription()}" - assert fabricator.device.status is not None, f"Failed to get status on device of {fabricator.getDescription()}" - assert fabricator.getStatus() == fabricator.device.status, f"Internal status mismatch: fabricator: {fabricator.getDescription()}, device: {fabricator.device.status}" - assert fabricator.getStatus() == "idle", f"Status incorrect at {fabricator.getDescription()}, expected idle, got {fabricator.getStatus()}" - - @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") - @pytest.mark.dependency(name="test_add_job", depends=["test_connection"]) - @pytest.mark.order(7) - def test_add_job(self, function_setup): - fabricator = function_setup - file = "../server/xyz-cali-cube" - if self.shortTest: - file = file + "-mini" - if isinstance(fabricator.device, Ender3): - file = file + "_ENDER3.gcode" - elif isinstance(fabricator.device, PrusaMK4S): - file = file + "_MK4S.gcode" - elif isinstance(fabricator.device, PrusaMK4): - file = file + "_MK4.gcode" - with open(file, "r") as f: - assert fabricator.queue.addToFront( - Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name), - 3), f"Failed to add job on {fabricator.getDescription()}" - for job in fabricator.queue.getQueue(): - assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" - fabricator.queue.removeJob() - assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" - - @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") - @pytest.mark.dependency(depends=["test_home", "test_add_job"]) - @pytest.mark.order(10) - def test_gcode_print_time(self, function_setup): - fabricator = function_setup - expectedTime = 3 * 60 - file = "../server/xyz-cali-cube" - if self.shortTest: - file = file + "-mini" - if isinstance(fabricator.device, Ender3): - file = file + "_ENDER3.gcode" - expectedTime = 14 * 60 + 15 if self.shortTest else 28 * 60 - elif isinstance(fabricator.device, PrusaMK4S): - file = file + "_MK4S.gcode" - expectedTime = 180 if self.shortTest else 1800 - elif isinstance(fabricator.device, PrusaMK4): - file = file + "_MK4.gcode" - expectedTime = 10 * 60 if self.shortTest else 1800 - # expectedTime = 2040 # for my personal home test, 1072 - expectedMinutes, expectedSeconds = divmod(expectedTime, 60) - time = datetime.now() - with open(file, "r") as f: - fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name),fabricator.dbID) - fabricator.begin() - time = datetime.now() - time - minutes, seconds = divmod(time.seconds, 60) - fabricator.device.serialConnection.write(b"M31\n") - line = "" - while not re.search(r"\d+m \d+s", line): - line = fabricator.device.serialConnection.readline().decode("utf-8") - printMinutes, printSeconds = map(int, re.findall(r"\d+", line)) - printTime = printMinutes * 60 + printSeconds - - timeBoundary = max(120, expectedTime // 5) - assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" - assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bd3566dd..cb6c6dba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ alembic~=1.13.2 -Flask==3.0.3 +Flask~=3.0.3 Flask-Cors~=4.0.1 Flask-Migrate~=4.0.7 Flask-SocketIO~=5.3.6 -Flask_sqlalchemy==3.1.1 +Flask_sqlalchemy~=3.1.1 pluggy~=1.5.0 -pyserial==3.5 -pytest==8.3.3 -pytest-order==1.3.0 +pyserial~=3.5 +pytest~=8.3.3 +pytest-order~=1.3.0 pytest-dependency~=0.6.0 -python-dotenv==1.0.1 -Requests==2.32.3 +python-dotenv~=1.0.1 +Requests~=2.32.3 SQLAlchemy~=2.0.34 -tzlocal==2.1 -Werkzeug==3.0.3 -eventlet==0.37.0 -gunicorn==23.0.0 \ No newline at end of file +tzlocal~=2.1 +Werkzeug~=3.0.3 +eventlet~=0.37.0 +gunicorn~=23.0.0 \ No newline at end of file diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 381af993..b69ce109 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -26,10 +26,10 @@ class Device(ABC): status: str = "idle" verdict: str = "" - def __init__(self, serialPort: ListPortInfo | SysFS): + def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): self.serialPort = serialPort self.serialID = serialPort.serial_number - self.logger = Logger(self.serialPort.device, self.DESCRIPTION) + self.logger = Logger(self.serialPort.device, self.DESCRIPTION, consoleLogger=consoleLogger, fileLogger=fileLogger) self.status = "idle" self.verdict = "" diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 4c9926ed..2731d779 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -36,11 +36,13 @@ class Fabricator(db.Model): devicePort = db.Column(db.String(50), nullable=False) - def __init__(self, port: ListPortInfo | SysFS, name: str = "", addToDB: bool = False): + def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", addToDB: bool = False, consoleLogger=None, fileLogger=None): + if port is None: + return assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) assert isinstance(name, str) - self.device: Device = Fabricator.createDevice(port) + self.device: Device = Fabricator.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger) self.job: Job | None = None self.queue: Queue = Queue() self.status: str = "idle" @@ -73,31 +75,32 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: @staticmethod - def createDevice(serialPort: ListPortInfo | SysFS | None): + def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None) -> Device | None: """creates the correct printer object based on the serial port info""" if serialPort is None: return None if serialPort.vid == PrusaPrinter.VENDORID: if serialPort.pid == PrusaMK4.PRODUCTID: - return PrusaMK4(serialPort) + return PrusaMK4(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(serialPort) + return PrusaMK4S(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(serialPort) + return PrusaMK3(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: return None elif serialPort.vid == EnderPrinter.VENDORID: model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(serialPort) + return Ender3Pro(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif "Ender-3" in model: - return Ender3(serialPort) + return Ender3(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(serialPort) + return Replicator2(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: + #TODO: assume generic printer, do stuff return None @classmethod diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 12ef2389..9b74b781 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -2,9 +2,7 @@ from typing_extensions import Buffer, Callable from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer -from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import hasResponsecodes, checkOK from Mixins.usesMarlinGcode import usesMarlinGcode diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 0f629bd4..b565e3a7 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -22,7 +22,7 @@ def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggin info.append("%(levelname)s") if showFile: info.append("%(module)s.%(funcName)s:%(lineno)d") - formatString = " - ".join(info + ["%(message)s"]).lstrip(" - ") + formatString = " - ".join(info + ["%(message)s"]) if consoleLogger is not None: console_handler = logging.StreamHandler(consoleLogger) console_handler.setFormatter(CustomFormatter(formatString)) @@ -118,8 +118,8 @@ class CustomFormatter(logging.Formatter): } RESET_CODE = "\033[0m" # Reset to default color - def format(session, record): + def format(self, record): # Apply color based on log level - color = session.COLOR_CODES.get(record.levelname, session.RESET_CODE) + color = self.COLOR_CODES.get(record.levelname, self.RESET_CODE) message = super().format(record) - return f"{color}{message}{session.RESET_CODE}" + return f"{color}{message}{self.RESET_CODE}" diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/usesMarlinGcode.py index 74d8fe81..de3e0540 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/usesMarlinGcode.py @@ -114,6 +114,8 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): if callables[1](line): self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) return True + except UnicodeDecodeError as e: + continue except Exception as e: self.logger.error(e) return False From 84463a4c6a7b9d2868fa56fbf7be6f191f24df7e Mon Sep 17 00:00:00 2001 From: iron768 Date: Thu, 7 Nov 2024 20:19:49 -0500 Subject: [PATCH 045/194] feat: improve emulator and emu test server --- printeremu/go.mod | 1 - printeremu/go.sum | 6 -- printeremu/src/emulator.go | 116 ++++++++++++++++++++++------- printeremu/testserver/src/index.ts | 71 +++++++++++++++--- 4 files changed, 150 insertions(+), 44 deletions(-) diff --git a/printeremu/go.mod b/printeremu/go.mod index 907810d2..da0d32bc 100644 --- a/printeremu/go.mod +++ b/printeremu/go.mod @@ -7,6 +7,5 @@ require github.com/gorilla/websocket v1.5.0 require ( github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gomodule/redigo v1.8.9 // indirect - github.com/googollee/go-socket.io v1.8.0-rc.1 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect ) diff --git a/printeremu/go.sum b/printeremu/go.sum index 4ee0d513..b9c852b5 100644 --- a/printeremu/go.sum +++ b/printeremu/go.sum @@ -9,12 +9,6 @@ github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUz github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/googollee/go-socket.io v1.6.0 h1:zbz0kEERgeYL/yEu9pBXSIyZEBluiNc2AJaMFmFjIOY= -github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= -github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8= -github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= -github.com/googollee/go-socket.io v1.8.0-rc.1 h1:Y5DV+pKDw2KFBtdEyxBp8mSuuU4XS3eHGNb2E3bLACI= -github.com/googollee/go-socket.io v1.8.0-rc.1/go.mod h1:oZhC7XylbziHxXhVdXvz6qQB0/jmNc4V3d9jgaEBKHI= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index dd6ff775..c582d2b4 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -48,43 +48,109 @@ func Init(id int, device string, description string, hwid string, name string, s } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { - // Connect to the WebSocket server - serverURL := "ws://127.0.0.1:8000" - conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) - if err != nil { - log.Fatal("Failed to connect to server:", err) - } - defer conn.Close() + serverURL := "ws://127.0.0.1:8000" - fmt.Println("Connected to WebSocket server") + conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) - // Send a message to the server - err = conn.WriteMessage(websocket.TextMessage, []byte("Hello from Go client!")) - if err != nil { - log.Fatal("Failed to send message:", err) - } + if err != nil { + log.Fatal("Failed to connect to server:", err) + } - // Read the server's response - _, message, err := conn.ReadMessage() - if err != nil { - log.Fatal("Failed to read message:", err) - } + defer conn.Close() + + fmt.Println("Connected to WebSocket server") + + RegisterPrinter(conn, printer) + + pingTicker := time.NewTicker(5 * time.Second) + defer pingTicker.Stop() + + for { + select { + case <-ctx.Done(): + fmt.Println("Context canceled, closing connection.") + return + case <-pingTicker.C: + pingMessage := map[string]interface{}{ + "event": "ping", + "data": "alive", + } + + jsonPingMessage, err := json.Marshal(pingMessage) + + if err != nil { + log.Println("Failed to marshal ping message:", err) + return + } + + if err := conn.WriteMessage(websocket.TextMessage, jsonPingMessage); err != nil { + log.Println("Error sending ping message:", err) + return + } - fmt.Println("Received from server:", string(message)) + default: + messageType, message, err := conn.ReadMessage() - // Keep the connection alive - time.Sleep(5 * time.Second) + if err != nil { + log.Println("Error reading message:", err) + return + } + + if messageType == websocket.TextMessage && string(message) == "close" { + fmt.Println("Received 'close' signal from server, ending connection.") + return + } + + var parsedMessage map[string]interface{} + + if err := json.Unmarshal(message, &parsedMessage); err != nil { + log.Println("Error parsing received message:", err) + continue + } + + event, ok := parsedMessage["event"].(string) + + if !ok { + log.Println("Missing or invalid 'event' field:", parsedMessage) + continue + } + + data, _ := parsedMessage["data"] + + switch event { + case "info": + if message, exists := parsedMessage["message"].(string); exists { + log.Println("Info:", message) + } else { + fmt.Println("Missing or invalid 'message' field in 'info' event data") + } + + case "error": + log.Println("Error:", data) + + default: + log.Println("Received from server:", string(message)) + } + } + } } -func RegisterPrinter(c *SocketIOClient, printer *Printer) { - jsonPrinter, err := json.Marshal(printer) +func RegisterPrinter(conn *websocket.Conn, printer *Printer) { + message := map[string]interface{}{ + "event": "emuprintconnect", + "data": printer, + } + + jsonMessage, err := json.Marshal(message) + if err != nil { log.Println("Failed to marshal printer object:", err) return } - log.Println("Emitting emuprintconnect event with JSON data:", string(jsonPrinter)) - //c.Emit("emuprintconnect", string(jsonPrinter)) + log.Println("Registering printer...") + + conn.WriteMessage(websocket.TextMessage, []byte(jsonMessage)) } // Run function for G-code command input and processing diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts index 2c72f5ce..9d8a3651 100644 --- a/printeremu/testserver/src/index.ts +++ b/printeremu/testserver/src/index.ts @@ -1,30 +1,77 @@ import { createServer } from "http"; -import WebSocket from "ws"; // Import the WebSocket library +import WebSocket from "ws"; -// Create an HTTP server const httpServer = createServer(); -// Create a WebSocket server (using the existing HTTP server) const wss = new WebSocket.Server({ server: httpServer }); -// Handle WebSocket client connections +const eventHandlers = { + emuprintconnect: (ws, data) => { + const printerId = data.Id; + const printerInfo = data; + + if (!printerId) { + console.log('No printerId provided, cannot register printer'); + ws.send(JSON.stringify({ event: 'error', message: 'No printerId provided' })); + return; + } + + printersMap.set(printerId, { ws, printerInfo }); + + console.log(`Printer connected with ID: ${printerId}`); + ws.send(JSON.stringify({ event: 'info', message: 'Successfully registered printer with ID ' + printerId + ' and name ' + printerInfo.Name })); + }, + ping: (ws, data) => { + // do nothing! + // console.log('Received ping event'); + + if (data.data !== 'alive'){ + ws.send(JSON.stringify({ event: 'error', message: 'Invalid ping data' })); + } + } + // TODO: more events +}; + +const printersMap = new Map(); + wss.on('connection', (ws) => { console.log('WebSocket client connected'); - - // Listen for incoming messages from WebSocket clients + ws.on('message', (message) => { - console.log('Received from WebSocket client:', message); - // Echo the message back to the client - ws.send('Hello from WebSocket server!'); + let parsedMessage; + + try { + parsedMessage = JSON.parse(message); + } catch (err) { + console.log('Failed to parse message:', err); + return; + } + + const { event, data } = parsedMessage; + + const handler = eventHandlers[event]; + + if (handler) { + handler(ws, data); + } else { + console.log('Unknown event type:', event); + ws.send(JSON.stringify({ event: 'error', message: 'Unknown event type' })); + } }); - // Handle WebSocket client disconnection ws.on('close', () => { console.log('WebSocket client disconnected'); + + for (const [printerId, printerData] of printersMap) { + if (printerData.ws === ws) { + printersMap.delete(printerId); + console.log(`Printer disconnected and removed with ID: ${printerId}`); + break; + } + } }); }); -// Start the HTTP server (which is used by the WebSocket server) httpServer.listen(8000, () => { - console.log("WebSocket server is running on http://localhost:8000"); + console.log("WebSocket server is running on http://127.0.0.1:8000"); }); From b1132231ca7826139f700224f2cbe5a56d280042 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:36:09 -0500 Subject: [PATCH 046/194] refactored old ports.py into new Ports.py --- server/Classes/Ports.py | 179 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 788b1e11..6e821960 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -2,15 +2,22 @@ import serial.tools.list_ports from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS - +from flask import Blueprint, jsonify, request +from sqlalchemy.exc import SQLAlchemyError +from Classes.Fabricators.Fabricator import Fabricator +from Classes.Fabricators.Device import Device +from Classes.Ports import Ports +from Classes.serialCommunication import sendGcode class Ports: @staticmethod - def getPorts(): + def getPorts() -> list[ListPortInfo | SysFS]: + """Get a list of all connected serial ports.""" return serial.tools.list_ports.comports() @staticmethod def getPortByName(name: str) -> ListPortInfo | SysFS | None: + """Get a specific port by its device name.""" assert isinstance(name, str) ports = Ports.getPorts() for port in ports: @@ -20,9 +27,175 @@ def getPortByName(name: str) -> ListPortInfo | SysFS | None: @staticmethod def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: + """Get a specific port by its hardware ID.""" assert isinstance(hwid, str) ports = Ports.getPorts() for port in ports: if hwid in port.hwid: return port - return None \ No newline at end of file + return None + + @staticmethod + def getRegisteredFabricators() -> list[Fabricator]: + """Get a list of all registered fabricators.""" + fabricators = Fabricator.queryAll() # Assuming Fabricator has a method to query all instances + registered_fabricators = [] + for fab in fabricators: + port = Ports.getPortByName(fab.devicePort) + if port: + registered_fabricators.append(fab) + return registered_fabricators + + @staticmethod + def diagnosePort(port: ListPortInfo | SysFS) -> str: + """Diagnose a port to check if it is functional by sending basic G-code commands.""" + try: + device = Fabricator.createDevice(port) # Create a Device instance using Fabricator logic + if not device: + return "Device creation failed." + + device.connect() + sendGcode("M115") # Standard G-code command to get firmware information + response = device.getSerialConnection().readline().decode("utf-8").strip() + device.disconnect() + + return f"Diagnosis result for {port.device}: {response}" + except Exception as e: + return f"Error diagnosing port {port.device}: {e}" + +# Blueprint for ports routes +ports_bp = Blueprint("ports", __name__) + +@ports_bp.route("/getports", methods=["GET"]) +def getPorts(): + """Get a list of all connected ports.""" + try: + ports = Ports.getPorts() + return jsonify([port.device for port in ports]) + except Exception as e: + print(f"Error getting ports: {e}") + return jsonify({"error": "Failed to retrieve ports"}), 500 + +@ports_bp.route("/getfabricators", methods=["GET"]) +def getRegisteredFabricators(): + """Get a list of all registered fabricators.""" + try: + fabricators = Ports.getRegisteredFabricators() + return jsonify([{ + "name": fab.name, + "description": fab.description, + "hwid": fab.hwid, + "devicePort": fab.devicePort, + "status": fab.getStatus() + } for fab in fabricators]) + except Exception as e: + print(f"Error getting registered fabricators: {e}") + return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 + +@ports_bp.route("/register", methods=["POST"]) +def registerFabricator(): + """Register a new fabricator with the system.""" + try: + data = request.get_json() + device = data['fabricator']['device'] + description = data['fabricator']['description'] + hwid = data['fabricator']['hwid'] + name = data['fabricator']['name'] + + # Create a new fabricator instance using the Fabricator class + new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) + new_fabricator.description = description + new_fabricator.hwid = hwid + + return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) + except SQLAlchemyError as db_err: + print(f"Database error during registration: {db_err}") + return jsonify({"error": "Database error occurred"}), 500 + except Exception as e: + print(f"Error registering fabricator: {e}") + return jsonify({"error": "Failed to register fabricator"}), 500 + +@ports_bp.route("/deletefabricator", methods=["POST"]) +def deleteFabricator(): + """Delete a fabricator from the system.""" + try: + data = request.get_json() + fabricator_id = data['fabricator_id'] + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + + if fabricator: + Fabricator.query.filter_by(dbID=fabricator_id).delete() + Fabricator.addToDB() + return jsonify({"success": True, "message": "Fabricator deleted successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 + except Exception as e: + print(f"Error deleting fabricator: {e}") + return jsonify({"error": "Failed to delete fabricator"}), 500 + +@ports_bp.route("/editname", methods=["POST"]) +def editName(): + """Edit the name of a registered fabricator.""" + try: + data = request.get_json() + fabricator_id = data['fabricator_id'] + new_name = data['name'] + + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + if fabricator: + fabricator.setName(new_name) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 + except Exception as e: + print(f"Error editing fabricator name: {e}") + return jsonify({"error": "Failed to edit fabricator name"}), 500 + +@ports_bp.route("/diagnose", methods=["POST"]) +def diagnoseFabricator(): + """Diagnose a fabricator based on its port.""" + try: + data = request.get_json() + device_name = data['device'] + port = Ports.getPortByName(device_name) + + if port: + diagnosis_result = Ports.diagnosePort(port) + return jsonify({"success": True, "message": diagnosis_result}) + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error diagnosing fabricator: {e}") + return jsonify({"error": "Failed to diagnose fabricator"}), 500 + +@ports_bp.route("/movehead", methods=["POST"]) +def moveHead(): + """Move the head of a fabricator. Deprecated??????""" + try: + data = request.get_json() + device_name = data['port'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator(port) + result = fabricator.device.home() # Ensuring `home()` method from Device is used + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error moving head: {e}") + return jsonify({"error": "Failed to move fabricator head"}), 500 + +@ports_bp.route("/movefabricatorlist", methods=["POST"]) +def moveFabricatorList(): + """Change the order of fabricators.""" + try: + from app import printer_status_service # Ensure integration aligns with the new naming convention + data = request.get_json() + fabricator_ids = data['fabricator_ids'] + + result = printer_status_service.moveFabricatorList(fabricator_ids) + return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) + except Exception as e: + print(f"Error moving fabricator list: {e}") + return jsonify({"error": "Failed to move fabricator list"}), 500 From 4579332ef669c50039e145333b9255da840f2491 Mon Sep 17 00:00:00 2001 From: iron768 Date: Thu, 7 Nov 2024 23:08:09 -0500 Subject: [PATCH 047/194] fix: remove unneeded variable --- printeremu/src/emulator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index c582d2b4..f592ab85 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -115,7 +115,7 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { continue } - data, _ := parsedMessage["data"] + data := parsedMessage["data"] switch event { case "info": From dea6e85cfb50fbf7da0bfa30ec51c983ba55cd70 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 14:37:02 -0500 Subject: [PATCH 048/194] fix: time formatting if tests are longer than a minute --- Tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 01473789..3a9e0df5 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -101,9 +101,9 @@ def pytest_sessionfinish(session, exitstatus) -> None: summary += f"\033[32m in {session_duration:.2f}s" if session_duration > 3600: - summary += f" ({session_duration // 3600:.0f}:{session_duration % 3600 // 60:.0f}:{session_duration % 60:.2f})" + summary += f" ({session_duration // 3600:.2f}:{session_duration % 3600 // 60:.2f}:{session_duration % 60:.2f})" elif session_duration > 60: - summary += f" ({session_duration // 60:.0f}:{session_duration % 60:.2f})" + summary += f" ({session_duration // 60:.2f}:{session_duration % 60:.2f})" if session.config.failed_count > 0: headerText = "\n" + line_separator("FAILURES", symbol="=") From 0d9331e0c4cdd32b9897daa3dc741ff253b42f78 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:52:04 -0500 Subject: [PATCH 049/194] fix: notes for Ports.py --- server/Classes/Ports.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 6e821960..333eb409 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -6,7 +6,6 @@ from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Fabricator import Fabricator from Classes.Fabricators.Device import Device -from Classes.Ports import Ports from Classes.serialCommunication import sendGcode class Ports: @@ -36,6 +35,7 @@ def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: return None @staticmethod + #TODO: @nate, why does this method exist? why not just use the Fabricator.queryAll? if that doesnt do what this does, just rewrite it to do what you need, I'm not at attached to its current implementation def getRegisteredFabricators() -> list[Fabricator]: """Get a list of all registered fabricators.""" fabricators = Fabricator.queryAll() # Assuming Fabricator has a method to query all instances @@ -47,6 +47,7 @@ def getRegisteredFabricators() -> list[Fabricator]: return registered_fabricators @staticmethod + #TODO: @nate should this be in ports, or should it be in the fabricator or device class? if it was, there would be no need for creating a device def diagnosePort(port: ListPortInfo | SysFS) -> str: """Diagnose a port to check if it is functional by sending basic G-code commands.""" try: @@ -55,7 +56,9 @@ def diagnosePort(port: ListPortInfo | SysFS) -> str: return "Device creation failed." device.connect() + #TODO: @nate, M115 isn't supported by generic gcode sendGcode("M115") # Standard G-code command to get firmware information + #TODO: @nate, generic devices don't have serial responses response = device.getSerialConnection().readline().decode("utf-8").strip() device.disconnect() @@ -67,6 +70,7 @@ def diagnosePort(port: ListPortInfo | SysFS) -> str: ports_bp = Blueprint("ports", __name__) @ports_bp.route("/getports", methods=["GET"]) +#TODO: @nate, what is this gonna be used for? def getPorts(): """Get a list of all connected ports.""" try: @@ -98,15 +102,15 @@ def registerFabricator(): try: data = request.get_json() device = data['fabricator']['device'] - description = data['fabricator']['description'] - hwid = data['fabricator']['hwid'] + # TODO: @nate the description and hwid are assigned in the constructor, so this is redundant + # description = data['fabricator']['description'] + # hwid = data['fabricator']['hwid'] name = data['fabricator']['name'] # Create a new fabricator instance using the Fabricator class new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) - new_fabricator.description = description - new_fabricator.hwid = hwid - + # new_fabricator.description = description + # new_fabricator.hwid = hwid return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") @@ -125,7 +129,7 @@ def deleteFabricator(): if fabricator: Fabricator.query.filter_by(dbID=fabricator_id).delete() - Fabricator.addToDB() + Fabricator.updateDB() return jsonify({"success": True, "message": "Fabricator deleted successfully"}) else: return jsonify({"error": "Fabricator not found"}), 404 From 86c45ca31c377cc30c0cbfbf93d314a6437ea4cf Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:54:43 -0500 Subject: [PATCH 050/194] feat: better logging for devices --- server/Classes/Fabricators/Device.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index b69ce109..94045169 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -19,16 +19,12 @@ class Device(ABC): PRODUCTID: int | None = None DESCRIPTION: str | None = None MAXFEEDRATE: int | None = None - serialID: str | None = None serialConnection: serial.Serial | None = None - serialPort: ListPortInfo | SysFS | None = None homePosition: Vector3 | None = None - status: str = "idle" - verdict: str = "" def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): - self.serialPort = serialPort - self.serialID = serialPort.serial_number + self.serialPort: ListPortInfo | SysFS | None = serialPort + self.serialID: str | None = serialPort.serial_number self.logger = Logger(self.serialPort.device, self.DESCRIPTION, consoleLogger=consoleLogger, fileLogger=fileLogger) self.status = "idle" self.verdict = "" @@ -42,8 +38,12 @@ def connect(self): self.serialConnection.reset_input_buffer() return True except Exception as e: - # let the printer parent class deal with the error - return e + if self.logger is None: + print(e) + else: + self.logger.error("Error connecting:") + self.logger.error(e) + return False def disconnect(self): if self.serialConnection: From 4ba2396b152074d9db40086aade337c689a4ee64 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:55:46 -0500 Subject: [PATCH 051/194] feat: created vanilla gcode implementation for a future implementation of a generic gcode device --- server/Mixins/gcode/usesVanillaGcode.py | 87 +++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 server/Mixins/gcode/usesVanillaGcode.py diff --git a/server/Mixins/gcode/usesVanillaGcode.py b/server/Mixins/gcode/usesVanillaGcode.py new file mode 100644 index 00000000..3587f666 --- /dev/null +++ b/server/Mixins/gcode/usesVanillaGcode.py @@ -0,0 +1,87 @@ +from time import sleep + +from typing_extensions import Buffer + +from Classes.Fabricators.Device import Device +from Classes.Vector3 import Vector3 +from Mixins.hasResponseCodes import alwaysTrue, checkOK, checkXYZ, checkExtruderTemp, checkBedTemp + + +class usesVanillaGcode: + homeCMD: Buffer = b"G28\n" + + callablesHashtable = { + "G0": [alwaysTrue, checkOK], # rapid positioning + "G1": [alwaysTrue, checkOK], # linear interpolation + "G2": [alwaysTrue, checkOK], # clockwise arc + "G3": [alwaysTrue, checkOK], # counter-clockwise arc + "G4": [alwaysTrue, checkOK], # dwell + "G28": [alwaysTrue, checkXYZ], # Home + } + + def goTo(self: Device, loc: Vector3, isVerbose: bool = False): + assert isinstance(loc, Vector3) + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) + return loc == self.getToolHeadLocation() + + def home(self, isVerbose: bool = False): + try: + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + self.sendGcode(usesVanillaGcode.homeCMD, isVerbose=isVerbose) + return True + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error homing:") + self.logger.error(e) + return + + def parseGcode(self: Device, file: str, isVerbose: bool = False): + assert isinstance(file, str) + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + try: + with open(file, "r") as f: + self.logger.info(f"Printing {file}") + for line in f: + if line.startswith(";") or line == "\n": + continue + if isVerbose: self.logger.debug(line.strip("\n")) + if self.status == "paused": + self.pause() + while self.status == "paused": + sleep(1) + if self.status == "cancelled": + self.verdict = "cancelled" + self.logger.info("Job cancelled") + return True + elif self.status == "printing": + self.resume() + if self.status == "cancelled": + self.verdict = "cancelled" + self.logger.info("Job cancelled") + return True + self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) + self.verdict = "complete" + self.logger.info("Job complete") + return True + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error cancelling job:") + self.logger.error(e) + self.verdict = "error" + return True + + + def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): + assert self.serialConnection.is_open + assert isinstance(gcode, bytes) + self.serialConnection.write(gcode) + self.logger.debug(gcode.decode("utf-8")) + return True From e7ac408e73b3500b3826e5f8bd33482a6e094021 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:56:34 -0500 Subject: [PATCH 052/194] feat: moved stuff that was vanilla to there. --- server/Mixins/{ => gcode}/usesMarlinGcode.py | 27 ++++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) rename server/Mixins/{ => gcode}/usesMarlinGcode.py (91%) diff --git a/server/Mixins/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py similarity index 91% rename from server/Mixins/usesMarlinGcode.py rename to server/Mixins/gcode/usesMarlinGcode.py index de3e0540..b36ab353 100644 --- a/server/Mixins/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -6,25 +6,22 @@ from Classes.LocationResponse import LocationResponse from Classes.Vector3 import Vector3 from Mixins.canPause import canPause +from Mixins.gcode.usesVanillaGcode import usesVanillaGcode from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue, checkBedTemp, checkExtruderTemp, hasResponsecodes -class usesMarlinGcode(canPause, hasResponsecodes, metaclass=ABCMeta): - homeCMD: Buffer = "G28\n".encode("utf-8") - cancelCMD: Buffer = "M112\n".encode("utf-8") - keepAliveCMD: Buffer = "M113 S1\n".encode("utf-8") - doNotKeepAliveCMD: Buffer = "M113 S0\n".encode("utf-8") - statusCMD: Buffer = "M115\n".encode("utf-8") - getLocationCMD: Buffer = "M114\n".encode("utf-8") - pauseCMD: Buffer = "M601\n".encode("utf-8") - resumeCMD: Buffer = "M602\n".encode("utf-8") - getMachineNameCMD: Buffer = "M997\n".encode("utf-8") +class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=ABCMeta): + cancelCMD: Buffer = b"M112\n" + keepAliveCMD: Buffer = b"M113 S1\n" + doNotKeepAliveCMD: Buffer = b"M113 S0\n" + statusCMD: Buffer = b"M115\n" + getLocationCMD: Buffer = b"M114\n" + pauseCMD: Buffer = b"M601\n" + resumeCMD: Buffer = b"M602\n" + getMachineNameCMD: Buffer = b"M997\n" callablesHashtable = { - "G0": [alwaysTrue, checkOK], # Move - "G1": [alwaysTrue, checkOK], # Move - "G28": [alwaysTrue, checkXYZ], # Home "G29 P1": [alwaysTrue, checkOK], # Auto bed leveling "G29 P9": [checkOK, checkOK], # Auto bed leveling "M73": [alwaysTrue, checkOK], # Set build percentage @@ -36,11 +33,13 @@ class usesMarlinGcode(canPause, hasResponsecodes, metaclass=ABCMeta): "M190": [alwaysTrue, checkBedTemp], # Wait for bed to reach target temp } + callablesHashtable = {**usesVanillaGcode.callablesHashtable, **callablesHashtable} + def goTo(self: Device, loc: Vector3, isVerbose: bool = False): assert isinstance(loc, Vector3) assert isinstance(isVerbose, bool) assert isinstance(self, Device) - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{int(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) return loc == self.getToolHeadLocation() From 2c33855afd0abbbddbfd5e64cd18da2363a092b1 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:57:16 -0500 Subject: [PATCH 053/194] fix: moved file, so imports needed to be changes --- server/Classes/Fabricators/Printers/Ender/EnderPrinter.py | 4 +--- .../Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py | 5 ++--- server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index eb2ae8f5..2b4c9311 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,11 +1,9 @@ from abc import ABCMeta -from typing import Callable from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import hasResponsecodes, alwaysTrue -from Mixins.usesMarlinGcode import usesMarlinGcode +from Mixins.gcode.usesMarlinGcode import usesMarlinGcode class EnderPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): diff --git a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py index 178d29e3..d24cd8be 100644 --- a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py +++ b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py @@ -1,11 +1,10 @@ from abc import ABCMeta -from typing import Callable from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import hasResponsecodes, checkOK, alwaysTrue -from Mixins.usesMarlinGcode import usesMarlinGcode +from Mixins.hasResponseCodes import hasResponsecodes +from Mixins.gcode.usesMarlinGcode import usesMarlinGcode class MakerBotPrinter(Printer, hasEndingSequence, hasResponsecodes, metaclass=ABCMeta): VENDORID = 0x23C1 diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 9b74b781..ccc4e69f 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -1,9 +1,9 @@ from abc import ABCMeta, abstractmethod -from typing_extensions import Buffer, Callable +from typing_extensions import Buffer from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.usesMarlinGcode import usesMarlinGcode +from Mixins.gcode.usesMarlinGcode import usesMarlinGcode class PrusaPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): From 819d437e67f01e4a02e0b1a6c39c54a27f1fd4aa Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 8 Nov 2024 15:58:10 -0500 Subject: [PATCH 054/194] fix: optimized imports and added static method to commit db. --- server/Classes/Fabricators/Fabricator.py | 31 +++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 2731d779..b0cb8b46 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -5,21 +5,9 @@ from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Device import Device -from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 -from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro -from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter -from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter -from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 -from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 -from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 -from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S -from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter -from Classes.Ports import Ports -from Classes.Queue import Queue from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence #from Mixins.hasStartupSequence import hasStartupSequence -from Classes.Jobs import Job from models.db import db from datetime import datetime, timezone @@ -42,8 +30,12 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", addToDB: b assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) assert isinstance(name, str) + from Classes.Queue import Queue + from Classes.Jobs import Job + self.device: Device = Fabricator.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger) self.job: Job | None = None + self.queue: Queue = Queue() self.status: str = "idle" @@ -79,7 +71,13 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi """creates the correct printer object based on the serial port info""" if serialPort is None: return None + from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter + from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter + from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter if serialPort.vid == PrusaPrinter.VENDORID: + from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 + from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 + from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: return PrusaMK4(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif serialPort.pid == PrusaMK4S.PRODUCTID: @@ -89,6 +87,8 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi else: return None elif serialPort.vid == EnderPrinter.VENDORID: + from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 + from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: return Ender3Pro(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) @@ -97,6 +97,7 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: + from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: return Replicator2(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: @@ -107,11 +108,17 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi def queryAll(cls) -> list["Fabricator"]: """return all fabricators in the database""" fabList = [] + from Classes.Ports import Ports for fab in cls.query.all(): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) return fabList + @classmethod + def updateDB(cls): + """commits all changes to the db""" + db.session.commit() + def addToDB(self): db.session.add(self) db.session.commit() From e89726159ec7cff99b71bc4fcd3c62cdc091c154 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Sat, 9 Nov 2024 18:27:37 -0500 Subject: [PATCH 055/194] Correct issues with diagnose and repair/ remove deprecated stuff --- server/Classes/Fabricators/Device.py | 61 +++++++++++++++++--- server/Classes/Ports.py | 83 +++++++++++----------------- 2 files changed, 84 insertions(+), 60 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 94045169..8bb06ee4 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -1,17 +1,13 @@ from abc import ABC, abstractmethod - from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from typing_extensions import Buffer - from Classes.Vector3 import Vector3 from Classes.Logger import Logger import serial import serial.tools.list_ports - from Mixins.canPause import canPause - class Device(ABC): # static variables MODEL: str | None = None @@ -76,13 +72,62 @@ def getToolHeadLocation(self) -> Vector3: pass def repair(self): - pass + """Attempt to repair the device connection by closing and reopening the serial connection.""" + try: + if self.MODEL and "Ender" in self.MODEL: + # If the device is an Ender, skip specific repair commands + if self.logger: + self.logger.info(f"Repair skipped for {self.MODEL}") + return "Repair not necessary for Ender devices." + + if self.serialConnection: + self.logger.info("Closing existing connection for repair.") + self.serialConnection.close() + + # Attempt to reconnect + self.logger.info("Attempting to reconnect for repair.") + self.connect() + + if self.serialConnection and self.serialConnection.is_open: + self.logger.info("Repair successful: connection reopened.") + return "Repair successful." + else: + return "Repair failed: unable to reopen connection." + except Exception as e: + self.logger.error(f"Error during repair: {e}") + return f"Repair failed with error: {e}" + + def diagnose(self): + """Diagnose the device by sending basic G-code commands and checking responses.""" + try: + if self.MODEL and "Ender" in self.MODEL: + # If the device is an Ender, skip the diagnosis + self.logger.info(f"Diagnosis skipped for {self.MODEL}") + return "Diagnosis not necessary for Ender devices." + + self.logger.info("Starting device diagnosis.") + if not self.connect(): + return "Diagnosis failed: unable to connect." + + self.logger.info("Sending diagnostic G-code command (e.g., M115).") + self.sendGcode(b"M115\n") + + response = self.serialConnection.readline().decode("utf-8").strip() + self.disconnect() + + if response: + self.logger.info(f"Diagnosis response: {response}") + return f"Diagnosis successful: {response}" + else: + return "Diagnosis failed: no response from device." + except Exception as e: + self.logger.error(f"Error during diagnosis: {e}") + return f"Diagnosis failed with error: {e}" def hardReset(self, newStatus: str): - if self.serialConnection.is_open: + if self.serialConnection and self.serialConnection.is_open: self.serialConnection.close() - - pass + # Additional reset logic can be added here if needed. def getModel(self): return self.MODEL diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 333eb409..788eaa48 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -34,57 +34,16 @@ def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: return port return None - @staticmethod - #TODO: @nate, why does this method exist? why not just use the Fabricator.queryAll? if that doesnt do what this does, just rewrite it to do what you need, I'm not at attached to its current implementation - def getRegisteredFabricators() -> list[Fabricator]: - """Get a list of all registered fabricators.""" - fabricators = Fabricator.queryAll() # Assuming Fabricator has a method to query all instances - registered_fabricators = [] - for fab in fabricators: - port = Ports.getPortByName(fab.devicePort) - if port: - registered_fabricators.append(fab) - return registered_fabricators - - @staticmethod - #TODO: @nate should this be in ports, or should it be in the fabricator or device class? if it was, there would be no need for creating a device - def diagnosePort(port: ListPortInfo | SysFS) -> str: - """Diagnose a port to check if it is functional by sending basic G-code commands.""" - try: - device = Fabricator.createDevice(port) # Create a Device instance using Fabricator logic - if not device: - return "Device creation failed." - - device.connect() - #TODO: @nate, M115 isn't supported by generic gcode - sendGcode("M115") # Standard G-code command to get firmware information - #TODO: @nate, generic devices don't have serial responses - response = device.getSerialConnection().readline().decode("utf-8").strip() - device.disconnect() - - return f"Diagnosis result for {port.device}: {response}" - except Exception as e: - return f"Error diagnosing port {port.device}: {e}" - # Blueprint for ports routes ports_bp = Blueprint("ports", __name__) @ports_bp.route("/getports", methods=["GET"]) -#TODO: @nate, what is this gonna be used for? -def getPorts(): - """Get a list of all connected ports.""" - try: - ports = Ports.getPorts() - return jsonify([port.device for port in ports]) - except Exception as e: - print(f"Error getting ports: {e}") - return jsonify({"error": "Failed to retrieve ports"}), 500 @ports_bp.route("/getfabricators", methods=["GET"]) def getRegisteredFabricators(): """Get a list of all registered fabricators.""" try: - fabricators = Ports.getRegisteredFabricators() + fabricators = Fabricator.queryAll() return jsonify([{ "name": fab.name, "description": fab.description, @@ -102,15 +61,10 @@ def registerFabricator(): try: data = request.get_json() device = data['fabricator']['device'] - # TODO: @nate the description and hwid are assigned in the constructor, so this is redundant - # description = data['fabricator']['description'] - # hwid = data['fabricator']['hwid'] name = data['fabricator']['name'] # Create a new fabricator instance using the Fabricator class new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) - # new_fabricator.description = description - # new_fabricator.hwid = hwid return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") @@ -164,17 +118,42 @@ def diagnoseFabricator(): port = Ports.getPortByName(device_name) if port: - diagnosis_result = Ports.diagnosePort(port) - return jsonify({"success": True, "message": diagnosis_result}) + fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + if fabricator: + diagnosis_result = fabricator.diagnose() + return jsonify({"success": True, "message": diagnosis_result}) + else: + return jsonify({"error": "Failed to create fabricator for diagnosis"}), 500 else: return jsonify({"error": "Device not found"}), 404 except Exception as e: print(f"Error diagnosing fabricator: {e}") return jsonify({"error": "Failed to diagnose fabricator"}), 500 +@ports_bp.route("/repair", methods=["POST"]) +def repairFabricator(): + """Repair a fabricator based on its port.""" + try: + data = request.get_json() + device_name = data['device'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + if fabricator: + repair_result = fabricator.repair() + return jsonify({"success": True, "message": repair_result}) + else: + return jsonify({"error": "Failed to create fabricator for repair"}), 500 + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error repairing fabricator: {e}") + return jsonify({"error": "Failed to repair fabricator"}), 500 + @ports_bp.route("/movehead", methods=["POST"]) def moveHead(): - """Move the head of a fabricator. Deprecated??????""" + """Move the head of a fabricator. Deprecated if no longer needed.""" try: data = request.get_json() device_name = data['port'] @@ -182,7 +161,7 @@ def moveHead(): if port: fabricator = Fabricator(port) - result = fabricator.device.home() # Ensuring `home()` method from Device is used + result = fabricator.device.home() # Use home() method from Device return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) else: return jsonify({"error": "Device not found"}), 404 @@ -194,7 +173,7 @@ def moveHead(): def moveFabricatorList(): """Change the order of fabricators.""" try: - from app import printer_status_service # Ensure integration aligns with the new naming convention + from app import printer_status_service data = request.get_json() fabricator_ids = data['fabricator_ids'] From 6ddc9214fd1d48562fdf35c7d52a18dd0016f87d Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sat, 9 Nov 2024 22:40:21 -0500 Subject: [PATCH 056/194] fix: moved the routes for the ports to the controller file --- server/Classes/Ports.py | 156 +--------------------- server/controllers/ports.py | 258 ++++++++++++++++++------------------ 2 files changed, 129 insertions(+), 285 deletions(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 788eaa48..d66c1861 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -2,11 +2,6 @@ import serial.tools.list_ports from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS -from flask import Blueprint, jsonify, request -from sqlalchemy.exc import SQLAlchemyError -from Classes.Fabricators.Fabricator import Fabricator -from Classes.Fabricators.Device import Device -from Classes.serialCommunication import sendGcode class Ports: @staticmethod @@ -32,153 +27,4 @@ def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: for port in ports: if hwid in port.hwid: return port - return None - -# Blueprint for ports routes -ports_bp = Blueprint("ports", __name__) - -@ports_bp.route("/getports", methods=["GET"]) - -@ports_bp.route("/getfabricators", methods=["GET"]) -def getRegisteredFabricators(): - """Get a list of all registered fabricators.""" - try: - fabricators = Fabricator.queryAll() - return jsonify([{ - "name": fab.name, - "description": fab.description, - "hwid": fab.hwid, - "devicePort": fab.devicePort, - "status": fab.getStatus() - } for fab in fabricators]) - except Exception as e: - print(f"Error getting registered fabricators: {e}") - return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 - -@ports_bp.route("/register", methods=["POST"]) -def registerFabricator(): - """Register a new fabricator with the system.""" - try: - data = request.get_json() - device = data['fabricator']['device'] - name = data['fabricator']['name'] - - # Create a new fabricator instance using the Fabricator class - new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) - return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) - except SQLAlchemyError as db_err: - print(f"Database error during registration: {db_err}") - return jsonify({"error": "Database error occurred"}), 500 - except Exception as e: - print(f"Error registering fabricator: {e}") - return jsonify({"error": "Failed to register fabricator"}), 500 - -@ports_bp.route("/deletefabricator", methods=["POST"]) -def deleteFabricator(): - """Delete a fabricator from the system.""" - try: - data = request.get_json() - fabricator_id = data['fabricator_id'] - fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() - - if fabricator: - Fabricator.query.filter_by(dbID=fabricator_id).delete() - Fabricator.updateDB() - return jsonify({"success": True, "message": "Fabricator deleted successfully"}) - else: - return jsonify({"error": "Fabricator not found"}), 404 - except Exception as e: - print(f"Error deleting fabricator: {e}") - return jsonify({"error": "Failed to delete fabricator"}), 500 - -@ports_bp.route("/editname", methods=["POST"]) -def editName(): - """Edit the name of a registered fabricator.""" - try: - data = request.get_json() - fabricator_id = data['fabricator_id'] - new_name = data['name'] - - fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() - if fabricator: - fabricator.setName(new_name) - return jsonify({"success": True, "message": "Fabricator name updated successfully"}) - else: - return jsonify({"error": "Fabricator not found"}), 404 - except Exception as e: - print(f"Error editing fabricator name: {e}") - return jsonify({"error": "Failed to edit fabricator name"}), 500 - -@ports_bp.route("/diagnose", methods=["POST"]) -def diagnoseFabricator(): - """Diagnose a fabricator based on its port.""" - try: - data = request.get_json() - device_name = data['device'] - port = Ports.getPortByName(device_name) - - if port: - fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method - if fabricator: - diagnosis_result = fabricator.diagnose() - return jsonify({"success": True, "message": diagnosis_result}) - else: - return jsonify({"error": "Failed to create fabricator for diagnosis"}), 500 - else: - return jsonify({"error": "Device not found"}), 404 - except Exception as e: - print(f"Error diagnosing fabricator: {e}") - return jsonify({"error": "Failed to diagnose fabricator"}), 500 - -@ports_bp.route("/repair", methods=["POST"]) -def repairFabricator(): - """Repair a fabricator based on its port.""" - try: - data = request.get_json() - device_name = data['device'] - port = Ports.getPortByName(device_name) - - if port: - fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method - if fabricator: - repair_result = fabricator.repair() - return jsonify({"success": True, "message": repair_result}) - else: - return jsonify({"error": "Failed to create fabricator for repair"}), 500 - else: - return jsonify({"error": "Device not found"}), 404 - except Exception as e: - print(f"Error repairing fabricator: {e}") - return jsonify({"error": "Failed to repair fabricator"}), 500 - -@ports_bp.route("/movehead", methods=["POST"]) -def moveHead(): - """Move the head of a fabricator. Deprecated if no longer needed.""" - try: - data = request.get_json() - device_name = data['port'] - port = Ports.getPortByName(device_name) - - if port: - fabricator = Fabricator(port) - result = fabricator.device.home() # Use home() method from Device - return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) - else: - return jsonify({"error": "Device not found"}), 404 - except Exception as e: - print(f"Error moving head: {e}") - return jsonify({"error": "Failed to move fabricator head"}), 500 - -@ports_bp.route("/movefabricatorlist", methods=["POST"]) -def moveFabricatorList(): - """Change the order of fabricators.""" - try: - from app import printer_status_service - data = request.get_json() - fabricator_ids = data['fabricator_ids'] - - result = printer_status_service.moveFabricatorList(fabricator_ids) - return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) - except Exception as e: - print(f"Error moving fabricator list: {e}") - return jsonify({"error": "Failed to move fabricator list"}), 500 + return None \ No newline at end of file diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 71f9c5e6..6f3ac59e 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,154 +1,152 @@ -# get connected serial ports -import serial -import serial.tools.list_ports -import time -from flask_cors import cross_origin from sqlalchemy.exc import SQLAlchemyError -from flask import Blueprint, jsonify, request, make_response -from models.printers import Printer -# from app import printer_status_service -# from models.jobs import Job -# from models.PrinterStatusService import PrinterStatusService -# from app import printer_status_service +from flask import Blueprint, jsonify, request +from Classes.Fabricators.Fabricator import Fabricator +from Classes.Ports import Ports +# Blueprint for ports routes ports_bp = Blueprint("ports", __name__) -@ports_bp.route("/getports", methods=["GET"]) -def getPorts(): - printerList = Printer.getConnectedPorts() - return jsonify(printerList) - -# method to get printers already registered with the system -@ports_bp.route("/getprinters", methods=["GET"]) -def getRegisteredPrinters(): - try: - res = Printer.get_registered_printers() - return res +@ports_bp.route("/getports", methods=["GET"]) +@ports_bp.route("/getfabricators", methods=["GET"]) +def getRegisteredFabricators(): + """Get a list of all registered fabricators.""" + try: + fabricators = Fabricator.queryAll() + return jsonify([{ + "name": fab.name, + "description": fab.description, + "hwid": fab.hwid, + "devicePort": fab.devicePort, + "status": fab.getStatus() + } for fab in fabricators]) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + print(f"Error getting registered fabricators: {e}") + return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 @ports_bp.route("/register", methods=["POST"]) -def registerPrinter(): - try: - from app import printer_status_service - data = request.get_json() # get json data - # extract data - device = data['printer']['device'] - description = data['printer']['description'] - hwid = data['printer']['hwid'] - name = data['printer']['name'] - - - res = Printer.create_printer(device=device, description=description, hwid=hwid, name=name, status='ready') - if(res["success"] == True): - id = res['printer_id'] - # hwid_parts = hwid.split('-') # Replace '-' with the actual separator - # hwid_without_location = '-'.join(hwid_parts[:-1]) - thread_data = { - "id": id, - "device": device, - "description": description, - "hwid": hwid, - "name": name - } - - printer_status_service.create_printer_threads([thread_data]) - - return res - +def registerFabricator(): + """Register a new fabricator with the system.""" + try: + data = request.get_json() + device = data['fabricator']['device'] + name = data['fabricator']['name'] + + # Create a new fabricator instance using the Fabricator class + new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) + return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) + except SQLAlchemyError as db_err: + print(f"Database error during registration: {db_err}") + return jsonify({"error": "Database error occurred"}), 500 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + print(f"Error registering fabricator: {e}") + return jsonify({"error": "Failed to register fabricator"}), 500 -@ports_bp.route("/deleteprinter", methods=["POST"]) -def delete_printer(): - try: +@ports_bp.route("/deletefabricator", methods=["POST"]) +def deleteFabricator(): + """Delete a fabricator from the system.""" + try: data = request.get_json() - printerid = data['printerid'] - # res = printer_status_service.deleteThread(printerid) - res = Printer.deletePrinter(printerid) - return res + fabricator_id = data['fabricator_id'] + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + + if fabricator: + Fabricator.query.filter_by(dbID=fabricator_id).delete() + Fabricator.updateDB() + return jsonify({"success": True, "message": "Fabricator deleted successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + print(f"Error deleting fabricator: {e}") + return jsonify({"error": "Failed to delete fabricator"}), 500 + @ports_bp.route("/editname", methods=["POST"]) -def edit_name(): - try: - data = request.get_json() - printerid = data['printerid'] - name = data['name'] - res = Printer.editName(printerid, name) - return res +def editName(): + """Edit the name of a registered fabricator.""" + try: + data = request.get_json() + fabricator_id = data['fabricator_id'] + new_name = data['name'] + + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + if fabricator: + fabricator.setName(new_name) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + print(f"Error editing fabricator name: {e}") + return jsonify({"error": "Failed to edit fabricator name"}), 500 + @ports_bp.route("/diagnose", methods=["POST"]) -def diagnose_printer(): +def diagnoseFabricator(): + """Diagnose a fabricator based on its port.""" try: - data = request.get_json() - device = data['device'] - res = Printer.diagnosePrinter(device) - return res + data = request.get_json() + device_name = data['device'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + if fabricator: + diagnosis_result = fabricator.diagnose() + return jsonify({"success": True, "message": diagnosis_result}) + else: + return jsonify({"error": "Failed to create fabricator for diagnosis"}), 500 + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error diagnosing fabricator: {e}") + return jsonify({"error": "Failed to diagnose fabricator"}), 500 + +@ports_bp.route("/repair", methods=["POST"]) +def repairFabricator(): + """Repair a fabricator based on its port.""" + try: + data = request.get_json() + device_name = data['device'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + if fabricator: + repair_result = fabricator.repair() + return jsonify({"success": True, "message": repair_result}) + else: + return jsonify({"error": "Failed to create fabricator for repair"}), 500 + else: + return jsonify({"error": "Device not found"}), 404 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - -# @ports_bp.route("/repairports", methods=["POST", "GET"]) -# def repair_ports(): -# try: -# ports = serial.tools.list_ports.comports() -# print("PORTS: ", ports) -# for port in ports: -# hwid = port.hwid # get hwid -# print("HWID: ", hwid) - -# hwid_without_location = hwid.split(' LOCATION=')[0] -# printer = Printer.getPrinterByHwid(hwid_without_location) -# if printer is not None: -# if(printer.getDevice()!=port.device): -# printer.editPort(printer.getId(), port.device) -# printerthread = findPrinterObject(printer.getId()) -# printerthread.setDevice(port.device) -# return {"success": True, "message": "Printer port(s) successfully updated."} - - # except Exception as e: - # print(f"Unexpected error: {e}") - # return jsonify({"error": "Unexpected error occurred"}), 500 - + print(f"Error repairing fabricator: {e}") + return jsonify({"error": "Failed to repair fabricator"}), 500 + @ports_bp.route("/movehead", methods=["POST"]) def moveHead(): - try: + """Move the head of a fabricator. Deprecated if no longer needed.""" + try: data = request.get_json() - port = data['port'] - - res = Printer.moveHead(port) - if res == "none": - return {"success": False, "message": "Head move unsuccessful."} - - return {"success": True, "message": "Head move successful."} + device_name = data['port'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator(port) + result = fabricator.device.home() # Use home() method from Device + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + else: + return jsonify({"error": "Device not found"}), 404 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - -# def findPrinterObject(printer_id): -# threads = printer_status_service.getThreadArray() -# return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer - -@ports_bp.route("/moveprinterlist", methods=["POST"]) -def movePrinterList(): + print(f"Error moving head: {e}") + return jsonify({"error": "Failed to move fabricator head"}), 500 + +@ports_bp.route("/movefabricatorlist", methods=["POST"]) +def moveFabricatorList(): + """Change the order of fabricators.""" try: from app import printer_status_service data = request.get_json() - printersIds = data['printersIds'] - # change the order of the printers threads - res = printer_status_service.movePrinterList(printersIds) - if res == "none": - return {"success": False, "message": "Printer list not updated."} - - return jsonify({"success": True, "message": "Printer list successfully updated."}) + fabricator_ids = data['fabricator_ids'] + + result = printer_status_service.moveFabricatorList(fabricator_ids) + return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 \ No newline at end of file + print(f"Error moving fabricator list: {e}") + return jsonify({"error": "Failed to move fabricator list"}), 500 From 1fe851ad41fcffa1f699d3d652b1263fb2001adb Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 10 Nov 2024 21:57:37 -0500 Subject: [PATCH 057/194] fix: formatted time to finish tests correctly --- Tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 3a9e0df5..7c8db321 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -101,9 +101,9 @@ def pytest_sessionfinish(session, exitstatus) -> None: summary += f"\033[32m in {session_duration:.2f}s" if session_duration > 3600: - summary += f" ({session_duration // 3600:.2f}:{session_duration % 3600 // 60:.2f}:{session_duration % 60:.2f})" + summary += f" ({session_duration // 3600:.0f}:{session_duration % 3600 // 60:.0f}:{session_duration % 60:.2f})" elif session_duration > 60: - summary += f" ({session_duration // 60:.2f}:{session_duration % 60:.2f})" + summary += f" ({session_duration // 60:02.0f}:{session_duration % 60:02.2f})" if session.config.failed_count > 0: headerText = "\n" + line_separator("FAILURES", symbol="=") From c38fad4132ead3d4609721aa345f01441e82a0e7 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 10 Nov 2024 21:58:12 -0500 Subject: [PATCH 058/194] fix: updated mk4s to 50% model so its actually comparable. --- Tests/test_fabricator.py | 2 +- server/xyz-cali-cube-mini_MK4S.gcode | 13030 +++++++++++++++++++------ 2 files changed, 10062 insertions(+), 2970 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 88cc79d0..6d0345c0 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -113,7 +113,7 @@ def test_gcode_print_time(): expectedTime = 14 * 60 + 15 if shortTest else 28 * 60 elif isinstance(fabricator.device, PrusaMK4S): file = file + "_MK4S.gcode" - expectedTime = 180 if shortTest else 1800 + expectedTime = 8 * 60 + 45 if shortTest else 1800 elif isinstance(fabricator.device, PrusaMK4): file = file + "_MK4.gcode" expectedTime = 10 * 60 if shortTest else 1800 diff --git a/server/xyz-cali-cube-mini_MK4S.gcode b/server/xyz-cali-cube-mini_MK4S.gcode index fbb469d6..c29c82bf 100644 --- a/server/xyz-cali-cube-mini_MK4S.gcode +++ b/server/xyz-cali-cube-mini_MK4S.gcode @@ -1,1223 +1,1658 @@ -; generated by PrusaSlicer 2.8.1+win64 on 2024-10-28 at 18:14:42 UTC +; generated by PrusaSlicer 2.8.1+win64 on 2024-11-10 at 04:11:14 UTC ; -; thumbnail_QOI begin 16x16 608 -; cW9pZgAAABAAAAAQBAD+XV1dxf6QYzW/iP6BenQoyaHh/rRmF/7RaQDA/sByI/6WiXz+ZWVlKMb+iG -; E6Nf6/YACcTDXAoC/+sYNV/nd3dyjE/qVkIzX+uV0AEjXD/sR2J/6LhX7+YGBgKMD+a15R/sJnDDX+ -; v2AANcT+wGEANaEv/qOJb/5tbW0o/m9SNP6nVAD+zGcANcKbTaKW/rNaABw1/ppNAP5sUDT+xcXFKA -; j+h0QAp/H+w2IAp/HB/sBgABA1Ev6CQQD+cDgA/q6Sdv65ubn+XV1dCB7Aorb+sVkAHDXBDh3+j0gA -; Hf5zVzv+YGBgKAj+h0QApcM3Hv6ZTQD+yGQAFP6ORwAdo6X+ikUAHf5mSi4owAgeN6OlHsA3/nY7AB -; 3ALB3AGyjACB7AnFwrEh4dwKK2o5UdPhsowP5lWEz+f0kRHlYewR3A/phMAB0C/mw/Ef5hVkwowZ7U -; /nVPKf6VSwDAHsAdOwId/mhIKSjEtYj+w7Kh/o5TGB7AHR7+bzoG/mRPOijE/paWlv7i4uL+mpqa/m -; RgW/56TB0eHf5sPxH+XltXKMOsiP7AwMCxiC6HiCjACJs9KMUAAAAAAAAAAQ== +; thumbnail_QOI begin 16x16 652 +; cW9pZgAAABAAAAAQBAD+XV1dnYh/w/6PYTPA/mliXKYef38KwbCIm4gKwaHh/rNlF/7RaQDA/rZoGf +; 5qXVAKpYiwiI+ICsCpiKqIjoj+h2A5Nf6/YACcTDXAny/+j2I0GcCviJyICsCiiP6wby01/rldABI1 +; w/60Zhf+Y11WGREZ/mteUP7CZws1EjXE/sBhADUF/nlfRRnA/m9RNP6nVAD+zGcANcKbTaKW/rNaAB +; w1/ppNAP5mSi7+goKCGf52WDv+h0QAp/H+w2IANcH+wGAAEDUS/oJBAP5wOAD+jHBU/oCAgIqI/nBT +; NR7Aorb+sVkAHKK2wQ4d/o9IAB0bGcD+b1I0HqXDNx7+mU0A/shkABT+jkcAHaOl/opFAB0b/l1dXR +; kIHjejpR7AN/52OwAdwCwdwBsos4j+cVM2HsCcXP6VSwASHh3AorajlR0+Gyh//nRnW/6DTRYeVh7B +; HcD+mEwAHQL+bD8R/mxhVzoowKfE/n9aNP6VSwDAHsAdOwId/mhIKSjApYgowaqI/qCPfv6HTBEewB +; 0e/m86Bv5kTzoowVXAKLaI/p6env5vb2+Zw/6CVCUeHf5sPxH+XltXKMEZpYidiP6MjIymiAYowKaI +; /nxfQY9OKMEZwD5AAAAAAAAAAAE= ; thumbnail_QOI end ; ; -; thumbnail_QOI begin 313x173 13832 -; cW9pZgAAATkAAACtBAD+XFxc8H/96aSI/p+fn/7z8/OsiMCSiP60tLT+b29vKP392BnuKP3sf7qI/t -; ra2ibAnIj+09PT/oiIiP5fX18o/f3WGewo/e3+c19M/q1lHf6BYUD+ZmZm/rCwsP75+fkmwIqI/qen -; p/5nZ2co/f3VGeoo/e7+l2Mu/tFpAMCfL/6QYjT+ZmBZ/oWFhf7n5+cmwJmI/sbGxv59fX0o/f3UGe -; go/e7+a15R/rtnETXD/rRmF/5sX1L+bW1t/sDAwCbBgoj+mZmZ/mRkZCj9/dIZ5yj97v6IYTr+0WkA -; xgU7/mBgYP6Xl5f+8PDwJsAy/rq6uv5ycnIo/f3PEf6xsbEZ5Sj97qHh/qVkIzXJMP5lX1i1L/7S0t -; ImwJ2I/tfX1/6NjY0VKP39zLqI/t7e3iYZ4yj97/5zX0z+wmcMNcufLx3+Y2Nj/qqqqv739/cmwI6I -; /q2trf5qamoo/f3K/qKiov719fUmwBnhKP3w/p5kKTXPIv5mX1n+gICA/uLi4ibADP7Ly8v+goKCKP -; 39x7CI/szMzCbCGeAo/e89IP7RaQDRIP5zYEwr/ri4uP79/f0mwIWI/p+fn/5lZWUo/f3E/o+Pj/7u -; 7u4mwxneKP3wOzXUBTv+Z2Ba/pCQkCcmwJeI/sDAwP53d3co/f3Bqoj+ubm5/v7+/ibEGdwo/fCh4f -; 6tZR3+0WkA1zD+bF9S/nFxcf7KysomwAj+29vb/pGRkf5iYmIo/fz+fX19ESbGGdso/fD+emBGBTXZ -; Bf6IYTr+YWFh/qKiov709PQmwJGI/rKysv5ubm4o/fkV/qqqqv74+PgmxxnZKP3xIjXdDTG7Hv7d3d -; 0mwJuI/tHR0f6Ghob+Xl5eVf32s4j+1NTUJskZ1yj98f5zX0z+wmcMNd/+u2cR/oJhQD7+s7OzGybA -; iIj+paWl/mdnZyj99P6Xl5f+8fHxJsoZ1ij98Ts14p8vO/5nYFr+iIiIHCbAmIj+xMTE/nx8fCj98a -; yI/sHBwSbMGdQo/fEYMDXlMP5sX1In/sPDwybAF4GIDv5jY2Mo/e7+g4OD/unp6SbNGdMo/fEINegF -; OxX+m5ubFCbAk4j+uLi4/nFxcSj96wL+r6+vGybMnYj+vLy8GdEo/fINNesw/mVfWLYv/tXV1SbAKv -; 7W1tb+i4uLFSj96BD+3NzcJs2PiP6VlZX+ZGRkmIjQKP3x/nNfTP7CZww17QUdEf6tra3++Pj4JsCM -; iP6rq6v+aWlpKP3m/p+fnwEmzSn+d3d3iIgoGc4o/fL+l2MuNfH+nmQp/mZgWRP+5OTkJsA9/snJyf -; 6AgIAo/eOviDwmzQH+pqam/mZmZijBGc0o/fH+c19M/rtnEf7RaQDzIP5zYEz+a2tr/ru7uxcmwISI -; /p2dnf5lZWWYiP3gGv7t7e0mzf7d3d3+gYGBFZ2IwhnMKP3xOzX2BTsV/pOTk/7v7+8mwJaI/r29vf -; 51dXUo/d0g/rW1tf79/f0mzJqI/re3twk3KMMZyij98aHh/q1lHTXV/q1XAKKmNeD+tGYX/mxfUv5y -; cnL+zs7OJsCdiP7a2toWJCj92r2I/uLi4ibNioj+jo6O/mJiYijFGcko/fH+emBG/spoBjXV/pRKAP -; 5wOAD+h0QA/plNAP7MZwA13wX+gWFAJP6lpaX+9fX1JsA2/rCwsAko/dcG/qenp/739/cmzf7FxcX+ -; dHR0BijGGcco/fIiNdX+v2AA/nw+AJouwB7Aorb+ul0ANeAi/mZfWb0e/t/f3ybAm4j+z8/P/oWFhS -; j91bKI/tHR0SbNk4j+n5+f/mVlZSjIGcYo/fE9/rtnETXV/qBQAB3CNx7B/qdUABw13yD+c2BM/mho -; aP61tbX++/v7JsCHiP6jo6P+ZmZmKP3S/pOTk/7w8PAmzT/+fX19FSjJGcUo/fH+kGI0NdX+v2AA/o -; JBAB3BABKipv6VSwAewf6xWQA14J8vO/5nYFoa/uvr6ybALv7CwsL+enp6g4j9zw3+vb29Js0u/rGx -; sf5paWkoyxnDKP3xGDA11Q4dwv6aTQA1wZ5aDjceDjXiMP5sX1I2/sbGxibAF4CI/pWVlf5jY2Mo/c -; z+gICA/ufn5ybNhoj+iIiIMyjMGcIo/fH+iGE6/tFpANWdW/6ORwAdwaO1/rldADXE/r5gAP6QSQCk -; 1DXjBQgk/p6env7z8/MmwJKI/ra2tjYo/ckz/q2trQwmzED+vr6+/nFxcY2IKM0ZwSj98f6lZCM11T -; E+HcH+lEoANccc/qNSADXlDf5mX1m4Hv7Y2NgmwCoh/omJif5fX18o/ca2iP7Z2dkmzZCI/piYmP5k -; ZGQozxnAKP3wPf7CZww11QT+cDgAwv6tVwA1yf6xWQD+yGQANeX+u2cR/oFhQP5lZWX+r6+vDCbAi4 -; j+qKio/mhoaCj9xP6cnJz+8/PzJs0W/nl5eQYo/f3F/pdjLjXV/r9gAP58PgCaLsH+gkEA/sVjADXK -; /rpdAKXDNeafLyv+ZmBZ/oSEhP7m5uYmwJmI/sfHx/5+fn7+XV1d/cGuiP7FxcUmzZaI/qqqqv5nZ2 -; co/f3F/nNfTP67ZxE11Q4dwiU1zJkfNDXo/rRmF/5zYEwJ/r6+vibBgoj+mpqaEZmI/C3+7OzsJs2B -; iP6CgoL+YWFhKP39xf6QYjQ11Z1b/ohEAB3B/oJBABI1zg416QU7/mBgYP6Wlpb+8PDwJsCViP67u7 -; v+dHR0KPkR/rOzs/78/PwmzJyI/rm5uf5tbW03KP39xKHh/qVkIzXV/q1XAB3CBDXQ/qxWADXrMP5s -; X1IB/tHR0SbAOf7Y2Nj+jY2NFSj2u4j+39/fJs2MiP6QkJD+Y2NjKP39xf5zX0z+wmcMNdUj/nA4AM -; GjtS810Sc17J8v/oFhQP5iYmL+qamp/vb29ibAjoj+rq6u/mtrayjzN/6kpKQQJs3+yMjI/nZ2domI -; KP39xSI11f6/YAD+fD4Ami7BAP7FYwA10g417iL+Zl9Z/n9/f/7h4eG+iMCaiCn+g4ODKPGwiP7Ozs -; 4mzZOI/qKiov5mZmYo/f3FPSA11f6gUAAdwg411P6xWQD+yGQANe4g/nNgTP5paWn+t7e3OSbAhYj+ -; oKCgLyju/pCQkP7v7+8mzSz+fn5+gogo/f3FOzXVEgAdwQASNdUPnXs1758vO/5nYFr+jo6O/u3t7S -; bAl4j+wcHB/nh4eCjrqoj+urq6Js2ZiP60tLT+ampqNyj9/cSh4f6tZR011Q4dwv6aTQA115kfmR81 -; 8TD+bF9S/nBwcP7JyckmwAj+3Nzc/pKSkv5iYmKbiOgX/uXl5SbNiIgaMyj9/cX+emBG/spoBjXUFP -; 6USgAdwaO1/rNaADXZ/qxWADXyBf6IYTr+YWFh/qGhof709PQmwJGIMv5ubm4o5RX+q6urPSbMVf7B -; wcH+cnJyNyj9/cX+nmQpNdX+v2AA/nw+AB3BIRQ12g419A0xuh7+29vbJsCbiP7S0tL+iIiINyjitI -; j+1tbWJs0F/pubm/5lZWUo/f3F/nNfTP7CZww11f6aTQAdwg413P6sVgA19f67ZxH+gmFAL/6ysrIb -; JsCJiP6mpqb+Z2dnKOD+mJiY/vLy8ibNA/57e3sGKP39xTs11f6/YAD+gkEA/nA4AMEA/sVjADXd/r -; FZADX2ny87/mdgWv6Ghob+6enpJsCYiP7FxcX+fX19KN0r/sLCwibNl4j+rKysPij9/cUYMDXVDh3C -; BDXf/rVbAP7IZAA19zD+bF9S/m5ubhMmwBeCiP6YmJj+Y2NjKNr+hYWF/urq6ibNg4gA/mFhYSj9/c -; X+iGE6NdaaLv52OwCdW8A+/rldADXg/r5gADQ1+AU7/mBgYP6ZmZn+8fHxJsCUiP65ubn+cXFxKNcC -; /rCwsP77+/smzCr+u7u7/m9vbzco/f3F/qVkIzXYnVv+jkcAo7U14gEbNfow/mVfWLYv/tTU1CbAnI -; g//oyMjBUo1LmI/t3d3SbNjoj+lJSU/mNjY5qI/f3FPf7CZww1/cP+sVkANfufL/6BYUD+ZGRkCS4m -; wI2I/qysrP5qamoo0v6goKD+9PT0Js3+y8vL/nd3dwYo/f3F/pdjLjX9xf6sVgA1/SL+ZmBZ/oGBgf -; 7j4+MmwJqICwQoz7CICybNAf6lpaX+ZmZmKP39xT3+u2cRNf3GDjX9wCD+c2BM/mtra/66urr+/f39 -; JsCEiP6enp7+ZWVlKMw4/u7u7ibN/tvb2/6AgICAiCj9/cX+bl1M/sxnAKK2/cf+sVkA/shkADXbFP -; 6WSwAFHDXdny/+blpGFf6SkpInJsCWiP6+vr4fKMkv/re3twgmzAz+tra2/mxsbJKIKP39xv5tVDr+ -; lUsA/sNiADX9xv61WwABNdr+v2AA/nw+AJtN/odEAP6ZTQD+yGQANdsS/oJBAP5lTTT+XV1dN7SI/s -; zMzCbACP7a2tol/mFhYSjGvogCJs2KiDj+YmJiKP39yDYeorb+sVkANf3FmR80Ndn+mk0A/nA4AMAr -; HsA3GzXZDh3AEf5dXV3BJP6kpKQQJsCQiP6xsbEYKMMG/qmpqf739/cmzf7ExMT+dHR0Bij9/cn+bV -; Q6HsH+mU0A/shkADX9w55aDjXXEgAdwf6EQgA3HsH+o1IANdadW/6ORwAdwf5lTTQowga9iP7e3t4m -; wJuI/tDQ0P6Ghob+Xl5eKMCyiP7T09MmzZKI/p6eniAo/f3L/m1UOh7Corb+tVsANf3D/qxWADXWDh -; 3CBDUB/p5QAB7AGzXV/rldAD4dwhEoxKqI/rS0tP77+/smwIiI/qSkpC/+lZWV/vDw8CbN/tXV1TkV -; KP39zDYexDgcNf3BJzXUnVsCHcGjtTE1wRwb/shkADXVBB3E/mVNNCjFFf6Kior+6+vrJsAujYgmzS -; 7+sLCw/mlpaSj9/c42HsWn8f6+YAA1/cAnNdMxPh3B/pRKADXb/r9gAP58PgAdxf5lTTQoxn+xiP7F -; xcUmz4WILSQo/f3P/m1UOh7Gorb+sVkA/sxnADX8BQE10QQdwv6tVwA12w4dx/5lTTQoxzP+sLCw/v -; z8/CbMOf69vb3+cHBwjogo/f3QNh7I/plNAP7IZAA1+w/ANc8SHx3B/oJBAP7FYwA12hT+iEQAHcgR -; KMa3iDsmzZCI/peXl/5kZGQo/f3S/m1UOh7Jorb+sVkANfqZHxs1zv6nVAAdwv6gUAA12y8dyv5lTT -; T+XV1dxf6dnZ0yJs3+zs7O/nl5eQZA/f3TNh7L/plNABw1+Q41zBQhHcEAEjXbIx3LESjDroj+x8fH -; Js2ViP6oqKj+Z2dnKP391Tb+h0QAzDc0NfgnNcsvHcIENdsSHx3MESjCC/7t7e0mzSr+sbGx/mhoaC -; j9/dY2Hs4OHDX2JzXKIx3Bo7X+s1oANdslHc4RKMAg/rS0tDkmz5WI/ry8vP50dHQo/f3VNh7PK/7D -; YgA19Sc1yBIfHcEA/stmADXaEgAdzxH+XV1dDP7g4OAmzYyI/p6env7Q0NAmwJ2I/tnZ2f6Ojo4kKP -; 390zYe0KK2BTX0Bf7IZAA1xv6gUAAdwg412w4d0f5nTjb+paWl/vb29ibN/sfHx/51dXUGo4j+qKio -; HybAj4j+r6+vCSj9/dL+bVQ6/odEANL+mU0A/shkADXyDzQ1xBIAHcEAEjXaFCMd0v6zm4ImzZOI/q -; Ghof5mZmYowQa/iBUmwJqI/s7Ozv6EhIQo/f3RNh7TNzQ18QEbNcMOHcIENdsS/nw+AB3T/sCoj/7/ -; ///M/tfX1xeCiCjErIj+tra2OSbAhogkLyj9/c82HtX+p1QA/sxnADXw/rBZADXBFP6ORwAdwT7+s1 -; oANdsEHdX+wKiP/v///8qZiP6ysrL+ampqlIgoxhX+jY2NGCbALv7BwcH+eXl5KP39zjYe1qfx/sNi -; ADXv/rBZADXA/rldAD4dwf6USgA12xIAHdb+wKiP/v///8mHiAszm4jJN7KI/sjIyCbAQICI/pOTkz -; ObiP39zDYe16K2/rFZABw17f6wWAA1/ppNAB3C/q1XADXbDh3Y/sCoj/7////HCP7AwMD+cnJyjIgo -; zKSI/qCgoDImwJKI/rS0tP5vb28o/f3L/m1UOh7Z/plNAP7IZAClw+wQ/nw+AB3BAP7FYwA12p1b/o -; 5HAB3Z/sCoj/7////GkYj+mpqa/mVlZSjPN7qI/tra2ibAnIj+0tLS/oiIiAYo/f3JNh7aorYbNev+ -; rFYAHcElNdsQPh3a/sCoj/7////F/tHR0f56enoGKNKpiP6xsbH++fn5JsCKiP6np6f+Z2dnKP39yD -; Ye3P6jUgD+zGcANen+sVkAHQD+v2AANdv+lEoAHdz+wKiP/v///8OWiP6srKw+KNUG/oWFhf7o6Ogm -; wJmI/sbGxv59fX0o/f3HNh7dK/66XQA16BIENdsSHx3No6Udzf7AqI/+////woKI/oSEhP5hYWEo13 -; +viP7AwMAmwYGI/pmZmf5kZGQo/f3FNh7eorYFHKK25p1bNdslHc7+hUIA/p9QAB3N/sCoj/7////A -; Kv67u7v+bm5ukIgo2qOI/piYmP7x8fEmwDL+ubm5/nJycij9/cQ2HuD+mU0A/shkADX9wzP+gkEA/n -; A4AM4i/sNiAP56PQAdzf7AqI/+////jYj+kpKS/mNjY5qI3Te2iP7S0tImwDn+19fX/o2NjRUo/f3C -; Nh7horYFNf3B/q1XAB3O/oBAAP6zWgD+w2IA/qRSAB3O/sCoj/7Kysr+d3d3BkDgAv6rq6v+9/f3qI -; jACf6tra3+ampqKP39wTYe4/6ZTQAcorb7nVv+lEoAHc4j/sNiAMH+ej0AHc7+gGdP/mZmZpeI4wb+ -; gICA/uPj4ybAmoga/oKCgij9/cA2HuQ3NP7RaQD5Eh8dzjv+w2IAwf6pVQAdz/5mTTX+XV1d5iv+ub -; m5/v39/SbAhYj+n5+fICj9/DYe5v6nVAAcNfYEHc+jpf7DYgDCFB3P/mVNNP5dXV3nFf6QkJD+7u7u -; JsCXiP6/v7/+d3d3KP37Nh7np/H+w2IANfMSAB3Q/qRSACjBGR3QEf5dXV3of7OIGv7////ACAr+kZ -; GR/mJiYij9+TYe6KK2BRw18A4d0aOl/sNiAML+gEAAHdAR/l1dXeok/qOjo/709PQmwJGI/rKysv5t -; bW0o/fg2Hur+mU0A/shkAKXD7Z1b/o5HAB3S/qRSAP7DYgDBEB3REf5dXV3rf72I/t3d3SbAm4j+0N -; DQ/oaGhjdV/fY2Huuitv61WwA16/65XQD+djsAHdKjpf7DYgDC/oBAAB3REf5dXV3tqoj+s7Oz/vr6 -; +ibAiIj+paWl/mdnZyj99TYe7f6jUgAcorboBB3U/qRSAP7DYgDBEB3SEf5dXV3uFS3+6urqJsCYiP -; 7ExMQqKP30Nh7up/H+ul0ANeUS/nw+AB3Uo6X+w2IAwv6KRQAd0hH+XV1d73+wiP7Dw8MmwFWBiP6X -; l5f+Y2NjKP3yNh7vorb+sVkAHDXi/qdUAB3W/p9QAP7DYgDBEB3TEf5dXV3xFf6bm5v+8vLyJsAj/r -; i4uP5wcHAo/fE2HvH+mU0A/shkADXfFP6IRAAd1jv+w2IAwiwd0xH+XV1d8n+3iD8mwJyIMP6Li4v+ -; X19fKP3vNh7yorb+sVkANd3+rVcAHdgi/sNiAMGbPR3UEf5dXV30p4j+rq6u/vj4+CbAjIj+qqqq/m -; lpaSj97jYe9P6ZTQD+zGcANdr+lEoAHdg7/sNiAML+j0gAHdT+ZU00/l1dXfUGE/7l5eUmwD08/oCA -; gCj97TYe9Tc0/tFpANcSHx3ZIv7DYgDBMR3VEf5dXV32f62I/ry8vBcmwIOI/pycnP5lZWUo/es2Ht -; Gitg7+vmAA/qxWADce3w4cNdT+oFAAHdo7/sNiAMIKHdUR/l1dXfgV/pOTk/7v7+8mwJaI/r29vf51 -; dXUo/er+bVQ6Hs/+nlAA/sNiADXCD/6VSwAe3ysoNdH+v2AAAB3bASjBnWsd1hH+XV1d+X+1iAcmwJ -; 2I/tnZ2f6Pj48kKP3oNh7PorYFNcMcDh7fNwU1zw4d3Dv+w2IAwv6PSAAd1hH+XV1d+yT+p6en/vX1 -; 9SbAj4j+sLCw/mxsbCj95zYe0QH+yGQANcMP/pBJAB7f/plNAP7IZAA1yxT+jkcAHd3+mU0A/sNiAM -; Gdax3XEf5dXV38Br6I/uDg4CbAGwf+hYWFKP3mNh7SNzQ1wxwfHt83NDXJMf52OwAd3Tv+w2IAwv6P -; SAAd1xH+XV1d/cCriP61tbUqJsCHiP6ioqL+ZmZmKP3kNh7UDhw1w/6xWQA3Ht8OHDXGBB3fAf7DYg -; DBnWsd2BH+XV1d/cGjiBr+7OzsJsAu/sLCwv55eXko/eM2HtUr/sNiADXDKCse3ysoNcP+v2AA/oJB -; AB3fOzEowf6PSAAd2BH+XV1d/cJ/soj+xsbGJsAX/t3d3f6UlJT+YmJim4j94TYe1qK2/rFZABw1wh -; wOHt83BRw1wA4d4QH+w2IAwZ1rHdkR/l1dXf3EJP6fn5/+8/PzJsCSiBD+b29vKP3g/m1UOh7YAf7I -; ZAA1wTH+ej0AHuH+mU0A/sJhAP6IRAAd2UYdxTsxpdPB/o9IAB3ZEf5dXV39xX+5iCwmwCr+09PT/o -; mJif5fX18o/d42Htmitv61WwA1BB2iph7imi4d251LHcQK/sNiAMGdax3aEf5dXV39x6iI/rCwsP75 -; +fkmwIqI/qioqP5oaGgo/d02HtubPR3AMx7imi4d3DlCHcIxpdPBIx3aEf5dXV39yAb+hISE/ufn5y -; bAPf7Hx8f+fn5+KP3cNv6HRADb/nc8AB3AMx7iHB3dnlqcbKXDHf6PSAD+w2IAwZ1rHdsR/l1dXf3J -; f66I/r+/v/7////Bgoj+mpqa/mRkZCj92jb+h0QA2wYdwDMe4hwd3hKbTRL+s1oA/sNiAMEBHdv+ZU -; 00/l1dXf3LFf6Wlpb+8PDwJsCViP66urr+c3NzKP3ZNv6HRADbBh3AMx7iHB3hIgqjpQod3BH+XV1d -; /cw3toj+0dHRJsCdiP7Y2Nj+jY2NFSj91zYe2wacXMAzHuIcHeL+hUIA/rldAP6ZTQAd3BEo/c6liP -; 6qqqr+9vb2JsCOiP6tra06KP3WNh7bBh3AoqYex6fx/rpdAP6MRgAe1hwd46OlwB3cESj9z6KI/n9/ -; f/7i4uK9iMAM/szMzCj91jYe2/53PAAdwKKmHsb+sVkANcCeWv6eUAAe1ZouHf3FESj90a2I/re3tz -; kmwCj91jYe2wYdwDMexDf+vmAANcMFNx7THB39xREo/dIVFv7u7u4mKP3WNh7bBh3AMx7Fp/H+w2IA -; NcP+rFYAHtMcHf3FEf5dXV3903+ziAso/dY2HtsGHcAzHsaitgX+zGcANcEc/pBJAB7Smi4d/cURKP -; 3VpIgo/dY2HtsGHcAzHsgB/shkADXBNB7SHB3dNx3jESj9/e42HtsGHcAzHsk3/rVbADXB/plNAB7R -; HB3cp/H+u14A/qhUAP51OgAd4f5lTTQo/f3u/m1UOh7bBh3AMx7L/qNSAP7MZwA1Dzce0JouHdz+pF -; IA/rxeADyeWv6DQQAd4BEo/f3uNh7bBh3AMx7MKzQ1/qdUAB7QHB3bKv69XwA/wDz+eT0AHeD+ZU00 -; KP397jYe2wYdwDMezTcF/shkADcezxwd2/6mUwAHwD8lHc+jtcAdzhEo/f3uNh7b/nc8AB3AMx7P/p -; lNAA4ezxwd2qXT/r9gAArAByodzv6USgA1Mf58PgAdzREo/f3uNh7bBh3AMx7iHB3aOP7BYQBWCpkP -; /nA4AM7+rVcANcGdW/6aTQAdzBEo/f3uNh7bBh3AMx7iHB3ZpdP+wWEAwVoC/nA4AMwA/sVjADXDEj -; 4dyxEo/f3uNh7bBh3AoqYe4hwd2Tj+wWEAwT/+cDgAzA41xAQdzREo/f3u/mdXRv6ERgYe2gYdwDMe -; 4hwd2KOl/sFhAMIC/nA4AMoAEjXDEgAdzv5iVEYo/f3v/mVYTP56TB0e2QYdwDMe4hwd2Dj+wWEAwT -; /+cDgAyx8QNcIOHc7+bD8R/l9YUSj9/fGe1P5yUC4JHtcGnFzAMx7iHB3XO/7BYQDCAv5wOADMPiX+ -; y2YAwAIdzv5mSi4o/f31B/5/SREe1gYdwDMe4hwd1zj+wWEAwT/+cDgAzyE+Hc0r/mFWTCj9/fc9/n -; VPKR7VBh3AMx7iHB3WO/7BYQDC/pNKAP5wOADf/mhIKSj9/fsH/oJHDB7TBh3AMx7iHB3WOP7BYQDB -; P/5wOADeolz+ZE86KP39/T0qHtIGHcAzNx7hHB3VO/7BYQDC/phMAP5wOADdKwEo/f39wv5vUjQJHt -; AGHSM1D/6QSQAe4Bwd1f6eTwD+wWEAwT/+ej0Amz3cESj9/f3FMBYezwYvNcGeWv6nVAAe35ouHdQ7 -; /sFhAMI4/nA4ANsr/mFWTCj9/f3HPT4JHs0g/sxnADXDDzce3ZouHdT+nk8A/sFhAMIUmz3aNCj9/f -; 3LBzIezTf+ul0ANcOeWv6eUAAe3JouHdM7/sFhAMI4/nA4ANmiXP5jUUAo/eS6iIeIKP3jPf53TSMe -; zv6nVAD+zGcANcMFHtuaLh3TOf7BYQDCFJs92P5rQRf+XltXKP3l/vLy8v6enp4kKP3kNgkezafx/s -; NiADXCKB7bmi4d0jv+t1wApdPBOP5wOADYEf5dXV395/7////A/sbGxv5vb2+PiCj94zAqHs2itgU1 -; wZkfHtscHdI5/sFhAMIUmz3W/mw/EQGlH/3o/vf39ybAjYj+i4uL/mBgYCj94z0+/oRGBh7N/plNAP -; 7IZAA1mR8e2xwd0Tv+t1wApdPB/qhUAP5wOADW/mhIKf5dXV396v6ioqL+5ubmuYjAnIj+tbW1DSj9 -; 5AcyHs03/rpdAKXDHtscHdH+jkcA/sFhAML+f0AA/nA4ANSiXP5hVkz+XV1d/ez+hISE/s7Ozv76+v -; omwIGI/n19fYKIKP3jPT4ezv6ZTQAe2xwd0f63XACl08H+slkA/nA4ANT+aUYjKP3vr4j+sLCw/u7u -; 7rGIwJaI/qampiQo/eT+Z1dG/oRGBh7qHB3QAv7BYQDC/olFAP5wOADT/mRPOij98ST+j4+P/tra2v -; 78/PwmwP7Ozs7+cnJyjIgo/eM9/npMHR7pHB3Q/rdcAKXTwSb+cDgA0iv+X1hRKP30uIj+vb29/vX1 -; 9aqIwJCI/pOTk/5gYGAo/eT+b1I0CR7nHB3P/o5HAP7BYQDCAv5wOADR/mZKLij996iIGf7i4uImwF -; X+u7u7/mtrazco/eMw/n9JER7mHB3P/sJhAKGXn2nAmz3+cDgA0P5sPxEaKP36/oCAgP7Jycn++Pj4 -; p4jAhoj+goKCBij9457UPh7lHB3O/pRKAP7RaQBWnEydSwL+cDgAz/5oSCko/f2siP6rq6v+6+vrJs -; CZiBj+ZGRkKP3jrYj+uqmY/pZbIP6HRADjmi4dzv7FYwA1wf67XgAdzg7+Y1FAKP39wQb+i4uL/tbW -; 1v77+/smwD/+dXV1Nyj94P6GhoY6JpTE/rSKYB7iHB3N/pRKADXBJR3O/mtBF/5eW1co/f3EtIj+uL -; i4/vLy8ibAkoj+m5ubFSj93RH+sbGxKibC/tK5n/6OUBAe4JouHc3+xWMANf6/YAAAHc7+ZU00KP39 -; x6aI/peXl/7f398XJsD+w8PD/m5ubpCIKP3auoj+3t7eJsX+6NzP/qVzQB7fHB3M/o5HADX+p1QAHc -; 7+bD8R/l9YUSj9/cq+iP7ExMT+9/f3JsCLiP6IiIgVKP3Y/qGhof719fUmx5TE/ruWcB8e3RwdzP65 -; XQD+lEoAHc40KP39zaqI/qWlpf7n5+cmwJuI/rOzs/5nZ2co/dWwiP7Ly8smy/7h0L/+llsg/odEAN -; wcHcum4sAdzaJc/mJURij9/c9//oeHh/7Q0NAbJsD+3d3dKjdV/dL+jo6O/u7u7ibN/tTQzP6AWjT+ -; h0QA2xwd2wMo/f3TsYj+srKy/vDw8CbAlYj+o6Oj/mFhYSj9z6mIPRcmzAz+tra2/mtrazf+Z1dGCR -; 7ZHB3a/mRPOij9/dWliP6RkZH+29vb/v39/SbAGv5xcXE3KP3Mv4j+4+PjJs2JiP6MjIwzKMH+YlpR -; /npMHR7YHB3Y/mw/Ef5fWFEo/f3Yuoj+v7+//vb29qmIwI+I/pCQkP5gYGAo/ckV/qmpqf739/cmzB -; f+w8PD/nR0dIuIKMOe1P5vUjQJHtaaLh3X/mZKLij9/duoiP6fn5/+5OTkJsBA/ri4uP5qamoo/cey -; iP7T09MmzZKI/p2dnSCYiMcwFh7VHB3V/mw/Ef5hVkwo/f3eE/7Ly8sMJsAC/oCAgP5fX18o/cT+lp -; aWFCbN/tTU1DkVKMme1P51Tyke1Bwd1P5oSCko/f3hOv6tra3+7e3tJsAu/qqqqv5jY2Mo/cGsiP7A -; wMAmzR/+r6+vHCjNB/6CRwwe0pouHdIO/mRPOij9/eMV/o2Njf7X19f+/Pz8JsD+0tLSATco/BP+6e -; npJs2EiP6Hh4ckKM89Kv6HRADRmi4d0f5sPxH+X1hRKP395rWI/rq6uv7z8/MmwAX+mJiYFSj5M/6u -; rq7++vr6Jsw5/r29vf5wcHA3KNL+b1I0CR7PHJo+0P5lTTQo/f3pp4j+mZmZ/uHh4SbBNf5tbW03KP -; a3iAomzZCIPxEo1TAWHs4cHc4r/mFWTCj9/ez+fX19/sbGxv74+PgmwA3+hYWFBij0/p6enjImzf7N -; zc3+eHh4BijXntQ+CR7MHB3NNCj9/e+qiP6np6f+6enpJsAM/rGxsS8o8a+I/sjIyCbNlYg+/mdnZy -; jbB/6CRwwey5ouHcuiXP5jUUAo/f3xBv6IiIgD/vv7+ybAO/54eHiGiCju/ouLi/7t7e0mzf7e3t7+ -; gYGBgIgo3Z7U/ndNIx7KHB3K/mpEHf5eW1co/f30soj+tLS0/vHx8SbAlIj+oKCgJCjrIAH+/f39Js -; wb/ri4uP5tbW2RiCjg/m1UOv6ERgYeyBwdyf5lTTQo/f33pYj+lJSU/t3d3QgmwP7IyMgFN1XovYj+ -; 4eHhJs2LiP6Pj4/+Y2NjKOMw/npMHR7HHB3H/mw/Ef5fWFEo/f36vIj+wcHBLibAjoj+jY2N/mBgYC -; jlN/6mpqYfJs0P/nV1dQYo5Z7U/nVPKQkexRwdxv5mSi4o/f39qYj+oaGh/uXl5SbAOf62trb+aWlp -; KOOxiP7Q0NAmzZOI/qCgoP5lZWUo6Qf+gkcMHsSaLh3EpR/+YVZMKP39/cL+hISE/s7Ozv75+fkmwI -; GI/n5+fgYo4P6SkpL+8PDwJs3+19fX/n19fYOIKOs9Ph7DHB3D/mlGIyj9/f3Fr4j+r6+v/u7u7ibA -; loj+p6enMyjdq4j+vb29Js2ZiP6ysrL+ampqKO/+Z1dG/oRGBh7BHB3C/mRPOij9/f3HpIj+jo6O/t -; nZ2TkmwP7Pz8/+c3NzNyja/n9/f/7m5ua5iM2GiP6JiYkzKPGe1CoewBwdwP5sPxEBKP39/cq3iP68 -; vLz+9PT0JsCQiCEVKNck/qysrAwmzED+v7+//nFxcTco9P5vUjT+hEYGHB0RKP39/c2niP6bm5v+4u -; LivYjAVf69vb3+bGxsNyjUtYj+2NjYJs0F/pmZmf5lZWUo9zD+c0gdGij9/f3Q/n9/fy3++Pj4p4jA -; L/6Dg4MGKNIK/vLy8ibNJf56enoGKP39/f3QrIj+qqqq/urq6ibAPf6urq4gKM+tiP7ExMQmzRD+q6 -; ur/mdnZyj9/f3eAAAAAAAAAAE= +; thumbnail_QOI begin 313x173 19860 +; cW9pZgAAATkAAACtBAD+WVlZ3n/Ms4j+zMzMuYj+mZmZ/l9fXzuiiP6Li4v+2tralIj+dHR0hojlf/ +; 3avYj+4uLim4j+h4eH/l1dXQqjiP6ampr+5OTk/r29vRgK/c9V41XcO83+kJCQ/ufn54aIPTvDGP7A +; wMD+4+Pj/peXlxk74Ar92xn+pKSk/vPz8/67u7sYCsO7iP7Q0NCpiP6JiYkZCv3PO+FV2zvMq4j+ur +; q6/uvr6/6oqKj+Y2NjO8UG/pmZmTP+v7+//mpqajvdCv3A/nFeSv6sZRz+gF8/CtexiP7R0dG3iP6V +; lZX+YGBgCsUC/qqqqv7m5ub+ra2t/mZmZgr90TveVdk7zf5/f3/+39/fmoj+hYWFCjvIv4gpsIgeO9 +; kK/cL+lmIt/tFpAMCfL/6PYTP+YlxVCtT+kZGR/u7u7v7Kysr+d3d3CskA/tra2iX+fHx8Cv3SVdxV +; 1zvNN/6oqKj+7Ozs/rm5uf5qamo7y6qIDf7m5ub+rq6uAjvUCv3D/mpdUP67ZhE1w/6zZRc0CtER/r +; +/v/7w8PD+pKSk/mRkZArLrogq/uPj4/6dnZ3+YWFhCv3SO9os1jvNt4j+09PTM/6Tk5P+XV1dO80K +; AP7X19eciP55eXk70Qr9xP6HYDk1xp8v/o9hMwrP/n5+fj4O/oKCgv5cXFwKzSj+lJSU/uHh4YKIFA +; r90zvYVdQ7zQr+l5eX/unp6f7Hx8f+c3NzO9Er/rm5uf7l5eX+nZ2dNzvMCv3FOv6lZCI1yf6zZRc6 +; Css3/qysrP7y8vL+tbW1KwrRt4j+y8vLsoj+j4+P/l1dXQr90zvWLNI7zq6I/sHBwf7q6ur+oqKi/m +; JiYjvTKP6Tk5P+39/fh4j+bm5uO8kK/cb+cV5K/sJnCzXLBf6AXz8KyRT+2dnZAhb+X19fCtMV/qOj +; oyD+tLS0/mlpaQr91DvUVdE7zv6Ghob+4uLikoj+gICAO9e6iB65iP6Ojo4KO8UK/cf+nWMo/tFpAM +; 8TOgrG/piYmP7x8fH+xMTEMgrX/n9/f/7X19ch/oKCggr91TvRu4gszzvOp4j+r6+v/u3t7f6ysrL+ +; Z2dnO9moiP6ioqIg/rW1tf5mZmY7wgr9x/5xXkobNdEbJArDPv7GxsYY/p6env5iYmIK2S/+tLS0/u +; bm5v6kpKT+Y2NjCv3UO84K/qCgoP74+PgszjvOH/7Y2NiniP6NjY0ZO9z+gICA/tPT0w4mCv3J/o9h +; M/7RaQDUBSw6CsD+hoaG/uvr64eIFxkK3AcGiYgfCv3VO8uwiP7Nzc3+////wP5ZWVnMO84Z/p+fnz +; r+wsLC/m9vbzvfDf6zs7Mg/qWlpf5hYWEK/cWh4f6sZRw11/6zZRf+b2JVMv7y8vL+rq6uDQrfs4j+ +; xcXFu4j+lJSU/l5eXgr91TvI/o2Njf7w8PAmwSzLO86xiP7Jycm+iP6bm5v+YGBgO99/wKKIOP7c3N +; wp/nJycgr9w/54XkT+ymgGNdmoL/7btIz+ioqKKArhN/6dnZ3+5OTkKv5sbGwK/dU7xTMq/v7+/ibC +; LMk7zzgviYj+enp6O98KxLWIE/7i4uIw/l1dXQr9wP6dYyg13f6lZCL+YlxVCuO+iAMd/oeHh/5cXF +; wK/dU7whv+5eXlJsMaLMg7zqqI/re3t/7s7Oz+q6urETveCsck/pycnP7j4+P+vLy8HAr7/nFeSv7C +; Zws13/67ZhH+gF8/CuMR/q2trS/+qqqq/mVlZQr91TuiiP6oqKj++vr6JsKYiP6enp47LMY7z/58fH +; z+3d3dnYj+iIiIClXdCsv+e3t7FqyI/oWFhf5bW1v5/o9hMzXiny8s/mJcVQrjHv7c3NyRiP56enoK +; /dS0iP7V1dUmw/7e3t7+dHR0O8BVxTvOpIj+paWl/uzs7P68vLz+a2trO90KzqqI/qysrC/+q6ur/m +; NjYwr1oeH+s2UXNeX+tGYX/mteUP5cXFzQCtGviP6+vr4z/pubmxWbiP3RIf7z8/Mmwp2I/rCwsDc7 +; wSzDO882JbOI/pWVlTc73ArRGS3+2dnZmIj+eHh4CvP+h2A5NeifL/6PYjQZ2QrIooj+l5eXMwQ2Cv +; 3Oq4giJsOMiP6Dg4M7w1XCO88h/ujo6IKIHzvcCtWxiP68vLz+5OTk/pubmzediPAGNev+tGYX/mNd +; VhneCsMB/s3Nza+I/o2NjSgK/cv+goKC/urq6ibDMS8KO8MswTvOrYgXK/6kpKT+YmJiO9wK16SI/p +; aWlv7g4OAx/m1tbQrtJP7CZws17QX+gWA//lxcXOMz/qenp/7m5ub+sbGx/mhoaAr9yAb+r6+v/vz8 +; /CbClYg/CsI7wiw7zyL+4eHhlYgTO9wK27yI/srKyrSI/oyMjArr/pZiLTXx/p5jKAkZ4xMdA/5/f3 +; 8K/ca5iP7d3d3+////w/7X19f+b29vCsU70Ab+rKys/uzs7BANO9sK3qiI/qWlpS/+srKy/mVlZQrn +; /nFeSv67ZhE18/67ZxH+cl5LGeMN/re3t/7l5eX+oaGhAhkK/cL+nZ2d/vf39ybCm4j+qamp/l1dXQ +; rHO84BPxX+j4+PGTvaCuIi/tXV1cD+fn5+CuX+j2EzNfYF/o9iNBnjKP6RkZEVhogBGcIK+q6ICybD +; IP58fHwKyjvLCv6cnJwrMf5xcXE72wrkHBAg/qKiov5gYGAK4aHy/qxlHDXV/q1XAKKmNeD+tGYXPP +; 5cXFzjtIj+yMjIt4j+kpKS/l9fXxnDCvb+i4uL/u7u7ibCVf68vLwCCss7yhz+xcXF/ujo6P6enp4V +; O9oK5yj+kJCQ/t7e3ouIFArdGcD+eV9F/spoBjXV/pRKAP5wOAD+h0QA/plNAP7MZwA13wURGeMV/p +; +fnyD+uLi4/mtraxnFCvGmiP63t7cXJsKRiBYKzTvJ/oqKiv7k5OSNiP58fHw72grrt4gxvYj+k5OT +; GQrYGcL+nmMoNdX+v2AA/nw+AB3AHsCitv66XQA14Bb+Y11WGeM5/tTU1D/+hYWFGccK7Qz+4+PjJs +; P+zs7OOgrOO8eoiP60tLT+7Ozs/q6urv5mZmY72grtMwYR/rm5uf5oaGgK1BnD/nJeS/67ZxE11f6g +; UAAdwjcewf6nVAAcNd8gLhnjL/6wsLD+5ubm/qioqP5lZWUZyAroKP6lpaX++fn5JsKZiP6ioqIZCs +; 87xr+I/tvb238L/ltbWzvZCvH+fn5+/tHR0amIIgrQf8U4NdUS/oJBAB3BABKipv6VSwAewf6xWQA1 +; 4J8vOP5jXVYZ4wv+3d3djogMGckK5Rj+0tLSJsOBiP53d3cK0VXEo4gz/uvr6/6+vr4YO9oK86yI/q +; +vry/+qamp/mJiYgrLGcYJMDXVDh3C/ppNADXBnloONx4ONeIw/mteUBnjsIj+wcHBJP6ZmZn+YGBg +; GcoK4f6SkpL+8vLyJsJAARUK0jvDtIj+zc3NuIj+mJiY/l9fXzvZCvYZ/ouLi/7a2tqViP51dXUKxx +; nI/ohhOTXVnVv+jkcA/nA4AMGjtf65XQA1xP6+YAD+kEkApNQ14wX+iGE5GeOiiCz+4+Pj/r+/vycZ +; ywrdqYj+wMDA/v///8OOiP6Hh4cK1FXC/pGRkf7o6OiEiP54eHg72gr5J/6/v78CLDcKwhnK/qVkIv +; 7RaQDVMT4dwf6USgA1xxz+o1IANeUG/mNdVhnjuogWq4gaKBnLCtr+f39/DbeIw/7Hx8f+aGhoCtVV +; wKuI/ru7uzr+p6en/mNjYzvZCvwVLP7i4uIE/mxsbAoZyv5yXkv+wmcLNdX+mk0AHcL+rVcANcn+sV +; kA/shkADXl/rtnEf6BYD8Z4wL+qqqq/ubm5v6urq7+Z2dnGcwK1jf+ra2t/vv7+ybClogsCtc7/oCA +; gP7f39+ZiP6EhIQ72gr9wQwpsYj+ioqKGcn+lmIu/tFpANX+v2AA/nw+AJouwQD+xWMANcr+ul0Apc +; M15p8vI/5jXVYZ4zH+2tral4j+fn5+Gc0K07eI/tvb2ybDOyOJiNj+qamp/uzs7P64uLj+ampqkIjZ +; Cv3CGcCpiP6oqKgvNiAZxS7+u2cRNdUOHcIlNcwom0016DAuGeOtiP66urr+5OTk/p+fn/5iYmIZzQ +; rQLBAmwioYNwrZ/uLi4gP+XV1dO9kK/cAZxf6FhYU/ITkZw/6PYjQ11RT+iEQAHcH+gkEAEjXODjXp +; BTgZ4yj+lJSUJIOIIxnOCsytiB4mw4mI/n9/fwrbuIg72Qr9wBnIr4j+ubm5/uXl5QYVGaHh/qVkIj +; XV/q1XAB3CBDXQ/qxWADXrMDwZ4yP+ysrKtIj+kJCQ/l5eXhnOCsn+h4eH/u3t7bKIw/7AwMD+ZWVl +; Ctw72Qr9GcujiP6Tk5P+39/fiIj+hHFd/sJnC/7RaQDV/pRKAB3Bo7UvNdEnNewF/oFgPxnjpYj+oq +; Ki/uXl5f61tbUrGc8KxRX+tLS0CCbCk4gDCt472Ar8Gc+5iP7SmF011f6/YAAfHcEA/sVjADXSDjXu +; /p5jKAkZ4/5/f38/VSIZzwrDvIj+4ODgv4jD/tLS0v5tbW0K3zvWCvwZ0P5yXkv+u2cRNdX+oFAAHc +; IONdT+sVkA/shkADXuIC4Z46uI/rOzs/7m5ub+paWl/mRkZBnPChn+oqKi/vj4+CbCmoggKArgO9UK +; +xnRODXVEgAdwQASNdUPNDXvny84CRnj/o2Njf7e3t6MiP53d3cZzrCI/s/PzybDhIj+eXl5CuI71A +; r6GdGh4f6sZR011Q4dwgQ115kfmR818f60Zhc8GeOyiDG9iP6Wlpb+X19fGcv+j4+P/vDw8CbCCP63 +; t7f+YWFhCuM70wr5GdL+eV9F/spoBjXUFP6USgAdwaO1/rNaADXZ/qxWADXyBf6IYTkZ4wb+nJyc/u +; Tk5P68vLwYj4jIqIg5/v7+/ibCj4j+ioqKCuU70Qr5GdP+nmMoNdUSHx3B/ohEABQ12g419P6lZCIJ +; GeMMA6eI/oiIiP5dXV0Zxf59fX0vJsP+ysrKKxkK5TvQCvgZ0/5yXkv+wmcLNdUEHcIONdz+rFYANf +; X+u2cR/oFgPxnjqIj+rKysLwn+ZmZmGcKiiP6qqqr++vr6JsKYiP6dnZ0ZwgrkO88K+BnT/o9iNDXV +; EgAdwQD+xWMANd3+sVkANfafLzj+Y11WGeP+h4eH/tzc3JOI/nx8fICIwLSI/tfX1ybD/t3d3f50dH +; QZxArjO84K9xnTCTA11f6nVAAdwgQ13/61WwD+yGQANfcwPBnjroj+vb29Av6cnJydiP709PQmwp2I +; /rCwsP5gYGCciMYK4jvNCvYZ1P6IYTk11jM+HcA+/rldADXgDzQ1+J8vOBnjN/6goKD++Pj4JsOLiC +; IZygrgO8sK9xnUBjXYFP6ORwAjNeKbTRs1+jAJGeH+hISE/uvr6ybD/sTExP5nZ2cZzArfO8oK9hnU +; /nJeSws1/cP+sVkANfufL/6BYD8Z3qSI/rGxsTkmwhf+1NTUHBnOCt47yQr1GdX+lmIuNf3FJzX9/p +; 5jKAkZ27mI/t/f3ybD/tbW1v6QkJD+2NjYm4j+gYGBGc4K3TvICvUZ1C4gNf3GDjX9wCAuGdn+n5+f +; /vf39ybCm4j+qampNxmsiP62trb+5eXl/qKiov5jY2MZzQrcO8cK9BnV/m1cS/7MZwA1/cf+sVkAAT +; XbnVv+lksABRw13Z8v/m1ZRRnWr4gpJsMg/nx8fBnCfyX+4ODgh4gQGc0K2zvGCvQZ1v5sUzn+lUsA +; /sNiADX9xv61WwABNdr+v2AA/nw+AJtN/odEAP6ZTQD+yGQANdsSAP5lTDQZ1f6NjY3+7+/vJsIX/r +; u7uwIZxbOI/sfHx7iI/pOTk/5fX18ZzAraO8UK8xnYJ/6HRACitv6xWQA1/cUoNDXZ/ppNAP5wOADA +; Kx7ANxs12Q4dwAwZ0wL+ubm5FybCkYj+jo6OGcikiP6fn58gDP5sbGwZzArZO8QK8xnZJx7B/plNAP +; 7IZAA1/cOeWg411/6/YAAAHcH+hEIANx7B/qNSADXWFP6ORwAdwf5lTDQZ0r6I/uTk5CbD/s7OzgkZ +; y7+I/tPT06SI/oaGhv5dXV0ZywrYO8MK8xnaJx7CN/61WwA1/cP+rFYANdb+p1QAHcIENQH+nlAAHs +; AbNdX+uV0A/nY7AB3CDBnQooj+p6en/vr6+ibCPf6hoaEoGc2piP6vr6/+5ubm/qmpqSAZywrXO8IK +; 8hnc/mxTOR7EOP7MZwA1/cH+rFYANdQUAh3Bo7UxNcEc/rVbAAE11QQdxAwZz7KIISbDgYj+dnZ2Gd +; H+iYmJ/t3d3Sn+enp6GcoK1zvBCvIZ3f5sUzkexafx/r5gADX9wP6sVgA10zE+HcH+lEoANdv+v2AA +; /nw+AB3FDBnO/pOTk/7y8vImwp2IMv5hYWEZ0wn+wMDA/uLi4iwkGckK1jvACvIZ3v5sUzkexqK2/r +; FZAByitvwFATXRBB3C/q1XADXbDh3HDBnMqoj+wsLCJsONiP6GhoYZ1qKI/piYmP7j4+P+wMDA/m9v +; bxnJCtUPGQrwGeAnHsj+mU0A/shkAKXD+/6+YADANc/+v2AAH5ouwQD+xWMANdoU/ohEAB3IDBnL/o +; GBgf7p6ekmw/7GxsYNGdkQ/s/Pz6yI/oyMjP5dXV0ZyFXU/ubm5v6pqan+ZGRkCu4Z4SceyaK2BTX6 +; mR+ZHzXODh3C/qBQADXb/q1XAB3KDBnJpIj+rq6u/vv7+ybCloj+mJiYGdyniP6oqKj+5ubm/rCwsP +; 5oaGgZyArTLf7c3NyQiP55eXkK7H/i/mxTOR7L/plNAP7MZwA1+Q41zBQh/nA4AMEAEjXb/pRKAB3L +; /mVMNBnIt4j+3NzcJsP+2dnZ/nJycoqI3yIsmIj+fn5+GccK1Dr+v7+//uLi4v6ZmZkVCukZ4ycezD +; f+ul0ANfj+rFYANcv+rVcAHcL+mk0ANdsSHx3MDBnH/pubm/729vapiMIq/qysrP5fX18Z4a2I/rm5 +; uf7k5OT+oKCgAhnGVdSiiP6Xl5f+4+Pj/sDAwP5ubm4K5xnk/mxTOR7O/qdUAP7MZwCitvb+rFYANc +; r+lEoAHcE+/rNaADXbJR3O/mVMNBnFrYj+ycnJJsOIiP5/f38Z5Cj+kpKS/uHh4YSIMhnGCtW5iAet +; iP6MjIwoQOMZ5v5sUzkezyv+w2IANfX+rFYANcgS/nw+AB3B/oJBABQ12hIAHc8MGcT+iYmJ/u7u7r +; GIw/6/v78gGee1iP7Jycm1iP6RkZH+Xl5eGcQK1qaI/qenp/7m5uYF/mdnZwrhGef+bFM5HtCitv6x +; WQA19AX+yGQANcYlHcIONdsOHdEMGcKmiP62trb+/f39oojCkog0GeqkiP6hoaH+5eXl/re3t/5qam +; oZxArX/oKCgv7Z2dmZiBcK3xnoJx7S/plNAP7IZAA18v6+YACdezXEEgAdwQASNdqdWyMd0gwZwb2I +; /uLi4ibD/tHR0RgZ7Rf+1dXVwP6EhIQZxArXPi4R/qCgoP5iYmIK3BnpJx7TN/66XQA18QEbNcMOHc +; IENdsS/nw+AB3T/mVMNBl//qSkpP75+fkmwgwRKBnvqoj+sbGx/ubm5v6mpqb+ZGRkGcIK2Bn+kZGR +; /uHh4YSIMgraGeonHtUO/sxnADXw/rBZADXBnVv+jkcAHcGjtRA12wQd1f5lTDQY/tDQ0CbDg4j+eX +; l5GfMp/t7e3oyI/nh4eBnCCtm1iP7IyMg3NP5eXl4K1xnrJx7Wp/H+w2IANe/+sFkANcD+uV0APh3B +; IzXbEv6CQQAd1v6Qd1/+8fHxJsII/ra2tv5iYmIZ9Rj+w8PDJP6Xl5f+YGBgGcAK2gb+oKCgIC7+am +; pqCtUZ7Cce16K2/rFZABw17f6wWAA1BB3C/q1XADXb/qdUAB3Y/sCoj/7////Cj4j+ioqKGfgG/pub +; m/7k5OT+vb29GBnAVdv+fHx8MD/+hISECtMZ7H/+jnVbHtn+mU0A/shkADXsEP58PgAdwQD+xWMANd +; qdW/6ORwAd2f7AqI/+////wTwrGfu7iP7R0dEs/omJiSgK3KqI/rGxsf7m5ub+pqam/mRkZArQGewY +; NP7JsJce2jcbNev+rFYAHcH+oFAANdsQPh3a/sCoj/7///+XiP6cnJz+XFxc/cAR/qurq/7m5ub+ra +; 2t/mZmZgrdGv7e3t6NiC4Kzhns/pGRkf7v7+8a/oNqUB7cOBw16QUd/oJBABI12yMd3P7AqI/+3Nzc +; /nR0dIiI/cP+hYWF/tvb2xY5/ltbW92xiP7CwsIk/peXlwYKyxnrIP6+vr7+8PDw/qampiD+bFM5Ht +; 2n8f66XQA16BIENdsS/nw+AB3No6Udzf6Da1L+YGBgGf3Froj+vLy8/uPj4/6dnZ3+YWFhCtyjiP6a +; mpoCF/5tbW0KyRnr/n5+fv7m5uaSiP6Dg4P+XV1dGSce3qK2/rFZABw15p1bNdsl/nA4AM7+hUIA/p +; 9QAB3N/mVMNBn9xyj+lZWV/uLi4oGI/nFxcQrdu4j+0NDQLAsZCsYZ6gb+rKys/vLy8v62trb+a2tr +; GcEnHuD+mU0A/shkADX9w5ou/oJBAB3O/p9QAP7DYgD+ej0AHc3+ZU00Gf3JMv7MzMyxiP6Ojo7+XV +; 1dCtyoiP6qqqov/q2trf5mZmYKxBnqtYgsqoj+kZGRFRnCJx7hNwU1/cH+rVcAHc7+gEAAEP7DYgD+ +; pFIAHc4R/l1dXcAZ/cgk/qSkpP7l5eX+tLS0/mlpaQrd/oWFhf7a2tol/nx8fArDGen+mJiY/vHx8f +; 7FxcX+dHR0GcQnHuP+mU0A/sxnAKK2+51b/pRKAP5wOADOI/7DYgDB/no9AB3O/mVNNP5dXV3BGf3H +; CsD+gICA/tfX152I/oGBgQrdroj+u7u7/uTk5P6enp4kCsAZ6A3+xsbG/u7u7v6fn5/+Y2NjGcX+bF +; M5HuSitv66XQD+0WkA+RL+fD4AHc7+pFIA/sNiAMH+qVUAHc/+ZU00/l1dXcJV/ccKwKuI/rS0tCD+ +; o6Oj/mNjYwrcKP6UlJT+4uLigYj+cXFxGej+hoaG/uvr6wP+f39/KBnGJx7m/qdUAP7MZwA19v6aTQ +; Adz6Ol/sNiAML+ej0AHc8R/l1dXcMZ/cYKwRkH/t/f3y3+dXV1Ct23iBqyiP6Pj4/+Xl5eGeQk/rKy +; sv7y8vL+r6+v/mlpaRnIJx7np/H+w2IANfMS/oJBAB3Q/qRSACjB/qlVAB3QEf5dXV3EVf3FCsOziP +; 7Gxsa6iCE3Ctwk/qOjoyABHBniuogGwP6Li4s3Gcn+bFM5HuiitgX+zGcANfAOHdGjpf7DYgDC/oBA +; AB3QEf5dXV3FGf3FCsOkiP6dnZ3+5OTk/rq6uv5ra2sK2hnBJj9A/oKCghnff/6hoaEj/r6+vv5wcH +; AZyyce6v6ZTQD+yGQApcPtnVv+jkcAHdL+pFIA/sNiAMH+s1oAHdH+ZU00/l1dXcUZ/cUKxb+I/tPT +; 06SICtoZwquI/rS0tP7m5ub+pKSk/mRkZBncOv7Nzc29iP6ZmZn+YmJiGcwnHus3/rVbADXr/rldAP +; 52OwAd0qOl/sNiAML+gEAAHdH+ZU00/l1dXcYZ/cQKxqmI/q6urgrZGcUH/t/f3y0fGdoH/u3t7Tj+ +; enp6Gc7+bFM5Hu3+o1IAHKK26AQd1P6kUgD+w2IAwRAd0v5lTTT+XV1dxxn9xArhGceyiP7FxcUVMP +; 5fX18Z1qeIKv7x8fH+qKio/mZmZhnP/mxTOR7up/E0NeX+v2AA/nw+AB3Uo6X+w2IAwv6KRQAd0hH+ +; XV1dyBn9wwrgGckG/p2dnSAq/mxsbBnUOSCViP6Ghob+Xl5eGdAnHu+itv6xWQAcNeL+p1QAHdb+n1 +; AA/sNiAMEQHdMR/l1dXcgZ/cMK4BnLGwOmiP6IiIgoGdCiiP6pqan+8/Pz/ri4uP5tbW0Z0ice8f6Z +; TQD+yGQANd+dW/6IRAD+cDgA1qOl/sNiAMIsHdMR/l1dXckZ/cIK3xnNqYj+ra2t/ubm5v6rq6v+Zm +; ZmGc6ziP7V1dWxiP6Tk5P+YWFhGdMnHvKitgU13f6tVwAd2CL+w2IAwTEd1BH+XV1dyhn9wgrdGdD+ +; h4eH/tzc3JKI/nt7e4GIzP6VlZX+8PDw/sfHx/52dnYZ1Sf+h0QA9P6ZTQD+zGcANdr+lEoAHdg7/s +; NiAML+j0gAHdQR/l1dXcoZ/cJV3RnRr4gX/uPj4/6bm5skGciriP7Dw8P+7+/v/qGhof5kZGQZ1ice +; 9Tc0Ndf+v2AA/nw+AB3Z/p9QAP7DYgDBMR3V/mVNNP5dXV3LGf3BVdwZ06KI/peXl/7i4uL+wcHB/n +; BwcBnGE/7p6emMiP6BgYEoGdf+bFM6HtGitv6nVAD+vmAA/qxWADce3w7+zGcANdT+oFAAHdo7/sNi +; AML+j0gAHdUR/l1dXcsZ/cFV2xnWuIj+zc3Nr4j+jY2N/l5eXkDCpIj+sLCw/vLy8v6ysrL+ampqGd +; n+bVQ6Hs/+nlAA/sNiADXCD/6VSwAe3ysoNdES/oJBAB3b/plNACjBnWsd1hH+XV1dzBn9wFXbGdem +; iP6np6f+5ubmI/5oaGgZwLiI/t3d3aSIOAYZ2X82Hs+itv6xWQA1wxwOHt83BTXPDh3cO/7DYgDC/o +; 9IAB3WEf5dXV3MGf3AVdoZ2hP+2NjYm4j+gICAvYj+8vLy/sHBwf5ycnIZ2n/ANh7R/plNAP7IZACl +; w8MP/pBJAB7f/plNAP7IZAA1yxT+jkcA/nA4AN3+mU0A/sNiAMGdax3XEf5dXV3NGf1V2hnbDRf+9/ +; f3Vf6goKD+Y2NjGdoowTYe0jc0NcMc/p5QAB7fNzQ1yTH+djsAHd07/sNiAML+j0gAHdcR/l1dXc0Z +; /cBV2BncGv7s7OyOiKuIgoj+dXV1Gdkowjb+h0QA1A4cNcMFNx7fDhw1xv6aTQAd3wH+w2IAwZ1rHd +; gR/l1dXc0Z/cBV1xnbM/63t7f+8fHx/qurq/5nZ2epiP7Hx8e4iP6Tk5P+X19fGdcowjb+h0QA1Sv+ +; w2IANcMoKx7fKyg1w/6/YAAAHd+jpTEowf6PSAAd2BH+XV1dzhn9VdcZ2r2I/uPj45mI/oeHh/5eXl +; 5AwKSI/qCgoCD+ubm5/mtraxnVKMM2/odEANaitv6xWQAcNcIcDh7fNwUcNcAOHeEB/sNiAMGdax3Z +; Ef5dXV3OGf1V1hnZKP6lpaUy/ru7u/5ubm4ZxDn+1NTUP/6GhoYoGdIoxDYe2AH+yGQANcEx/no9AB +; 7h/plNAP7CYQD+iEQAHdlGHcU7MaXTwf6PSAAd2RH+XV1dzhn9VdYZ2LGIA7WI/pWVlf5hYWEZxqmI +; /rCwsC/+qKioIBnQKMU2Htk3/rVbADUEHaKmHuKaLh3bnUsdxP6PSAD+w2IAwZ1rHdoR/l1dXc4Z/V +; XVGdj+kpKS/u/v7/7Kysr+d3d3Gcr+ioqK/t7e3in+eXl5Gc8oxf5tVDoe25s9HcAzHuIcHdw5Qh3C +; MaXTwf6USgAd2hH+XV1dzxn8CtUZ1iD+wMDA/vDw8P6kpKQgGcwY/sHBwf7i4uL+mZmZ/mBgYBnMKM +; Y2Htv+dzwAHcCiph7iHB3dnlqcbBId/o9IAP7DYgDBnWsd2/5lTTT+XV1dzxn8VdQZ1iY+kIj+g4OD +; KBnOo4gsAv6/v7/+bm5uGcooxzb+h0QA2/53PAAdwDMe4hwd3hKbTRL+s1oA/sNiAMEBHdsR/l1dXc +; 8Z/ArUGdSjiP6tra3+8vLy/rW1tf5qamoZ0rqI/tDQ0KqIGigZyCjHNv6HRADb/nc8AB3AMx7iHB3h +; /p9QAP6+XwCjpQod3BH+XV1dzxn8VdQZ07aIOwIWFRnUp4j+qampL/6vr6/+Z2dnGcYoyP5tVDoe2w +; YdwDMe4pouHeL+hUIAMQEd3BEozxn8CtMZ0yz+8fHx/sPDw/5zc3MZ2P6EhIQ7l4j+fn5+GcQoyTYe +; 2wYdwDMex6fx/rpdAP6MRgAe1hwd46OlwB3cESjPGfwK0xnRrYj+x8fH/u3t7f6enp4CGdoc/rq6uv +; 7k5OT+n5+f/mJiYhnCKMk2/odEANv+dzwAHcCiph7G/rFZAP7RaQDAnlr+nlAAHtWaLh39xf5lTTQo +; zxn8CsQ+CssZ0f6Hh4f+6+vrhogXGd0o/pSUlP7h4eGEiDIZwCjKNv6HRADbBh3AMx7Eorb+vmAANc +; MFNx7THB39xREozxn8CsMT/tzc3ArLf8+liP60tLT+8vLy/q6urv5oaGgZ4LaI/srKyrSI/pCQkP5f +; X18oyjYe2/53PAAdwDMexSv+w2IANcP+rFYAHtMcHf3FEf5dXV3PGfsKwhX+qamp/ufn5xwKyhnPLv +; 7g4OA3/oqKiv5eXl4Z4iT+o6Oj/uXl5f62trb+a2trKMk2HtsGHcAzHsaitgX+zGcANcEc/pBJAB7S +; mi4d/cURKM8Z+wrBuYj+0tLSpYj+hoaGKArKGc0o/qKiov7z8/P+vr6+/m9vbxnm/n9/fz9VMSjI/m +; 1UOh7bBh3AoqYeyP6ZTQD+yGQANcH+ul0AHtIcHd03HeMRKM8Z+wooLCD+ubm5/m1tbQrMGcywiP7P +; z8+7iP6Xl5f+YmJiGeiriP6zs7Mv/qampv5lZWUoxjYe2wYdwKKmHsk3/rVbADXB/plNAB7RHB3cp/ +; H+u14A/qhUAP51OgAd4f5lTTQozxn7roj+wsLCv4j+k5OT/mBgYArMGcz+j4+P/u7u7v7MzMz+eXl5 +; GekowTj+39/fi4j+eHh4KMX+bVQ6Htv+dzwAHcAzHsv+o1IA/sxnADX+vmAANx7Qmi4d3P6kUgD+vF +; 4APJ5a/oNBAB3gESjPGfr+iYmJ/t/f34iI/nZ2dgrOGcqoiP68vLwU/qenp/5mZmYZ6ijCsoj+xMTE +; JA7+YWFhKMP+bVQ6/odEANv+dzwAHcAzHsyn8TQ1/qdUAB7QHB3bKv69XwA/wFr+eT0AHeD+ZU00KM +; 8Z+KiI/rGxsf7m5ub+oqKiEQrPGcn+fHx8/uXl5ZSI/oWFhSgZ6ijEFf6cnJz+5OTkCP5ubm4owjYe +; 2wYdwKKmHs03Bf7IZAA3Hs8cHdv+plMAB8A//qBQAB3Po7XAHc7+ZU00KM9V9wz+1tbWnYj+gYGBGQ +; rPGciiiP6qqqr+8/Pz/re3twkZ7CjGDP7S0tIs/omJiTcowDYe2/53PAAdwDMez/6ZTQAOHs8cHdql +; 0/6/YABWwAcqHc7+lEoANf65XQD+fD4AHc0RKM8Z9aOI/qCgoC/+s7Oz/mpqahlV0BnHtIj+19fXIP +; 6SkpL+YGBgGe0ox6iI/q2trS/+rKys/mdnZyg2Htv+dzwAHcAzHuIcHdo4/sFhAFZGmQ/+cDgAzv6t +; VwA1wZ1b/ppNAB3MESjPGfQn/snJybSI/o6Ojv5fX18ZVdEZxv6Wlpb+8PDw/sbGxv51dXUZ7n/K/o +; eHh/7c3NyTiP59fX02/odEANv+dzwAHcAzHuIcHdml0/7BYQDBWv6ORwD+cDgAzP6CQQD+xWMANcMS +; /nY7AB3LESjPVfP+j4+P/uLi4v7BwcH+cnJyGcEK0RnEq4j+xMTE/u7u7v6goKD+ZGRkGe8oy66I/r +; 29vf7j4+P+noRrHtsGHcCiph7iHB3ZOP7BYQDBnWv+cDgAzP6nVAA1xP6aTQAdzf5oTzcozhnyPi7+ +; 5eXl/pycnP5jY2OZiMIK0BnE/oSEhP7q6uqJiP6AgIAoGfAozKKI/paWlv7Kuqn+jk8PHtr+dzwAHc +; AzHuIcHdijpf7BYQDC/o5HAP5wOADKAP6/YAD+0WkAwxIAHc7+vK6g/oCAgCjNGfE1/tvb25OI/n19 +; fRnEVdAZwqSI/rGxsf7y8vIU/mlpaRnyKM63iP64q57+pnhJHtn+dzwAHcAzHuKaLh3YOP7BYQDBnW +; v+cDgAyx/+s1oA/tFpAMIOHc7+bD8R/m1mX/62trYg/qOjo/5iYmIoyxnvFf6mpqb+5+fnCQ0ZxArR +; GcG5iP7e3t6iiP6MjIz+X19fGfIo0KaI/qCcmP62lXP+jU4PHtf+dzwAHcCiph7iHB3XO/7BYQDC/o +; 5HAP5wOADMo7X+oFAA/stmAMACHc7+ZkouKMCiiP6RkZE3jYj+c3NzKMoZ7raI/s/Pzywt/l5eXhnF +; CtEZwP6enp7+8vLy/sDAwP5xcXEZ9CjS/oGBgf69rJv+m2QtHtb+dzwAHcAzHuIcHdc4/sFhAME//n +; A4AM/+iEQAPh3NK/5hVkwowzL+xMTEvYj+lJSU/l5eXijHGe0o/paWliD+u7u7/m5ubhnHCtE6/svL +; y/7r6+v+mpqa/mJiYhn1KNMN/rCsp/6yjGYe1QYdwKKmHuIcHdajpf7BYQDC/pNKAP5wOADf/mhIKS +; jGpoj+n5+f/uTk5Cr+ampqKMYZ7K2IF/7i4uI//mFhYRnICtAp/u3t7Qf+e3t7Gfco1Df+kJCQ/se2 +; pf6VWh8e0/53PAAdwKKmHuKaLh3WOP7BYQDBnWv+cDgA3qJc/mRPOijJ/n5+fv7R0dGpiP6FhYUoxR +; nr/oaGhv7e3t48PRnJCs+niP64uLj+8fHx/qmpqf5nZ2cZ+CjWs4j+wLy3/qZ4SR7SBh3Aoqb+jEYA +; HuGaLh3Vo6X+wWEAwv6YTAD+cDgA3f5sPxH+X1hRKMsN/q+vr/7m5ub+qqqq/mRkZCjCGeqniP6urq +; 7+5+fn/qWlpf5lZWUZygrOv4j+4+PjmIj+h4eH/l5eXhn5KNck/p+fn/67noD+jk8PlcTQ/nc8AB3+ +; lEoA/tFpAP6+YAD+kEkAHuAcHdX+nk8A/sFhAME//no9AJs93P5lTTQozjf+i4uL/tra2paI/nd3dy +; jBGekuMMD+g4ODKBnLVcwo/qenp/7z8/P+ubm5/m1tbRn7KNm/iP6/sqb+m2QtHs8G/q1XAP7RaQDB +; nlr+p1QAHt+aLh3Uo6X+wWEAwjj+cDgA2/5sPxH+YVZMKNGyiP6/v78C/pqamv5fX18oGec3/p2dnf +; 7m5ub+tbW1/mtraxnMCswY/tPT07SIIf5gYGAKGft/2qmI/qqlof6xi2X+jU4OHs2m4v7MZwA1ww/+ +; jEYAHt2aLh3U/p5PAP7BYQDCFJs92v5oSCko1CT+mZmZ/uHh4YGIGBnmGP7Gxsa4iP6QkJAVGc0Ky/ +; 6Tk5P+7+/v/snJyf52dnYKwRn7KNz+ioqK/sOyof6VWh8ezaK2/rpdADXDnlr+nlAAHtyaLh3To6X+ +; wWEAwjj+cDgA2aJc/mNRQCjXvIj+zMzMsoj+i4uLGeT+jIyM/uLi4jH+dHR0Gc8Kyv7BwcE2/qOjo/ +; 5kZGQKwhn7KN0Y/rq2sv6sglgezv6nVAD+zGcANcP+sVkAHtuaLh3T/phMAP7BYQDCFJs92P5rQRf+ +; XltXKNmoiP6oqKj+5ubm/rCwsP5mZmYZ4C8Q/ubm5v6fn5/+Y2NjGc8Ky/7W1tb+gYGBGQrCGfwo3q +; KILP6/poz+jk8PHs2n8f7DYgA1wige25ouHdI7/rdcAKXTwTj+cDgA2P5lTTT+XV1d2xkoAD8wCBne +; CP7Z2dmXiP5/f38oGdAKy66ICsQZ/CjguYj+uq2g/qZ4SR7Norb+sVkANcGZHx7bmi4d0jn+wWEAwh +; SbPdb+bD8R/l9YUaUf2xnCOj3+5eXl/qCgoP5gYGAZ2hX+o6Oj/ufn5/6vr6/+aWlpGdEK0xn8KOGn +; iP6jn5v+sIpk/o1ODh7N/plNAP7IZAA1mR8e25ouHdE7/rdcAKXTwf6oVAD+cDgA1v5oSCn+XV1d3R +; nDBhL+39/fiYj+cHBwGdi1iP7Nzc2uiBr+Xl5eGdJV0xn8KOP+hISE/r+unf6VWh8ezaK2/rpdAKXD +; HtscHdH+jkcA/sFhAML+f0AA/nA4ANSiXP5hVkz+XV1d3RnGuYj+xsbGu4j+kZGRKBnVIf7k5OT+vr +; 6+BRnUCtMZ/CjkrIj+s6+q/rKNZh7O/plNAB7bmi4d0Sal08H+slkA/nA4ANT+aUYjKN4ZyKeI/qGh +; oSD+t7e3/mhoaBnSDf68vLwR/piYmP5iYmIZ1ArUGfwo5X8S/si3phMe6hyaPtD+jkcA/sFhAML+iU +; UA/nA4ANP+ZE86KN8Zyv6AgID+0tLSpoj+goKCGdAx/t3d3Y6IGxnWCtQZ/H/ntYj+wr66/qZ4SR7p +; HJo+0Cal08Em/nA4ANIr/l9YUSjfGcwN/rKysi/+p6enMxnMM/6rq6v+5+fn/qioqP5mZmYZ1grVGf +; wo6KSI/qKiov68n4H+jU8PHuccHc/+jkcA/sFhAMIC/nA4ANH+ZkouKOEZzTf+jY2N/tvb25OI/nR0 +; dBnKuYj+09PTpIj+hoaGNxnXVdUZ/Sjp/n5+fv7AtKf+m2QtHuYcHc/+wmEAoZefacAm/nA4AND+bD +; 8R/mFWTCjhGdAF/sHBwf7j4+P+l5eXNxnGN/6bm5v+5eXlPf5sbGwZ2FXWGf0o6qqI/qyoo/6yjGYe +; 5Rwdzv6USgD+0WkAVpxMnUv+jkcA/nA4AM/+aEgpKOIZ0iT+m5ub/uPj4/6/v7/+a2trGcQ6/sPDw7 +; 2I/pOTkyQZ2VXWGf0o7P6MjIz+xLSj/pVaHx7jHB3O/sVjADXB/rteAB3Oolz+Y1FAKOIZ1b+IB66I +; /oiIiP5cXFzC/oqKihWGiP52dnYZ2grXGf0o7bGI/r24tP6sglge4hwdzSM1wf6gUAAdzv5rQRf+Xl +; tXKOMZ1i/+qqqq/ubm5v6urq7+ZWVlwP6ysrIv/qKiov5kZGQZ2wrXGf3AKO0JAv7SuZ/+jlAQHuCa +; Lh3N/sVjADX+v2AA/oJBAB3O/mVNNCjkGdgoLf7Y2NgsBgP+gYGBKBnbCtgZ/cAo7P6Ghob+7Ozss4 +; jA/ujcz/6lc0Ae3xyaPsz+jkcANf6nVAAdzv5sPxH+X1hRKOQZ2qOI/q2trf709PTADQYZ3ArZGf0o +; 6iT+srKy/vz8/CbCjcT+soxm/o1ODh7dHB3M/rldAP6USgAdzjQo5Rnas4j+ysrKsoj+j4+PqIgVho +; g2jYjaCtoZ/cAo6LqI/t/f3ybD/tXV1QW2iP7BsJ/+lVofHtwcHcum4sAdzaJc/mJURijlGdr+kZGR +; /uPj4/7AwMD+cnJyGcC6iP7IyMgG/o6OjhnYCtsZ/cAo5/6goKD++Pj4JsKbiP6pqan+X19fKMCuiP +; 62sq3+so1nHtuaLpo+2/5pRiMo5hnZPv65ubn+5eXl/pubm/5jY2MZwqiI/qSkpC/+tLS0PhnWVdsZ +; /cEo5K+I/s3NzSbDhYj+fHx8KMN//paWlv7JuKf+jk8PHtkcHdr+ZE86KOYZ2f6BgYH+3NzcOP58fH +; yAiMb+goKC/tTU1KOI/oCAgBnUCtwZ/cEo4wf+7+/vJsJV/rq6uv5kZGQoxreI/r61rf6meEoe2Bwd +; 2Cv+X1hRKOYZ2CQN/ufn5/6rq6v+Z2dnGcitiP60tLQv/qSkpCQZ0QrdGf3CKOCniBsXJsKRiAcoya +; WI/p+blv69n4L+jU4PHtaaLh3X/mZKLijnGdi4iP7R0dEs/oiIiDcZyjcW/t3d3Y+I/nNzcxnQCt0Z +; /cJ/3zn+5OTkJsM4CSjMBP7Ctqn+m2QtHtUcHdUrGv5dXV3nGdco/piYmCD+urq6/m5ubhnOFP7Dw8 +; O/iP6VlZUoGc0K3xn9wijcBv6pqan++vr6JsI9/qGhoSjPq4j+rqqm/rKMZh7Umi6aPtT+aEgpKOgZ +; 166I/sDAwDMw/mFhYRnQpoj+np6e/uTk5P67u7srGcsK4Bn9wijbs4j+1dXVJsOAiC4o0hb+xrWkDx +; 7SHB3SolwCKOgZ1/6Hh4cGiYguGdT+fX19/tDQ0KuI/oWFhRnKVeAZ/cMo2f6VlZX+8/PzJsKdiP6z +; s7MzKNQF/r+7t/6meEn+h0QA0Rwd0f5sPxH+X1hRKOgZ1qeI/q+vr/7n5+f+pKSk/mVlZRnWq4gYLz +; r+ZGRkGccK4Rn9xCjWPiImw42I/oaGhijXo4g3/rudgP6OTw8ezxwd0P5lTTQo6RnWvIj+1tbWIf6D +; g4MoGdgo/oqKiiwlLhnFCtkwKArGGf3EKNT+goKC/unp6SbD/sbGxv5paWko2r6I/r6xpP6bZC0ezp +; ouHc4r/mFWTCjoGdajiP6enp4v/rW1tf5ra2sZ3Cf+vr6+/uTk5P6bm5sGGcJV2v7j4+P+vLy8/mlp +; aQrFGf3EKNIkNv78/PwmwpWI/piYmCjdqYj+qKSg/rGLZf6NTg4ezJoumj7NNCjpGdYn/sfHx7eIFh +; UZ3hX+l5eX/uHh4YKI/m1tbRnBCtr+fHx8/s/Pz6yIAP5bW1vEGf3FKNC4iP7d3d0mwywji4jg/omJ +; if7CsaD+lVof/odEAMscHcuiXP5jUUAo6RnWB/7i4uIi/nNzcxniPf7Ly8s3/ouLixkK3C/+rKys/u +; bm5v6rq6v+Y2NjCsIZ/cYozv6dnZ3+9vb2JsKciAkVnYjir4j+ubSw/qyCVx7KHB3K/mpEHf5eW1co +; 6BnWqoj+tra2/uXl5f6enp4CGeSpiP6mpqb+5ubm/rGxsSAK3Bk8LJiILgrCGf3GKMuuiP7Kysomwy +; /+fn5+KOUG/piYmP6+pYz+jk8PHsgcmj7J/mVNNCjpGdb+f39//tra2hYXGej+hISEP1UICt0Y/r29 +; vf7k5OT+m5ub/l5eXp2IwBn9xyjJGv7u7u6xiMP+vr6+ICjouIj+uayf/qZ4SR7HHB3HK/5fWFEo6B +; nWpIj+paWl/ufn5/6urq4NGekKr4gf/uXl5f6hoaEGCtwG/paWlv7g4OCEiBgKwBn9xyjGAv63t7f+ +; /f39JsKSiP6RkZEo6wL+op6Z/rCKZAQexRwdxv5mSi4o6RnWI/7Ozs47/oqKijcZ6QrBNzT+3t7ei4 +; gFCt28iP7Kyso3/oyMjAoZ/cgoxLyI/uLi4ibDJRgo7v6Dg4P+vq2cDx7EHB3EpR/+YVZMKOgZ1ij+ +; lZWVEf69vb0FGeoKxDL+xcXFFf6SkpIZCtypiP6lpaUv/rKysv5mZmYZ/cgowX8g/vn5+SbCDP6kpK +; Q3KPCsiP6xran+so1mHsOaLh3D/mlGIyjpGdatiP69vb3+4+Pj/piYmP5iYmIZ6grGM/6goKD+5OTk +; /ri4uA0K3SL+1dXVwP5+fn4Z/cmyiP7S0tImw4KI/nl5eSjzN/6SkpL+x7emEx7Bmi6aPsL+ZE86KO +; gZ1/6FhYX+3t7eCwwZ6wrJF/7R0dEsIgrdKx/+5eXl/qKiov5gYGAZ/cYD/vHx8SbCQB/+YmJiKPa0 +; iP7Cvbn+pnhJHsAcHcD+bD8RASjnGdcz/qysrP7n5+f+p6enLxnrCsusiP6wsLD+5ubm/qioqDMK3K +; OI/pCQkP7e3t48FBn9w6mI/r+/v/7////Djoj+iYmJKPmkiP6goKD+vJ6B/o1PDxwd/mVNNCjnGdi6 +; iCGiiAD+Xl5eGesKzSj+i4uL/tvb2wf+dHR0h4jdMv7FxcW8iP6Tk5MoGf3A/n9/f/7n5+e4iMP+yc +; nJ/mpqaij8/n19ff7As6f+n3VK/oV7cCjmGdg3/pycnC/+t7e3/mxsbJCI7ArQs4j+wMDA/uPj4/6Y +; mJgoCtwzFf7k5OT+ubm5/mlpaRn7o4j+rKysKibCl4j+m5ubGcAo/aqI/rGxsS8N/mZmZijiGdmwiA +; C6iAP+YGBgGexV0hX+mZmZ/uLi4jU6CtsZwBc0qIgiGfm2iP7a2tomw/7b29syGcMo/Rr+3t7ejoj+ +; eXl5KN8Z2hokAP51dXUZ7VXVDCmxiP6IiIgKywAAAAAAAAAB ; thumbnail_QOI end ; ; -; thumbnail_QOI begin 440x240 20680 -; cW9pZgAAAbgAAADwBAD+XFxc/cl//f3TpIj+h4eH/tzc3P7////CjYj+vr6+/oSEhCQo/f3911X9xy -; j9/daiiLCI/ru7u/74+PgmwZqI/tTU1P6enp7+aWlpKP39/dYZ/cUo/f3ZofKlHv6VlZX+6enpJsFV -; iIj+tbW1/nx8fIKIKP39/dQZ/cMo/f3ZoeH+rWUdoeH+a15R/mBgYLeI/srKyv78/PwmwZaI/svLy/ -; 6Tk5P+ZGRkKP39/dMZ/cEo/f3a/oFhQP7KaAah4cAF/ohhOjeqiP6jo6P+8vLyJsE5g4j+rKys/nR0 -; dCj9/f3RoogZ/Sj9/dv+pWQjNcQNGCT+goKC/tfX1ybCkIj+wsLC/omJif5iYmIo/f39zq2I/r29vR -; n7KP392/5zX0z+wmcMNcb+u2cRHQauiP60tLT+9vb2JsGbiA7+oaGh/mxsbCj9/f3M/oWFhf7f398m -; Gfko/f3c/pdjLjXJBf6QYjQiM/6Pj4/+5OTkJsIc/rm5uf5/f3+AiCj9/f3IqIj+qqqqEKqIwBn4KP -; 392wP+u2cRNcwwAxWziP7ExMT++/v7JsEf/s7Ozv6YmJj+ZmZmKP39/cYu/tHR0SbCGfYo/f3c/ohh -; OjXPBTs3L/6dnZ3+7u7uJsFAhoj+sLCwLoaI/f39w6SI/pmZmTYmwxn0KP393KHhDTXSMBgVvIg0Js -; Ij/sbGxv6NjY3+Y2NjKP39/cCwiP7Dw8MqJsQZ8ij9/d09EjXUny/+gWFAN62I/qysrP709PQmwSqA -; iP6mpqb+b29vKP39/P6Li4v+5eXlJsYZ8Sj9/d3+nmQpNdgi/mVeWCT+ioqK/t/f3ybCjIj+vb29/o -; KCghUo/f34qoj+srKy/vf39ybHGe8o/f3dPf67ZxE12iA9/l9fX7GI/r6+vv75+fkmwZmI/tLS0v6c -; nJz+aWlpKP399v59fX0OJsn+XFxc7Sj9/d47Nd2fLzsiAv6Xl5f+6urqJsFAiIj+tLS0/nt7ezco/f -; 3yAv6fn5/+8vLyJsoZ7Cj9/d2h4f6tZR014DD+a15RFbmI/szMzAgmwZWI/snJyf6SkpL+ZGRkKP39 -; 8LWIPAgmyxnqKP393v56YEYFNeIF/ohhOjcc/qWlpf7z8/MmwZyIg4j+qqqq/nNzcyj9/e1//pGRkf -; 7p6ekmzRnoKP393/6eZCk15g3+ZV5YJP6EhIT+2dnZJsIn/sHBwR4kKP396qyI/rq6uv75+fkmzhnn -; KP393v5zX0wSNej+u2cRHf5fX1+viP63t7f+9/f3JsEM/tbW1v6goKD+a2trKP396P6Dg4P+3d3dJt -; AZ5X/9/d87NesFO/5lXlgzA/7m5uYmwVWKiP64uLj+fn5+Bij9/eSoiP6np6cBJtEZ4yj9/d8YMDXu -; MP5rXlH+YGBgtYgPKibBH/7Nzc3+lpaW/mZmZij9/eIQBybTGeIo/f3fCDXxBTs3qYj+n5+f/vDw8C -; bBnYiFiP6urq7+dnZ2KP393xUO/u3t7SbTARngKP394P6lZCM19DD+ZF5Xoy8XISbCBf7ExMQaMyj9 -; /dyviAT++vr6JtOAiP6QkJAZ3yj9/d/+c19MEjX2ny8d/l9fXwn+r6+v/vX19SbBGyz+pKSk/m5ubi -; j9/dr+iYmJ/uPj4ybTmYj+wMDA/nl5eTMZ3Sj9/eD+l2Mu/tFpAPr+nmQp/mVeWDP+jIyM/uHh4SbC -; i4j+u7u7/oGBgRUo/f3WLzb+9vb2JtMr/p2dnf5qamoGQBncKP393/5zX0z+u2cRNfwgPQayiP7Bwc -; EbJsEu/tHR0f6ampr+aGhoKP391L6IISbTQDj+gYGBg4gowRnaKP394P6QYjQ1/cEFOzeniP6ampr+ -; 7OzsJsEIh4j+srKyDCj9/dEz/p2dnf7x8fEm05OI/qqqqv5wcHAVnYjCGdko/f3fGAA1/cQw/mxfUh -; W6iAf+/v7+JsGUiP7IyMgl/mRkZCj9/c4FDzkm0/7a2toaLzcowxnXKP394P56YEb+ymgGNf3GBR03 -; rIj+qKioMibBnIiCiA3+cXFxjIj9/cwW/ujo6CbTLv65ubn+dnZ2i4goxRnWKP394P6eZCk1/coi/m -; VeWCQeCibCjoj+v7+//oWFhSQo/f3Iq4j+tra2/vj4+KeI04eI/piYmBw3KMYZ1Sj9/d/+c19M/rtn -; ETX9zCA9BrCI/rq6uv74+PgmwZqIIf6enp4rKP39xv6AgIA7JtMq/sjIyP59fX2GiCjIGdMo/f3g/p -; BiNP7RaQD9zwU7IjP+lJSU/ujo6CbBFy8f/nx8fIKIKP39whH+pKSkMibTkIj+paWl/m1tbQYoyRnS -; KP3936HhMDXemi7+plMA/sxnADXtMAMVtoj+ycnJ/vz8/CbBEP7Ly8sh/mVlZSj9/cC3iP7MzMwXJt -; P+1dXV/oaGhoCIKMsZ0Cj9/eD+iGE6Nd/+rVcA/nA4AKfx/pVLAP66XQA17QUIN6qI/qKiov7x8fEm -; wTmEiP6srKz+dXV1KP37oogh/uzs7CbTloj+s7OzASQozBnPKP394P6lZCM13/6USgAdwCqlw6K2/r -; FZABw17f6tZR0YJAT+1tbWJsI2/sPDw/6JiYn+YmJiKP34rYj+vr6+/vr6+ibTgoj+k5OT/mhoaJaI -; KM0Zzij9/d/+c19M/sJnDDXe/r9gAP58PgAdwSoewf6ZTQD+yGQANe3+u2cR/oFhQAauiP6ysrL+9v -; b2JsEb/tjY2P6ioqIYKP32/oeHhxUm0wwi/np6egIozxnMKP394P6XYy413/6aTQD+cDgAwyqlw8Ki -; tgU17Z8vK/5lXlikHv6Ojo7+4+PjJsKKiP66urr+gICABij98qmI/qysrBAm042I/p+fnzqUiCjQGc -; so/f3fPSD+0WkA3hL+gkEAHcMA/rhdAP6QSQAew/6eUAA17v60Zhc9/mBgYLKI/sPDw/76+vomwZiI -; /s/Pz/6YmJg+KP3wu4j+0tLSJtNA/tDQ0BODiCjSGcoo/f3fOzXf/qdUAP5wOADE/qBQADXAnlr+o1 -; IAHsM17wU7/l5eXiD+nJyc/u3t7SbBCAL+sbGxPSj97aSI/pqamjYm05WI/qysrP5xcXEVKNNVySj9 -; /d6h4f6lZCM13p1b/o5HAB3DAP6/YAA1wzT+jEYAHsH+yGQANfAw/mxfUhW8iP7R0dEmwpOI/sfHxw -; f+Y2NjKP3qJzH+/Pz8JtP+3Nzc/o2NjT6XiCjUVcco/f3f/nNfTP7CZww13v65XQD+djsAHcMENcYB -; H/6HRADA/rpdADXxBf6BYUA3Ov6rq6v+9PT0JsGciAr+pqam/nBwcCj96P6MjIz+5ubmJtOZiP68vL -; z+d3d3MyjWGcYo/f3f/p5kKTXfBP5wOADDPv6zWgA1yBz+rFYA/oxGAP61WwA18yL+ZV5YJDz+3t7e -; JsKNiP69vb3+g4ODFSj95KuIMv739/cm04mIO/5paWmViCjXGcUo/f3e/nNfTP67ZxE13v6/YAD+fD -; 4AHcP+iEQAFDXLNA419CA9BgUIDCbBmYj+09PT/p2dnRyUiP3i/n5+fv7Y2Ngm0yoaF4aIKNkZwyj9 -; /d/+kGI0Nd8O/nA4AMQONc7+mU0ANfWfLzv+ZV5YAv6Wlpb+6urqJsFVL/60tLT+e3t7Nyj93gL+oa -; GhIybTkoj+pqam/m9vbwYo2hnCKP393hj+rWUdNd6dWyEdw/6CQQD+v2AANc/+nlAANfcw/mteURW4 -; iBr+/f39JsGViP7Kysr+kpKSESj93LWICwgm0/7X19f+iIiI/mZmZjco2xnBKP393v56YEYFNd7+rV -; cAHcQENdEfNfgF/ohhOjeqiP6kpKT+8vLyJsGdiIOIOv5zc3Mo/dk3Aysm05eI/rW1tf51dXWMiCjd -; GcAo/f3e/p5kKTXf/pRKAB3DPjE10v6nVAD+yGQANfn+pWQj/mVeWCT+g4OD/tjY2CbCj4j+wcHB/o -; iIiCQo/datiP67u7sMJtOEiP6VlZX+aGhoNyjeGSj9/d3+c19M/sJnDDXe/r9gAP58PgCaLsMjNdT+ -; sVkANDX6IP6BYUD+X19fr4j+tbW1LibBDP7W1tb+oaGh/mtrayj91P6EhIT+3d3dJtObiP7FxcX+e3 -; t7iIiaiP39/cE7Nd8l/nA4AMQvNdX+vmAA/qxWADX7ny87/mVeWDP+kZGR/ubm5ibBF4qI/ri4uP5/ -; f38GKP3QqIj+qamp/vT09KuI046IJP5sbGwGKP39/cCh4f67ZxE13hL+gkEAHcMA/sVjADXWm03+o1 -; IANf3+tGYX/mteURW0iP7Gxsb++/v7JsGXiP7Ozs7+l5eX/mZmZij9zrmI/tDQ0CbTF/7S0tIxgYgo -; /f39wQg13/6nVAAdxP6gUAA12Tg1/cAFOzcv/p6env7v7+8mwTmGiP6vr68fKP3LFf6YmJj+7u7uJt -; OViDb+cnJyFSj9/f3B/qVkIzXeFP6ORwD+cDgAwwASNdr+mU0ANf3CMBijL7yI/tPT0ybCkoj+xcXF -; Kf5jY2Mo/ciwiAQqJtP+3t7e/pCQkP5nZ2eXiCj9/f3A/nNfTP7CZww13v65XQD+djsAHcP+mk0ANd -; z+nlAA/shkADX9wgX+gWFABgn+rq6u/vX19SbBG4CI/qWlpf5vb28o/cb+ioqK/uTk5CbTmYj+v7+/ -; /nh4eIqIKP39/cH+l2MuNd8E/nA4AMM+/q1XADXd/rFZAKfhNf3E/p5kKf5lXlgz/ouLi/7g4OC/iM -; KMiP68vLwT/mBgYCj9wqqI/rCwsP729vYm04uI/pycnP5qamoGKP39/cD+c19M/rtnETXe/r9gAP6C -; QQAdwwD+xWMANd7+tVsAorY1/cUgPQayiP7AwMAMJsGZiP7R0dH+m5ub/mhoaCj9wL+IPybTnYj+zc -; 3N/oCAgISIKP39/cH+kGI0/tFpAN8OHcQONeCZHw41/cafLzs3Ef6ZmZk6JsFAiIj+s7Oz/np6eoOI -; +6WI/p6env7x8fEm05OIHP5wcHAVKP39/cCh4f6tZR013p1bIR3D/oJBABI14Z5a/qNSADX9yDD+bF -; 9SFRv+zc3NFybBlIj+yMjI/pGRkREo+AX+x8fHCCbT/tnZ2Qv+ZmZmmIgo/f39wP56YEb+ymgGNd7+ -; rVcAHcQENeT+nlAANf3JBf6IYTo3q4j+p6enMibBKoKI/qmpqf5ycnKLiPYl/ujo6CbTl4j+uLi4/n -; Z2diQo/f39wf6eZCk13/6USgAdw6O1/rNaADXl/qNSADX9y/6lZCP+ZV5YJP6Ghob+29vbJsKOiP7A -; wMAPJCjyrIg9/vj4+CbTIP6Xl5ccNyj9/f3A/nNfTP7CZwz+0WkA3v6/YAD+fD4AHcMhFDXmOAE1/c -; v+u2cRLQaviP65ubn++Pj4JsGaiP7V1dX+n5+fKyjwEwom0yr+yMjI/nx8fAIo/f39wTs13/6gUAAd -; xP6nVAA16P6xWQCk1DX9zJ8vOyIz/pOTk/7o6OgmwReJiP63t7f+fX19NyjsEf6lpaUyJtOPiP6kpK -; T+bW1tkogo/f39wKHh/rRmFzXe/r9gAAAdwwD+xWMANek0nlo1/c4w/mteURW2iC3+/Pz8JsGWiP7M -; zMz+lZWV/mVlZSjqt4j+zc3NJtT+1NTU/oWFhYGIKP39/cH+iGE6Nd8OHcQENeuZHw41/c8FOzepiP -; 6hoaH+8fHxJsE5hYj+ra2t/nV1dYiI5wb+lpaW/uzs7CbTloj+sbGx/nNzcxUo/f39wf6lZCM13p1b -; Ix3Do7UxNe3+nlAANf3R/rRmF/5kXlejL7+I/tXV1SbCkYj+w8PDC/5iYmIo5K6I/r+/v/76+vqliN -; OBiP6SkpL+aGhoNyj9/f3A/nNfTP7CZwz+0WkA3/6tVwA+HcIjNe8fNf3Sny/+gWFABq2I/rGxsf72 -; 9vYmwRv+2NjY/qOjo/5tbW0o4v6Hh4f+4eHhJtMM/sLCwv56enqJiCj9/f3B/pdjLjXi/r9gAP6CQQ -; D+cDgAwC818P6eUAA1/dMF/p5kKf5lXlgz/o6Ojv7i4uImwouI/rq6uv6AgICAiCjeqYj+ra2tECbT -; jIj+np6e/mtrawYo/f39wD3+u2cR/tFpAOSdWyX+xWMANfH+o1IA/shkADX91CA9FbKIE/76+vomwZ -; iI/tDQ0P6ZmZn+Z2dnKNy8iP7T09Mm00AW/oKCgoOIKP39/cE7Nf3e/rFZADQ1/dWfLzv+Xl5eIP6b -; m5v+7e3tJsEIhoj+sbGxDCjZpIgZ/vDw8CbTlIj+q6ur/nFxcRUo/f39wKHh/qVkIzX93/6+YACZLz -; X91/60Zhf+bF9SFbuIJSbCk4j+x8fH/o+Pj/5jY2Mo1jb+xcXFOSbT/tvb2/6MjIw+Nyj9/f3APf7C -; Zww1/eABDjX92J8v/oFhQDesiP6qqqr+9PT0JsGciIGI/qenp/5wcHAo1Af+5+fnJtMu/ru7u/53d3 -; eLiCj9/f3B/p5kKTX94x816p5aNesi/mVeWCT+iIiI/t3d3SbCjYj+vr6+/oSEhCSciNCriP61tbX+ -; 9/f3JtM+LP5paWk3KP39/cH+rWARNf3k/plNADXonVv+iEQA/phMAP7IZAA16/6pXhEoBjb+vLy8/v -; j4+CbBmoj+1NTU/p2dnRyUiM7+f39//tnZ2f7////TnIj+ysrK/n19fYeIKP39/cP+iUgG/rFZAP7M -; ZwA1/eIfATXm/q1XAP5wOADA/oZDAKO1/rVbADXpDh0owH8C/pWVlf7p6ekmwVWIiBD+fHx8NyjKAv -; 6ioqIyJtORiP6mpqY2Bij9/f3E/oRGBp7U/plNAP7IZAA1/eH+sVkAATXl/pRKAB3BFh7AH/7MZwA1 -; 5RT+iEQAHcAowhW3iAv+/f39JsGViAv+k5OTESjItoj+y8vLFybT/tbW1v6Hh4f+ZmZmNyj9/f3FCZ -; 7UwKK2BTX94Bs0NeP+v2AA/nw+AB3CFh7BpcM0NeP+rVcAHcIow38N/qOjo/7y8vImwZ2Ig4g6/nR0 -; dCjFooj+k5OT/uvr6ybTl4j+tLS0/nR0dCQo/f39xwkewv6ZTQAcNf3eDw414v6gUAAdxP6KRgAewq -; K2/qxWABw14P6USgAdwyjFJP6CgoL+19fXJsKQiP7CwsIt/mJiYijCrYj+vb29DCbTg4j+lJSUDZaI -; KP39/cgJHsOlwzQ1/d2bTTg14P6/YAD+gkEAHcMA/sVjAEb+nlAAHsKn8Rw13hL+fD4AHcT+XV1dxg -; auiP60tLT+9vb2JsGbiA7+oaGh/mxsbCjA/oWFhf7f398m0xv+xcXF/np6eomIKP39/cr+hEYGHsSi -; tv6nVAAcNf3c/p5QADXfDh3EJTXBHA43Hg413yUdxijHf6SI/pCQkP7l5eUmwoqI/rm5uf6FhYX+q6 -; ur/vT09CbTjoj+oKCg/mxsbJOIKP39/cv+hEYGHsb+mU0A/sNiADX92x813RQjHcOjtRI1xA/ANd4S -; /oJBAB3H/l1dXcmjiDL+xcXFKibBl4g2JtMX/tHR0f6Dg4OCiCj9/f3NCR7HorYFNf3aOP7IZAA12x -; L+fD4AHcMjNecOHckoyn+oiP6dnZ3+7u7uJtaViP6urq7+cnJyFZ2I/f39zgkeyf6ZTQD+yGQANf3Y -; BaTUNdoEHcT+rVcANeYU/pRKAB3KKMykiCn+8vLyJtT+3d3dB/5nZ2c3Vf39/c8JHsqitjQ1/df+tV -; sAwDXYEv6CQQAdwwD+xWMANeYSHx3LKMz+i4uL/uXl5SbTPf6+vr7+eHh4iogo/f390QkezA7+zGcA -; Nf3VmR8ONdcOHcT+oFAANecEHc3+XV1dyj7+srKy/vb29ibTioj+m5ub/mpqagYo/f390gkezafx/s -; NiADX91J5a/p5QADXVFP6ORwAdwwASNeYSAB3O/l1dXcn+fX19/tfX1ybTOf7MzMz+f39/ESj9/f3U -; CR7ONwUcNf3THzXU/rldAP52OwAdwwQ15/6nVAAd0CjHpoj+n5+f/vLy8q2I05KI/qioqP5vb28VKP -; 39/dUJHtD+mU0A/shkADX90h810wQdwz7+s1oANeadW/6ORwAd0SjGtIj+ycnJ/v39/SbT/tjY2P6J -; iYn+ZmZmmIgo/f391gke0aK2GzX90TgBNdAS/nw+AJouw/6IRAD+y2YANeYxPh3SKMR//pGRkf7p6e -; km0wj+19fX/oeHhyQo/f392Ame1NM4/sxnADX9zwWk1DXPJR3E/qdUADXnBB3UKMOsiP66urr++Pj4 -; JtWPiP7AwMD+h4eHJCj9/f3XCZ7U1Cs0/tFpAP3OD5tNNc0UAB3DAP7FYwA15hIfHdUowiIZJtMIOS -; bBmog//qCgoP5ra2so/f391gke1aK2Bf7MZwA1/cyZHw41zP6tVwAdxAQ15yUd1/5dXV3AEf6np6f+ -; 9PT0JtMn/qamppaI/ufn5ybBVYqI/re3t/5+fn6BiCj9/f3UCR7X/plNAP7IZAA1/cz+mU0ANcv+lE -; oAHcOjtTE15jMAHdgouIj+zs7OJtNV/tPT0/6FhYWBiJqIEP7Hx8c5JsGWiP7Nzc3+lpaW/mVlZSj9 -; /f3TCZ7U2DcFNf3LATXJ/r9gAB8dwyM15/6tVwAd2v6Xl5cYJtMQ/rCwsDIVKMB/qYj+oKCg/vDw8C -; bBOYWI/q6urv52dnYo/f390gke2gEcNf3J/p5QABw1xwQdxC815yMd2/76+vom04CI/pGRkf5oaGg3 -; KMOkiL6I/tTU1P7////CBf7ExMQa/mJiYij9/f3QCR7borb+ul0ANf3I/qdUAP7IZAA1xRL+gkEAHc -; MA/sVjADXmEv58PgAd3CbSDP7BwcH+eXl5iogoxqKIrYj+sLCwECbBGyz+pKSk/m5ubij9/f3P/oRG -; Bh7dDhw1/cb+sVkANDXEDh3EJTXn/ppNAB3eJtGMiP6enp7+ampqBijJpYj+jIyM/uHh4SbCi4j+u7 -; u7/oGBgf5gYGAo/f39zQke3qfx/sNiADX9xQ+bTTXCFP6ORwAdwwASNeYSAB3fJs8IBwQgmIjMFbGI -; /sHBwf76+vomwZiI/tHR0f6ampoNKP39/cwJHt+itgUcNf3DAf6sVgA1wf65XQD+djsAHcP+mk0ANe -; cOHeEmzpSI/qqqqhQVKM5/IDv+7OzsJsEIh4j+srKyDCj9/f3L/oRGBh7h/plNAP7IZAA1/cP+sFgA -; NcAEHcM+LzXmnVsCHeImzf7a2top/mZmZjco0RW6iP7Pz88XJsEy/sjIyP6Pj4/+Y2NjKP39/ckJHu -; Kitv61WwA1/cIOEh8dwwD+xWMANeYxPh3jJssu/rq6uv53d3eLiCjUf6yI/qmpqf709PQmwZyIgYj+ -; qKio/nFxcSj9/f3ICR7k/qNSAP7MZwA1/cD+pFIAHcQONecEHeUmyoeI/pmZmf5paWk3KNekiP6Hh4 -; cZJsKNiP6/v7/+hYWFJCj9/f3G/oRGBp7U5afx/rpdADX9/p1OAB3C/oJBAP6/YAA15hIfHeb+//// -; yCr+ycnJ/n19fYeIKNoGNhv++Pj4JsGaiCH+np6e/mpqaij9/f3FCR7morYF/sxnADX7/qtWAB3BBD -; XnDh3oJseQiP6lpaX+bm5uBijcfwL+lJSU/unp6SbBFy/+tra2/nx8fDco/f39wwke6P6ZTQD+yGQA -; Nfr+r1gAHT7+s1oANeadW/6IRAAd6SbG/tXV1f6Hh4f+ZmZmNyjfFS48/vz8/CbBloj+y8vL/pSUlP -; 5lZWUo/f39wgme1OmitgU1+TH+jkcAFDXm/q1XAB3VpdMd0ybEEP6zs7P+dHR0jYgo4n+qiP6ioqL+ -; 8vLyJsE5hIj+rKysASj9/f3B/oRGBh7r/plNAP7MZwA1951bNef+lEoAHdU7/oBAAB3TJsODiP6Tk5 -; MNNyjlJP6BgYH+1tbWJsI2/sPDw/6JiYn+YmJiKP39/Qke7KK2NP7RaQD94P6/YAD+fD4AHdT+gEAA -; MZo+HdQmwQwi/np6eomIKOgGroj+s7Oz/vb29ibBm4j+2NjY/qKiohgo/f38CR7uDhyitv3dJf5wOA -; DVAf7DYgDA/oBAAB3UJsCNiP6goKD+bGxsBkDqf6SIFv7k5OQmwoqI/rq6uv5/f38GKP39+v6ERgYe -; 76fx/sNiAKfx/doSAB3U/oBAAP6zWgAowBAd1f79/f3+0NDQ/oODgyCYiO6jiLOI/sTExCqkiMEf/s -; /Pz/6YmJj+Z2dnKP39+Qke8KK2Bf7RaQD92A7+cDgA1SP+w2IAwv6KRQAd1f6srKz+cXFxFZ2I8H+o -; iP6dnZ3+7u7uJsEIhoj+sLCw/nh4eIWI/f34/oRGBh7yAf7IZAA1/dSdW/6ORwAd1KOl/qRSAP7DYg -; DCEB3WPjdV8xW8iP7R0dEmwpOI/sbGxv6Ojo7+Y2NjKP399gke86K2/rpdADX90hL+djsAHdU7/sNi -; AMMsHdb+XV1d9gasiP6srKz+9PT0JsEqgIj+pqam/nBwcCj9/fX+hEYGHvUO/sxnADX9z/6aTQAd1q -; XT/sNiAMObPR3X/l1dXfgzPP7e3t4mwo2I/r29vSIVKP398/6ERgYe9iv+w2IANf3MEgAd1zsowywd -; 1/5dXV35BgUIDCbBmYj+09PT/pycnP5paWko/f3yCR73orb+sVkA/sxnADX9yQ4d2KOl/sNiAMMxHd -; j+XV1d+n8C/peXl/7q6uomwUCJiP60tLT+e3t7Nyj9/fAJHvn+mU0A/shkADX9xp1b/o5HAB3Z/qRS -; AP7DYgDD/o9IAB3Y/l1dXfwVuIj+zMzMCCbBlYgLA/5kZGQo/f3vCR76orb+tVsANf3E/q1XAB3ao6 -; X+w2IAwzEd2f5dXV39fw3+pKSk/vPz8ybBnIiDiP6qqqr+c3NzKP397gke/DgcNf3B/pRKAB3b/qRS -; AP7DYgDDCh3Z/l1dXf3BJP6EhIT+2dnZJsIn/sHBwf6Hh4ckKP397Ame1P2n8TQ1/P6/YAD+fD4AHd -; ujpf7DYgDDnWsd2v5dXV39wgaviP62trb+9/f3JsEMP/6goKA6KP396wke/cCitgUcNfn+oFAAHd3+ -; pFIA/sNiAMP+j0gAHdr+XV1d/cN/M/6RkZH+5ubmJsEXioj+uLi4/n5+fgYo/f3pCR79wv6ZTQD+w2 -; IANfYSAB3do6Uow51rHdv+XV1d/cWjiLSIDyomwZeI/s3Nzf6Wlpb+ZmZmKP396Ake/cOitgU19P6n -; VAAd3/6kUgD+w2IAw/6PSAAd2/5dXV39xn8v/p+fnzYmwZ2Ihoj+r6+v/nZ2dij9/ecJHv3F/plNAB -; w18BQjHd+jpf7DYgDDnWsd3P5dXV39yCQX/tPT0ybCkoj+xcXF/oyMjP5iYmIo/f3lCR79xqK2/rpd -; ADXu/r9gAP58PgAd4P6kUgD+w2IAw/6PSAAd3P5dXV39yaKIrYj+rq6u/vX19SbBm4iAiBH+bm5uKP -; 395P6ERgYe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd3+XV1d/csz/ouLi/7h4eEmwouI/ry8vP6CgoIV -; KP394gke/cmn8f7DYgA16BL+gkEAHeL+pFIAKMMjHd3+XV1d/cwGsoj+wMDAGybBLv7R0dH+mpqa/m -; hoaCj9/eEJHv3KNwUcorblDh3jo6X+w2IAwwod3v5dXV39zX+miP6ZmZn+7OzsJsEIh4j+srKy/nl5 -; eSj9/eD+hEYGHv3MAf7IZAA14p1bAh3k/p9QAP7DYgDD/pRKAB3e/l1dXf3PFbqI/s7Ozv7+/v4mwZ -; SILf6QkJD+ZGRkKP393gke3KK2Huw3/rVbADXg/rldAD4d5Dv+w2IAwwod3/5dXV390H+siP6np6cy -; JsEqgoj+qamp/nJycouI/f3dCR7YorYO/rpdADWZH/6eUAAe7f6jUgD+zGcANd0EHeYiKMP+mU0AHd -; /+XV1d/dKkiP6Ghob+29vbJsKOiP6/v7/+hYWFJCj9/dsJHtWn8f6sVgD+w2IANcMcDjce7Cs0NdoS -; /nw+AB3mOyjDnWv+ej0AHd/+XV1d/dMGsIj+ubm5/vj4+KeIwZqIMP6fn5/+ampqKP392gke1Tc0Nc -; b+vmAA/pBJAB7sNwUcNdcOHegi/sNiAMMBHeD+XV1d/dR/MyH+6OjoJsEXiYj+t7e3/n19fTco/f3Y -; CR7XDhw1xRwOHu0B/shkADXUnVv+iEQAHeg7/sNiAMMK/no9AB3g/l1dXf3WFbaILf78/PwmwRD+zM -; zM/pWVlSAo/f3XCR7Yp/H+w2IANcYP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3913+p -; iP6hoaH+8fHxJsE5hIj+ra2t/nV1dSj9/dYJHtmitgUcNcUc/p5QAB7tARw1z/6USgAd6qOl/sNiAM -; MK/no9AB3h/l1dXf3ZpIi/iP7V1dUmwpCI/sPDw/6KioozKP391Ake2wH+yGQApcPGBTce7Dc0Ncz+ -; v2AA/nw+AB3r/plNAP7DYgDD/qRSAB3i/l1dXf3aooiuiP6xsbH+9vb2JsGbiP7Y2Nj+o6OjGCj9/d -; MJHtw3/rVbADXGmR8rHu0OHDXJ/qBQAP5wOADso6UoxP56PQAd4v5dXV393DP+jo6O/uPj4ybCioj+ -; urq6/oCAgAYo/f3RCR7eOP7MZwCitsUcDh7tK/7DYgA1xhL+gkEAHe0BKMP+pFIAHeP+XV1d/d2jiL -; KIIv76+vomwZiI/tDQ0Cw+KP390Ake3ys0NcYP/pBJAB7sNwU1xA4d7qOlMaXTwxQd4/5dXV393n8g -; /pubm/7t7e0mwUAC/rGxsf54eHgo/f3PCR7gorYFHDXFNP6KRgAe7QH+yGQANcCdW/6ORwAd7/6ZTQ -; D+w2IAw/6kUgAd5P5dXV394BW7iCUmwpOI/sfHxwf+Y2NjKP39zQme1OIB/shkADXDDh3+hEIAHu43 -; NFr+djsAHe+jpTGl08P+ej0AHeT+XV1d/eF/rIj+qqqq/vT09CbBnIiBiP6np6f+cHBwKP39zAke46 -; K2/rFZADXAnVshHcALHvAd6EYdxv6PSAD+w2IAw/6kUgAd5f5dXV394yT+iIiI/t3d3SbCjYj+vr6+ -; /oSEhBWdiP39yv6ERgYe5f6ZTQD+rlcAHcILHvAd6J1LEh3F/rldAKXTw/56PQAd5f5dXV395AaxiP -; 68vLz++Pj4JsE9/tPT0/6dnZ3+aWlplIj9/ckJHub+djsAHcILHvAd6Zkfoacdwwr+w2IAw/6pVQAd -; 5v5dXV395X8CP/7p6ekmwVWIiP61tbX+fHx8Nyj9/ccJntTmPh3CCx7wHeqbTZ1Lp/EdwTGl08MUHe -; b+XV1d/ecVuIj+ysrKCCbBlYgL/pOTkxEo/f3GCR7mPh3C/oRCAB7wHetGmi6lwx3+gEAA/sNiAMMZ -; Hef+XV1d/eg3qoj+o6OjIybBnYiDiP6rq6v+c3NzKP39xQke5j4dwgse8B3tBBI7/sNiAMM1Hef+XV -; 1d/eok/oODg/7Y2Ngmwjb+wsLCLTMo/f3DCR7mPp1bwgse8B3vo6UZ/sNiAMEZHej+XV1d/euiiK6I -; /rW1tS4mwRv+19fX/qGhof5ra2so/f3CCR7mPh3CCx7wHfH+lEoA/r5fAKOlNR3o/l1dXf3sNzP+kJ -; CQ/uXl5SbCiYj+ubm5/n9/fwYo/f3ACR7mPh3CCx7LpcMe4h3yNf6kUgAd6Sj97hW0iP7FxcUqpIjB -; H/7Ozs7+l5eX/mZmZij9/Qke5j4dwgseyif+0WkAD/6MRgAe4B394Sj9738v/p6enjYmwTmGiP6vr6 -; /+d3d3KP38CR7mPh3CCx7IEv7DYgA1wZ5a/p5QAB7fHf3h/l1dXf3xFb2I/tLS0ibCkogA/oyMjCj9 -; +wke5j4dwgsexzg1xf6xWQD+jEYAHt0d/eEo/fIGOv6tra3+9fX1JsEbgIgo/fsJHuY+HcILHscFNc -; aZHzce3B394f5dXV399KWIGv7f398mwij9+wke5j4dwgseyP6ZTQAcNcUFHtwd/eEo/fWiiLKI/r+/ -; v/75+fmmiMAo/fsJHuY+HcILHsk3NDXEHP6VSwAe2x394Sj99n8R/piYmP7r6+smKP37CR7mPp1bwg -; sey/6nVAAcNcM0Htsd6aK2HfMo/fgVuYj+zc3NKP37CR7mPh3CCx7MK/7DYgA1wx8e2h3pp/H+qFQA -; /nk9AB3x/l1dXf35N6uIKP37CR7mPh3CCx7NorYFNcKbTTce2R3p/p9QAP67XgCeWh4d8Cj9/f36CR -; 7mPh3CCx7P/plNAP7IZAA1wSce2R3oEf68XgA8wf6aTQA2He4o/f39+gke5j4dwgse0Dc0NcCeWhIe -; 2B3o/qBQAD/BPMA3He4o/f39+gke5j4dwgse0g4cNTQe2B3nNv69XwDAP8GeWh3vKP39/foJHuY+Hc -; ILHtMr/sNiADX+mU0AHtcd5/6hUAAHwT/A/pFJAB3v/l1dXf39/foJHuY+HcILHtQ3BTQ3HtYd5jYK -; wQfAnWs2HdX+lEoA/stmACMd1Sj9/f36CR7mPh3CCx7WAR8e1h3m/qNSAP7AYQBSCsAHJR3V/q1XAD -; XBMT4d0yj9/f36CR7mPh3CCx7wHeWjpf7BYQDBRgrA/no9AJs90/6CQQD+xWMANcOdW/6ORwAd0ij9 -; /f36CR7mPh3CCx7wHeU4/sFhAMJW/qFRAP5wOADTJTXGFAAd0Sj9/f36CR7mPh3CCx7wHeQ7/sFhAM -; T+f0AA/nA4ANEAEjXGMT4d0ij9/f36CR7mPh3CCx7wHeT+nk8A/sFhAMMv/nA4ANEENccEHdQo/f39 -; +v56TB0e5j4dwgse8B3jO/7BYQDEMv5wOADPPi81xhL+fD4AHdT+bD8RKP39/fqe1P51TykJHuT+dj -; sAHcILHvAd4/6YTAD+wWEAw/6yWQD+cDgA0AIUNcUOHdX+aEgpKP39/f3+Z1dG/oJHDB7jPh3CCx7w -; HeI7/sFhAMT+iUUA/nA4ANEAMTXCFCEd1KJc/mJURij9/f39wT3+dU8pHuL+djsAHcILHvAd4jn+wW -; EAwyb+cDgA0z7+p1QAFDUvHdX+aUYjKP39/f3FBwke4D4dwgse8B3hOyal08MC/nA4ANUhIx3V/mRP -; Oij9/f39xz0qHt8+HcILHvAd4f6ORwD+wWEAwz/+cDgA7Sv+X1hRKP39/f3K/m9SNAke3T4dwgse8B -; 3gOyal08MC/nA4AOz+ZkouKP39/f3N/mVYTP5/SREe3D4dwgse8B3gAv7BYQDDP/5wOADrK/5hVkwo -; /f39/c89/nVPKR7b/nY7AB3CCx7wHeAmpdPDAv5wOADqNCj9/f390wcyHtk+HcEjDjce7x3fAv7BYQ -; DDP/5wOADpolz+Y1FAKP39/f3VPSoe2D4dwC81wA/+kEkAHu4d3yal08P+k0oA/nA4AOj+a0EX/l5b -; Vyj9/f392AgJHtY+AP7FYwA1wp5a/qdUAB7tHd4C/sFhAMM//nA4AOj+ZU00KP39/f3bMBYe1f6dTw -; A1xg/+jEYAHusd3ial08M5/nA4AOYrGij9/c2uiAYo/f3MPf51TykJHtM3BRw1xRz+nlAAHuod3QL+ -; wWEAwz/+ej0Amz3lNCj9/c/+0tLS/n19fYOIKP39zQcyHtT+mU0A/shkADXGBTce6B3dJqXTwzj+cD -; gA5KJcKSj9/dD+////kIj+np6e/mZmZpiIKP39zD3+d00jHtSitv61WwA1xpkf/pVLAB7nHdz+iUUA -; /sFhAMQUmz3j/mlGI/5dXV39/dImwJyI/sXFxf50dHQVKP39zf5tVDoJHtQfHDXFmR8e5x3c/rdcAK -; XTwzj+cDgA4/5kTzr+XV1d/f3T/v///8KGiP6QkJD+YmJinIgo/f3MMP56TB0e1BL+ul0ANcSZHx7n -; Hdsp/sFhAMQUmz3h/mw/Ef5fWFGlH/391P7X19f++vr6JsGYiP60tLT+bW1tBij9/cw9Pgke06K2Jx -; w1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXf391v6IiIgT/u/v7ybC/tjY2P6Dg4P+YWFhKP39 -; zQcWHtT+mU0A/sNiADXBKB7n/nA4ANoL/sFhAMQUmz3fKxr+XV1d/f3Ytoj+q6ur/t/f372IJsGTiP -; 6jo6P+aGhologo/f3MPT4e1KK2BTXAmR8e5x3aCKjww/6oVAD+cDgA3/5oSCn+XV1d/f3bp4j+k5OT -; /srKyv709PQmwUAL/nd3dxUo/f3NB/6CRwwe1P6ZTQD+yGQAnloe5x3ZqPD+wWEAxDL+cDgA3aJc/m -; RPOv5dXV39/d1/voj+tbW1/ubm5riIJsGKiP6Wlpb+Y2NjNyj9/cw9Kh7UorYnHucd2f6oVAD+wWEA -; w/6yWQD+cDgA3Sv+X1hRKP394KyI/p2dnf7T09P++Pj4JsE9/ry8vP5wcHCPiED9/c3+b1I0CR79Hd -; gU/sFhAMQp/nA4ANz+ZU00KP394xX+hISE/r6+vv7s7Owmwv7d3d0tJJyI/f3NMBYe/B3YOP7BYQDD -; mz3+cDgA2ysaKP395gX+p6en/tzc3L+IpIjBlYj+qqqq/mpqapSIKP39zJ7U/nVPKf6ERgYe+h3XFP -; 7BYQDE/o5HAP5wOADaNCj9/emmiP6Ojo7+x8fHIybC/tDQ0P57e3sVKP39zf5nV0b+gkcM/odEAPkd -; 1zj+wWEAw51r/nA4ANkO/mNRQCj9/ey7iP6xsbH+4+PjuogmwY6I/pubm/5lZWU3KP39zJ7U/ndNIx -; 74Hdam0v7CYQBaw/6ORwD+cDgA2P5rQRf+XltXKP397qqILCX+9/f3JsGbiP7Dw8P+cnJyFSj9/c3+ -; bVQ6CR72Hdb+rVcA/s1nAJs9RsE//nA4ANgR/l1dXf398Qb+gICA/rq6uv7p6ekmwoSI/o6Ojv5iYm -; Io/f3NMP56TB0e9R3Vo7X+0WkAwFacTJ1LwP6ORwD+cDgA1v5sPxH+X1hRKP399LCI/qOjo/7Y2Nj+ -; +vr6JsGXiBQYBij9/cw9/npVL/6LTAwe8/5wOADVLzXCnmr+vV8AHdY0KP399zP+ioqKIv7w8PCviM -; L+1dXV/oCAgCQo/f3L/oGBgf7b29v+4dC//pZbIP6HRADyHdQ+/tFpAMOdW/6CQQAd1A7+YlRGKP39 -; +riI/q2trf7g4OC8iCbBkoj+oaGh/mdnZ5eIKP39x6eI/qSkpP7z8/MmwJTE/ruWcB7xHdQvNcIvHd -; UDKP39/aiI/pWVlf7MzMz+9fX1JsE5/sjIyP52dnaKiCj9/cW3iP7Nzc3+/v7+JsMh/o5QEB7vHdOj -; tTXC/pRKAB3V/mRPOij9/f3BN7+I/re3t/7n5+cXJsGJiP6UlJQzNyj9/cEGMP7s7OwmxiL+rH5QHu -; 4d0y81wP6/YAD+fD4AHdQr/l9YUSj9/f3ErYj+n5+f/tXV1f75+fkmwZmI/rm5uf5vb2+QiCj9/Sv+ -; vr6+GybJ/sqtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o/f39x6SI/oWFhf6/v7/+7e3tsojCCv -; 6GhoYkKP37/oeHh/7g4OAmzP7o3M/+nWcw/odEAOsd0v6nVAAS/oJBAB3U/mw/ERoo/f39yrWI/qmp -; qf7d3d2+iCbBMv6np6f+aWlpN1X996mI/qysrBAmzpTE/ruWcB7qHdGjtQ4d1TQo/f39zaeI/pCQkC -; 0yJsFV/s7Ozv56enqGiCj99byI/tLS0ibS/uHQv/6WWyD+h0QA6B3RPh3Uolz+Y1FAKP39/dAM/rKy -; sv7k5OS5iCbBjYgs/mRkZDco/fGkiP6bm5sFJtMy/qmlof5+VCoe5x3n/mtBF/5eW1co/f390quI/p -; qamv7R0dH+9/f3JsGbiP7AwMD+cXFxBij97zb+xMTE/vz8/CbTGf6NjY3+Z2dnl4j+bVQ6/oRGBh7l -; Heb+ZU00KP39/dUV/oKCgv68vLz+6urqJsKCiP6Li4v+YmJiKP3tOP7m5uYm0y45/nh4eDMowf5lWE -; z+fEoXHuQd5P5sPxEBKP39/dixiP6kpKT+2traGybBloj+rq6u/mxsbAYo/ekN/rS0tC4m04mI/pqa -; mv5qamoGKMOe1P51Tyn+hEYGHuId4/5oSCko/f3926aI/oyMjP7FxcX+8fHxJsL+09PT/n5+foOIKP -; 3nF/7Y2Ngm0yr+y8vLF4aIKMf+Z1dG/oJHDB7h/nA4AOEO/mJURij9/f3euYj+r6+v/uLi4rqIJsGQ -; iP6fn5/+ZmZmNyj94wL+oaGh/vLy8ibTBf6np6f+b29vFSjJPf51Tyke4B3g/mlGIyj9/f3hL/6Wlp -; b+zc3N/vb29ibBKv7Gxsb+dHR0FSj94bWI/srKyv7+/v4m0/7X19f+iIiILzcozAcJHt4d3/5kTzoo -; /f3946KIv4j+uLi4DbaIJsGHiP6RkZH+YmJiNyj93Tf+k5OT/urq6ibTH/61tbX+dXV1jIgoz/5iWl -; H+ekwdHt0d3f5sPxH+X1hRKP39/eauiP6goKD+1tbW/vn5+SbBLv62trb+bm5uBij9262I/ru7uwwm -; 04SI/pWVlf5paWk3KNGe1P5vUjQJHtsd3P5mSi4o/f396ST+h4eH/sHBwf7u7u4mwv7Z2dn+hISEJC -; j92TH+3t7eJtObiAD+e3t7iIgo1f5lWEwW/odEANod2v5sPxH+YVZMKP39/ey2iP6qqqo3vYgmwZSI -; /qSkpP5oaGiWiCj91SD+qamp/vT09CbTGP6ioqL+bGxsBijXPT4e2R3Z/mhIKSj9/f3vp4j+kpKSCw -; EmwUD+zMzM/nh4eIiIKP3TuYj+0NDQJtMX/tLS0jEgKNsH/oJHDB7XHdeiXP5kTzoo/f398Te9iP60 -; tLQvCCbBi4j+l5eX/mNjYzco/c8V/piYmCcm05WI/q+vr/5zc3MVKN2e1P56TB0e1v5wOADW/mw/Ef -; 5fWFEo/f399KyI/pycnP7T09P++Pj4JsEM/r29vf5wcHAGKP3NsIj+wsLC/vv7+ybT/t7e3v6QkJD+ -; Z2dnl4go4P5vUjT+hEYGHtQd1f5lTTQo/f399xX+g4OD/r29vf7s7Owmwv7e3t7+iYmJ/mJiYij9y/ -; 6Kior+5OTkJtM9/r+/v/54eHgCKOMwFh7THdMrGij9/f36Bf6mpqb+29vbKqSIwZWI/qysrDoGKP3H -; Pv6xsbH+9vb2JtOLiBn+ampqBijlntT+dU8p/oRGBh7RHdI0KP39/f0C/o6Ojg/+8vLyJsL+0dHR/n -; x8fBUo/cU5PybTnYg4/oCAgISIKOn+Z1dG/oJHDB7QHdCiXP5jUUAo/f39/cK7iP6wsLD+4+Pjuogm -; wScZ/mZmZpiIKP3BpYj+np6e/vHx8SbTI/6pqan+cHBwFSjrntT+d00jHs8dz/5qRB3+XltXKP39/f -; 3Eqoj+mJiY/s/Pzx8mwSr+xMTE/nNzcxUo/QX+x8fHCCbTLAsvmIgo7v5tVDoJntTN/nA4AM7+ZU00 -; KP39/f3HBv5/f3/+urq6/unp6baIwoWI/o+PjzM3KPk3Jf7o6Ogm0x/+uLi4/nZ2djMo8TD+ekwdHs -; wdzP5sPxH+X1hRKP39/f3KGP6ioqL+2NjY/vr6+ibBl4j+s7OzGAYo96yIPf74+Pgm04aI/peXlxw3 -; KPOe1P51TykJHsr+cDgAy/5mSi4o/f39/c2liDz+w8PD/u/v7ybCP/6BgYEkKPX+goKC/tzc3CbTnI -; j+yMjI/nx8fIeIKPcH/oJHDB7JHcmlHxoo/f39/dC3iP6srKz+4ODgvIgmwRT+oqKi/mhoaDco8aeI -; /qWlpf7z8/Mm0yf+pKSk/m5ubgYo+T0+HsgdyP5pRiMo/f39/dOoiP6UlJT+y8vL/vX19SbBOf7Jyc -; n+dnZ2iogo77iIOCbU/tTU1P6FhYUvKP0H/oRGBh7GHcf+ZE86KP39/f3VN76I/ra2tv7m5uYXJsGK -; iP6UlJT+Y2NjNyjrBv6Wlpb+7OzsJtOWiP6xsbH+c3NzJCj9wT3+ekwdHsUdxSv+X1hRKP39/f3YrY -; j+np6e/tTU1AwmwZmI/rq6uv5vb28GKOk6/r+/v/76+vqliNOBiP6SkpINlogo/cT+b1I0/oRGBh7D -; HcT+ZU00KP39/f3bJAD+v7+//uzs7LOIwhn+h4eHJCjnHv7h4eEm0wz+wcHB/np6egIo/ccw/n9JEf -; 6HRADCHcL+bD8R/mFWTCj9/f393rSI/qioqBm/iCbBlYj+qamp/mpqajco46mI/q6urhAm04yI/p6e -; nv5ra2sGKP3JntQ+/oRGBh7AHcH+aEgpKP39/f3hAv6Pj48t/vPz8ybBF/7Pz88bhogo4Rv+09PTJt -; NAFhMgKP3NB/6CRwweHaJc/mNRQCj9/f395LyI/rKysv7k5OQIJsGNiDsgmYgo3aSI/pycnP7w8PAm -; 05SI/qurqxQVKP3PPf53TSOUiP5eW1co/f39/eariDv+0dHRLibBm4gEFBUo2zb+xcXF/vz8/CbTCv -; 6MjIz+Z2dnl4go/f39/f39Ff6BgYH+u7u7/urq6ibCgogp/mJiYijZ/o6Ojv7n5+cm0y4q/nd3dzMo -; /f39/f39w7GI/qSkpCwbJsGWiP6wsLD+bGxsBijVDf61tbX+9/f3JtM+/pmZmf5paWk3KP39/f33AA -; AAAAAAAAE= +; thumbnail_QOI begin 440x240 28616 +; cW9pZgAAAbgAAADwBAD+WVlZ7n/S/oyMjP7p6emxiP6vr6/+YWFhO8Ek/ra2tv78/PyLiP6JiYk79n +; /9/f6ZmZn+9PT0ooj+paWl/l1dXQrBrYj+u7u7G4mI/oGBgf5cXFwK/e479FXsO9KqiP61tbUbPv6G +; hoYKO8MZPP7r6+utiP6zs7MRO/EK/f3ADf7Hx8f+/f39gYj+fHx8CsQZ/pKSkjo9/qqqqiQK/e878i +; zqO9M5N76I/sPDw/5nZ2c7x6+I/sjIyDn+29vb/np6ejvtf/3ZoeEK5P6Hh4f+6urqsIj+uLi4/mNj +; YwrHtYj+zMzMOf7W1tb+dHR0Cv3wO/As6TvSpYj+o6Oj/vT09JyI/pWVlSg7ySj+m5ub/vX19UAC/l +; 5eXjvof/3aOv6sZRyg8/5qXVAK4aWI/rS0tP77+/uQiP6MjIwKyhX+oqKiMkD+l5eXNwr98DvuLOc7 +; 07aI/s7Ozgj+1dXV/nJycjvNu4j+2NjYOf7Ly8v+b29vO+UK/dv+gF8//spoBqHhwAX+h2A5Ct+9iP +; 7d3d2/iP7Jycn+bGxsCs3+fHx8/tvb2/78/Pz+xcXFHJKI/fI76yzlO9MZ/pOTk/7t7e2riP6oqKgG +; O88G/q2trSo6/pGRkTvhCv3d/qVkIjXEBv5iXFUK2xn+oaGh/vf3952I/p2dnRkKz6qI/rS0tP75+f +; mPiP6IiIgZCv3yO+ks4zvUPv69vb0qhoj+f39/CjvRCv6Dg4P+5ubmDP67u7v+ZmZmO90K/d3+cV5K +; /sJnCzXG/rtmEf6AXz8K2bGIB/79/f3+2dnZ/nd3dwrT/ouLi/7n5+e0iP6ysrL+YmJiCv3zO+dV4j +; vU/oKCgv7j4+M5/ry8vBE71SD+wcHB/vz8/IWI/oCAgDvZCv3f/pZiLf7RaQDJBf6PYTM6Ctb+jo6O +; /u/v7z3+sLCwFQrVCQA5/tvb2/56enqBiP30O+VV4DvUp4j+q6ur/vf395aI/o+Pjxk71xkDFKOI/q +; mpqRU71Qr93/5qXVD+u2YRNcz+s2UXNArTqIj+vLy8/vz8/ImI/oSEhArYN/6cnJz+8PDwpYj+oKCg +; /l9fXwr99DvjLN871LqIMDn+zc3N/m1tbTvbtoj+09PTOf7S0tL+c3NzO9EK/eH+h2A5Nc+fL/6PYT +; MK0f59fX3+4+PjKv7Dw8P+aGhoCtu7iDD+/Pz8/szMzBgK/fU74DebiN071KOI/pqamhSliBUGm4jd +; N/6lpaU9loj+mJiY/lxcXDvNCv3hoeH+pWQiNdL+s2UXOgrNooj+qqqqDBT+lZWVGQrdqIj+ra2tH5 +; aIBygK/fQ73r6I/uTk5CzbO9WwiP7ExMQ5/tvb2/56eno74H/+fHx8/uHh4bmI/sLCwv5paWk7ygr9 +; 4v5xXkoLNdQF/oBfPwrLtoj+1dXV/vz8/AP+cnJyCuH+hYWF/uPj4yr+urq6/mRkZAr99Tvb/qSkpC +; qkiCzaO9X+iYmJPir+s7Oz/mFhYTvjqYgbOYqIHjvGCv3k/p1jKDXYEzoKyP6Wlpb+8/PzLv6pqak3 +; CuOuiP6+vr4qhoj+f39/GQr99TvXsogw/v///8Es2DvVAv6zs7MMj4g8GTvlGRr+7OzsLv6xsbECO8 +; IK/eT+cV5K/rtmETXaGyQKxayI/sXFxTmFiP5+fn4K5ij+lZWV/u3t7auI/qenp/5hYWEK/fY71P6Q +; kJAQJsIs1zvVv4j+29vb/v39/f7GxsYcO+mxiCk5/tnZ2f54eHg7f/3l/o9hMzXdBSz+YlxVCsL+g4 +; OD/unp6bGI/ru7u/5kZGQK6baI/s/Pzzn+1NTUIwr99zvQJP7CwsImxP5ZWVnVO9U3Ff7z8/PAHTc7 +; 6yj+np6eH5uIFf5eXl4K/eI6/qxlHDXg/rNlFzQKpYj+srKyGwkHGQrrJCAynIj+lJSUNwr99jvO/o +; CAgBwmxSzUO9WziP7Ly8sI/tfX1/51dXU77go9/tvb2zn+ycnJ/m5ubo2I/eD+eF5EBaHh4gX+mnNM +; /tvb2zkaGI6I7/5/f3/+3t7eORP+Z2dnCv33O8oK/q6urv7+/v5/xYOILNI71Qol/uvr666I/qurqx +; U77QrCFf6xsbH++/v7j4gHCv3e/p1jKDXm/uCfXf6ln5gZCvGriP63t7cMjYj+hYWFGQr99lXIt4j+ +; 29vbJsUX/rm5uSQs0TvVrIj+urq6KomI/oKCgv5bW1s76wrGGf6GhoYNsIj+uLi4Lwr92v5xXkoLNe +; j+u2YR/oBfPwryGQccsYj+rq6u/mJiYgr99zvF/pmZmf74+PgmxY+I/ouLiwo7Vc871v6AgID+4eHh +; Of6/v7/+ZWVlO+sKyj4xOYOI/n5+fgr92P6PYTP+0WkA6wUs/mJcVQryGP7IyMg5/tnZ2f54eHgK/f +; g7wSD+ysrK/v///8b+y8vL/mlpaTvBVc471RX+qKioHyf+kZGR/l1dXTvqCs0oPzJ//qenpxUK/dQ6 +; /rNlFzXuKP5qXVAK8qSI/p+fn/7x8fEB/p2dnf5fX18K/fc7ACcmxZmIKBk7wizMO9a4iBII/tHR0f +; 5vb2876wrQuIj+1dXVOf7Q0NAjCv3S/odgOTXxBf6PYTMK876I/tjY2P78/Pw8/mtrawr99aOILv7+ +; /v4mxf7c3Nz+dnZ2O8RVyzvVooj+l5eX/u/v76iI/qOjowY76grTBv6pqakbk4g/GQr9z/6lZCI19P +; 60Zhf+Y11WGdIK3qmI/rCwsC4r/oyMjP5dXV0K/fIf/uLi4ibFCP6xsbH+X19fO8UsyjvVrogEOYKI +; /nx8fAo76QrWGf5/f3/+5OTkG/6/v7/+aWlpCv3M/nFeSv7CZws19p8v/oFgPxnfCtIe/uXl5Sr+t7 +; e3/mNjYwr98P6hoaEqpIjFKwAKO8YsyDvWD/7m5ua2iC7+YmJiO+kK2qmI/r6+vjmIiAAK/cr+lmIt +; Nfr+nmMoCRnlCsuviAQqN/59fX0ZCv3sKwMmxjH+ZmZmO8gsxzvVM/6vr689koj+i4uLGTvoCt0oBy +; eniP6urq4zCv3G/nFeSv67ZhE1/P67ZxH+cl5LGesKxSj+mJiY/u7u7qmI/qSkpBUK/er+jY2N/vPz +; 8ybFAf6VlZUoCjvILMU71r6I/tnZ2f79/f0t/mpqajvoCuGziCU5/tbW1h8K/cT+j2EzNf3BBf6PYj +; QZ8ArBuIgDOTT+cHBwCv3npoj+v7+//v///8b+19fX/nFxcQrCO8dVxDvVpIj+np6e/vLy8gH+nJyc +; /l5eXjvnCuQ3/qGhoS6ZiP6dnZ3+XV1dCv3AoeH+rGUcNf3E/rRmF/5rXlD+XFxc8jP+qKioEJmI/p +; KSkjcK/eT+fX19/ufn5ybFKg0GCsU7xSzDO9WyiP7Jycn+/f39/tnZ2f53d3c76ArmGb6I/t3d3Sr+ +; xsbGCQr8/nheRP7KaAY1/cYF/oFgPxnz/oGBgf7h4eE5/r+/v/5nZ2cK/eEZ/qqqqgiiiMWGiP5+fn +; 4KyDvEVcE71Qr+jY2N/urq6rCI/q6urv5hYWE75wrqJP61tbUqjYgaCvr+nWMoNf3KFv5jXVYZ8qyI +; /rq6uv75+fmLiCL+XV1dGQr93bWI/tjY2CbFVf69vb0CCso7wyzAO9WriP63t7f++vr6jIgACjvmCu +; 0o/omJiSutiBAgCvb+cV5K/rtmETX9zP67ZxH+cl5LGfIo/pGRkf7r6+sM/qurqzMZwgr92P6Wlpb+ +; 9/f3JsWRiAcZCs07wSw71f59fX3+39/fOf7CwsI+O+YK8a6I/sfHxzmAiP58fHwK9P6PYTM1/c+fL/ +; 6PYjQJny/ytIj+y8vL/vz8/A4fGcQK/dOpiB4mxv7Pz8/+a2trCtA71hX+paWl/vX19ZuI/pOTkyg7 +; 5grzN/6ampoBVf6kpKT+X19fCvCh8jA13pou/qZTAP7MZwA17TD+a15QGfIV/qKioiPA/pmZmf5fX1 +; 8Zxgr9ziL+7OzsJsUM/qGhoTcK0lXUtogl/v39/f7U1NQUO+YK97qIHTn+zc3NBQrsGcD+iGE5Nd/+ +; rVcA/nA4AKfx/pVLAP66XQA17Z8vARnzv4j+29vbOf7Gxsb+ampqGchV/cko/rOzsxcmxf7e3t49Ct +; U70Rn+lJSU/u7u7qqI/qampgY75Qr6Ff6srKz++vr6kogSGQrnGcL+pWQiNd/+lEoAHcCn8aXDorb+ +; sVkAHDXt/qxlHf5jXVYZ8qmIMv74+PiQiP6KioooGckK/cW5iP7f398mxRf+tLS0FQrWO9A+/r6+vj +; mFiP5+fn4KO+UK/Bki/ubm5gz+vLy8/mhoaAriGcT+cl5L/sJnCzXe/r9gAP58PgAdwSoewf6ZTQD+ +; yGQANe3+u2cR/oFgPxnz/ouLi/7n5+e0iP60tLQCGcsK/cH+nZ2dGybFjYj+iIiIGQrXO88i/uTk5L +; iI/rq6uv5kZGQ75Qr9wqqI/sHBwTmGiP6CgoIK3hnG/pZiLjXf/ppNAB3Dp/EewjcFNe2fLyP+Y11W +; GfKwiDH++/v7gYj+e3t7gYjNCvquiCUmxv7Hx8cNCtk7zaiI/qysrP739/eViP6NjY0ZO+UK/cSiiP +; 6RkZH+8PDwpYj+q6ur/mFhYQrZGcf+cl5L/rtnETXeEv6CQQAdwwD+uF0A/pBJAJtNw/6eUAA17jD+ +; cl5LGfKjiP6bm5sFpoj+oaGhFRnNVff+iYmJ/vHx8SbFECwoCto7zLuI/tbW1gj+zMzM/mxsbDvlCv +; 3ItYj+09PTORIQCtUZyf6PYjQ13/6nVAAdxP6gUAA1wJ5a/qNSAB7DNe+fL/6PYjQZ87qI/tTU1Dn+ +; zc3N/m5ubhnPCvIV/ry8vP7+/v4mxf7a2tr+c3NzCtxVyij+m5ubFKSI/p+fn/5eXl475H/9y6SI/q +; SkpD2XiP6ampooCtAZyqHh/qVkIjXenVv+jkcAHcMA/r9gADXD/rpdAP6MRgAewf7IZAA18DD+a15Q +; GfKniDr+9vb2GP6Pj4/+Xl5eGc8K77+I/uXl5SbFnYj+rKys/l9fXwrdVckr/sXFxQj+29vb/nl5eT +; vlf/3NGf58fHz+4ODguogx/mtrawrMGcwuCzXe/rldAP52OwAdwwQ1xgH+nlAAHsA0NfEF/oFgPxnz +; Iv7j4+O5iP68vLz+ZmZmGdAK7P6mpqb+/Pz8JsWIiP6BgYEK3zvIGv7o6OiziP6ysrL+YWFhO+QK/d +; GoiP65ubk5Pv6JiYkKyRnN/p5jKDXf/ppNAB3Do7X+s1oANcgc/qxWAP6MRgD+tVsANfMW/mNdVhny +; rYj+vb29/vr6+oiI/oCAgCgZ0Qrns4g/JsUX/sHBwf5kZGQK4DvGEf60tLQbDS0KO+QK/dMoGv7s7O +; yriP6zs7MRCsQZzv5yXkv+u2cR/tFpAN4S/nw+AB3D/ohEABQ1yzQONfQgLhnyooj+lJSUCT3+qKio +; JBnSCuT+k5OT/vb29ibFk4j+kJCQGQrhO8X+e3t7/tzc3P79/f0A/mhoaDvkCv3XOv7Ly8s5/tra2v +; 56enoKwH/Q/o9iNDXfDh3EDjXO/plNADX1BTj+Y11WGfK1iP7Pz885/tXV1f50dHQZ0wrgAv7Dw8Mm +; xv7T09MnCuNVwwb+oqKi/vT09CP+lpaWKDvkCv3ZN/6dnZ3+9fX1IzMGGc4J/qxlHTXenVv+iEQAHc +; P+gkEA/r9gADXP/p5QADX3/rRmF/5rXlAZ8iT+paWl/vPz852IPwYZ0wrd/oGBgf7q6uomxZuI/qSk +; pDcK5DvCJ/7Nzc0I/tbW1v50dHQ75Qr92xm7iP7a2tr++/v7/srKyv5vb28ZzP55X0X+ymgGNd7+rV +; cAHcT+mk0ANdEfNfgF/ohhORnz/n5+fv7d3d05/sTExBwZ1ArZGf6wsLD+/v7+JsWCiP57e3sK5lXA +; Cv6RkZH+7OzsrYj+qamp/mBgYDvkCv3bGcKliAX++/v7kIglGcr+nmMoNd/+lEoAHcM+/rldADXSDv +; 7IZAA1+f6lZCL+Y11WGfKriP62trYMjoj+h4eH/l1dXRnUCta3iP7c3NwmxRf+uLi4JArnO6yI/ru7 +; u/77+/uIiP6BgYEKO+QK/dp/xSj+hYWFPgz+ubm5/mdnZxnG/nJeS/7CZws13hL+fD4AHcP+lEoANd +; T+sVkApNQ1+v67ZxH+gWA/GfP+jY2N/ujo6Bv+sLCw/mNjYxnVCtP+m5ub/vn5+SbFGP6Li4sZVegE +; /uPj4zn+vr6+/mVlZTvkCv3ZGco+/sTExDmEiP6AgIAZxP6PYjT+0WkA3/6gUAAdxP6tVwA11f6+YA +; D+rFYANfufLzgJGfKxiP7Hx8c5/tra2v55eXkZ1QrQPikmxv7Ly8v+aWlpCur+9vb2l4j+kJCQKJ2I +; 5Ar92BnNN/6VlZX+8vLyooj+qKioJBnACf67ZxE13hL+gkEAHcMA/sVjADXWm03+o1IANf3+tGYXPB +; nyo4j+np6e/vHx8aOIN/5gYGAZ1QrN/oeHh/7v7+8mxZiI/p2dnf5dXV0K6/7Pz8/+bm5uO+UK/dcZ +; 0beI/tXV1Tn+0dHRMv6IYTk13w4dxP6gUAA12Tg1/cAF/o9iNBnzvIj+19fXOf7Kysr+bGxsGdYKyQ +; b+ubm5/v7+/ibF/tvb2/51dXWGiO0GO+QK/dcZ1BUN/vn5+f7ZmFc13p1b/o5HAB3DABI12v6ZTQA1 +; /cL+tGYX/mNdVp8v8qiI/q+vry46B/5eXl4Z1QrHvIj+4+PjJsUINhUK7jvkCv3WGdf+c19M/sRpDj +; Xe/rldAP52OwAdw/6aTQA13P6eUAD+yGQANf3CBf6BYD8Z8x7+5OTkKv65ubn+ZWVlGdYKxP6jo6Mq +; JsWKiP6EhIQK8DvjCv3VGdj+lmIuNd8EHcM+LzXd/rFZAA81/cT+nmMoCRnyroj+wMDAKoSI/n5+fh +; nXCsCxiP7U1NQmxv7ExMT+ZmZmCvE74Qr91RnY/nJeS/67ZxH+0WkA3hIAHcMAMzXe/rVbAKK2Nf3F +; IC4Z8jf+l5eX/u7u7qmI/qWlpSQZ1v6Pj4/+9PT0JsWUiP6UlJQoCvI74Ar91BnZODXf/qdUAB3EDj +; XgmR8ONf3Gny84GfO3iP7R0dE5/tLS0v5xcXEZ06aI/sHBwSbG/tfX1/5wcHAK9DvfCv3TGdmh4f6s +; ZR013p1b/ohEAB3DABI14Z5a/qNSADX9yP60Zhc8GfIz/qenpwGbiP6Tk5P+X19fGdD+f39//ujo6L +; eIxZuIPgYK9TveCv3SGdr+eV9F/spoBjXe/q1XAB3E/ppNADXkHzX9yQX+iGE5GfP+gICA/uDg4Dn+ +; wcHB/mdnZxnO/qysrAgmxRH+fX19Cvc73Ar90hnb/p5jKP7RaQDf/pRKAB3Do7X+s1oANeU4Nf3L/q +; VkIv5jXVYZ8qyIDP75+fmMiP6EhIT+XV1dGcq1iP7Z2dkmxVX+vLy8/mNjYwr4O9sK/dEZ2/5yXkv+ +; wmcLNd7+v2AA/nw+AB3DIf7LZgA15jj+yGQANf3L/rtnER4Z8ij+kJCQ/urq6hv+ra2tMxnI/peXl/ +; 739/cmxTYHKBnACvc72gr90Rnb/o9iNDXf/qBQAB3E/qdUADXo/rFZAKTUNf3Mny84CRnys4j+ysrK +; /vz8/P7Y2Nj+d3d3GcWqiP7IyMgmxv7Ozs7+bGxsGcMK9jvZCv3QGduh4TA13hIA/nA4AMMA/sVjAD +; XpNJ5aNf3OMDwZ8qSI/qGhof7y8vJ//pqamv5fX18Zwv6FhYX+7e3tJsWZiP6fn583GcYK9FXYCv3P +; Gdz+iGE5Nd8OHcT+mk0ANeuZHw41/c8FOBnzvoj+2traOS3+a2trGaOI/rW1tRcmxf7d3d3+eHh4Gc +; kK81XWCv3PGd3+pWQiNd4U/pRKAB3DPv65XQA17f6eUAA1/dEwCRnyqYj+srKy/vj4+JGI/p+fn/7g +; 4OAmxUD+s7Oz/mFhYRnLCvI71Qr9zxnc/nJeS/7CZws13/6tVwA+HcL+lEoANe8fNf3SBf6BYD8Z8/ +; 6Kior+9PT0JsaMiP6Hh4cZzgrxO9QK/c4Z3f6WYi414hL+gkEAHcAvNfAfNf3TBRYJGfCviP7R0dEm +; xv7Hx8cNGdAK8DvTCv3NGd0u/rtnETXkFCUzNfH+o1IA/shkADX91CAuGe7+jIyM/vLy8ibFloj+mJ +; iYNxnTCu470gr9zRnd/o9iNDX93v6xWQCk1DX91Z8vOBnrJP69vb0mxhf+19fX/nBwcBnVCu070Qr9 +; zBndCf6lZCI1/d8PmS81/dcwPBno/nx8fP7m5uYmxSr+ra2tVf729vaXiP6RkZE3GdQK7DvQCv3MGd +; 0u/sJnCzX94AH+p1QANf3Yny8RGeV//qioqP79/f0mxS/+gICAGcD+g4OD/uLi4rqI/r6+vv5mZmYZ +; 1ArrO84K/cwZ3hb+0WkA/eP+nlAANeqeWjXrFv5jXVYZ4jb+19fXJsVV/r+/v/5lZWUZwqyI/ry8vP +; 76+vqJiP6BgYEoGdMK6jvNCv3MGd7+rWARNf3k/plNADXoFCH+mEwA/shkADXr/qldERnh/pWVlf72 +; 9vapiMWSiP6QkJAoGcQo/pOTk/7s7Ow9/qmpqSQZ0wrpO8wK/csZ4P6JSAb+sVkA/sxnADX94v6eUA +; ABNeb+rVcA/nA4AMD+hkMAo7X+tVsANen+p1QAHRnfqIj+xcXFJsYD/m5ubhnItIj+zc3N/vz8/D/+ +; dHR0GdMK6DvLCv3LGeH+hEUGn8P+mU0A/shkADX94f6xWQABNeX+lEoAHcEWHsAfHDXlnVshHcAZ3v +; 6Dg4P+6+vrJsUM/qOjo/5fX18ZyiQC/vPz80D+l5eXBhnSCuc7ygr9yhnjBB7ANwU1/eAborY14/6/ +; YAD+fD4AHcIWHsGlwzQ14y8dwhncooj+srKyFybFFf57e3sZzv59fX3+3NzcOQD+aWlpk4jSCuY7yQ +; r9yhnkBB7C/plNAP7MZwA1/d4P/qdUADXi/qBQAB3E/opGAB7Corb+rFYAHDXg/pRKAB3DGdu4iP7e +; 3t4mxRf+t7e3/mJiYhnQqoj+tbW1DI+I/oiIiCgZ0QrlO8gK/coZ5QQewxI0Nf3dm03+o1IANeD+v2 +; AA/oJBAB3DAP7FYwBG/p5QAB7Cp/EcNd4S/nw+AB3EGdr+nJycDCbFCf6Kior+XV1dVdMpDbOI/rGx +; sf5jY2MZ0ArlO8cK/ckZ5wQexKK2Dhw1/dz+nlAANd8OHcQlNcEcDjceDjXfJR3GGditiAcmxv7Kys +; ocGdaxiP7GxsY5/tvb2/56enoZ0FXkO8YK/ckZ6AQexv6ZTQD+w2IANf3bHzXdnVsjHcM+EjXE/r5g +; AMA13hIAHccZ1y3+8PDwJsWXiP6bm5v+Xl5eGdgG/p2dnQWliP6fn5/+YGBgGc9V4zvFCv3JGekEHs +; eitv6xWQA1/do4/shkADXbEv58PgAdwyM15w4dyRnVFf66uroXJsX+29vb/nV1dRncu4g/ORoYGc9V +; 4v58fHwKO8IK/cgZ6wQeyf6ZTQD+yGQANf3YBTQ12v6aTQAdxP6tVwA15hQjHcoZ1L2I/uTk5CbFnY +; j+rq6uJBneqIj+ra2t/vb29gn+jo6O/l5eXhnOCuE9/qSkpDc7wAr9yBns/oRFBh7KorY0Nf3X/rVb +; AMA12BIAHcMAMzXmEv58PgAdyxnT/qWlpSomxQ0iGeL+hoaG/uTk5Dn+urq6/mVlZRnOCuD+09PTOR +; L+cHBwCv3IGe0EHswO/sxnADX91ZkfDjXXDh3EJTXn/ppNAB3NGdGyiP7V1dUmxRf+w8PD/mZmZhnk +; rYj+v7+/KoWI/n9/f/5dXV0ZzArgpYj+q6ur/vX19ZuI/pGRkSgK/cQZ7/6ERQYezSv+w2IANf3Unl +; r+nlAANdUU/o5HAB3DAP6/YAA15hIAHc4Z0DT+9PT0q4jFMv6Tk5P+XV1dGeaiiP6Wlpb+7e3tqoj+ +; pqamJBnMCt87wP6CgoL+4eHhOf7BwcEgCv3CGfD+hEUGHs6itv6xWQAcNf3THzXU/rldAD4dw/6aTQ +; A15w4d0BnOp4j+wsLCJsb+1tbW/nBwcBnqtoj+0NDQOf7U1NQjGcwK3jsKwK2I/ru7u/75+fmNiP6C +; goIZCv0Z8f6ERQYe0P6ZTQD+yGQANf3SHzXT/ppNAB3DPv6zWgA15hT+jkcAHdEZzf6AgID+6enpJs +; WbiP6mpqb+X19fGeymiC/+9PT0m4j+lJSUBhnLCt07CsL+kpKSCa+I/q2trQYK+xny/oRFBh7RN/61 +; WwD+0WkA/dE4/shkADXQ/r9gAP58PgAdw/6IRAAUNeYxPh3SGcso/q6urhcmxYSICBnw/n9/f/7f39 +; 85/sLCwg0ZygrjBf7MzMw5/tjY2P51dXUK+RnzBB7TOP7MZwA1/c/+sVkApNQ1z/6gUAD+cDgAxA41 +; 5/6aTQAd1BnKI/7a2tr+////xRf+u7u7/mNjYxnyq4j+t7e3DI2I/oWFhSgZyQrjpIj+o6OjMqKI/p +; qamv5eXl4K9Rn1/oRFBh7UKzQ1/c7+vmAAGzXNFP6CQQAdwwD+xWMANeYSHx3VGcn+mZmZPSbFj4j+ +; jY2NKBn0KP6Pj4/+6enpsYj+rq6u/mJiYhnJCuT+e3t7/tzc3Dn+yMjI/mlpaQrzf/YEHtWitgX+zG +; cANf3MmR8ONcz+rVcAHcT+mk0ANeclHdcZxz4LJsb+zc3N/mtraxn4soj+ycnJOf7Z2dn+eHh4GcgK +; 5SD+tbW1/vf395OI/omJif5dXV0K8Bn3/oRFBh7X/plNAP7IZAA1/cz+mU0ANcv+lEoAHcOjtTE15p +; ouAB3YGcb+hoaG/u7u7ibFmYj+n5+f/l5eXhn6Ff6goKD+8vLyMv6cnJz+YGBgnIjHCub+i4uL/ujo +; 6LOI/ra2tv5hYWEK7hn4BB7YorYFNf3LATXJEv58PgAdw/6USgA15y8d2hnEo4j+t7e3FybF/t3d3f +; 54eHgZ/cC9iCw5LToZxwrmsYj+xsbGKoOI/np6egrsGfkEHtoBHDX9yf6eUAAcNcf+mk0AHcQvNecj +; HdsZw7uI/uLi4ibFQP6ysrIkGf3CIP6wsLD+9/f3K/6MjIz+Xl5eGcUK56KI/p2dnf7w8PAu/qGhoT +; cK6Rn6/oRFBh7borY0Nf3IDv7IZAA1xRIAHcMA/sVjADXmEv58PgAd3BnCJP76+vomxSv+h4eH/l1d +; XRn9xf6IiIj+5ubmKv63t7f+Y2NjGcUK6LuIPzn+0NDQ/m5ubgrnGfsEn8PdDhw1/cb+sVkANDXEDh +; 3E/qBQADXn/ppNAB3eGcA6/tLS0ibGD/5oaGgZ/cg6/sHBwSqDiP59fX0ZxArpp4gY/vX19ZmIBygK +; 5Bn8/oRFBh7ep/H+w2IANf3F/r5gAJtNNcIU/o5HAB3DABI15hIAHd8Z/o2NjTImxZWI/paWlv5eXl +; 4Z/co3/piYmDaoiP6kpKQVGcMK6v6EhIT+4+PjOf6+vr4gCuIZ/QQe36K2BRw1/cObTf6sVgA1wf65 +; XQA+nVvD/ppNADXnDh3h/r+/v/7////G/tjY2P5ycnIZ/c64iP7T09M5/tDQ0P5wcHAZwgrrrogX/v +; n5+YuI/oCAgBkK3xn9wP6ERQYe4f6ZTQD+yGQApcP9w/6wWAA1wP6aTQD+cDgAwz7+rVcANeYU/o5H +; AB3iJsUq/qmpqRUZ/dCniBwQmYj+kZGR/l5eXhnBCusZ/pWVlf7t7e2tiBwGCt0Z/cH+hEUGHuKitv +; 61WwA1/cIO/r9gAP58PgAdwwD+xWMANeYxPh3jJsSGiP5/f38Z/dT+goKC/uHh4Tn+v7+//mZmZhnA +; Cu0j/s/Pzzn+1tbW/nNzcwrbGf3BO/6MTQ4e5P6jUgD+zGcANf3A/qRSAB3EDjXn/ppNAB3l/v///8 +; JV/r6+vv5kZGQZ/dYN/rq6uv76+vqKiBP+XV1dGQrtFf6np6f+9PT0Vf6Xl5f+Xl5eCtgZ/cCuiP7I +; yMj+/f39/o5QEB7lp/H+ul0ANf3+nU4AHcIA/r9gADXmEv58PgAd5ibBkYj+j4+PKBn92Cj+kZGR/u +; vr6wz+q6ur/mFhYQrv/n5+fv7e3t45/sXFxf5nZ2cK1hn9wP6IiIj+6+vrDP63t7f+hkcIHuaitv6x +; WQAcNfv+q1YAHcEENef+p1QAHegmwP7R0dH+bW1tGf3ctIj+zMzMOf7X19f+dXV1Cu8v/ri4uP74+P +; iQiP6Hh4cZCtMZ/aaI/ra2tio6/oyMjBn+hEUGn8Po/plNAP7IZAA1+v6vWAAdo7X+s1oANeYUIR3p +; DP6ioqIGGf3epYgz/vLy8sD+mJiYBgrvB/7q6uoq/rKyshUK0Rn9voj+3t7eOf7JyckYGcAEHumitv +; 6xWQA1+TH+jkcAFDXm/q1XAP5wOADVpdMd0xsZ/eK/iP7b29s5/sbGxv5qamqRiO8YPP77+/uBiP55 +; eXkKz3/8KP6jo6P+9/f3nYj+nZ2d/l1dXRnBBB7r/plNABw1951bNef+lEoAHdU7/oBAAB3TGf3kqY +; j+s7OzPQ3+iYmJKAruo4j+oKCg/vHx8aWI/p+fnzcKzBn8soj+zs7OCP7Y2Nj+eHh4GcMEHuyitv66 +; XQD+0WkA/eAS/nw+AJou1P6AQAAxmj4d1CgZ/eUa/ufn5yr+s7Oz/mJiYgrvPf7Y2Ng5/s3NzQkKyh +; n8Fv7w8PCoiP6wsLAkGcQEHu7+p1QAHKK2/d3+oFAA/nA4ANUB/sNiAMD+gEAAHdT+XV1dwBn95Qn+ +; xMTEOYCIGwrvqIj+sbGx/vb29peIKSgKx3/7IP69vb05ioj+hISEGcYEHu+n8f7DYgCn8f3aEv6CQQ +; Ad1P6AQAAQKMAQHdX+XV1dwRn95aKI/pubm/7w8PAf/qGhof5fX1+ciO/+h4eHLzn+u7u7/mRkZArF +; Gfv+f39//uTk5LeI/sPDw/5paWkZxwSfw/Citv6xWQD+0WkA/dgOHdUj/sNiAML+ikUAHdX+XV1dwh +; n95ArAu4j+1dXVOTgnCu+viP7CwsL++vr6iIj+fX19GQrCGfoG/qurq/75+fmXiP6VlZUoGcgEHvIB +; /shkADX91J1b/o5HAB3Uo6X+pFIA/sNiAMIQHdb+XV1dwxn94wrBqIj+rKys/vb29paIFigK7hn+mZ +; mZ/u7u7j3+pqamBgrBGfm2iD85AyMZygQe8zc0Nf3SEv52OwAd1Tv+w2IAw/6KRQAd1v5dXV3EGf3j +; CsIx/uPj4zn+u7u7IArvuYgDOf7U1NT+cXFxChn5/peXl/7z8/Mu/qmpqQYZywQe9f6nVAD+zGcANf +; 3P/ppNAB3WpdP+w2IAw5s9Hdf+XV1dxRn94grDrYj+vb29G4eI/oCAgBkK7qaI/qqqqv709PSdiP6T +; k5P+Xl5eGfYN/sXFxTmEiCYZzf6ERQYe9qfx/sNiAKfx/cz+v2AA/oJBAB3XOyjDLB3X/l1dXcYZ/e +; EKxCj+lJSUGD3+p6en/mBgYArv/oGBgf7h4eE5E/5nZ2cZ9P6FhYX+6enpGyogGc7+hEUGHveitv6x +; WQD+zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV3HGf3hCsW2iP7Pz885/tXV1f5zc3MK7hkNKgyNiAAoGf +; CliP6zs7MbCf6Pj48Z0AQe+f6ZTQD+yGQANf3GnVv+jkcAHdn+pFIA/sNiAMP+j0gAHdj+XV1dyBn9 +; 4FXGJP6lpaX+8/PznYj+lZWV/l5eXgrsGcH+kpKS/uvr67CI/q+vryQZ7ruI/tzc3Dn+y8vL/m5ubo +; 6I0QQe+qK2/rVbADX9xP6tVwAd2qOl/sNiAMMxHdn+XV1dyRn93wrIF/7d3d05Ig0K6hnDtIgaOf7Z +; 2dkuGet//p+fnx9V/qGhoSgZ0gQe/P6jUgAcNf3B/pRKAB3b/qRSAP7DYgDD/o9IAB3Z/l1dXcoZ/d +; 9VyKuI/ra2tgyNiP6GhoYZCucZxRX+o6Oj/vLy8hD+nJyc/l9fX52I6LCI/szMzP79/f3+29vb/np6 +; ehnUBB79KzQ1/BL+fD4AHdujpf7DYgDDnWsd2v5dXV3LGf3eVckZ/o6Ojv7o6OiyiDYK5hnIv4j+29 +; vbOf7Jycn+a2trGeb+jY2N/u7u7gz+s7Oz/mJiYhnVBB79wDf+sVkAHDX5JR3d/qRSAP7DYgDD/o9I +; AB3a/l1dXcsZ/d5Vy7KI/sjIyDkK5hnJqYj+tLS0PZOI/oqKiv5eXl4Z4qeI/rq6ujkN/oeHhxnXBJ +; /D/cL+mU0A/sNiADX2Ev6CQQAd3aOlKMOdax3b/l1dXcwZ/d1VzAb+np6eCuUZzAv+5+fntIj+uLi4 +; Ahng/n19ff7h4eE5/sXFxf5qamoZ2AQe/cOitgU19A4d3/6kUgD+w2IAw/6PSAAd2/5dXV3NGf3dVf +; MZzgkAKoSI/nx8fCgZ3KKI/qenpwwj/piYmCgZ2QQe/cUBHDXwFP6USgD+cDgA36Ol/sNiAMOdax3c +; /l1dXc0Z/d1V8hnQN/6cnJz+8PDwp4j+o6Oj/l9fX52I2rSI/tPT0/79/f3+1dXV/nR0dBnbBB79xq +; K2NDXu/r9gAB8d4P6kUgD+w2IAw/6PSAAd3P5dXV3OGf3cVfIZ0rqIMP78/Pz+0tLSBRnYIf7y8vIu +; /qysrBUZ3AQe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd3+XV1dzxn921XxGdSniP6tra0Qmoj+kJCQ/l +; 5eXhnUq4gTOYaI/oGBgRne/oRFBh79yafx/sNiADXoEv6CQQAd4v6kUgAow/6USgAd3f5dXV3PGf3b +; CvAZ1/6EhIT+4+PjOf7AwMD+ZmZmGdL+goKC/ufn5yr+vr6+/mdnZxnfBB79yqK2/rFZAByituUOHe +; Ojpf7DYgDDnWsd3v5dXV3QGf3bVe8Z2K2IF/76+vqLiBMoGc4V/rCwsBsn/pGRkSgZ4AQe/cz+mU0A +; /shkADXiFP6ORwAd5P6fUAD+w2IAwyMd3v5dXV3QGf3bCu4Z2ij+lZWV/uzs7Bv+q6urFRnMuYj+2t +; raOf7Ozs7+cHBwGeIEHtw3Huw3/rVbADXg/rldAP52OwAd5KOl/sNiAMOdax3f/l1dXdEZ/dpV7Rnd +; togHOf7X19cQGcr+m5ub/vX19cD+pKSk/l5eXhnjBB7Yorb+p1QA/rpdADWZH/6eUAAe7f6jUgD+zG +; cANd3+mk0AHeYiKMP+mU0AHd/+XV1d0Rn92lXtGd4V/qampv7z8/N//pmZmf5eXl4Zxq6IPDmAiP57 +; e3uBiOQo/oRGBh7Vp/H+rFYA/sNiADXDHA7+jEYAHuwrNDXaEv58PgAd5jsow51r/no9AB3f/l1dXd +; IZ/dlV7Bnh/n5+fv7d3d05/sbGxv5paWkZxAv+7OzsDP62trb+Y2NjGeR/wP6ERgYe1Tc0Ncb+vmAA +; /pBJAB7sN/6xWQD+zGcANdcOHegi/sNiAMP+mU0AHeD+XV1d0hn92QrrGeOqiP63t7f++Pj4kYj+iY +; mJ/l5eXhnApogu/vv7+4+ICxnlKMEJHtcO/sxnADXFHA4e7QH+yGQANdSdW/6IRAAd6Dv+w2IAw51r +; /no9AB3g/l1dXdMZ/dlV6hnl/o6Ojv7p6ekq/rS0tDO5iP7f3985Lf5sbGwZ5SjC/oRGBh7Yp/H+w2 +; IANcYP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3TGf3ZCukZ57KILf77+/uLiD0y/pub +; mygZ5ijCCR7ZNwX+zGcANcUc/p5QAB7tARw1z/6USgAd6qOl/sNiAMOdaxQd4f5dXV3TGf3ZVekZ6L +; WI/unp6baIVS4VGeYowwke2wH+yGQANcYFNx7sNzQ1zP6/YAD+fD4AHev+mU0A/sNiAMP+pFIAHeL+ +; XV1d1Bn92AroGej+kZGR/vDw8D3+uLi4/tnZ2Tn+z8/PJxnkKMQJHtw3GzXGmR8rHu0O/sxnADXJ/q +; BQAB3so6UoxP56PQAd4v5dXV3UGf3YCugZ5qmI/r+/vzmJiP6Dg4MZqIj+sLCw/vb29peIB/5eXl4Z +; 4SjFCR7eOBw1xRwOHu0r/sNiADXGEgAd7QEow/6kUgAd4/5dXV3UGf3YCucZ5v6AgIAgKv7BwcH+aG +; hoGcL+h4eHIDn+vb29/mVlZRnfKMYJntTfK/66XQD+0WkAxg/+kEkAHuyitv6xWQA1xA4d7qOlMaXT +; wxQd4/5dXV3VGf3XCuYZ5aOI/q2trQyXiP6UlJQoGcSviAT++vr6iYj+f39/KBndKMYJHuA3/rFZAB +; w1xTT+ikYAHu0B/shkADXAnVv+jkcAHe/+mU0A/sNiAMP+pFIAHeT+XV1d1Rn91wrmGeS3iP7X19f+ +; /f39/tHR0f5ycnIZxyj+mJiY/u7u7gz+qKioFRnbKMcJHuIB/shkADXD/qdUAP5wOAD+hEIAHu43/r +; pdAFo+He+jpTGl08P+ej0AHeT+XV1d1Rn91wrlGeT+mJiY/vT09KOI/qenpwYZyjL+0dHROf7V1dUy +; GdkoyAke4zcFNcCdW/6IRAD+cDgAwAse8B3oRh3G/o9IAP7DYgDD/qRSAB3l/l1dXdYZ/dZV5RnirY +; j+xsbGOYOI/n19fRnNpYj+qampAUD+lZWV/l5eXhnWKMkJHuX+mU0A/q5XAB3CCx7wHeidSxIdxTGl +; 08P+ej0AHeX+XV1d1hn91grkGeL+hoaG/urq6hv+ubm5ERnQ/oGBgQa9iP7ExMT+aGhoGdUoyQke5v +; 52OwAdwgse8B3pmR+hpx3D/o9IAP7DYgDD/qlVAB3m/l1dXdZV/dZV5BngJP60tLQqkYj+jY2NGdMN +; /rq6uv75+fmOiA8oGdIoyv6ERgae1Ob+djsAHcILHvAd6ptNnUsSHcH+uV0ApdPDFB3m/l1dXdYZ/d +; YK4xngvIj+3d3dOf7Kysr+bW1tGdb+kZGR/uvr6yr+sbGxJBnQf8sJHuY+HcL+hEIAHvAd6xIQpcMd +; /oBAAP7DYgDD/qlVAB3n/l1dXdZV/dYK4xneKP6hoaEunYj+n5+fKBnYs4gaKv7a2to9Gc8oywke5j +; 4dwgse8B3tBBL+pFIA/sNiAMM1Hef+XV1d1hn91grjGd0Y/s3Nzf79/f0sPRnbo4j+oqKiIx/+np6e +; BhnMKMwJHuY+HcILHvAd76Ol/qlVAP7DYgDBGR3o/l1dXddV/dUK4hndB/7v7+8M/rKysv5hYWEZ3r +; 6I/tra2jka/mxsbBnKKM3+hEYGHuY+HcILHvAd8f6USgD+vl8Ao6U1Hej+XV1d1xn91VXiGdsR/ru7 +; uzmLiP6FhYUZ4SD+s7OzLpWI/oyMjP5eXl4ZyCjN/oRGBh7m/nY7AB3CCx7LpcMe4h3yNf6kUgAd6S +; jXGf3VCsqoiArUGdv+fX19/uLi4rmI/sTExP5paWkZ5Dz+5+fnOf65ubkRGcYozgke5v52OwAdwgse +; yv6sVgD+0WkA/r5gAP6MRgAe4B394SjXGf3VCsn+fn5+MwrUGdmiiP6pqan++fn5mIj+lpaWKBnmsI +; gxKoWICCgZxCjO/oRGBh7mPh3CCx7IEv7DYgA1wZ5a/p5QAB7fHf3h/l1dXdcZ/dUKxyj+q6urLpuI +; CtMZ2bWI/tTU1P79/f3+09PT/nNzcxnpN/6bm5s2qYj+paWl/mBgYBnCKM8JHuY+HcILHsf+o1IANc +; X+sVkA/oxGAB7dHf3hKNcZ/dVVxhT+1dXVKv7W1tYyCtMZ2P6VlZX+8/PzLv6qqqoGGey5iCEIIRQZ +; wSjPCR7mPh3CCx7HBTXGmR83Htwd/eH+XV1d1xn91QrF/paWliM9/qysrP5eXl4K1BnWq4gxOTMmGe +; +miAn+9fX1m4j+kpKSNyjQ/oRGBh7m/nY7AB3CCx7I/plNABw1xf6xWQAe3B394SjXGf3VCsOsiP7F +; xcUqiIj+gICACtUZ1iL+6Ojosoj+vLy8/mVlZRnyIv7i4uK6iP7BwcE+KM8JHub+djsAHcILHsmitv +; 66XQD+0WkAxBz+lUsAHtsd/eEo1xn91QrC/oSEhA0b/r+/vyAK1hnUFf6xsbEbk4j+kJCQGfWtiP69 +; vb0MjIgi/l5eXijNCR7mPh3CCx7LDv7MZwA1wzQe2x3porYd8yjXGf3VCsAG/rKysgyWiP6RkZEZCt +; YZ1LmI/tra2jn+zc3N/m9vbxn3KP6UlJT+7OzsG/6tra3+YmJiKMz+hEYGHuY+HcILHswr/sNiADXD +; Hx7aHemn8f6oVAD+eT0AHfH+XV1d1xn91ArAvIj+29vb/vv7+xY2jIjYGdP+nZ2d/vb29lX+oqKi/l +; 1dXRn4KMC1iDg5/tjY2P52dnYoywke5j6dW8ILHs2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3wKNcZ +; /dX+n5+fEH/+pKSkGQrZGdGviP7Kysr+/f39/tzc3P56enqCiPoowST+paWlMn/+m5ubFSjJCR7mPh +; 3C/oRCAB7P/plNAP7IZAA1wSce2R3opcP+vF4APMH+mk0A/nU6AB3uKNcZ/dOwiBoqg4j+e3t7gIja +; GdH+ioqK/u3t7Qz+tbW1/mJiYhn7KMP+fX19/tzc3Dkt/mpqaijI/oRGBh7mPh3C/oRCAB7Qorb+ul +; 0ANcAc/pBJAB7YHej+oFAAP8E8wDcd7ijXVf3SKRgM/re3tzMK2xnPp4j+uLi4/vv7+46I/omJiRn8 +; KMWqiP62trb++Pj4koj+ioqK/l5eXijGCR7m/nY7AB3C/oRCAB7SDv7MZwA1NB7YHec2/r1fAMA/wZ +; 5aHe8o1xn90DP+urq6/vr6+is8Ct0Zzr+I/uDg4Dn+x8fHOhn9KMf+jY2N/unp6bKIHwIoxQme1OY+ +; HcILHtOn8f7DYgA1/plNAB7XHef+oVAAB8E/wP6RSQAd7/5dXV3XGf3PCP7g4OAq/snJyToK3RnNKP +; 6mpqY9moj+mpqaKBn9wCjIsYj+x8fHKoOI/nt7eyjECZ7U5j4dwgse1KK2BTQ3HtYd5jb+vl8AwQfA +; nWs2HdX+lEoA/stmACMd1SjWGf3OKP6np6f+9/f3nYj+m5ubKBlV3RnMs4j+0dHR/v39/f7W1tb+dX +; V1Gf3BKMqiiP6enp7+8fHxLv6ioqL+YGBgKMIJHub+djsAHcILHtb+mU0Ao6Ue1h3m/qNSAP7AYQBS +; WsAHJR3V/q1XADXB/rldAD4d0yjWGf3NtIj+0tLS/vv7+/7Y2NgQGcBV3hnL/pKSkhQ9/q2trQYZ/c +; IozLuI/tfX1zn+0NDQBSjBCR7mPp1bwgse8B3lo6X+wWEAwUZWwP56PQCbPdP+gkEA/sVjADXDnVv+ +; jkcAHdIo1hn9zCH+8fHxp4j+sLCwBhnBVd4ZyaqI/sDAwDmIiP6BgYEZ/cQozaeI/q+vr/729vaYiP +; 6Pj48GKAke5j4dwgse8B3lOP7BYQDCVv6hUQD+cDgA0/6gUAD+0WkAxp1bAB3RKNYZ/cov/sLCwhuM +; iP6CgoIZwwrdGcn+gICALyr+wMDA/mdnZxn9xCjQ/oaGhv7k5OQ5/r6+vv5mZmYJHub+djsAHcILHv +; Ad5Dv+wWEAxP5/QAD+cDgA0QAS/tFpAMYxPh3SKNYZ/ckT/ubm5ir+wsLC/mdnZxnECt0Zxwb+rq6u +; G5WIAxn9xijROv7AwMAbEf6FRgce5v52OwAdwgse8B3k/p5PAP7BYQDD/q1XAP5wOADR/ppNAP7RaQ +; DHBB3UBijVGf3HBv6vr68Ml4ghGcUK3hnGt4j+2NjYCP7Pz8/+cHBwGf3HKNJ//peXl/7t7e3+rH5P +; HuY+nVvCCx7wHeM7/sFhAMT+f0AA/nA4AM8+LzXGEv58PgAd1P6LXTD+xsbG/m1tbSjTGf3HEP7Y2N +; gq/tLS0v5xcXEZxgreGcX+mZmZ/vT09KKI/qampv5eXl4Z/cgo1LaI/snFwP66lG7+jlAQHuQ+nVvC +; Cx7wHeP+mEwA/sFhAMP+slkA/nA4ANAC/stmADXF/qdUAB3V/mhIKf63t7f+/Pz8jIj+jIyMKNIZ/c +; b+m5ubAaOI/qenpygZx1XdGcStiP7Hx8f+/f39gYj+fHx8Gf3JKNaliP6oqKj+1sW0/pZbIP6HRADj +; /nY7AB3CCx7wHeI7/sFhAMT+iUUA/nA4ANEAMTXCFP6IRAAd1KJc/mJURih//ouLi/7r6+s9/rW1tf +; 5mZmYo0Bn9xK6I/snJySqFiP59fX0ZyQrdGcP+h4eHOhv+uLi4/mNjYxn9yijY/oCAgP7X08/+upVu +; /odEAOI+HcILHvAd4v6YTAD+wWEAwyb+cDgA0z7+p1QAFKO1/q1XAB3V/mlGIyjDrog8/vz8/P7b29 +; sIKM8Z/cP+iYmJ/uvr6xv+urq6/mNjYxnJVd4ZwSQQKjr+jIyMGf3MKNmriP65ubn+28q5Hx7gPh3C +; Cx7wHeE7JqXTw/6ORwD+cDgA1SEjHdX+ZE86KMWjiP6cnJz+9fX1QP6kpKQkKMxV/cIz/re3t/76+v +; qSiP6NjY0ZywreGcC9iP7e3t45/snJyf5sbGwZ/c0o2/6QkJD+49/b/qx+Tx7fPh3CCx7wHeH+jkcA +; /sFhAMOda/5wOADt/mw/Ef5fWFEoyLqI/tnZ2Tn+zMzM/nFxcSjLGf3Bvog3Kv7Ly8v+bW1tGcwK3i +; j+oqKi/vf3952I/p2dnRn9z3/csog8/sirjR8e3T4dwgse8B3gOyal08MC/nA4AOz+ZkouKMskJyqR +; iP6Tk5Moyhn9KBEuEP6fn58oGc0K3Rj+zs7O/v39/Sz+d3d3Gf3PKN4V/qGhof7bz8L+nWcwHtw+Hc +; ILHvAd4AL+wWEAw51r/nA4AOsr/mFWTCjNf/6FhYX+5ubms4j+vLy8HCjHGf02FiqAiP54eHgZzlXd +; /o6Ojv7v7++piP6xsbH+YWFhGf3QKOC9iP7Szsr+upVuHts+HcILHvAd4Cal08MC/nA4AOo0KNEN/s +; LCwv78/PyFiP6Dg4Moxhn8/pGRkTYM/rOzsxUZzwrbqIj+vLy8/vz8/C/+hISEGf3SKOGoiP6ysrL+ +; 2ci3/pZbIP6HRADZPh3B/pRKAP6nVAD+jEYAHu8d3/6ORwD+wWEAwz/+cDgA6aJc/mNRQCjToogS/v +; Hx8RD+q6ur/mNjYyjEGfog/r+/v/76+vqNiAAZ0Qra/n5+fv7j4+Mq/sPDww0Z/dMo4/6JiYn+39vX +; /qx+Tx7Y/nY7AB3A/q1XADXA/r5gAP6QSQAe7h3f/rdcAKXTw/6TSgD+cDgA6P5rQRf+XltXKNa1iP +; 7U1NQI/tPT0/51dXUowhn6/oCAgP7k5OQq/sXFxf5paWkZ0QrZo4j+qqqqDJeIMBn91Sjkr4gi/seq +; jP6OUBAe1j6m4v7FYwCm4sKeWv6nVAAe7R3e/o5HAP7BYQDDP/5wOADo/mVNNCjZFf6mpqY9Nv6amp +; o3KMAZ+Df+rKysPZqI/peXlxnTCti2iP7V1dUI/tLS0v5ycnIKwBn91CjlNzv+2My//p1nLx7V/p1P +; ADXGD/6MRgAe6x3eJqXTw/6YTAD+cDgA5v5sPxEaKNt//n5+fiQbIv5sbGwZ+LeI/tbW1iohMhnUCt +; f+lpaW/vPz86SI/qmpqTcKwBn91SjnEP7MyMP+upVu/o5PEB7Torb+sVkA/sxnADXFHP6eUAAe6h3d +; Av7BYQDDnWv+ej0Amz3l/mhIKSjfqIj+u7u7/vz8/IqIPBn2/piYmDI9/qurq/5eXl4Z1ArWrIj+xc +; XFOSQXCsIZ/dUo6KaIOv7XxrX+llsg/odEANT+mU0A/shkADXGBf6MRgAe6P5wOADdJqXTw/6jUgD+ +; cDgA5KJc/mJURijhBv6NjY3+7OzsLv6ysrIgGfKtiP7Gxsb++/v7h4j+f39/GdYK1f6Dg4P+6enpG/ +; 67u7v+ZGRkCsMZ/dUo6iL+2tXR/rOJXx7UN/61WwA1xpkf/pVLAB7nHdz+iUUA/sFhAMT+ej0Amz3j +; /mlGI/5dXV3lsIj+zc3NOSz+eXl5GfD+hoaG/ujo6LKI/r29vSAZ1wrTBv6ysrIbk4gHGQrEGf3VKO +; usiP68vLz+zbSb/o5QEB7U/p5QAP7MZwA1xZkfHuf+cDgA3P63XACl08P+o1IA/nA4AOP+ZE86/l1d +; XeUZwAb+n5+f/vb29pyI/qGhof5fX18Z7KWI/rS0tP75+fmViCUZ2ArTu4j+29vb/vz8/P7Ly8v+bm +; 5ujYjGGf3VKO3+k5OT/tXJvP6sfk8e1KXD/rpdADXEmR8e5x3bKf7BYQDEFJs94f5sPxH+X1hRpR/m +; GcK9iP7b29s5/srKyv5vb28Z6ryI/tzc3L+I/s7OzieOiNlV0/729vZV/qCgoBkKxxn91SjutIj+xc +; G9/rqUbv6OUBAe0zf+rFYAHDXCmR8e5x3b/rJZAKjwwzj+cDgA4f5mSi7+XV1d5xnEJP6ysrIqj4j+ +; j4+PGej+oaGhEH/+oqKiKBnZCtT+29vbDIKIyRn91SjvpIj+pKSk/tXEs/6dZzAe1P6ZTQD+w2IANc +; EoHucd2v6EQgD+wWEAxP56PQCbPd/+bD8R/mFWTP5dXV3oGcUo/oeHhw2xiP65ubk+GeSxiP7MzMwq +; goj+enp6GdsK1CQKyhn91X/xv4j+1NDM/rqVbv6HRADUN/6xWQA1wJkfHucd2gio8MP+qFQA/nA4AN +; /+aEgp/l1dXekZyKyIAP78/PyDiP5/f38Z4v6Ojo7+7e3trIj+tra2JBncCuEZ/dUo8qmI/rW1tf7a +; ybj+llsg/odEANQB/shkAJ5aHucd2ajw/sFhAMQy/nA4AN2iXAL+XV1d6hnJo4j+l5eX/vPz83/+qK +; ioJBnep4j+u7u7/vr6+pCI/oiIiBndCuIZ/dUo9P6MjIz+4dzY/qx+Tx7UNyce5x3ZEf7BYQDDCP5w +; OADd/mw/Ef5fWFEo6hnMt4j+1tbWOf7Q0NAyGdz+fX19/uHh4bqI/sjIyP5qamoZ3griGf3VKPWwiP +; 7Gxsb+x6qM/o5QEB79HdgU/sFhAMT+iUUA/nA4ANz+ZU00KOwZzaSI/qmpqRuTiA4oGdiiiBwunIg7 +; KBneCuMZ/dYo9Qb+nZ2d/trNwP6dZy8e/B3Y/qNSAP7BYQDDmz3+cDgA2/5sPxEa/l1dXewZzyj+gI +; CA/uTk5Bv+wMDA/mpqahnWtYj+09PTKv7Y2Nj+dXV1GeAK4xn91ij3uoj+zsrG/rqVbv6OTxAe+v5w +; OADXpdP+wWEAxP6ORwD+cDgA2jQo7RnSIP6+vr45Ef6GhoYZ1P6VlZX+8fHxp4j+r6+vFRngCuQZ/d +; Yo+KeI/q6urv7Yx7b+llsg/odEAPkd1zj+wWEAw51r/nA4ANmiXP5jUUAo7hnTNxb+7u7uqIg2/mNj +; YxnQPv7ExMQqiYj+goKCGeIK5Bn91ij6/oWFhf7c2NT+s4lfHvgd1qbS/sJhAFrD/o5HAP5wOADY/m +; tBF/5eW1co7hnWs4glOf7X19cuGc4i/ufn5xv+wcHBLxniCuUZ/dYo+yv+v7+//s61m/6OUBAe9h3W +; /q1XAP7NZwCbPUbBP/5wOADY/mVNNP5dXV3vGdgG/qKiov739/eaiP6enp7+Xl5eGcoV/rGxsf75+f +; mXiP6Tk5MoGeMK5Rn91yj7N/6Wlpb+18q9/qx+Tx71HdWjtf7RaQDAVpxMnUvAAv5wOADW/mw/Ef5f +; WFEo7xnaKL6I/t7e3r2I/sfHx/5tbW0ZyLqI/tra2ir+0NDQ/nBwcBnkCuYZ/dco/baI/sjEvwn+jl +; AQlNTzHdUvNcKeav69XwAd1jQo8Rncpoj+tbW1Ko6I/o2NjRnG/p2dnf709PQu/qampv5dXV0Z5Qrm +; Gf3XKP3ApIj+p6en/tbFtP6WWyD+h0QA8h3Uo7U1w51b/oJBAB3Uolz+YlRGKPEZ3qKI/omJif7q6u +; o9/ra2tv5mZmYZwq+I/snJySqEiAgZ5grnGf3XKP3C/n9/f/7X0s7+upVuHvEd1P6tVwA1wi8d1QMo +; 8hnhroj+x8fHOYCICP5cXFzA/ouLi/7r6+sM/rm5uTMZ5wrnGf3YKP3Cqoj+zs7O/uHQv/6OUBCU1O +; 8d06O1NcL+lEoAHdX+ZE86KPIZ46OI/pqamgFV/qenpwwbOv6MjIwZ6AroGf3YKP3BBv60tLT+/v7+ +; f5TE/qx+UB7uHdMvNcD+v2AA/nw+AB3U/mw/Ef5fWFEo8xnluoj+39/fF8D+0dHRGBnpCugZ/dgo/c +; AfBibD/sqtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o9BnlKP6lpaUuPTmRiP6UlJQoGecK6Rn9 +; 2Sj8/p6env76+vomxf7o287+nWcwHusd0v6nVAASAB3UK/5hVkwo9Bnls4j+0NDQKv7a2tr+eXl5qo +; j+5ubms4j+vb29/mlpaRnlCuoZ/dko+q6INCbG/sjIyBP+0c3J/rqVbh7qHdE+/qdUAB3V/mhIKf5d +; XV31GeX+kpKS/vDw8Az+sbGxFRnAqogEOYaIIhnkCusZ/dko+P6Li4v+8fHxJsWXiP6ZmZn+X19fKK +; iI/rGxsf7ZyLf+llsg/odEAOgd0T4d1KJc/mNRQCj1GeQv/sDAwBuMiP6EhIQZw6KI/pGRkQWliP6s +; rKz+YmJiGeEK7Bn92Sj2M/68vLwmxjv+dHR0KMP+iIiI/t7a1v6ziV8e5x3n/mtBF/5eW1co9Rnk/o +; GBgf7l5eUq/sTExP5oaGgZxrSI/tPT0/79/f3+1NTU/nV1dRngCuwZ/dko9b+IICbFnYgJJCjFOv7C +; wsL+zrWc/o5QEB7lHeYRKPYZ4wb+rq6uPZmIPygZyBX+pKSkPZeI/pubm/5eXl4Z3VXtGf3aKPP+qK +; ioOSbFiIj+goKCKMg3/pmZmf7YzL/+pHI/HuQd5P5sPxH+X1hRKPYZ47iI/tfX1yoS/nJychnLKP59 +; fX3+4ODgKjH+bGxsGdsK7hn92ijxsoj+1tbWJsUX/sHBwf5mZmYoywH+y8fC/rqVbh8e4h3j/mhIKS +; j3GeMs/vPz8y7+qqqqNxnOp4j+ubm5OT7+iYmJGdoK7hn92yjv/pSUlP729vYmxZOIAzcozTMr/tfG +; tf6WWyD+h0QA4R3holz+YlRGKPcZ4q6I/sfHxyqHiP5/f38Z0Tf+jIyM/uzs7C7+s7Oz/mVlZRnXCu +; 8Z/dwo7CAx/v///8b+1NTUNijRE/7Z1dH+upVu/odEAOAd4P5pRiMo+Bni/oeHh/7q6uob/ry8vP5k +; ZGQZ1LCI/svLy/78/Pw7/np6ehnVCvEZ/dso6yL+6+vrJsWbiP6kpKT+YGBgKNOsiP67u7v+3Mu6/o +; 5QEJTU3h3f/mRPOij4GeEk/rW1tf75+fmViP6Pj48Z1wb+nZ2d/vb29iP+oqKiBhnTCvEZ/dx/6Df+ +; sbGxFybFgoj+fHx8KNf+kpKS/tzUy/6sfk8e3R3d/mw/Ef5fWFEo+BnhvYj+3d3dvoj+zMzM/m5ubh +; navIg7Kho2GdEK8hn93H/nt4j+3d3dJsUX/ri4uP5jY2OaiNkF/sTAvP7Jq43+jlAQHtsd3P5mSi4o +; +RnhM/729vZV/qGhoSgZ3KWI/rCwsCqQiP6QkJAZzwrzGf3dKOX+m5ub/vn5+SbFjoj+jIyMNyjbFf +; 6jo6P+3M/D/p1nMB7aHdor/mFWTCj5GeAn/s7Ozv78/PyAiP56enqCiN8oDz4M/rq6uv5nZ2cZzFXl +; tYgKzRn93SjiHP7Nzc0mxv7Ly8v+a2trKN++iP7Tz8sOHtkd2TQo+RnhFv7u7u4M/rW1tSQZ4j7+w8 +; PDOYSI/oGBgRnLCuX+7Ozs/pOTkygKyxn93SjhLf7v7+8mxS7+nZ2d/l9fX0DhLzL+2sm4/pZbIP6H +; RADXHdeiXP5kTzoo+RngqIj+vb29/vr6+o+I/oeHhxnlNzAjooj+qampJBnICub+4uLiG/68vLz+aG +; hoCsoZ/d4o3iT+urq6FybF/tvb2/53d3co5f6Kior+4NzX/qx+T/6HRADWHdYr/l9YUSj5GeD+fn5+ +; M7mI/sfHx/5qamoZ6LaI/tXV1f78/Pz+0tLS/nNzcxnGVeeqiAg5M/6Dg4MKyRn93yjcvIj+5OTkJs +; VA/rCwsCQo57CI/sXFxf7Iqoz+jlAQlNTUHdX+ZU00KPoZ3zf+qqqq/vf395yILCgZ6qSI/qenp/75 +; +fkn/piYmCgZxAroKCX+7e3tqYj+q6ur/mJiYgrIGf3fKNr+pKSkKibFioj+hYWFKOoG/pycnP7Zzc +; D+nWcvHtP+cDgA0/5sPxH+YVZMKPlV4CMhKj/+dHR0Ge0oF/7j4+O3iP7BwcH+ampqGcIK67OIBzkh +; /nV1dQrHGf3gKNewiCEmxjH+Z2dnKO25iP7NycX+upVu/o5PEB7RHdI0KPoZ4P6Wlpb+8vLypoj+ra +; 2tBhnwqIj+vLy8/vz8/ImI/oeHhxnACu0V/qGhof729vY2/pqamjcKxRn94CjWJf709PQmxTL+lJSU +; NyjvpogY/tfHtv6WWyD+h0QA0B3Qolz+Y1FAKPoZ3w3+xcXFKoiI/oGBgRnzN/6NjY3+7e3tHwX+Y2 +; NjCu/+e3t7/tzc3L+I/sPDw/5ra2sKxX/94CjTAv7BwcEmxv7X19f+cXFxKPP+hISE/tvX0/6ziV8e +; zx3P/mpEHf5eW1co+RngMf7o6Ogb/r+/vy8Z9rGIBzn+2NjY/nd3dwrvAv60tLQqjIgLCsQZ/eEo0f +; 6AgIANt4jFG/6oqKgVKPUr/r6+vv7OtJv+jlAQHs3+cDgAzv5lTTQo+hnfFf6ysrIMNv6SkpIoGfgG +; /qCgoP739/eaiP6fn5/+Xl5eCu4oCxw9I/5lZWUKwhn94ijON/6tra3+/v7+JsWFiP5/f38o+f6VlZ +; X+1sq9/qx+Tx7MHcz+bD8R/l9YUSj5GeC7iP7b29sq/s/Pz/5wcHAZ+lUZvYj+3NzcKv7IyMj+bW1t +; Cu+viP7Hx8c5/tra2v57e3sKwn/94ijMtYg7/v///8UX/ry8vCAo+yP+x8O//rqUbh+U1Mody/5mSi +; 4o+hng/p6env719fWiiP6kpKQoGfoKwqaI/rS0tP77+/scOArvpIj+mpqa/vLy8sD+oaGh/mBgYArA +; Gf3jKMr+mJiYPSbFNv6Ojo7+Xl5eKP2kiP6mpqb+1sW0/pZbIP6HRADJ/nA4AMmlH/5hVkwo+RngsI +; j+y8vLKoOI/nx8fBn7CsQZ/oiIiBw9/re3t/5mZmYK77uI/tjY2P78/Pwa/m9vbwoZ/eUoxqqI/snJ +; ySbGFhgo/cP+fn5+/tXRzf66lW4eyP5wOADI/mlGIyj6GeD+jIyM/uzs7K2I/ri4uP5iYmIZ+wrHrY +; j+xsbGOYGI/n19fQrvpoj+rKysDJGIJf5dXV0Z/eUoxP6FhYX+7e3tJsWZiP6goKAGKP3FPi7+2sq5 +; /o5QEB7GHcf+ZE86KPkZ4KeI/rm5uf76+vqRiAsZ/ArJo4j+mJiYMsD+pqamBgruGf6Dg4P+5OTkGw +; wNGf3lKMGjiP62trb+/v7+JsX+3t7e/nl5eSj9yTj+4t3Z/qx+Tx7F/nA4AMX+bD8R/l9YUSj4GeH+ +; fHx8/uDg4Co8/mxsbBn8Csy5iP7X19f+/Pz8Fv5xcXEK7z7+wMDAORX+gICAGf3lKLqI/uHh4SbFQP +; 6zs7MzKP3LJ/7Hx8f+yKqN/o5QEJTUwx3E/mVNNCj5GeAo/qenp/739/ediP6cnJwoVfwKzgb+q6ur +; /vr6+pOIMBkK7qOI/pKSkv7u7u4BPjMZ/eP+oKCgGybFjIgtKP3OBv6enp7+2s7B/p1nLx7CHcIr/m +; FWTCj4GeEF/tLS0ir+2dnZ/nZ2dhn9wArPGf6BgYH+5OTkG/69vb0NCu8U/tHR0TkD/nNzcxn94K+I +; AybG/sfHxw0o/dG7iP7QzMf+upVu/o5PEJXDwB3B/mhIKSj4GeL+k5OT/vDw8KiI/rCwsP5gYGAZ/c +; AK0iD+v7+/OYeIIgrvFf6kpKQuJ/6YmJj+Xl5eGf3dKSOtiMWWiB0GKP3Tp4gF/tjIt/6WWyD+h0QA +; /nA4AKJc/mNRQCj3GeKqiP7CwsIbjIgiGf3BCtQo/o+Pj/7v7+8Q/q2trf5iYmIK7hn+fn5+/t/f3y +; r+wMDA/mpqahn92jP+vr6+/v///8b+2NjYMij91/6Hh4f+3dnV/rOJX/6MYjj+Z2NgKPYZ4wQvKv7D +; w8MNGf3BCte0iAP+/Pz8/tXV1f51dXUK7hkR/ri4uCqJiB4Z/dj+fX19LybFKv6rq6sVGSj92K6INR +; sR/oCAgDco8hnjo4j+r6+v/vj4+JmI/pWVlSgZ/cEK2Qb+oqKiLpmI/pycnCgK7H/ANzj+6+vrLjb+ +; ZGRkGf3UKP6pqan+/f39JsUvNRnDKP3XN/6Xl5cnq4gc/mFhYSjvGeQQHSoD/nJychn9wgrbGb+I/t +; /f37yI/sXFxf5ra2sK7BnBCf7Kyso5Hf55eXkZ/dKziP7X19cmxVX+wMDAIBnGKP3Xt4j+0dHROf7W +; 1tYBKOwZ5f6bm5v+9PT0Lv6oqKg3Gf3CVd6niP63t7c5i4j+ioqKCtMAAAAAAAAAAQ== ; thumbnail_QOI end ; ; -; thumbnail_QOI begin 480x240 22316 -; cW9pZgAAAeAAAADwBAD+XFxc/d1//f3TpIj+h4eH/tzc3P7////CjYj+vr6+/oSEhCQo/f393KuI/r -; W1tf739/cmyv5cXFz92yj9/daiiLCI/ru7u/74+PgmwZqI/tTU1P6enp7+aWlpKP39/dr+gICA/tnZ -; 2SbMGf3ZKP392aHypR7+lZWV/unp6SbBVYiIEP58fHyCiCj9/f3WAv6jo6P+8/PzJs0Z/dco/f3Zoe -; H+rWUdoeH+a15R/mBgYLeI/srKyv78/PwmwZaI/svLy/6Tk5P+ZGRkKP39/dS2iBoXJs4Z/dUo/f3a -; /oFhQP7KaAah4cAF/ohhOjcNAv7y8vImwTmDiP6srKz+dHR0KP39/dGiiBL+6+vrJtAZ/dMo/f3b/q -; VkIzXEDRgk/oKCgv7X19cmwpCI/sLCwv6JiYn+YmJiKP39/c6tiP69vb0MJtEZ/dEo/f3b/nNfTP7C -; Zww1xv67ZxEdBq6I/rS0tP729vYmwZuIDv6hoaH+bGxsKP39/cz+hYWF/t/f3ybTGf3PKP393P6XYy -; 41yQX+kGI0IjP+j4+P/uTk5CbCHP65ubn+f39/gIgo/f39yKiI/qqqqhCqiNONiBn9zij9/dsD/rtn -; ETXMMAMVMv7ExMT++/v7JsEf/s7Ozv6YmJj+ZmZmKP39/cYu/tHR0SbTF/7Q0ND+goKCGf3MKP393P -; 6IYTo1zwU7Ny/+nZ2d/u7u7ibBQIaI/rCwsC6GiP39/cOkiP6ZmZk2JtOViP6tra3+cnJyFRn9yij9 -; /dyh4Q010jAYFbyINCbCk4j+xsbG/o2Njf5jY2Mo/f39wLCI/sPDwyom0/7d3d3+jo6O/mdnZzdVGf -; 3IKP393T0SNdSfL/6BYUA3rYj+rKysASbBKoCI/qampv5vb28o/f38/ouLi/7l5eUm05mI/r29vf54 -; eHgzKMEZ/cco/f3d/p5kKTXYIv5lXlgk/oqKiv7f398mwoyICBMVKP39+D7+srKy/vf39ybTHP6bm5 -; v+aWlpNyjCGf3FKP393f5zX0z+u2cRNdogPf5fX1+xiP6+vr7++fn5JsGZiP7S0tL+nJycHCj9/fb+ -; fX19DibTnYj+zMzM/n9/f4WIKMRV/cMo/f3eOzXdny87IgL+l5eX/urq6rWIwUCIiP60tLT+e3t7Ny -; j9/fIC/p+fn/7y8vIm05KI/qenpzaQiCjFGf3CKP393aHh/q1lHTXgMP5rXlEVuYgpCCbBlYj+ycnJ -; /pKSkhEo/f3wtYg8CCbT/tjY2P6JiYn+ZmZmNyjGGf3AKP393v56YEYFNeIF/ohhOjcc/qWlpf7z8/ -; MmwZyIg4j+qqqq/nNzcyj9/e1//pGRkf7p6ekm0x/+tra2/nV1dSQoyBn8KP393/6eZCk15g3+ZV5Y -; JP6EhIT+2dnZJsIn/sHBwR4kKP396qyI/rq6uv75+fkm04WI/pWVlf5oaGg3KMkZ+yj9/d7+c19MEj -; Xo/rtnEf6BYUAGr4j+t7e3/vf39ybBDP7W1tb+oKCg/mtrayj9/ej+g4OD/t3d3SbTm4j+x8fH/nt7 -; ewKaiMsZ+Sj9/d87NesFO/5lXlgzA/7m5uYmwVWKiP64uLj+fn5+Bij9/eSoiD4BJtOPiP6ioqL+bW -; 1tBijMGfco/f3foeH+tGYXNe4w/mteUf5gYGAQD/77+/smwZeI/s3Nzf6Wlpb+ZmZmKP394hD+z8/P -; JtNV/tLS0jEgKM4Z9ij9/d8INfEFOzepiP6fn5/+8PDwJsE5hYj+rq6u/nZ2dij9/d8VDv7t7e0m0w -; H+sLCwMhUozxn0KP394P6lZCM19DD+ZF5Xoy+9iCEmwpGI/sTExBr+YmJiKP393K+IBBsm04CI/pCQ -; kD43KNAZ8yj9/d/+c19MEjX2ny8d/l9fXwn+r6+v/vX19SbBGyz+pKSk/m5ubij9/do8/uPj4ybTmY -; j+wMDA/nl5eTMo0hnxKP394P6XYy7+0WkA+v6eZCn+ZV5YM/6MjIz+4eHhJsKLiP67u7v+gYGBFSj9 -; /dY+Nv729vYm0yv+nZ2d/mpqagZA0xnwKP393/5zX0z+u2cRNfwgPQayiP7BwcEbJsEu/tHR0f6amp -; r+aGhoKP391L6IISbTQDj+gYGBg4go1RnuKP394P6QYjQ1/cEFOzeniP6ampr+7OzsJsEIh4j+srKy -; DCj9/dEz/p2dnf7x8fEm05OI/qqqqv5wcHAVnYjWGe0o/f3fGAA1/cQw/mxfUhW6iP7Ozs7+/v7+Js -; GUiP7IyMgl/mNjYyj9/c4FDzkm0/7a2toaLzco1xnrKP394P56YEb+ymgGNf3GBR03rIj+qKioMibB -; nIiCiA3+cXFxjIj9/cz+j4+P/ujo6CbTLv65ubn+dnZ2i4go2RnqKP394P6eZCk1/coi/mVeWCT+h4 -; eH/tvb2ybCjoj+v7+//oWFhSQo/f3Iq4j+t7e3/vj4+KeI04eIDhw3KNoZ6Sj9/d/+c19M/rtnETX9 -; zCA9BrCI/rq6uv74+PgmwZqIIf6enp4rKP39xgQ7JtMq/sjIyP59fX0CKNwZ5yj9/eD+kGI0Nf3PBT -; siM/6UlJT+6OjoJsEXL/62trb+fHx8gogo/f3Cp4j+pKSkMibTkIgR/m1tbQYo3RnmKP3936HhMDXe -; mi7+plMA/sxnADXtMP5rXlEVtoj+ycnJ/vz8/CbBEP7Ly8sh/mVlZSj9/cC3iP7MzMwXJtP+1dXV/o -; aGhoCIKN8Z5Cj9/eD+iGE6Nd/+rVcA/nA4AKfx/pVLAP66XQA17QUIN6qI/qKiov7x8fEmwTmEiP6s -; rKz+dXV1KP37oogh/uzs7CbTloj+s7OzASQo4BnjKP394P6lZCM13/6USgAdwCqlw6K2/rFZABw17f -; 6tZR0YJAT+1tbWJsI2/sPDw/6JiYn+YmJiKP34rYj+vr6+/vr6+ibTgoj+kpKS/mhoaJaIKOEZ4ij9 -; /d/+c19M/sJnDDXe/r9gAP58PgAdwSoewf6ZTQD+yGQANe3+u2cR/oFhQAauiP6ysrL+9vb2JsEb/t -; jY2P6ioqIYKP32/oeHhxUm0wwi/np6egIo4xngKP394P6XYy413/6aTQD+cDgAwyqlw8KitgU17Z8v -; K/5lXlikHv6Ojo7+4+PjJsKKiP66urr+gICABij98qmI/qysrBAm042I/p+fnzqUiCjkGd8o/f3fPS -; D+0WkA3hL+gkEAHcMA/rhdAP6QSQAew/6eUAA17v60Zhc9/mBgYLKI/sPDw/76+vomwZiI/s/Pz/6Y -; mJg+KP3wu4j+0tLSJtNA/tDQ0BODiCjmGd4o/f3fOzXf/qdUAP5wOADE/qBQADXAnlr+o1IAHsM17w -; U7/l5eXiD+nJyc/u3t7SbBCAL+sbGxPSj97aSI/pqamjYm05SI/qysrP5xcXEVKOdV3Sj9/d6h4f6l -; ZCM13p1b/o5HAB3DAP6/YAA1wzT+jEYAHsEBNfAw/mxfUhW8iP7R0dEmwpOI/sfHxwf+Y2NjKP3qJz -; H+/Pz8JtP+3Nzc/o2NjT6XiCjoVdso/f3f/nNfTP7CZww13v65XQD+djsAHcMENcYBH/6HRADA/rpd -; ADXxBf6BYUA3Ov6rq6v+9PT0JsGciAr+pqam/nBwcCj96P6MjIz+5ubmJtOZiP68vLz+d3d3MyjqGd -; oo/f3f/p5kKTXfBP5wOADDPv6zWgA1yBz+rFYA/oxGAP61WwA18yL+ZV5YJDz+3t7eJsKNiP69vb3+ -; g4ODFSj95KuI/rOzs/739/cm04mIO/5paWmViCjrGdko/f3e/nNfTP67ZxE13v6/YAD+fD4AHcP+iE -; QAFDXLNA419CA9BgUIDCbBmYj+09PT/p2dnRyUiP3i/n5+fv7Y2Ngm0yoaF4aIKO0Z1yj9/d/+kGI0 -; Nd8O/nA4AMQONc7+mU0ANfWfLzv+ZV5YAv6Wlpb+6urqJsFVL/60tLT+e3t7Nyj93gL+oaGhIybTko -; j+pqam/m9vbwYo7hnWKP393hj+rWUdNd6dWyEdw/6CQQD+v2AANc/+nlAANfcw/mteURW4iBr+/f39 -; JsGViP7Kysr+kpKSESj93LWICwgm0/7X19f+iIiI/mZmZjco7xnVKP393v56YEYFNd7+rVcAHcQENd -; EfNfgF/ohhOjeqiP6kpKT+8vLyJsGciISIOv5zc3Mo/dk3Aysm05eI/rW1tf51dXWMiCjxGdQo/f3e -; /p5kKTXf/pRKAB3DPjE10v6nVAD+yGQANfn+pWQj/mVeWCT+g4OD/tjY2CbCj4j+wcHB/oiIiCQo/d -; atiP67u7sMJtOEiP6VlZX+aGhoNyjyGdMo/f3d/nNfTP7CZww13v6/YAD+fD4Ami7DIzXU/rFZADQ1 -; +iD+gWFA/l9fX6+I/rW1tS4mwQz+1tbW/qGhof5ra2so/dT+hISE/t3d3SbTm4j+xcXF/nt7e4iImo -; j0GdEo/f3eOzXfJf5wOADELzXV/r5gAP6sVgA1+58vO/5lXlgz/pGRkf7m5uYmwReKiP64uLj+f39/ -; Bij90KiI/qmpqf709PSriNOOiCT+bGxsBij1GdAo/f3doeH+u2cRNd4S/oJBAB3DAP7FYwA11ptN/q -; NSADX9/rRmF/5rXlEVtIj+xsbG/vv7+ybBl4j+zs7O/peXl/5mZmYo/c65iP7Q0NAm0xf+0tLSMYGI -; KPcZzyj9/d0INd/+p1QAHcT+oFAANdk4Nf3ABTs3L/6enp7+7+/vJsGdiIaI/q+vrx8o/csV/piYmP -; 7u7u4m05WINv5ycnIVKPgZzij9/d3+pWQjNd4U/o5HAP5wOADDABI12v6ZTQA1/cIwGKMvvIj+09PT -; JsKSiP7FxcUp/mNjYyj9yLCIBCom0/7e3t7+kJCQ/mdnZ5eIKPkZzSj9/dz+c19M/sJnDDXe/rldAP -; 52OwAdw/6aTQA13P6eUAD+yGQANf3CBf6BYUAGCf6urq7+9fX1JsEbgIj+paWl/m9vbyj9xv6Kior+ -; 5OTkJtOZiP6/v7/+eHh4iogo+xnMKP393P6XYy413wT+cDgAwz7+rVcANd3+sVkAp+E1/cT+nmQp/m -; VeWDP+i4uL/uDg4L+IwoyI/ry8vBP+YGBgKP3Cqoj+sLCw/vb29ibTi4j+nJyc/mpqagYo/FXLKP39 -; 2/5zX0z+u2cRNd7+v2AA/oJBAB3DAP7FYwA13v61WwCitjX9xSA9BrKI/sDAwAwmwZmI/tHR0f6bm5 -; v+aGhoKP3Av4g/JtOdiP7Nzc3+gICAhIgo/cAZySj9/dz+kGI0/tFpAN8OHcQONeCZHw41/cafLzs3 -; Ef6ZmZk6JsFAiIj+s7Oz/np6eoOI+6WI/p6env7x8fEm05OIHP5wcHAVKP3BGcgo/f3boeH+rWUdNd -; 6dWyEdw/6CQQASNeGeWv6jUgA1/cgw/mxfUhUb/s3NzRcmwZSI/sjIyP6RkZERKPgF/sfHxwgm0/7Z -; 2dkL/mZmZpiIKP3CGcco/f3b/npgRv7KaAY13v6tVwAdxAQ15P6eUAA1/ckF/ohhOjeriP6np6cyJs -; Eqgoj+qamp/nJycouI9iX+6OjoJtOXiP64uLj+dnZ2JCj9xBnGKP392/6eZCk13/6USgAdw6O1/rNa -; ADXl/qNSADX9y/6lZCP+ZV5YJP6Ghob+29vbJsKOiP7AwMAPJCjyrIg9/vj4+CbTIP6Xl5ccNyj9xR -; nFKP392v5zX0z+wmcM/tFpAN7+v2AA/nw+AB3DIRQ15jgBNf3L/rtnES0Gr4j+ubm5/vj4+CbBmoj+ -; 1dXV/p+fnyso8BMKJtMq/sjIyP58fHwCKP3HGcQo/f3aOzXf/qBQAB3E/qdUADXo/rFZAKTUNf3Mny -; 87IjP+k5OT/ujo6CbBF4mI/re3t/59fX03KOwR/qWlpTIm04+I/qSkpP5tbW2SiCj9yBnDKP392aHh -; /rRmFzXe/r9gAAAdwwD+xWMANek0nlo1/c4w/mteURW2iC3+/Pz8JsGWiP7MzMz+lZWV/mVlZSjqt4 -; j+zc3NJtT+1NTU/oWFhYGIKP3KGcIo/f3Z/ohhOjXfDh3EBDXrmR8ONf3PBTs3qYj+oaGh/vHx8SbB -; OYWI/q2trf51dXWIiOcG/paWlv7s7Owm05aI/rGxsf5zc3MVKP3LGcEo/f3Z/qVkIzXenVsjHcOjtT -; E17f6eUAA1/dH+tGYX/mReV6Mvv4j+1dXVJsKRiP7Dw8ML/mJiYijkroj+v7+//vr6+qWI04GI/pKS -; kv5oaGg3KP3MGcAo/f3Y/nNfTP7CZwz+0WkA3/6tVwA+HcIjNe8fNf3Sny/+gWFABq2I/rGxsf729v -; YmwRv+2NjY/qOjo/5tbW0o4v6Hh4f+4eHhJtMM/sLCwv56enqJiCj9zhko/f3Y/pdjLjXi/r9gAP6C -; QQD+cDgAwC818P6eUAA1/dMF/p5kKf5lXlgz/o6Ojv7i4uImwouI/rq6uv6AgICAiCjeqYj+ra2tEC -; bTjIj+np6e/mtrawYo/f396D3+u2cR/tFpAOSdWyX+xWMANfH+o1IA/shkADX91CA9FbKIE/76+vom -; wZiI/tDQ0P6ZmZn+Z2dnKNy8iP7T09Mm00AW/oKCgoOIKP39/ek7Nf3e/rFZADQ1/dWfLzv+Xl5eIP -; 6bm5v+7e3tJsEIhoj+sbGxDCjZpIj+nJyc/vDw8CbTlIj+q6ur/nFxcRUo/f396KHh/qVkIzX93/6+ -; YACZLzX91/60Zhf+bF9SFbuIJSbCk4j+x8fH/o+Pj/5jY2Mo1jb+xcXFOSbT/tvb2/6MjIw+Nyj9/f -; 3oPf7CZww1/eABDjX92J8v/oFhQDesiP6qqqr+9PT0JsGciIGI/qenp/5wcHAo1Af+5+fnJtMu/ru7 -; u/53d3eLiCj9/f3p/p5kKTX94x816p5aNesi/mVeWCT+iIiI/t3d3SbCjYj+vr6+/oSEhCSciNCriP -; 61tbX+9/f3JtM+LP5paWk3KP39/en+rWARNf3k/plNADXonVv+iEQA/phMAP7IZAA16/6pXhEoBjb+ -; vLy8/vj4+CbBmoj+1NTU/p2dnRyUiM7+f39//tnZ2f7////TnIj+ysrK/n19fYeIKP39/ev+iUgG/r -; FZAP7MZwA1/eIfATXm/q1XAP5wOADA/oZDAKO1/rVbADXpDh0owH8C/pWVlf7p6ekmwVWIiBD+fHx8 -; NyjKAv6ioqIyJtORiP6mpqY2Bij9/f3s/oRGBp7U/plNAP7IZAA1/eH+sVkAATXl/pRKAB3BFh7AH/ -; 7MZwA15RT+iEQAHcAowhW3iAv+/f39JsGViAv+k5OTESjItoj+y8vLFybT/tbW1v6Hh4f+ZmZmNyj9 -; /f3tCZ7UwKK2BTX94Bs0NeP+v2AA/nw+AB3CFh7BpcM0NeP+rVcAHcIow38N/qOjo/7y8vImwZ2Ig4 -; g6/nR0dCjFooj+k5OT/uvr6ybTl4j+tLS0/nR0dCQo/f397wkewv6ZTQAcNf3eDw414v6gUAAdxP6K -; RgAewqK2/qxWABw14P6USgAdwyjFJP6CgoL+19fXJsKQiP7CwsIt/mJiYijCrYj+vb29DCbTg4j+lJ -; SUDZaIKP39/fAJHsOlwzQ1/d2bTTg14P6/YAD+gkEAHcMA/sVjAEb+nlAAHsKn8Rw13hL+fD4AHcT+ -; XV1dxgauiP60tLT+9vb2JsGbiA7+oaGh/mxsbCjA/oWFhf7f398m0xv+xcXF/np6eomIKP39/fL+hE -; YGHsSitv6nVAAcNf3c/p5QADXfDh3EJTXBHA43Hg413yUdxijHf6SI/pCQkP7l5eUmwoqI/rm5uf6F -; hYX+q6ur/vT09CbTjoj+oKCg/mxsbJOIKP39/fP+hEYGHsb+mU0A/sNiADX92x813RQjHcOjtRI1xA -; /ANd4S/oJBAB3H/l1dXcmjiDL+xcXFKibBl4g2JtMX/tHR0f6Dg4OCiCj9/f31CR7HorYFNf3aOP7I -; ZAA12xL+fD4AHcMjNecOHckoyn+oiP6dnZ3+7u7uJtaViP6urq7+cnJyFZ2I/f399gkeyf6ZTQD+yG -; QANf3YBaTUNdoEHcT+rVcANeYU/pRKAB3KKMykiCn+8vLyJtT+3d3dB/5nZ2c3Vf39/fcJHsqitjQ1 -; /df+tVsAwDXYEv6CQQAdwwD+xWMANeYSHx3LKMz+i4uL/uXl5SbTPf6+vr7+eHh4iogo/f39+QkezA -; 7+zGcANf3VmR8ONdcOHcT+oFAANecEHc3+XV1dyj7+srKy/vb29ibTioj+m5ub/mpqagYo/f39+gke -; zafx/sNiADX91J5a/p5QADXVFP6ORwAdwwASNeYSAB3O/l1dXcn+fX19/tfX1ybTOf7MzMz+f39/ES -; j9/f38CR7ONwUcNf3THzXU/rldAP52OwAdwwQ15/6nVAAd0CjHpoj+n5+f/vLy8q2I05KI/qioqP5v -; b28VKP39/f0JHtD+mU0A/shkADX90h810wQdwz7+s1oANeadW/6ORwAd0SjGtIj+ycnJ/v39/SbT/t -; jY2P6JiYn+ZmZmmIgo/f39/cAJHtGiths1/dE4ATXQEv58PgCaLsP+iEQA/stmADXmMT4d0ijEf/6R -; kZH+6enpJtMI/tfX1/6Hh4ckKP39/f3CCZ7U0zj+zGcANf3PBaTUNc8lHcT+p1QANecEHdQow6yI/r -; q6uv74+Pgm1Y+I/sDAwP6Hh4ckKP39/f3BCZ7U1Cs0/tFpAP3OD5tNNc0UAB3DAP7FYwA15hIfHdUo -; wiIZJtMIOSbBmog//qCgoP5ra2so/f39/cAJHtWitgX+zGcANf3MmR8ONcz+rVcAHcQENeclHdf+XV -; 1dwBH+p6en/vT09CbTJ/6mpqaWiP7n5+cmwVWKiP63t7f+fn5+gYgo/f39/Ake1/6ZTQD+yGQANf3M -; /plNADXL/pRKAB3Do7UxNeYzAB3YKLiI/s7OzibTVf7T09P+hYWFgYiaiBD+x8fHOSbBloj+zc3N/p -; aWlv5lZWUo/f39+wme1Ng3BTX9ywE1yf6/YAAfHcMjNef+rVcAHdr+l5eXGCbTEP6wsLAyFSjAf6mI -; /qCgoP7w8PAmwTmFiP6urq7+dnZ2KP39/foJHtoBHDX9yf6eUAAcNccEHcQvNecjHdv++vr6JtOAiP -; 6RkZH+aGhoNyjDpIi+iP7U1NT+////wgX+xMTEGv5iYmIo/f39+Ake26K2/rpdADX9yP6nVAD+yGQA -; NcUS/oJBAB3DAP7FYwA15hL+fD4AHdwm0gz+wcHB/nl5eYqIKMaiiK2I/rCwsBAmwRss/qSkpP5ubm -; 4o/f399/6ERgYe3Q4cNf3G/rFZADQ1xA4dxCU15/6aTQAd3ibRjIj+np6e/mpqagYoyaWI/oyMjP7h -; 4eEmwouI/ru7u/6BgYH+YGBgKP39/fUJHt6n8f7DYgA1/cUPm001whT+jkcAHcMAEjXmEgAd3ybPCA -; cEIJiIzBWxiP7BwcH++vr6JsGYiP7R0dH+mpqaDSj9/f30CR7forYFHDX9wwH+rFYANcH+uV0A/nY7 -; AB3D/ppNADXnDh3hJs6UiP6qqqoUFSjOfyA7/uzs7CbBCIeI/rKysgwo/f398/6ERgYe4f6ZTQD+yG -; QANf3D/rBYADXABB3DPi815p1bAh3iJs3+2traKf5mZmY3KNEVuoj+z8/PFybBMv7IyMj+j4+P/mNj -; Yyj9/f3xCR7iorb+tVsANf3CDhIfHcMA/sVjADXmMT4d4ybLLv66urr+d3d3i4go1H+siP6pqan+9P -; T0JsGciIGI/qioqP5xcXEo/f398Ake5P6jUgD+zGcANf3A/qRSAB3EDjXnBB3lJsqHiP6ZmZn+aWlp -; NyjXpIj+h4eHGSbCjYj+v7+//oWFhSQo/f397v6ERgae1OWn8f66XQA1/f6dTgAdwv6CQQD+v2AANe -; YSHx3m/v///8gq/snJyf59fX2HiCjaBjYb/vj4+CbBmogh/p6env5qamoo/f397Qke5qK2Bf7MZwA1 -; +/6rVgAdwQQ15w4d6CbHkIj+paWl/m5ubgYo3H8C/pSUlP7p6ekmwRcv/ra2tv58fHw3KP39/esJHu -; j+mU0A/shkADX6/q9YAB0+/rNaADXmnVv+iEQAHekmxv7V1dX+h4eH/mZmZjco3xUuPP78/PwmwZaI -; /svLy/6UlJT+ZWVlKP39/eoJntTporYFNfkx/o5HABQ15v6tVwAd1aXTHdMmxBD+s7Oz/nR0dI2IKO -; J/qoj+oqKi/vLy8ibBOYSI/qysrAEo/f396f6ERgYe6/6ZTQD+zGcANfedWzXn/pRKAB3VO/6AQAAd -; 0ybDgoj+k5OTDTco5aSI/oGBgf7W1tYmwjb+w8PD/omJif5iYmIo/f395wke7KK2NP7RaQD94P6/YA -; D+fD4AHdT+gEAAMZo+HdQmwQwi/np6eomIKOgGroj+s7Oz/vb29ibBm4j+2NjY/qKiohgo/f395gke -; 7g4corb93SX+cDgA1QH+w2IAwP6AQAAd1CbAjYj+oKCg/mxsbAZA6n+kiBb+5OTkJsKKiP66urr+f3 -; 9/Bij9/f3k/oRGBh7vp/H+w2IAp/H92hIAHdT+gEAA/rNaACjAEB3V/v39/f7Q0ND+g4ODIJiI7qOI -; s4j+xMTEKqSIwR/+z8/P/piYmP5nZ2co/f394wke8KK2Bf7RaQD92A7+cDgA1SP+w2IAwv6KRQAd1f -; 6srKz+cXFxFZ2I8H+oiP6dnZ3+7u7uJsEIhoj+sLCw/nh4eIWI/f394v6ERgYe8gH+yGQANf3UnVv+ -; jkcAHdSjpf6kUgD+w2IAwhAd1j43VfMVvIj+0dHRJsKTiP7Gxsb+jo6O/mNjYyj9/f3gCR7zorb+ul -; 0ANf3SEv52OwAd1Tv+w2IAwywd1v5dXV32BqyI/qysrP709PQmwSqAiP6mpqb+cHBwKP39/d/+hEYG -; HvUO/sxnADX9z/6aTQAd1qXT/sNiAMObPR3X/l1dXfgzPP7e3t4mwo2I/r29vSIVKP39/d3+hEYGHv -; Yr/sNiADX9zBIAHdc7KMMsHdf+XV1d+QYFCAwmwZmI/tPT0/6cnJz+aWlpKP39/dwJHveitv6xWQD+ -; zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV36fwL+l5eX/urq6ibBQImI/rS0tP57e3s3KP39/doJHvn+mU -; 0A/shkADX9xp1b/o5HAB3Z/qRSAP7DYgDD/o9IAB3Y/l1dXfwVuIj+zMzMCCbBlYgLA/5kZGQo/f39 -; 2Qke+qK2/rVbADX9xP6tVwAd2qOl/sNiAMMxHdn+XV1d/X8N/qSkpP7z8/MmwZyIg4j+qqqq/nNzcy -; j9/f3YCR78OBw1/cH+lEoAHdv+pFIA/sNiAMMKHdn+XV1d/cEk/oSEhP7Z2dkmwif+wcHB/oeHhyQo -; /f391gme1P2n8TQ1/P6/YAD+fD4AHdujpf7DYgDDnWsd2v5dXV39wgaviP62trb+9/f3JsEMP/6goK -; A6KP39/dUJHv3AorYFHDX5/qBQAB3d/qRSAP7DYgDD/o9IAB3a/l1dXf3DfzP+kZGR/ubm5ibBF4qI -; /ri4uP5+fn4GKP39/dMJHv3C/plNAP7DYgA19hIAHd2jpSjDnWsd2/5dXV39xaOItIgPKibBl4j+zc -; 3N/paWlv5mZmYo/f390gke/cOitgU19P6nVAAd3/6kUgD+w2IAw/6PSAAd2/5dXV39xn8v/p+fnzYm -; wZ2Ihoj+r6+v/nZ2dij9/f3RCR79xf6ZTQAcNfAUIx3fo6X+w2IAw51rHdz+XV1d/cgkF/7T09Mmwp -; KI/sXFxf6MjIz+YmJiKP39/c8JHv3Gorb+ul0ANe7+v2AA/nw+AB3g/qRSAP7DYgDD/o9IAB3c/l1d -; Xf3JooitiP6urq7+9fX1JsGbiICIEf5ubm4o/f39zv6ERgYe/cgOHDXr/ppNAB3ho6X+w2IAw51rHd -; 3+XV1d/csz/ouLi/7h4eEmwouI/ry8vP6CgoIVKP39/cwJHv3Jp/H+w2IANegS/oJBAB3i/qRSACjD -; Ix3d/l1dXf3MBrKI/sDAwBsmwS7+0dHR/pqamv5oaGgo/f39ywke/co3BRyituUOHeOjpf7DYgDDCh -; 3e/l1dXf3Nf6aI/pmZmf7s7OwmwQiHiP6ysrL+eXl5KP39/cr+hEYGHv3MAf7IZAA14p1bAh3k/p9Q -; AP7DYgDD/pRKAB3e/l1dXf3PFbqI/s7Ozv7+/v4mwZSILf6QkJD+ZGRkKP39/cgJHtyith7sN/61Ww -; A14P65XQA+HeQ7/sNiAMMKHd/+XV1d/dB/rIj+p6enMibBKoKI/qmpqf5ycnKLiP39/ccJHtiitg7+ -; ul0ANZkf/p5QAB7t/qNSAP7MZwA13QQd5iIow/6ZTQAd3/5dXV390qSI/oaGhv7b29smwo6I/r+/v/ -; 6FhYUkKP39/cUJHtWn8f6sVgD+w2IANcMcDjce7Cs0NdoS/nw+AB3mOyjDnWv+ej0AHd/+XV1d/dMG -; sIj+ubm5/vj4+KeIwZqIMP6fn5/+ampqKP39/cQJHtU3NDXG/r5gAP6QSQAe7DcFHDXXDh3oIv7DYg -; DDAR3g/l1dXf3UfzMh/ujo6CbBF4mI/re3t/59fX03KP39/cIJHtcOHDXFHA4e7QH+yGQANdSdW/6I -; RAAd6Dv+w2IAwwr+ej0AHeD+XV1d/dYVtogt/vz8/CbBEP7MzMz+lZWVICj9/f3BCR7Yp/H+w2IANc -; YP/oxGAB7sNwU10v6tVwAd6v6ZTQAow/6kUgAd4f5dXV3913+piP6hoaH+8fHxJsE5hIj+ra2t/nV1 -; dSj9/f3ACR7ZorYFHDXFHP6eUAAe7QEcNc/+lEoAHeqjpf7DYgDDCv56PQAd4f5dXV392aSIv4j+1d -; XVJsKQiP7Dw8P+ioqKMyj9/fwJHtsB/shkAKXDxgU3Huw3NDXM/r9gAP58PgAd6/6ZTQD+w2IAw/6k -; UgAd4v5dXV392qKIroj+sbGx/vb29ibBm4j+2NjY/qOjoxgo/f37CR7cN/61WwA1xpkfKx7tDhw1yf -; 6gUAD+cDgA7KOlKMT+ej0AHeL+XV1d/dwz/o6Ojv7j4+MmwoqI/rq6uv6AgIAGKP39+Qke3jj+zGcA -; orbFHA4e7Sv+w2IANcYS/oJBAB3tASjD/qRSAB3j/l1dXf3do4iyiCL++vr6JsGYiP7Q0NAsPij9/f -; gJHt8rNDXGD/6QSQAe7DcFNcQOHe6jpTGl08MUHeP+XV1d/d5/IP6bm5v+7e3tJsFAAv6xsbH+eHh4 -; KP399wke4KK2BRw1xTT+ikYAHu0B/shkADXAnVv+jkcAHe/+mU0A/sNiAMP+pFIAHeT+XV1d/eAVu4 -; glJsKTiP7Hx8cH/mNjYyj9/fUJntTiAf7IZAA1ww4d/oRCAB7uNzRa/nY7AB3vo6UxpdPD/no9AB3k -; /l1dXf3hf6yI/qqqqv709PQmwZyIgYj+p6en/nBwcCj9/fQJHuOitv6xWQA1wJ1bIR3ACx7wHehGHc -; b+j0gA/sNiAMP+pFIAHeX+XV1d/eMk/oiIiP7d3d0mwo2I/r6+vv6EhIQVnYj9/fL+hEYGHuX+mU0A -; /q5XAB3CCx7wHeidSxIdxf65XQCl08P+ej0AHeX+XV1d/eQGsYj+vLy8/vj4+CbBPf7T09P+nZ2d/m -; lpaZSI/f3xCR7m/nY7AB3CCx7wHemZH6GnHcMK/sNiAMP+qVUAHeb+XV1d/eV/Aj/+6enpJsFViIj+ -; tbW1/nx8fDco/f3vCZ7U5j4dwgse8B3qm02dS6fxHcExpdPDFB3m/l1dXf3nFbiI/srKyggmwZWIC/ -; 6Tk5MRKP397gke5j4dwv6EQgAe8B3rRpoupcMd/oBAAP7DYgDDGR3n/l1dXf3oN6qI/qOjoyMmwZ2I -; g4j+q6ur/nNzcyj9/e0JHuY+HcILHvAd7QQSO/7DYgDDNR3n/l1dXf3qJP6Dg4P+2NjYJsI2/sLCwi -; 0zKP396wke5j6dW8ILHvAd76OlGf7DYgDBGR3o/l1dXf3rooiuiP61tbUuJsEb/tfX1/6hoaH+a2tr -; KP396gke5j4dwgse8B3x/pRKAP6+XwCjpTUd6P5dXV397Dcz/pCQkP7l5eUmwomI/rm5uf5/f38GKP -; 396Ake5j4dwgsey6XDHuId8jX+pFIAHeko/e4VtIj+xcXFKqSIwR/+zs7O/peXl/5mZmYo/f3nCR7m -; Ph3CCx7KJ/7RaQAP/oxGAB7gHf3hKP3vfy/+np6eNibBOYaI/q+vr/53d3co/f3mCR7mPh3CCx7IEv -; 7DYgA1wZ5a/p5QAB7fHf3h/l1dXf3xFb2I/tLS0ibCkogA/oyMjP5jY2Mo/f3kCR7mPh3CCx7HODXF -; /rFZAP6MRgAe3R394Sj98gY6/q2trf719fUmwRuAiP6lpaX+b29vKP394wke5j4dwgsexwU1xpkfNx -; 7cHf3h/l1dXf30pYga/t/f3ybCjIj+vLy8/oKCghUo/f3hCR7mPh3CCx7I/plNABw1xQUe3B394Sj9 -; 9aKIsoj+v7+//vn5+aaIwZmIA/6bm5v+aGhoKP394Ake5j4dwgseyTc0NcQc/pVLAB7bHf3hKP32fx -; H+mJiYOibBQIiI/rOzs/56enoo/f3fCR7mPp1bwgsey/6nVAAcNcM0Htsd6aK2HfMo/fgVuYj+zc3N -; FybBlIj+ycnJ/pGRkREo/f3dCR7mPh3CCx7MK/7DYgA1wx8e2h3pp/H+qFQA/nk9AB3x/l1dXf35N6 -; uI/qampjImwZyIg4j+qamp/nJycij9/dwJHuY+HcILHs2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3w -; KP37pIj+hYWFOybCjoj+wMDA/oaGhiQo/f3aCR7mPh3CCx7P/plNAP7IZAClw8EnHtkd6BH+vF4APM -; H+mk0ANh3uKP38Bq+I/ri4uP739/cmwZqIMP6fn5/+a2trKP392Qke5j4dwgse0Df+ul0ANcCeWhIe -; 2B3o/qBQAD/BPMA3He4o/f1/M/6SkpL+5+fnJsEXPv63t7f+fX19Nyj9/dcJHub+djsAHcILHtIOHD -; U0Htgd5zb+vV8AwD/Bnlod7yj9/cEVtYj+x8fH/vz8/KOIwZaI/szMzP6VlZX+ZWVlKP391gme1OY+ -; HcILHtMr/sNiADX+mU0AHtcd5/6hUAAHwT/A/pFJAB3v/l1dXf39wjepiP6goKD+8PDwJsE5hYj+ra -; 2t/nZ2dij9/dUJHub+djsAHcILHtSitv6xWQA0Nx7WHeY2/r5fAMEHwJ1rNh3V/pRKAP7LZgAjHdUo -; /f3EpIi+iP7U1NT+////wpGI/sTExBozKP390wke5j4dwgse1gGjpR7WHeb+o1IA/sBhAFIKwAclHd -; X+rVcANcH+uV0APh3TKP39xaKIrYj+sLCwECbBm4j+2dnZ/qOjo/5tbW0o/f3S/oRGBh7mPh3CCx7w -; HeWjpf7BYQDBRgrA/no9AJs90/6CQQD+xWMANcOdW/6ORwAd0ij9/celiP6NjY3+4uLiJsKLiP67u7 -; v+gYGBKP390Qke5j4dwgse8B3l/qNSAP7BYQDCVv6hUQD+cDgA0yU1xhQAHdEo/f3IBrOI/sLCwhsm -; wZiI/tDQ0P5eXl4o/f3QCR7mPh3CCx7wHeQ7/sFhAMT+f0AA/nA4ANEAEjXGMT4d0ij9/ck3IP6amp -; r+7OzsJsFA/nFxcQYo/f3P/oRGBh7mPh3CCx7wHeT+nk8A/sFhAMMv/nA4ANH+mk0ANccEHdQo/f3L -; o4i7iBYXJsAz/o2Njf5iYmIo/f3O/npMHR7mPh3CCx7wHeOjpf7BYQDEMv5wOADPPi81xhL+fD4AHd -; T+bD8RKP39zDesiP6pqan+9PT0JsAQBf5sbGwGKP39zJ7U/nVPKf6ERgYe5P52OwAdwgse8B3j/phM -; AP7BYQDD/rJZAP5wOADQAv7LZgA1xQ4d1f5oSCko/f3PJP6Hh4f+3NzcJsEh/n9/fyQo/f3N/mdXRv -; 6CRwz+h0QA4z4dwgse8B3iO/7BYQDE/olFAP5wOADRADE1whT+iEQAHdSiXP5iVEYo/f3RBrCI/vz8 -; /KOIwZGI/qCgoP5nZ2c3KP39zD3+dU8pHuL+djsAHcILHvAd4v6YTAD+wWEAw5s9/nA4ANM+/qdUAB -; Q1Lx3V/mlGIyj9/dQ3/szMzBCqiMGdiP7Hx8f+dXV1i4go/f3NBwme1OA+HcILHvAd4Tv+t1wApdPD -; Av5wOADVIabiHdX+ZE86KP391v59fX3+t7e3/ufn5xd/wT7+kpKSMzco/f3MPSoe3/52OwAdwgse8B -; 3h/o5HAP7BYQDDP/5wOADt/mw/Ef5fWFEo/f3YOv6fn5/+1dXVDCbBmIj+uLi4J5GIKP39zf5vUjQJ -; Ht0+HcILHvAd4Dv+t1wApdPDAv5wOADs/mZKLij9/dskD/7AwMD+7e3tsojC/tra2v6FhYUkKP39zf -; 5lWEz+f0kRHtw+HcILHvAd4AL+wWEAwz/+cDgA6yv+YVZMKP393rWIHP7e3t69iCbBlIj+pqam/mlp -; aZWIKP39zJ7U/nVPKR7b/nY7AB3CCx7wHeD+t1wApdPDAv5wOADqNCj9/eGniP6RkZH+ycnJ/vT09K -; uIwRf+zc3N/nl5eRUo/f3NB/6CRwwe2T4dwf6USgAO/oxGAB7vHd8C/sFhAMM//nA4AOmiXP5jUUAo -; /f3kvYj+s7Oz/uXl5biIJsGMiP6YmJgRmogo/f3MPf56TB0e2D6dW8D+rVcA/tFpAMD+vmAA/pBJAB -; 7uHd/+t1wApdPD/pNKAP5wOADo/mtBF/5eW1co/f3mq4j+m5ub/tLS0v74+PiniMGaiP6/v7/+cHBw -; Bij9/c3+b1I0CR7WPqbi/sVjADXCnlr+p1QAHu0d3gL+wWEAwz/+cDgA6P5lTTQo/f3pFf6CgoL+vL -; y8OrSIwoCI/ouLiyQo/f3NMBYe1f6dTwA1xg/+jEYAHusd3v63XACl08P+mEwA/nA4AOYr/mFWTCj9 -; /ew2/qWlpTv++vr6pYjBloj+ra2t/mtra5SIKP39zJ7U/nVPKQke0zf+sVkAHDXFHP6eUAAe6h3dAv -; 7BYQDDP/56PQCbPeX+aEgpKP3976aI/oyMjP7FxcX+8fHxJsID/n19fRUo/f3NB/6CRwwe1P6ZTQD+ -; yGQANcYFNx7oHd3+t1wApdPD/qNSAP5wOADkolz+YlRGKP398rqI/q+vr/7i4uK6iKOIwZCI/p6env -; 5mZmaYiCj9/cw9/ndNIx7Uorb+tVsANcaZH/6VSwAe5x3c/olFAP7BYQDE/no9AJs94/5pRiP+XV1d -; /f31L/6Xl5f+zs7O/vb29ibBnIgA/nR0dBUo/f3N/m1UOgke1P6eUAAcNcWZHx7nHdz+t1wApdPDOP -; 5wOADj/mRPOv5dXV39/fcG/n9/f/65ubn+6Ojot4jChoj+kJCQ/mJiYpyIKP39zDD+ekwdHtQS/rpd -; ADXEmR8e5x3bKf7BYQDEFJs94f5sPxH+X1hRpR/9/fo6/qGhof7X19f++vr6JsGYiP60tLT+bW1tBi -; j9/cw9Pgke06K2/qxWABw1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXf39/TP+iIiI/sLCwv7v -; 7+8mwv7Y2Nj+g4OD/mFhYSj9/c3+Z1dGFh7U/plNAP7DYgA1wSge5/5wOADaC/7BYQDEFJs93ysa/l -; 1dXf39/cK2iP6rq6v+39/fOSbBk4j+o6Oj/mhoaJaIKP39zD0+HtSitgU1wJkfHucd2gio8MP+qFQA -; /nA4AN/+aEgp/l1dXf39/cWniP6Tk5P+ysrK/vT09CbBQAv+d3d3FSj9/c0H/oJHDB7U/plNAP7IZA -; CeWh7nHdmo8P7BYQDEMv5wOADdolz+ZE86/l1dXf39/cd/voj+tbW1/ubm5riIJsGKiP6Wlpb+Y2Nj -; Nyj9/cw9Kh7UorYnHucd2f6oVAD+wWEAw/6yWQD+cDgA3Sv+X1hRKP39/cqsiP6dnZ3+09PT/vj4+C -; bBPf68vLz+cHBwj4hA/f3N/m9SNAke/R3YFP7BYQDEKf5wOADc/mVNNCj9/f3NFf6EhIT+vr6+/uzs -; 7CbC/t3d3S0knIj9/c0wFh78Hdg4/sFhAMObPf5wOADbKxoo/f390AX+p6enGb+IpIjBlYj+qqqq/m -; pqapSIKP39zJ7U/nVPKf6ERgYe+h3XFP7BYQDE/o5HAP5wOADaNCj9/f3Tpoj+jo6O/sfHxyMmwv7Q -; 0ND+e3t7FSj9/c3+Z1dG/oJHDP6HRAD5Hdc4/sFhAMOda/5wOADZDv5jUUAo/f391ruI/rGxsf7j4+ -; O6iCbBjogK/mVlZTco/f3MntT+d00jHvgd1qbS/sJhAFrD/o5HAP5wOADY/mtBF/5eW1co/f392KqI -; /pmZmSX+9/f3JsGbiP7Dw8P+cnJyFSj9/c3+bVQ6CR72Hdb+rVcA/s1nAJs9RsE//nA4ANgR/l1dXf -; 39/dsG/oCAgP66urr+6enpJsKEiP6Ojo7+YmJiKP39zTD+ekwdHvUd1aO1/tFpAMBWnEydS8D+jkcA -; /nA4ANb+bD8R/l9YUSj9/f3esIj+o6Oj/tjY2P76+vomwZeIFBgGKP39zD3+elUv/otMDB7z/nA4AN -; UvNcKeav69XwAd1jQo/f394TP+ioqKIv7w8PCviML+1dXV/oCAgCQo/f3L/oGBgf7b29v+4dC//pZb -; IP6HRADyHdQ+/tFpAMOdW/6CQQAd1A7+YlRGKP39/eS4iP6tra3+4ODgvIgmwZKI/qGhof5nZ2eXiC -; j9/ceniP6kpKT+8/PzJsCUxP67lnAe8R3ULzXCLx3VAyj9/f3nqIj+lZWV/szMzP719fUmwTn+yMjI -; /nZ2doqIKP39xbeI/s3Nzf7+/v4mwyH+jlAQHu8d06O1NcL+lEoAHdX+ZE86KP39/ek3v4j+t7e3/u -; fn5xcmwYmI/pOTkzM3KP39wQYw/uzs7CbGIv6sflAe7h3TLzXA/r9gAP58PgAd1Cv+X1hRKP39/eyt -; iP6fn5/+1dXV/vn5+SbBmYj+ubm5/m9vb5CIKP39K/6+vr4bJsn+yq2P/o5QEB7s/nA4ANKjtTXA/q -; BQAB3V/mZKLij9/f3vpIj+hYWF/r+/v/7t7e2yiMIK/oaGhiQo/fv+h4eH/uDg4CbM/ujcz/6dZzD+ -; h0QA6x3S/qdUABL+gkEAHdT+bD8RGij9/f3ytYj+qamp/t3d3b6IJsEy/qenp/5paWk3Vf33qYj+rK -; ysECbOlMT+u5ZwHuod0aO1Dh3VNCj9/f31p4j+kJCQLTImwVX+zs7O/np6eoaIKP31vIj+0tLSJtIh -; /pZbIP6HRADoHdE+HdSiXP5jUUAo/f39+Az+srKy/uTk5LmIJsGNiCz+ZGRkNyj98aSI/pubmwUm0z -; L+qaWh/n5UKh7nHef+a0EX/l5bVyj9/f36q4j+mpqa/tHR0f739/cmwZuI/sDAwP5xcXEGKP3vNv7E -; xMT+/Pz8JtMZ/o2Njf5nZ2eXiP5tVDr+hEYGHuUd5v5lTTQo/f39/RX+goKC/ry8vP7q6uomwoKI/o -; uLi/5iYmIo/e04/ubm5ibTLjn+eHh4MyjB/mVYTP58Shce5B3k/mw/EQEo/f39/cKxiP6kpKT+2tra -; GybBloj+rq6u/mxsbAYo/ekN/rS0tC4m04mI/pqamv5qamoGKMOe1P51Tyn+hEYGHuId4/5oSCko/f -; 39/cWmiP6MjIz+xcXF/vHx8SbC/tPT0/5+fn6DiCj95xf+2NjYJtMq/svLyxeGiCjH/mdXRv6CRwwe -; 4f5wOADhDv5iVEYo/f39/ci5iP6vr6/+4uLiuogmwZCI/p+fn/5mZmY3KP3jAv6hoaH+8vLyJtMF/q -; enp/5vb28VKMk9/nVPKR7gHeD+aUYjKP39/f3LL/6Wlpb+zc3N/vb29ibBKv7Gxsb+dHR0FSj94bWI -; /srKyv7+/v4m0/7X19f+iIiILzcozAcJHt4d3/5kTzoo/f39/c2iiL+I/ri4uA22iCbBh4j+kZGR/m -; JiYjco/d03/pOTk/7q6uom0x/+tbW1/nV1dYyIKM/+YlpR/npMHR7dHd3+bD8R/l9YUSj9/f390K6I -; /qCgoP7W1tb++fn5JsEu/ra2tv5ubm4GKP3brYj+vLy8DCbThIj+lZWV/mlpaTco0Z7U/m9SNAke2x -; 3c/mZKLij9/f390yT+h4eH/sHBwf7u7u4mwv7Z2dn+hISEJCj92TH+3t7eJtObiAD+e3t7iIgo1f5l -; WEwW/odEANod2v5sPxH+YVZMKP39/f3Wtoj+qqqqN72IJsGUiP6kpKT+aGhologo/dUg/qmpqf709P -; Qm0xj+oaGh/mxsbAYo1z0+Htkd2f5oSCko/f39/dmniP6SkpILASbBQP7MzMz+eHh4iIgo/dO5iP7Q -; 0NAm0xf+0tLSMSAo2wf+gkcMHtcd16Jc/mRPOij9/f392ze9iP60tLQvCCbBi4j+l5eX/mNjYzco/c -; 8V/piYmCcm05WI/q+vr/5zc3MVKN2e1P56TB0e1v5wOADW/mw/Ef5fWFEo/f39/d6siP6cnJz+09PT -; /vj4+CbBDP69vb3+cHBwBij9zbCI/sLCwv77+/sm0/7e3t7+j4+P/mdnZ5eIKOD+b1I0/oRGBh7UHd -; X+ZU00KP39/f3hFf6Dg4P+vb29/uzs7CbC/t7e3v6JiYkzKP3L/oqKiv7k5OQm0z3+v7+//nh4eAIo -; 4zD+f0kRHtMd0ysaKP39/f3kBf6mpqb+29vbKqSIwZWI/qysrDoGKP3HPv6xsbH+9vb2JtOLiBn+am -; pqBijlntT+dU8p/oRGBh7RHdI0KP39/f3nAv6Ojo4P/vLy8ibC/tHR0f58fHwVKP3FOT8m052IKf6A -; gICEiCjp/mdXRv6CRwwe0B3Qolz+Y1FAKP39/f3qu4j+sLCw/uPj47qIJsEnGf5mZmaYiCj9waWI/p -; 6env7x8fEm0yP+qamp/nBwcBUo657U/ndNIx7PHc/+akQd/l5bVyj9/f397KqI/piYmP7Pz88fJsEq -; /sTExP5zc3MVKP0F/sfHxwgm0ywLL5iIKO7+bVQ6CZ7Uzf5wOADO/mVNNCj9/f397wb+f39//rq6uv -; 7p6em2iMKFiP6Pj48zNyj5N/6QkJD+6OjoJtMf/ri4uP52dnYzKPEw/npMHR7MHcz+bD8R/l9YUSj9 -; /f398hj+oqKi/tjY2P76+vomwZeI/rOzsxgGKPesiD3++Pj4JtOGiP6Xl5ccNyjzntT+dU8pCR7K/n -; A4AMv+ZkouKP39/f31pYg8/sPDw/7v7+8mwj/+gYGBgIgo9f6CgoL+3NzcJtOciP7IyMj+fHx8h4go -; 9wf+gkcMHskdyaUfGij9/f39+LeI/qysrP7g4OC8iCbBFP6ioqL+aGhoNyjxp4j+paWl/vPz8ybTJ/ -; 6kpKT+bm5uBij5PT4eyB3I/mlGIyj9/f39+6iI/pSUlP7Ly8v+9fX1JsE5/snJyf52dnaKiCjvuIg4 -; JtMX/tTU1P6FhYUvKP0H/oRGBh7GHcf+ZE86KP39/f39N76I/ra2tv7m5uYXJsGKiP6UlJT+Y2NjNy -; jrBv6Wlpb+7OzsJtOWiP6xsbH+c3NzJCj9wT3+ekwdHsUdxSv+X1hRKP39/f39wq2I/p6env7U1NQM -; JsGZiP66urr+b29vBijpOv6/v7/++vr6pYjTgYj+kpKSDZaIKP3E/m9SNP6ERgYewx3E/mVNNCj9/f -; 39/cUkAP6/v7/+7Ozss4jCGf6Hh4ckKOce/uHh4SbTDP7BwcH+enp6Aij9xzD+f0kR/odEAMIdwv5s -; PxH+YVZMKP39/f39yLSI/qioqBm/iCbBlYj+qamp/mpqajco46mI/q6urhAm04yI/p6env5ra2sGKP -; 3JntQ+/oRGBh7AHcH+aEgpKP39/f39ywL+j4+PLf7z8/MmwRf+z8/PG4aIKOEb/tPT0ybTQBYTICj9 -; zQf+gkcMHh2iXP5jUUAo/f39/f3OvIj+srKy/uTk5AgmwY2IOyCZiCjdpIj+nJyc/vDw8CbTlIj+q6 -; urFBUo/c89/ndNI5SI/l5bVyj9/f39/dCriDv+0dHRLibBm4gEFBUo2zb+xcXF/vz8/CbTCv6MjIz+ -; Z2dnl4go/f39/f395xX+gYGB/ru7u/7q6uomwoKIKf5iYmIo2f6Ojo7+5+fnJtMuKv53d3czKP39/f -; 39/euxiP6kpKQsGybBloj+sLCw/mxsbAYo1Q3+tbW1/vf39ybTPv6ZmZn+aWlpNyj9/f39/c0AAAAA -; AAAAAQ== +; thumbnail_QOI begin 480x240 31136 +; cW9pZgAAAeAAAADwBAD+WFhYwn/9wH/S/oyMjP7p6emxiP6vr6/+YWFhO8Ek/ra2tv78/PyLiP6JiY +; k79n/9/f6ZmZn+9PT0ooj+paWl/l1dXQrBrYj+u7u7G4mI/oGBgf5cXFwK/e47/cK0iP7X19f+//// +; xB3Bf/070qqI/rW1tRs+/oaGhgo7wxk8/uvr662I/rOzsxE78Qr9/cAN/sfHx/79/f2BiP58fHwKxB +; n+kpKSOj3+qqqqJAr97zv9/pSUlP739/cmxR0s/TvTOTe+iP7Dw8P+Z2dnO8eviP7IyMg5/tvb2/56 +; eno77X/92aHhCuT+h4eH/urq6rCI/ri4uP5jY2MKx7WI/szMzDn+1tbW/nR0dAr98Dv5Av7FxcUmxv +; 7R0dEs/TvSpYj+o6Oj/vT09JyI/pWVlSg7ySj+m5ub/vX19UAC/l5eXjvof/3aOv6sZRyg8/5qXVAK +; 4aWI/rS0tP77+/uQiP6MjIwKyhX+oqKiMkD+l5eXNwr98Dv2BDomxZqI/qGhof5dXV0s+zvTtoj+zs +; 7OCP7V1dX+cnJyO827iP7Y2Ng5/svLy/5vb2875Qr92/6AXz/+ymgGoeHABf6HYDkK372I/t3d3b+I +; /snJyf5sbGwKzf58fHz+29vb/vz8/AAckoj98jvxGf6xsbH+/v7+JsWAiP55eXk7wCz5O9MZ/pOTk/ +; 7t7e2riP6oqKj+X19fO88G/q2trSo6/pGRkTvhCv3d/qVkIjXEBv5iXFUK2xkkLp2I/p2dnRkKz6qI +; /rS0tP75+fmPiP6IiIgZCv3yO+65iP7e3t4mxRf+tbW1FTvBLPc71D7+vb29KoaI/n9/fwo70Qr+g4 +; OD/ubm5gz+u7u7/mZmZjvdCv3d/nFeSv7CZws1xv67ZhH+gF8/CtmxiAf+/f39/tnZ2f53d3cK0/6L +; i4v+5+fntIj+srKy/mJiYgr98zvr/pycnAymiMWNiP6JiYkKO8JV9jvU/oKCgv7j4+M5/ry8vBE71S +; D+wcHB/vz8/IWI/oCAgDvZCv3f/pZiLf7RaQDJBf6PYTM6Ctb+jo6O/u/v7z3+sLCwFQrVsYgAOf7b +; 29v+enp6gYj99DvnrYj+zs7OJsb+yMjIPjvEVfQ71KeI/qurq/739/eWiP6Pj4/+XFxcO9cZA/7x8f +; GjiP6pqakVO9UK/d/+al1Q/rtmETXM/rNlFzQK06iI/ry8vP78/PyJiP6EhIQK2KOI/pycnP7w8PCl +; iP6goKD+X19fCv30O+QeBSbFH/6ZmZn+XFxcO8VV8zvUuogwOf7Nzc3+bW1tO9u2iP7T09M5/tLS0j +; I70Qr94f6HYDk1z58v/o9hMwrR/n19ff7j4+Mq/sPDw/5oaGgK27uIMP78/Pz+zMzMGAr99TvgN/66 +; uroXJsX+2traMoeIx1XxO9SjiP6ampoUpYgVBpuI3Tf+paWlPZaI/piYmBk7zQr94aHh/qVkIjXS/r +; NlFzoKzaKI/qqqqgwU/pWVlRkK3aiI/q2trR+WiP6Ojo4oCv30O96+iP7k5OQmxTkYNzvILO871bCI +; /sTExDn+29vb/np6ejvgf/58fHz+4eHhuYj+wsLC/mlpaTvKCv3i/nFeSgs11AX+gF8/Csu2iP7V1d +; X+/Pz8A/5ycnIK4f6FhYX+4+PjKv66urr+ZGRkCv31O9v+pKSkKibFiYj+gYGBO8os7jvVPP7n5+cq +; /rOzs/5hYWE746mIGzmKiB47xgr95P6dYyg12BM6Csj+lpaW/vPz8y7+qampNwrjroj+vr6+KoaI/n +; 9/fxkK/fU717KIMP7////FVf7CwsL+ZGRkO8ss7DvVAv6zs7MMDTwZO+UZGv7s7Owu/rGxsQI7wgr9 +; 5P5xXkr+u2YRNdobJArFrIj+xcXFOYWI/n5+fgrmKP6VlZX+7e3tq4j+p6en/mFhYQr99jvU/pCQkB +; AmxZOI/pGRkQo7zCzrO9W/iP7b29v+/f39/sbGxhw76bGIKTn+2dnZ/nh4eDt//eX+j2EzNd0FLP5i +; XFUKwv6Dg4P+6enpsYj+u7u7EQrptoj+z8/POf7U1NT+cnJyCv33O9AkEybGIf5ubm47zlXpO9U3Ff +; 7z8/PAHTc76yj+np6eH5uIFf5eXl4K/eI6/qxlHDXg/rNlF/5qXVAKpYj+srKyGwkHGQrrJCAynIj+ +; lJSUNwr99jvO/oCAgBwmxRv+pKSk/l1dXTvPLOg71bOI/svLywj+19fX/nV1dTvuCj3+29vbOf7Jyc +; knjYj94P54XkQFoeHiBf6ac0z+29vbORoYjojv/n9/f/7e3t45E/5nZ2cK/fc7ygr+rq6u/v7+/n/F +; g4j+e3t7O9Es5jvVCiX+6+vrroj+q6urFTvtCsIV/rGxsf77+/uPiAcK/d7+nWMoNeb+4J9d/qWfmB +; kK8auI/re3twyNiP6FhYUZCv32Vci3iP7b29smxRf+ubm5JDvSLOU71ayI/rq6uiqJiP6CgoL+W1tb +; O+sKxhn+hoaGDbCI/ri4uC8K/dr+cV5KCzXo/rtmEf6AXz8K8hkHHLGI/q6urv5iYmIK/fc7xf6ZmZ +; n++Pj4JsWPiP6Li4sKO9NV4zvW/oCAgP7h4eE5/r+/v/5lZWU76wrKPjE5g4j+fn5+Cv3Y/o9hM/7R +; aQDrBSz+YlxVCvIYLTn+2dnZ/nh4eAr9+DvBIP7Kysr+////xv7Ly8v+aWlpO9VV4jvVFf6oqKgfJ/ +; 6RkZEoO+oKzSg/Mn/+p6enFQr91Dr+s2UXNe4o/mpdUArypIj+n5+f/vHx8QH+nZ2d/l9fXwr99zsA +; JybFmYgoGTvWLOA71riIEgj+0dHR/m9vbzvrCtC4iP7V1dU5/tDQ0CMK/dL+h2A5NfEF/o9hMwrzvo +; j+2NjY/vz8/Dz+a2trCv31o4gu/v7+/ibF/tzc3P52dnY72FXfO9WiiP6Xl5f+7+/vqIj+o6OjBjvq +; CtMG/qmpqRuTiD8ZCv3P/qVkIjX0/rRmF/5jXVYZ0greqYj+sLCwLiv+jIyM/l1dXQr98h/+4uLiJs +; UI/rGxsf5fX1872SzeO9WuiP7BwcE5goj+fHx8CjvpCtYZ/n9/f/7k5OQb/r+/v/5paWkK/cz+cV5K +; /sJnCzX2ny/+gWA/Gd8K0h7+5eXlKv63t7f+Y2NjCv3w/qGhoSqkiMUrAAo72izcO9YP/ubm5raILv +; 5iYmI76QraqYj+vr6+OYiIAAr9yv6WYi01+v6eYygJGeUKy6+IBCo3/n19fRkK/ewrAybGMf5mZmY7 +; 3CzbO9Uz/q+vrz2SiP6Li4sZO+gK3SgHJ6eI/q6urjMK/cb+cV5K/rtmETX8/rtnEf5yXksZ6wrFKP +; 6YmJj+7u7uqYj+pKSkFQr96v6NjY3+8/PzJsUB/pWVlSgKO9ws2TvWvoj+2dnZ/v39/S3+ampqO+gK +; 4bOIJTn+1tbWHwr9xP6PYTM1/cEF/o9iNBnwCsG4iAM5NP5wcHAK/eemiP6/v7/+////xv7X19f+cX +; FxCsI721XYO9WkiP6enp7+8vLyAf6cnJz+Xl5eO+cK5Df+oaGhLpmI/p2dnf5dXV0K/cCh4f6sZRw1 +; /cT+tGYX/mteUP5cXFzyM/6oqKgQmYj+kpKSNwr95P59fX3+5+fnJsUqDQYKxTvZLNc71bKI/snJyf +; 79/f3+2dnZ/nd3dzvoCuYZvoj+3d3dKv7GxsYJCvz+eF5E/spoBjX9xgX+gWA/GfP+gYGB/uHh4Tn+ +; v7+//mdnZwr94Rn+qqqqCKKIxYaI/n5+fgrIO9hV1TvVCv6NjY3+6urqsIj+rq6u/mFhYTvnCuok/r +; W1tSqNiBoK+v6dYyg1/coW/mNdVhnyrIj+urq6/vn5+YuIIv5dXV0ZCv3dtYj+2NjYJsVV/r29vQIK +; yjvXLNQ71auI/re3t/76+vqMiAAKO+YK7Sj+iYmJK62IECAK9v5xXkr+u2YRNf3M/rtnEf5yXksZ8i +; j+kZGR/uvr6wz+q6urMxnCCv3Y/paWlv739/cmxZGIBxkKzTvVLNM71f59fX3+39/fOf7CwsI+O+YK +; 8a6I/sfHxzmAiP58fHwK9P6PYTM1/c+fL/6PYjQJny/ytIj+y8vL/vz8/A4fGcQK/dOpiB4mxv7Pz8 +; /+a2trCtA71FXRO9UV/qWlpf719fWbiP6Tk5MoO+YK8zf+mpqaAVX+pKSk/l9fXwrwofIwNd6aLv6m +; UwD+zGcANe0w/mteUBnyFf6ioqIjwP6ZmZn+X19fGcYK/c4i/uzs7CbFDP6hoaE3CtJV01XQO9W2iC +; X+/f39/tTU1BQ75gr3uogdOf7Nzc0FCuwZwP6IYTk13/6tVwD+cDgAp/H+lUsA/rpdADXtny8BGfO/ +; iP7b29s5/sbGxv5qamoZyFX9ySj+s7OzFybF/t7e3j0K1TvSLM871Bn+lJSU/u7u7qqI/qampgY75Q +; r6Ff6srKz++vr6kogSGQrnGcL+pWQiNd/+lEoAHcCn8aXDorb+sVkAHDXt/qxlHf5jXVYZ8qmIMv74 +; +PiQiP6KioooGckK/cW5iP7f398mxRf+tLS0FQrXO9EszjvUPv6+vr45hYj+fn5+CjvlCvwZIv7m5u +; YM/ry8vP5oaGgK4hnE/nJeS/7CZws13v6/YAD+fD4AHcEqHsH+mU0A/shkADXt/rtnEf6BYD8Z8/6L +; i4v+5+fntIj+tLS0AhnLCv3B/p2dnRsmxY2I/oiIiBkK2TvQLMw71SL+5OTkuIj+urq6/mRkZDvlCv +; 3Cqoj+wcHBOYaI/oKCggreGcb+lmIuNd/+mk0AHcOn8R7CNwU17Z8vI/5jXVYZ8rCIMf77+/uBiP57 +; e3uBiM0K+q6IJSbG/sfHxw0K3DvPLMs71KiI/qysrP739/eViP6NjY0ZO+UK/cSiiP6RkZH+8PDwpY +; j+q6ur/mFhYQrZGcf+cl5L/rtnETXeEv6CQQAdwwD+uF0A/pBJAJtNw/6eUAA17jD+cl5LGfKjiP6b +; m5sFpoj+oaGhFRnNVff+iYmJ/vHx8SbFEP6ZmZkoCt87zVXKO9S7iP7W1tYI/szMzP5sbGw75Qr9yL +; WI/tPT0zkSEArVGcn+j2I0Nd/+p1QAHcT+oFAANcCeWv6jUgAewzXvny/+j2I0GfO6iP7U1NQ5/s3N +; zf5ubm4ZzwryFf68vLz+/v7+JsX+2tra/nNzcwriVcwsyTvTKP6bm5sUpIj+n5+f/l5eXjvkf/3LpI +; j+pKSkPZeI/pqamigK0BnKoeH+pWQiNd6dW/6ORwAdwwD+v2AANcP+ul0A/oxGAB7B/shkADXwMP5r +; XlAZ8qeIOv729vYY/o+Pj/5eXl4Zzwrvv4j+5eXlJsWdiP6srKz+X19fCuRVyyzHO9Qr/sXFxQj+29 +; vb/nl5eTvlf/3NGf58fHz+4ODguogx/mtrawrMGcwuCzXe/rldAP52OwAdwwQ1xgH+nlAAHsA0NfEF +; /oFgPxnzIv7j4+O5iP68vLz+ZmZmGdAK7P6mpqb+/Pz8JsWIiP6BgYEK5zvKLMY71Br+6Ojos4j+sr +; Ky/mFhYTvkCv3RqIj+ubm5OT7+iYmJCskZzf6eYyg13/6aTQAdw6O1/rNaADXIHP6sVgD+jEYA/rVb +; ADXzFv5jXVYZ8q2I/r29vf76+vqIiP6AgIAoGdEK57OIPybFF/7BwcH+ZGRkCuk7ySzFO9MR/rS0tB +; sNLQo75Ar90yga/uzs7KuI/rOzsxEKxBnO/nJeS/67ZxH+0WkA3hL+fD4AHcP+iEQAFDXLNA419CAu +; GfKiiP6UlJQJPf6oqKgkGdIK5P6Tk5P+9vb2JsWTiP6QkJAZCus7yCzEO9P+e3t7/tzc3P79/f0A/m +; hoaDvkCv3XOv7Ly8s5/tra2v56enoKwH/Q/o9iNDXfDh3EDjXO/plNADX1BTj+Y11WGfK1iP7Pz885 +; /tXV1f50dHQZ0wrgAv7Dw8Mmxv7T09MnCu5VxyzDO9IG/qKiov709PQj/paWlig75Ar92Tf+nZ2d/v +; X19SMzBhnOCf6sZR013p1b/ohEAB3D/oJBAP6/YAA1z/6eUAA19/60Zhf+a15QGfIk/qWlpf7z8/Od +; iD8GGdMK3f6BgYH+6urqJsWbiP6kpKQ3CvA7xizCO9In/s3NzQj+1tbW/nR0dDvlCv3bGbuI/tra2v +; 77+/v+ysrK/m9vbxnM/nlfRf7KaAY13v6tVwAdxP6aTQA10R81+AX+iGE5GfP+fn5+/t3d3Tn+xMTE +; HBnUCtkZ/rCwsP7+/v4mxYKI/nt7ewrzVcUswDvSCv6RkZH+7OzsrYj+qamp/mBgYDvkCv3bGcKliA +; X++/v7kIglGcr+nmMoNd/+lEoAHcM+/rldADXSDv7IZAA1+f6lZCL+Y11WGfKriP62trYMjoj+h4eH +; /l1dXRnUCta3iP7c3NwmxRf+uLi4JAr1O8QsO9KsiP67u7v++/v7iIj+gYGBCjvkCv3af8Uo/oWFhT +; 4M/rm5uf5nZ2cZxv5yXkv+wmcLNd4S/nw+AB3D/pRKADXU/rFZAKTUNfr+u2cR/oFgPxnz/o2Njf7o +; 6Ogb/rCwsP5jY2MZ1QrT/pubm/75+fkmxRj+i4uLGVX2O9gE/uPj4zn+vr6+/mVlZTvkCv3ZGco+/s +; TExDmEiP6AgIAZxP6PYjT+0WkA3/6gUAAdxP6tVwA11f6+YAD+rFYANfufLzgJGfKxiP7Hx8c5/tra +; 2v55eXkZ1QrQPikmxv7Ly8v+aWlpCvlV1ST+qamp/vb29peI/pCQkCg75Ar92BnNN/6VlZX+8vLyoo +; j+qKioJBnACf67ZxE13hL+gkEAHcMA/sVjADXWm03+o1IANf3+tGYXPBnyo4j+np6e/vHx8aOIN/5g +; YGAZ1QrN/oeHh/7v7+8mxZiI/p2dnf5dXV0K+zvTuYj+1NTUCP7Pz8/+bm5uO+UK/dcZ0TL+1dXVOf +; 7R0dEy/ohhOTXfDh3E/qBQADXZODX9wAX+j2I0GfO8iP7X19c5/srKyv5sbGwZ1grJBv65ubn+/v7+ +; JsX+29vb/nV1dYaI/cA70Bn+mJiY/vDw8B/+oqKiBjvkCv3XGdQVDf75+fn+2ZhXNd6dW/6ORwD+cD +; gAwwASNdr+mU0ANf3C/rRmF/5jXVafL/KoiP6vr68uOgf+Xl5eGdUKx7yI/uPj4ybFCDYVCv3CO86v +; iP7CwsI5gIj+e3t7O+UK/dZ/1/5zX0z+xGkONd7+uV0A/nY7AB3D/ppNADXc/p5QAP7IZAA1/cKfL/ +; 6BYD8Z8x7+5OTkt4j+ubm5/mVlZRnWCsT+o6OjKibFioj+hISECv3FO8we/ubm5jn+tbW1/mJiYjvl +; Cv3VGdj+lmIuNd8EHcM+/q1XADXd/rFZAA81/cT+nmMoCRnyroj+wMDAKoSI/n5+fhnXCsCxiCEmxv +; 7ExMT+ZmZmCv3HO8mpiP6wsLD++fn5HP6KiooZO+QK/dUZ2P5yXkv+u2cR/tFpAN4SAB3DAP7FYwA1 +; 3v61WwCitjX9xSAuGfI3/peXl/7u7u6piP6lpaUkGdb+j4+P/vT09CbFlIj+lJSUKAr9yDvIvoj+2d +; nZCP7IyMgrO+UK/dQZ2Tg13/6nVAAdxA414JkfDjX9xp8vOBnzt4j+0dHROf7S0tL+cXFxGdOmiP7B +; wcEmxv7X19f+cHBwCv3KO8Y3/p+fn/7z8/PA/pqamjeciOUK/dMZ2aHh/qxlHTXenVv+iEQAHcMAEj +; Xhnlr+o1IANf3I/rRmFzwZ8jP+p6enAZuI/pOTk/5fX18Z0P5/f3/+6Ojot4jFm4g+Bgr9yzvFsoj+ +; ysrKCP7Y2Nj+dnZ2O+YK/dIZ2v55X0X+ymgGNd7+rVcA/nA4AMT+mk0ANeT+nlAANf3JBf6IYTkZ8/ +; 6AgID+4ODgOf7BwcH+Z2dnGc7+rKysCCbFEf59fX0K/c07wwoH/urq6gwJ/mBgYDvlCv3SGdv+nmMo +; /tFpAN/+lEoAHcOjtf6zWgA15Tg1/cv+pWQi/mNdVhnyrIj+ubm5/vn5+YyI/oSEhP5dXV0ZyrWILC +; bFVf68vLwCCv3OO8KriP64uLgbi4j+g4ODCjvlCv3RGdv+cl5L/sJnCzXe/r9gAP58PgAdwyH+y2YA +; NeY4/shkADX9y/67ZxEeGfIo/pCQkCsb/q2trTMZyP6Xl5f+9/f3JsU2BygZwAr9zTvB/n5+fv7g4O +; C8iAT+ZmZmO+YK/dEZ2/6PYjQ13/6gUAAdxP6nVAA16P6xWQCk1DX9zJ8vOAkZ8rOI/srKyjn+2NjY +; /nd3dxnFLy0mxv7Ozs7+bGxsGcMK/cw7poj+pqam/vX19ZqI/pKSkig75gr90BnboeEwNd4SAP5wOA +; DDAP7FYwA16TSeWjX9zjA8GfIV/qGhof7y8vIy/pqamv5fX18Zwv6FhYX+7e3tJsWZiP6fn583GcYK +; /cq1iP7R0dH+/f39/tPT0/5xcXGJiOcK/c8Z3P6IYTk13w4dxP6aTQA165kfDjX9z58vOBnzvoj+2t +; raOS3+a2trGaOI/rW1tf7+/v4mxf7d3d3+eHh4GckK/ck2qIj+paWlBpuI5gr9zxnd/qVkIjXenVv+ +; lEoAHcM+/rldADXt/p5QADX90TAJGfKpiP6ysrL++Pj4kYj+n5+f/uDg4CbFCP6zs7P+YWFhGcsK/c +; gV/n19fQo75gr9zxnc/nJeS/7CZws13/6tVwA+HcL+lEoANe8fNf3SBf6BYD8Z8/6Kior+9PT0JsaM +; iP6Hh4cZzgr9xwI75wr9zhnd/pZiLjXi/r9gAP6CQQAdwC818B81/dMFFgkZ8K+INCbG/sfHxw0Z0A +; r9xv6NjY0ZO+UK/c0Z3S7+u2cRNeQUJTM18f6jUgD+yGQANf3UIC4Z7v6MjIz+8vLyJsWWiP6YmJg3 +; GdMK/cQ5/r29vQI74wr9zRnd/o9iNDX93v6xWQCk1DX91Z8vOBnrJAgmxhf+19fX/nBwcBnVCv3D/s +; DAwP76+vqJiP5+fn4KO+AK/cwZ3Qn+pWQi/tFpAP3fD5kvNf3XMDwZ6P58fHz+5ubmJsUq/q2trVX+ +; 9vb2l4j+kZGRNxnUCv3D/paWlicM/qenpzc73gr9zBndLv7CZws1/eAB/qdUADX92J8vERnlf/6oqK +; j+/f39JsUv/oCAgBnAIv7i4uK6iP6+vr7+ZmZmGdQK/cE7wLeI/tDQ0Dn+1dXV/nJycjvbCv3MGd4W +; /tFpAP3j/p5QADXqnlo16xb+Y11WGeKziP7X19cmxVX+v7+//mVlZRnCrIj+vLy8GwL+gYGBKBnTCv +; 3AO8GliD7+9PT0QP6VlZUoO9gK/cwZ3v6tYBE1/eT+mU0ANeidWyH+mEwA/shkADXr/qldERnhMP72 +; 9vapiMWSiP6QkJAoGcQo/pOTk/7s7Ow9/qmpqSQZ0wr9O8P+fn5+/t/f372I/sPDwy871gr9yxng/o +; lIBv6xWQD+zGcANf3i/p5QAAE15v6tVwD+cDgAwP6GQwCjtf61WwA16f6nVAAdGd+oiP7FxcUmxv7S +; 0tL+bm5uGci0iP7Nzc05/tbW1v50dHQZ0wr8O8SsiP65ubn++fn5joj+hYWFCjvTCv3LGeH+hEUGn8 +; P+mU0A/shkADX94f6xWQABNeX+lEoAHcEWHsAfHDXlnVshHcAZ3v6Dg4P+6+vrJsUM/qOjo/5fX18Z +; yiQC/vPz80D+l5eXBhnSCvs7xv6Ojo4rKv6wsLAGO9EK/coZ4wQewDf+sVkANf3gG6K2NeP+v2AA/n +; w+AB3CFh7BpcM0NeP+rVcAHcIZ3KKI/rKysv7+/v4mxRX+e3t7Gc7+fX19/tzc3Dn+xcXF/mlpaZOI +; 0gr6O8eziP7Jycn++/v7/tra2v52dnaEiM8K/coZ5AQewv6ZTQD+zGcANf3eD/6nVAA14v6gUAAdxP +; 6KRgAewqK2/qxWABw14P6USgAdwxnbuIj+3t7eJsUX/re3t/5iYmIZ0KqI/rW1tQyPiP6IiIgoGdEK +; +TvIKP6goKD+8vLypIj+nZ2d/l1dXTvMCv3KGeUEHsMSNDX93ZtN/qNSADXg/r9gAP6CQQAdwwD+xW +; MARv6eUAAewqfxHDXeEv58PgAdxBna/pycnAwmxQn+ioqK/l1dXVXTKQ0q/rGxsf5jY2MZ0Ar5O8q+ +; iCw5Gv5ra2s7ygr9yRnnBB7EorYOHDX93P6eUAA13w4dxCU1wRwONx4ONd8lHcYZ2K2I/s7OzibG/s +; rKyhwZ1hj+xsbGOf7b29v+enp6GdBV+DvLAv6ysrL+9/f3Cf6Li4sZO8cK/ckZ6AQexv6ZTQD+w2IA +; Nf3bHzXdnVv+lEoAHcOjtRI1xP6+YADANd4SAB3HGdct/vDw8CbFl4j+m5ub/l5eXhnYBv6dnZ0FpY +; j+n5+f/mBgYBnPVfc7zS3+5ubmOf65ubn+YmJiO8UK/ckZ6QQex6K2/rFZADX92jj+yGQANdsS/nw+ +; AB3DIzXnDh3JGdUV/rq6uhcmxf7b29v+dXV1Gdy7iD85/svLyxgZz1X2O86wiP7Dw8MqhYj+fHx8Cj +; vCCv3IGesEHsn+mU0A/shkADX92AU0Ndr+mk0AHcT+rVcANeYUIx3KGdS9iP7k5OQmxZ2I/q6uriQZ +; 3qiI/q2trf729vYJ/o6Ojv5eXl4Zzgr1O88Z/pmZmf7v7++piP6kpKQ3O8AK/cgZ7P6ERQYeyqK2ND +; X91/61WwDANdgSAB3DAP7FYwA15hL+fD4AHcsZ0/6lpaUqJsUN/oODgxni/oaGhv7k5OQ5/rq6uv5l +; ZWUZzgr0O9G6iP7T09M5Ev5wcHAK/cgZ7QQezA7+zGcANf3VmR8ONdcOHcQlNef+mk0AHc0Z0bKI/t +; XV1SbFF/7Dw8P+ZmZmGeStiP6/v78qFf5/f3/+XV1dGcwK9DvSpoj+q6ur/vX19ZuI/pGRkSgK/cQZ +; 7/6ERQYezafx/sNiADX91J5a/p5QADXVFP6ORwAdwwD+v2AANeYSAB3OGdA0/vT09KuIxTL+k5OT/l +; 1dXRnmooj+lpaW/u3t7aqI/qampiQZzArzO9T+goKC/uHh4Tn+wcHBIAr9whnw/oRFBh7Oorb+sVkA +; HDX90x811P65XQA+HcP+mk0ANecOHdAZzqeI/sLCwibG/tbW1v5wcHAZ6raI/tDQ0Dn+1NTUIxnMCv +; I70wrArYj+u7u7/vn5+Y2I/oKCghkK/Rnx/oRFBh7Q/plNAP7IZAA1/dIfNdP+mk0AHcM+/rNaADXm +; FP6ORwAd0RnN/oCAgP7p6ekmxZuI/qampv5fX18Z7KaIL/709PQ2/pSUlAYZywrxO9MKwv6SkpIJr4 +; j+ra2tBgr7GfL+hEUGHtE3/rVbAP7RaQD90Tj+yGQANdD+v2AA/nw+AB3D/ohEABQ15jE+HdIZyyj+ +; rq6uFybFhIgIGfD+f39//t/f3zn+wsLCDRnKCvE70grEBf7MzMw5/tjY2P51dXUK+RnzBB7TOP7MZw +; A1/c/+sVkApNQ1z/6gUAD+cDgAxA415/6aTQAd1BnKI/7a2tr+////xRf+u7u7/mNjYxnyq4j+t7e3 +; DI2I/oWFhSgZyQrwVdEKxqSI/qOjozKiiP6ampr+Xl5eCvUZ9f6ERQYe1Cs0Nf3O/r5gABs1zRT+gk +; EAHcMA/sVjADXmEh8d1RnJLD0mxY+I/o2NjSgZ9Cj+j4+P/unp6bGI/q6urv5iYmIZyQrvVdAKyf57 +; e3v+3NzcOf7IyMj+aWlpCvN/9gQe1aK2Bf7MZwA1/cyZHw41zP6tVwAdxP6aTQA15yUd1xnHPgsmxv +; 7Nzc3+a2trGfiyiDw5/tnZ2f54eHgZyArvO88KyyD+tbW1/vf395OI/omJif5dXV0K8Bn3/oRFBh7X +; /plNAP7IZAA1/cz+mU0ANcv+lEoAHcOjtTE15pouAB3YGcb+hoaG/u7u7ibFmYj+n5+f/l5eXhn6Ff +; 6goKD+8vLyMv6cnJz+YGBgnIjHCu47zgrO/ouLi/7o6OiziP62trb+YWFhCu4Z+AQe2KK2BTX9ywE1 +; yRL+fD4AHcP+lEoANecvHdoZxKOI/re3txcmxf7d3d3+eHh4Gf3AvYgsOS06GccK7TvNCtCxiP7Gxs +; Yqg4j+enp6CuwZ+QQe2gEcNf3J/p5QABw1x/6aTQAdxC815yMd2xnDu4j+4uLiJsVA/rKysiQZ/cIg +; /rCwsP739/cr/oyMjP5eXl4ZxQrtO8wK0qKI/p2dnf7w8PAu/qGhoTcK6Rn6/oRFBh7borY0Nf3IDv +; 7IZAA1xRIAHcMA/sVjADXmEv58PgAd3BnCJP76+vomxSv+h4eH/l1dXRn9xf6IiIj+5ubmKv63t7f+ +; Y2NjGcUK7DvMCtS7iD85/tDQ0P5ubm4K5xn7BJ/D3Q4cNf3G/rFZADQ1xA4dxP6gUAA15/6aTQAd3h +; nAOv7S0tImxg/+aGhoGf3IOv7BwcEqg4j+fX19GcQK7DvLCtaniBj+9fX1mYgHKArkGfz+hEUGHt6n +; 8f7DYgA1/cX+vmAAm001whT+jkcAHcMAEjXmEgAd3xn+jY2NMibFlYj+lpaW/l5eXhn9yjf+mJiYNq +; iI/qSkpBUZwwrrO8oK2f6EhIT+4+PjOf6+vr4gCuIZ/QQe36K2BRw1/cObTf6sVgA1wf65XQA+nVvD +; /ppNADXnDh3h/r+/v/7////G/tjY2P5ycnIZ/c64iP7T09M5/tDQ0P5wcHAZwgrrO8kK266IF/75+f +; mLiP6AgIAZCt8Z/cD+hEUGHuH+mU0A/shkAKXD/cP+sFgANcD+mk0A/nA4AMM+/q1XADXmFP6ORwAd +; 4ibFKv6pqakVGf3Qp4gcEJmI/pGRkf5eXl4ZwQrqO8gK3Rn+lZWV/u3t7a2IHAYK3Rn9wf6ERQYe4q +; K2/rVbADX9wg7+v2AA/nw+AB3DAP7FYwA15jE+HeMmxIaI/n9/fxn91P6CgoL+4eHhOf6/v7/+ZmZm +; GcAK6jvICt8j/s/Pzzn+1tbW/nNzcwrbGf3B/pqamv6MTQ4e5P6jUgD+zGcANf3A/qRSAB3EDjXn/p +; pNAB3l/v///8JV/r6+vv5kZGQZ/dYN/rq6uv76+vqKiBP+XV1dGQrpVccK4RX+p6en/vT09FX+l5eX +; /l5eXgrYGf3Aroj+yMjI/v39/f6OUBAe5afx/rpdADX9/p1OAB3CAP6/YAA15hL+fD4AHeYmwZGI/o +; +PjygZ/dgo/pGRkf7r6+sM/qurq/5hYWEK6TvGCuT+fn5+/t7e3jn+xcXF/mdnZwrWGf3A/oiIiP7r +; 6+sM/re3t/6GRwge5qK2/rFZABw1+/6rVgAdwQQ15/6nVAAd6CbA/tHR0f5tbW0Z/dy0iP7MzMw5/t +; fX1/51dXUK6DvFCuYv/ri4uP74+PiQiP6Hh4cZCtMZ/aaI/ra2tio6/oyMjBn+hEUGn8Po/plNAP7I +; ZAA1+v6vWAAdo7X+s1oANeYUIR3pDP6ioqIGGf3epYgz/vLy8sD+mJiYBgrmO8QK6Qf+6urqKv6ysr +; IVCtEZ/b6I/t7e3jn+ycnJGBnABB7porb+sVkANfkx/o5HABQ15v6tVwD+cDgA1aXTHdMbGf3iv4j+ +; 29vbOf7Gxsb+ampqkYjlO8QK6hg8/vv7+4GI/nl5eQrPf/wo/qOjo/739/ediP6dnZ3+XV1dGcEEHu +; v+mU0AHDX3nVs15/6USgAd1f6kUgD+gEAAHdMZ/eSpiP6zs7M9Df6JiYkoCuNVwwrso4j+oKCg/vHx +; 8aWI/p+fnzcKzBn8soj+zs7OCP7Y2Nj+eHh4GcMEHuyitv66XQD+0WkA/eAS/nw+AJou1P6AQAAxmj +; 4d1CgZ/eUa/ufn5yr+s7Oz/mJiYgriO8IK7z3+2NjYOf7Nzc0JCsoZ/Bb+8PDwqIj+sLCwJBnEBB7u +; /qdUAByitv3d/qBQAP5wOADVAf7DYgDA/oBAAB3U/l1dXcAZ/eUJ/sTExDmAiBsK4TvCCvCoiP6xsb +; H+9vb2l4gpKArHf/sg/r29vTmKiP6EhIQZxgQe76fx/sNiAKfx/doS/oJBAB3U/oBAABAowBAd1f5d +; XV3BGf3looj+m5ub/vDw8B/+oaGh/l9fX5yI3zvBCvP+h4eHLzn+u7u7/mRkZArFGfv+f39//uTk5L +; eI/sPDw/5paWkZxwSfw/Citv6xWQD+0WkA/dgOHdUj/sNiAML+ikUAHdX+XV1dwhn95ArAu4j+1dXV +; OTgnCt47wAr1r4j+wsLC/vr6+oiI/n19fRkKwhn6Bv6rq6v++fn5l4j+lZWVKBnIBB7yAf7IZAA1/d +; SdW/6ORwAd1KOl/qRSAP7DYgDCEB3W/l1dXcMZ/eMKwaiI/qysrP729vaWiBYoCtxVCvcZ/pmZmf7u +; 7u49/qampgYKwRn5tog/OQMjGcoEHvM3NDX90hL+djsAHdX+pFIA/sNiAMP+ikUAHdb+XV1dxBn94w +; rCMf7j4+M5/ru7uyAK21UK+bmIAzn+1NTU/nFxcQoZ+f6Xl5f+8/PzLv6pqakGGcsEHvX+p1QA/sxn +; ADX9z/6aTQAd1qXT/sNiAMObPR3X/l1dXcUZ/eIKw62I/r29vRuHiP6AgIAZCv3Ypoj+qqqq/vT09J +; 2I/pOTk/5eXl4Z9g3+xcXFOYSIJhnN/oRFBh72p/H+w2IAp/H9zP6/YAD+gkEAHdf+pFIAKMMsHdf+ +; XV1dxhn94QrEKP6UlJQYPf6np6f+YGBgCv3Z/oGBgf7h4eE5E/5nZ2cZ9P6FhYX+6enpGyogGc7+hE +; UGHveitv6xWQD+zGcANf3JDh3Yo6X+w2IAwzEd2P5dXV3HGf3hCsW2iP7Pz885/tXV1f5zc3MK/dgZ +; DSoMjYgAKBnwpYj+s7OzGwn+j4+PGdAEHvn+mU0A/shkADX9xp1b/o5HAB3Z/qRSAP7DYgDD/o9IAB +; 3Y/l1dXcgZ/eBVxiT+paWl/vPz852I/pWVlf5eXl4K/dYZwf6SkpL+6+vrsIj+r6+vJBnuu4j+3Nzc +; Of7Ly8v+bm5ujojRBB76orb+tVsANf3E/q1XAB3ao6X+w2IAwzEd2f5dXV3JGf3fCsgX/t3d3TkiDQ +; r91BnDtIgaOf7Z2dkuGet//p+fnx9V/qGhoSgZ0gQe/P6jUgAcNf3B/pRKAB3b/qRSAP7DYgDD/o9I +; AB3Z/l1dXcoZ/d9VyKuI/ra2tgyNiP6GhoYZCv3RGcUV/qOjo/7y8vIQ/pycnP5fX1+diOiwiP7MzM +; z+/f39/tvb2/56enoZ1AQe/Ss0NfwS/nw+AB3bo6X+w2IAw51rHdr+XV1dyxn93lXJGf6Ojo7+6Ojo +; sog2/mJiYgr9zxnIv4j+29vbOf7Jycn+a2trGeb+jY2N/u7u7gz+s7OzMxnVBB79wDf+sVkAHDX5JR +; 3d/qRSAP7DYgDD/o9IAB3a/l1dXcsZ/d5Vy7KI/sjIyDn+2tra/nh4eAr9zhnJqYj+tLS0/vj4+JOI +; /oqKiv5eXl4Z4qeI/rq6ujkN/oeHhxnXBJ/D/cL+mU0A/sNiADX2Ev6CQQAd3aOlKMOdax3b/l1dXc +; wZ/d1VzAb+np6e/vHx8aOI/p2dnQYK/csZzAv+5+fntIj+uLi4Ahng/n19ff7h4eE5/sXFxf5qamoZ +; 2AQe/cOitgU19A4d3/6kUgD+w2IAw/6PSAAd2/5dXV3NGf3dVc29iP7X19c5/srKyv5ra2sK/ckZzg +; kAKoSI/nx8fCgZ3KKI/qenpwwj/piYmCgZ2QQe/cX+mU0AHDXwnVv+lEoA/nA4AN+jpf7DYgDDnWsd +; 3P5dXV3NGf3dVc6piDb+9/f3lIj+jIyMKAr9xhnQN/6cnJz+8PDwLv6jo6P+X19fnYjatIj+09PT/v +; 39/f7V1dX+dHR0GdsEHv3GorY0Ne7+v2AAHx3g/qRSAP7DYgDD/o9IAB3c/l1dXc4Z/dxV0P6Hh4f+ +; 5OTkKv64uLj+Y2NjCv3FGdK6iDD+/Pz8/tLS0gUZ2CH+8vLyLv6srKwVGdwEn8P9yP6nVAAcNev+mk +; 0AHeGjpf7DYgDDnWsd3f5dXV3PGf3bVdGuiP7AwMAqhIj+fX19GQr9whnUAv6tra0Qmoj+kJCQ/l5e +; XhnUq4gTOYaI/oGBgRne/oRFBh79yafx/sNiAKfx6BL+gkEAHeL+pFIAKMP+lEoAHd3+XV1dzxn92w +; rSKP6Xl5cnLv6kpKQVCv3AGdf+hISE/uPj4zn+wMDA/mZmZhnS/oKCgv7n5+cq/r6+vv5nZ2cZ3wQe +; /cqitv6xWQD+zGcAorbl/qdUAB3jo6X+w2IAw51rHd7+XV1d0Bn921XTt4gDOf7R0dH+cHBwCv0Z2K +; 2IF/76+vqLiBMoGc4V/rCwsBsn/pGRkSgZ4AQe/cz+mU0A/shkADXiFP6ORwAd5P6fUAD+w2IAw/6U +; SgAd3v5dXV3QGf3bCtSmiP6np6cQJ/6SkpL+Xl5eCvoZ2ij+lZWV/uzs7Bv+q6urFRnMuYj+2traOf +; 7Ozs7+cHBwGeIEHtyith7sN/61WwA14P65XQD+djsAHeSjpf7DYgDDnWsd3/5dXV3RGf3aVdb+gICA +; /uDg4Dn+wMDA/mdnZwr4Gd22iAc5/tfX1xAZyv6bm5v+9fX1wBH+Xl5eGeMEHtiitv6nVAD+ul0A/t +; FpAJkf/p5QAB7t/qNSAP7MZwA13f6aTQAd5iIow/6ZTQAd3/5dXV3RGf3aVdc+/rm5uf75+fmLiP6D +; g4MZCvYZ3qSI/qampv7z8/N//pmZmf5eXl4Zxq6IPDmAiP57e3uBiOQo/oRGBh7Vp/H+rFYA/sNiAD +; XDHA7+jEYAHuwrNDXaEv58PgAd5jsow51r/no9AB3f/l1dXdIZ/dlV2Bkl/urq6gz+rKysJAr0GeH+ +; fn5+/t3d3Tn+xsbG/mlpaRnE/oqKiv7s7OwM/ra2tv5jY2MZ5H/A/oRGBh7VNzQ1xv6+YAD+kEkAHu +; w3/rFZAP7MZwA11w4d6P6fUAD+w2IAw/6ZTQAd4P5dXV3SGf3ZCtqziBo5/tjY2P52dnYK8hnjqoj+ +; t7e3/vj4+JGI/omJif5eXl4ZwKaILv77+/srCxnlKMEJHtcO/sxnADXFHA4e7QH+yGQANdSdW/6IRA +; D+cDgA6Dv+w2IAw51r/no9AB3g/l1dXdMZ/dlV2qSI/qGhof7y8vIy/pqamgrxGeX+jo6O/unp6Sr+ +; tLS0M7mI/t/f3zkt/mxsbBnlKML+hEYGHtin8f7DYgA1xg/+jEYAHuw3BTXS/q1XAB3q/plNACjD/q +; RSAB3h/l1dXdMZ/dkK3L+I/tra2jkK8BnnJy3++/v7i4g9Mv6bm5soGeYowgke2TcF/sxnADXFHP6e +; UAAe7QEcNc/+lEoAHeqjpf7DYgDDnWsUHeH+XV1d0xn92VXdqoj+srKyCvAZ6LWI/unp6baIVS4VGe +; Yowwke2wH+yGQANcYFNx7sNzQ1zP6/YAD+fD4AHev+mU0A/sNiAMP+pFIAHeL+XV1d1Bn92AretIgK +; 7xno/pGRkf7w8PA9/ri4uP7Z2dk5/s/PzycZ5CjECR7cN/61WwA1xpkfKx7tDv7MZwA1yf6gUAAd7K +; OlKMT+ej0AHeL+XV1d1Bn92ArdAwUK7xnmIP6/v785iYj+g4ODGaiI/rCwsP729vaXiAf+Xl5eGeEo +; xQke3jgcNcUcDh7tK/7DYgA1xhIAHe0BKMP+pFIAHeP+XV1d1Bn92Arbqoj+wMDA/vr6+i8K7hnm/o +; CAgP7l5eUq/sHBwf5oaGgZwv6Hh4cgOf69vb3+ZWVlGd8oxgme1N8r/rpdAP7RaQDGD/6QSQAe7KK2 +; /rFZADXEDh3uo6UxpdPDFB3j/l1dXdUZ/dcK2v6AgID+5eXlKv7Dw8M+Cu0Z5aOI/q2trQyXiP6UlJ +; QoGcSviAQbiYj+f39/KBndKMYJHuA3/rFZAByitsU0/opGAB7tAf7IZAA1wJ1b/o5HAB3v/plNAP7D +; YgDD/qRSAB3k/l1dXdUZ/dcK2KOI/q6urv74+PiZiDAK7xnkt4j+19fX/v39/f7R0dH+cnJyGcco/p +; iYmP7u7u4M/qioqBUZ2yjHCR7iAf7IZAA1w/6nVAD+cDgA/oRCAB7uorb+ul0AWv52OwAd76OlMaXT +; w/56PQAd5P5dXV3VGf3XCtcy/tfX1yr+09PT/nFxcQrvGeT+mJiY/vT09KOI/qenpwYZyjL+0dHROf +; 7V1dUyGdkoyAke4zcFNcCdW/6IRAD+cDgAwAse8B3oRh3G/o9IAP7DYgDD/qRSAB3l/l1dXdYZ/dZV +; 1v6ZmZn+8/PzLg0oCvAZ4q2I/sbGxjmDiP59fX0ZzaWI/qmpqQFA/pWVlf5eXl4Z1ijJCR7l/plNAP +; 6uVwAdwgse8B3onUsSHcUxpdPD/no9AB3l/l1dXdYZ/dYK1K6I/sfHxyqGiP5+fn4K8Rni/oaGhv7q +; 6uob/rm5uREZ0P6BgYEGvYj+xMTE/mhoaBnVKMkJntTm/nY7AB3CCx7wHemZH6GnHcP+j0gA/sNiAM +; P+qVUAHeb+XV1d1lX91lXT/oeHhysb/ru7u/5jY2MK8hngpYj+tLS0/vv7+5GI/o2NjRnTDf66urr+ +; +fn5jogPKBnSKMr+hEYGntTm/nY7AB3CCx7wHeqbTZ1LEh3B/rldAKXTwxQd5v5dXV3WGf3WCtEV/r +; W1tQyUiAcK8xngvIj+3d3dOf7Kysr+bW1tGdb+kZGR/uvr6yr+sbGxJBnQf8sJHuY+HcL+hEIAHvAd +; 6xKaLqXDHf6AQAD+w2IAw/6pVQAd5/5dXV3WVf3WCtC+iP7d3d05/szMzBgK9Bnef/6hoaEunYj+n5 +; +fKBnYNhoq/tra2j0ZzyjLCR7mPh3CCx7wHe0EEv6kUgD+w2IAwzUd5/5dXV3WGf3WCs4Z/qKioh9V +; /qCgoBkK9RndGP7Nzc3+/f39/tnZ2T0Z26OIMyMf/p6engYZzCjMCR7mPh3CCx7wHe+jpf6pVQD+w2 +; IAwRkd6P5dXV3XVf3VCs0Y/s7OziqBiD0K9n/d/o6Ojv7v7++qiP6ysrL+YWFhGd6+iP7a2to5Gv5s +; bGwZyijN/oRGBh7mPh3CCx7wHfH+lEoA/r5fAKOlNR3o/l1dXdcZ/dVVzP6Pj48nDP60tLT+YGBgCv +; cZ2xH+u7u7OYuI/oWFhRnhqYj+s7OzLpWI/oyMjP5eXl4ZyCjN/oRGBh7m/nY7AB3CCx7LpcMe4h3y +; Nf6kUgAd6SjXGf3VCsoC/r29vf76+vqOiA8K+Bnb/n19ff7i4uK5iP7ExMQcGeQ8/ufn5zn+ubm5ER +; nGKM4JHub+djsAHcILHsr+rFYA/tFpAP6+YAD+jEYAHuAd/eEo1xn91QrJFzMq/sbGxhwK+RnZooj+ +; qamp/vn5+ZiI/paWligZ5rCIMSqFiAgoGcQozv6ERgYe5j4dwgseyBL+w2IANcGeWv6eUAAe3x394f +; 5dXV3XGf3VCsco/qurqy6biP6YmJgZCvkZ2bWI/tTU1P79/f3+09PT/nNzcxnpN/6bm5s2qYj+paWl +; /mBgYBnCKM8JHuY+nVvCCx7H/qNSADXF/rFZAP6MRgAe3R394SjXGf3VVcYU/tXV1Sr+1tbWMgr7Gd +; j+lZWV/vPz8y7+qqqqBhnsuYghCCEUGcEozwke5j4dwgsexwU1xpkfNx7cHf3h/l1dXdcZ/dUKxf6W +; lpYjPf6srKz+Xl5eCvwZ1quIMTkzJhnvpogJ/vX19ZuIAzco0P6ERgYe5v52OwAdwgseyP6ZTQAcNc +; X+sVkAHtwd/eEo1xn91QrDrIj+xcXFKoiI/oCAgAr9Gdb+g4ODDRv+vLy8/mVlZRnyIv7i4uK6iP7B +; wcE+KM8JHub+djsAHcILHsmitv66XQD+0WkAxBz+lUsAHtsd/eEo1xn91QrC/oSEhA0b/r+/vyAK/c +; AZ1BX+sbGxG5OI/pCQkBn1rYj+vb29DIyIIv5eXl4ozQke5j4dwgsey/6nVAD+zGcANcM0Htsd6aK2 +; HfMo1xn91QrABv6ysrIMloj+kZGRGQr9wBnUuYj+2traOf7Nzc3+b29vGfco/pSUlP7s7Owb/q2trf +; 5iYmIozP6ERgYe5j4dwgsezCv+w2IANcMfHtod6afx/qhUAP55PQAd8f5dXV3XGf3UCsC8iP7b29v+ +; +/v7/s/PzzaMiP3CGdP+nZ2d/vb29lX+oqKi/l1dXRn4KMC1iDg5/tjY2P52dnYoywke5j6dW8ILHs +; 2itgU1wptNNx7ZHen+n1AA/rteAJ5aHh3wKNcZ/dX+n5+fEH/+pKSkGQr9wxnRr4j+ysrK/v39/f7c +; 3Nz+enp6goj6KMEk/qWlpTJ//pubmxUoyQke5j4dwv6EQgAez/6ZTQD+yGQANcEnHtkd6KXD/rxeAD +; zB/ppNAP51OgAd7ijXGf3TsIgaKoOI/nt7e4CI/cQZ0f6Kior+7e3tDP61tbX+YmJiGfsow/59fX3+ +; 3NzcOS3+ampqKMj+hEYGHuY+HcL+hEIAHtCitv66XQA1wBz+kEkAHtgd6P6gUAA/wTzANx3uKNdV/d +; IpGAz+t7e3Mwr9xRnPp4j+uLi4/vv7+46I/omJiRn8KMWqiP62trb++Pj4koj+ioqK/l5eXijGCR7m +; /nY7AB3C/oRCAB7SDv7MZwA1NB7YHec2/r1fAMA/wZ5aHe8o1xn90DP+urq6/vr6+is8Cv3HGc6/iP +; 7g4OA5/sfHxzoZ/SjH/o2Njf7p6emyiB8CKMUJntTmPh3CCx7Tp/H+w2IANf6ZTQAe1x3n/qFQAAfB +; P8D+kUkAHe/+XV1d1xn9zwj+4ODgKv7Jyck6Cv3HGc0o/qampj2aiP6ampooGf3AKMixiP7Hx8cqg4 +; j+e3t7KMQJntTmPh3CCx7UorYFNDce1h3mNv6+XwDBB8CdazYd1f6USgD+y2YAIx3VKNYZ/c4o/qen +; p/739/ediP6bm5soGVX9xxnMs4j+0dHR/v39/f7W1tb+dXV1Gf3BKMqiiP6enp7+8fHxLv6ioqL+YG +; BgKMIJHub+djsAHcILHtb+mU0Ao6Ue1h3m/qNSAP7AYQBSWsAHJR3V/q1XADXB/rldAD4d0yjWGf3N +; tIj+0tLS/vv7+/7Y2NgQGcBV/cgZy/6SkpIUPf6tra0GGf3CKMy7iP7X19c5/tDQ0AUowQke5j6dW8 +; ILHvAd5aOl/sFhAMFGVsD+ej0Amz3T/oJBAP7FYwA1w51b/o5HAB3SKNYZ/cwh/vHx8aeI/rCwsAYZ +; wVX9yBnJqoj+wMDAOYiI/oGBgRn9xCjNp4j+r6+v/vb29piI/o+PjwYoCR7mPh3CCx7wHeU4/sFhAM +; JW/qFRAP5wOADT/qBQAP7RaQDGnVsAHdEo1hn9yi/+wsLCG4yI/oKCghnDCv3HGcn+gICALyr+wMDA +; /mdnZxn9xCjQ/oaGhv7k5OQ5/r6+vv5mZmYJHub+djsAHcILHvAd5Dv+wWEAxP5/QAD+cDgA0QAS/t +; FpAMYxPh3SKNYZ/ckT/ubm5ir+wsLC/mdnZxnECv3HGccG/q6urhuViAMZ/cYo0Tr+wMDAGxH+hUYH +; Hub+djsAHcILHvAd5P6eTwD+wWEAw/6tVwD+cDgA0f6aTQD+0WkAxwQd1AYo1Rn9xwb+r6+vDJeIIR +; nFCv3IGca3iP7Y2NgI/s/Pz/5wcHAZ/cco0n/+l5eX/u3t7f6sfk8e5j6dW8ILHvAd4zv+wWEAxP5/ +; QAD+cDgAzz4vNcYS/nw+AB3U/otdMP7Gxsb+bW1tKNMZ/ccQ/tjY2Cr+0tLS/nFxcRnGCv3IGcX+mZ +; mZ/vT09KKI/qampv5eXl4Z/cgo1LaI/snFwP66lG7+jlAQHuQ+nVvCCx7wHeP+mEwA/sFhAMP+slkA +; /nA4ANAC/stmADXF/qdUAB3V/mhIKf63t7f+/Pz8jIj+jIyMKNIZ/cb+m5ubAaOI/qenpygZx1X9xx +; nErYj+x8fH/v39/YGI/nx8fBn9ySjWpYj+qKio/tbFtP6WWyD+h0QA4/52OwAdwgse8B3iO/7BYQDE +; /olFAP5wOADRADE1whT+iEQAHdSiXP5iVEYof/6Li4v+6+vrPf61tbX+ZmZmKNAZ/cSuiP7JyckqhY +; j+fX19GckK/ccZw/6Hh4c6G/64uLj+Y2NjGf3KKNj+gICA/tfTz/66lW7+h0QA4j4dwgse8B3i/phM +; AP7BYQDDJv5wOADTPv6nVAAUo7X+rVcAHdX+aUYjKMOuiDz+/Pz8/tvb2wgozxn9w/6JiYn+6+vrG/ +; 66urr+Y2NjGclV/cgZwSQQKjr+jIyMGf3MKNmriP65ubn+28q5Hx7gPh3CCx7wHeE7JqXTw/6ORwD+ +; cDgA1SEjHdX+ZE86KMWjiP6cnJz+9fX1QP6kpKQkKMxV/cIz/re3t/76+vqSiP6NjY0Zywr9yBnAvY +; j+3t7eOf7Jycn+bGxsGf3NKNv+kJCQ/uPf2/6sfk8e3z4dwgse8B3h/o5HAP7BYQDDnWv+cDgA7f5s +; PxH+X1hRKMi6iP7Z2dk5/szMzP5xcXEoyxn9wb6INyr+y8vL/m1tbRnMCv3IKP6ioqL+9/f3nYj+nZ +; 2dGf3Pf9yyiDz+yKuNHx7dPh3CCx7wHeA7JqXTwwL+cDgA7P5mSi4oyyQnKpGI/pOTkyjKGf0oES4Q +; /p+fnygZzQr9xxj+zs7O/v39/Sz+d3d3Gf3PKN4V/qGhof7bz8L+nWcwHtw+HcILHvAd4AL+wWEAw5 +; 1r/nA4AOsr/mFWTCjNf/6FhYX+5ubms4j+vLy8HCjHGf02FiqAiP54eHgZzlX9x/6Ojo7+7+/vqYj+ +; sbGx/mFhYRn90CjgvYj+0s7K/rqVbh7bPh3CCx7wHeAmpdPDAv5wOADqNCjRDf7CwsL+/Pz8hYj+g4 +; ODKMYZ/P6RkZE2DP6zs7MVGc8K/cWoiP68vLz+/Pz8L/6EhIQZ/dIo4aiI/rKysv7ZyLf+llsg/odE +; ANk+HcH+lEoA/qdUAP6MRgAe7x3f/o5HAP7BYQDDP/5wOADpolz+Y1FAKNOiiBL+8fHxEP6rq6v+Y2 +; NjKMQZ+iD+v7+//vr6+o2IABnRCv3E/n5+fv7j4+Mq/sPDww0Z/dMo4/6JiYn+39vX/qx+Tx7Y/nY7 +; AB3A/q1XADXA/r5gAP6QSQAe7h3f/rdcAKXTw/6TSgD+cDgA6P5rQRf+XltXKNa1iP7U1NQI/tPT0/ +; 51dXUowhn6/oCAgP7k5OQq/sXFxf5paWkZ0Qr9w6OI/qqqqgyXiDAZ/dUo5K+IIv7Hqoz+jlAQHtY+ +; puL+xWMApuLCnlr+p1QAHu0d3v6ORwD+wWEAwz/+cDgA6P5lTTQo2RX+pqamPTb+mpqaNyjAGfg3/q +; ysrD2aiP6Xl5cZ0wr9wraI/tXV1Qj+0tLS/nJycgrAGf3UKOU3O/7YzL/+nWcvHtX+nU8ANcYP/oxG +; AB7rHd4mpdPD/phMAP5wOADm/mw/ERoo23/+fn5+JBsi/mxsbBn4t4j+1tbWKiEyGdQK/cH+lpaW/v +; Pz86SI/qmpqTcKwBn91SjnEP7MyMP+upVu/o5PEB7Torb+sVkA/sxnADXFHP6eUAAe6h3dAv7BYQDD +; nWv+ej0Amz3l/mhIKSjfqIj+u7u7/vz8/IqIPBn2/piYmDI9/qurq/5eXl4Z1Ar9wKyI/sXFxTkkFw +; rCGf3VKOimiDr+18a1/pZbIP6HRADU/plNAP7IZAA1xgX+jEYAHuj+cDgA3Sal08P+o1IA/nA4AOSi +; XP5iVEYo4Qb+jY2N/uzs7C7+srKyIBnyrYj+xsbG/vv7+4eI/n9/fxnWCv3+g4OD/unp6Rv+u7u7/m +; RkZArDGf3VKOoi/trV0f6ziV8e1Df+tVsANcaZH/6VSwAe5x3c/olFAP7BYQDE/no9AJs94/5pRiP+ +; XV1d5bCI/s3NzTks/nl5eRnw/oaGhv7o6OiyiP69vb0gGdcK+wb+srKyG5OIBxkKxBn91SjrrIj+vL +; y8/s20m/6OUBAe1P6eUAD+zGcANcWZHx7n/nA4ANz+t1wApdPD/qNSAP5wOADj/mRPOv5dXV3lGcAG +; /p+fn/729vaciP6hoaH+X19fGeyliP60tLT++fn5lYglGdgK+7uI/tvb2/78/Pz+y8vL/m5ubo2Ixh +; n91Sjt/pOTk/7Vybz+rH5PHtSlw/66XQA1xJkfHucd2yn+wWEAxBSbPeH+bD8R/l9YUaUf5hnCvYj+ +; 29vbOf7Kysr+b29vGeq8iP7c3Ny/iP7Ozs4njojZVfkZ/p6env729vZV/qCgoBkKxxn91SjutIj+xc +; G9/rqUbv6OUBAe06K2/qxWABw1wpkfHucd2/6yWQCo8MM4/nA4AOH+Zkou/l1dXecZxCT+srKyKo+I +; /o+Pjxno/qGhoRB//qKioigZ2Qr5sIga/v39/f7b29sMgojJGf3VKO+kiP6kpKT+1cSz/p1nMB7U/p +; lNAP7DYgA1wSge5x3a/oRCAP7BYQDE/no9AJs93/5sPxH+YVZM/l1dXegZxSj+h4eHDbGI/rm5uT4Z +; 5LGI/szMzCqCiP56enoZ2wr4/oyMjP7u7u6riP6zs7MkCsoZ/dV/8b+I/tTQzP66lW7+h0QA1Df+sV +; kANcCZHx7nHdr+slkAqPDD/qhUAP5wOADf/mhIKf5dXV3pGcisiAD+/Pz8g4j+f39/GeL+jo6O/u3t +; 7Qz+tra2JBncCvaniP65ubkqjYj+h4eHCswZ/dUo8qmI/rW1tf7aybj+llsg/odEANQB/shkAJ5aHu +; cd2ajw/sFhAMQy/nA4AN2iXAL+XV1d6hnJo4j+l5eX/vPz83/+qKioJBnep4j+u7u7/vr6+pCI/oiI +; iBndCvb+e3t7/uHh4TkA/mpqagrNGf3VKPQp/uHc2P6sfk8e1Df+rFYAHucd2RH+wWEAwwj+cDgA3f +; 5sPxH+X1hRKOoZzLeI/tbW1jn+0NDQMhnc/n19fSS6iP7IyMj+ampqGd4K9Cj+pqam/vj4+JqI/piY +; mBkKzhn91Sj1sIj+xsbG/seqjP6OUBAe/f5wOADYFP7BYQDE/olFAP5wOADc/mVNNCjsGc2kiP6pqa +; kbk4gOKBnYoogcLpyIOygZ3gr0Nv7S0tL+/f39/tXV1f50dHQK0Bn91ij1Bv6dnZ3+2s3A/p1nLx78 +; Hdj+o1IA/sFhAMObPf5wOADb/mw/ERr+XV1d7BnPKP6AgID+5OTkG/7AwMD+ampqGda1iP7T09Mq/t +; jY2P51dXUZ4Arz/pOTkyMu/qysrAYK0Rn91ij3uoj+zsrG/rqVbv6OTxAe+v5wOADXpdP+wWEAxP6O +; RwD+cDgA2jQo7RnSIP6+vr45Ef6GhoYZ1P6VlZX+8fHxPf6vr68VGeAK8quI/sHBwTmHiP6AgIAK0x +; n91ij4p4j+rq6u/tjHtv6WWyD+h0QA+R3XOP7BYQDDnWv+cDgA2aJc/mNRQCjuGdM3Fv7u7u6oiDb+ +; Y2NjGdA+/sTExCqJiP6CgoIZ4grx/oGBgf7n5+cq/r+/vy8K1Bn91ij6/oWFhf7c2NT+s4lfHvgd1q +; bS/sJhAFrD/o5HAP5wOADY/mtBF/5eW1co7hnWs4glOf7X19cuGc4iPhv+wcHBLxniCvCjiP6vr68b +; J/6RkZEZCtUZ/dYo+ysm/s61m/6OUBAe9h3W/q1XAP7NZwCbPUbBP/5wOADY/mVNNP5dXV3vGdgG/q +; Kiov739/eaiP6enp7+Xl5eGcoV/rGxsf75+fmXiBIoGeMK77iILDn+zs7O/m9vbwrXGf3XKPs3/paW +; lv7Xyr3+rH5PHvUd1aO1/tFpAMBWnEydS8AC/nA4ANb+bD8R/l9YUSjvGdoovoj+3t7evYj+x8fH/m +; 1tbRnIuoj+2traKv7Q0ND+cHBwGeQK7/6ampr+9fX1f/6kpKQoCtgZ/dco/TL+yMS//rqUbv6OUBCU +; 1PMd1S81wp5q/r1fAB3W/mhIKSjxGdymiP61tbUqjoj+jY2NGcb+nZ2d/vT09C7+pqam/l1dXRnlCu +; 2uiP7IyMg5gYj+e3t7CtoZ/dd//cCkiP6np6f+1sW0/pZbIP6HRADyHdSjtTXDnVv+gkEAHdSiXP5i +; VEYo8Rneooj+iYmJ/urq6j3+tra2/mZmZhnCOv7Jycn++/v7hIj+fX19GeYK7v7s7OwMHzMK2xn91y +; j9wv5/f3/+19LO/rqVbh7xHdT+rVcANcIvHdX+aUYjKPIZ4a6I/sfHxzmAiAj+XFxcwP6Li4v+6+vr +; DP65ubkzGecK7v7q6ur+ioqKCt0Z/dgo/cKqiP7Ozs7+4dC//o5QEJTU7x3To7U1wv6USgAd1f5kTz +; oo8hnjo4g7AVX+p6enDBs6/oyMjBnoCu+wiAreGf3YKP3BBv60tLT+/v7+f5TE/qx+UB7uHdMvNcD+ +; v2AA/nw+AB3U/mw/Ef5fWFEo8xnluoj+39/fF8D+0dHRGBnpCu/+f39/Ct4Z/dgo/cAfBv7////D/s +; qtj/6OUBAe7P5wOADSo7U1wP6gUAAd1f5mSi4o9BnlKP6lpaUuPTmRiP6UlJQoGecK8P709PT+p6en +; JArcGf3ZKPz+np6e/vr6+ibF/ujbzv6dZzAe6x3S/qdUABIAHdQr/mFWTCj0GeWziP7Q0NAq/tra2v +; 55eXmqiP7m5uaziP69vb0cGeUK8f7S0tI5NP5ycnIK2xn92Sj6OjQmxi0T/tHNyf66lW4e6h3Ro7X+ +; p1QAHdX+aEgp/l1dXfUZ5f6SkpL+8PDwDP6xsbEVGcCqiAQ5hogiGeQK8RUgLhg/KAraGf3ZKPj+i4 +; uL/vHx8SbFl4j+mZmZ/l9fXyioiP6xsbH+2ci3/pZbIP6HRADoHdE+HdSiXP5jUUAo9RnkL/7AwMAb +; jIj+hISEGcOiiP6RkZEFpYj+rKys/mJiYhnhCvMZ/n5+fv7g4OAq/r+/vxwK2Rn92Sj2M/68vLz+// +; //xjv+dHR0KMP+iIiI/t7a1v6ziV8e5x3n/mtBF/5eW1co9Rnk/oGBgf7l5eUq/sTExP5oaGgZxrSI +; /tPT0/79/f3+1NTU/nV1dRngCvWpiP65ubkqiYgPCtgZ/dko9b+IICbFnYgJJCjFOv7CwsL+zrWc/o +; 5QEB7lHeb+ZU00KPYZ4wb+rq6uPZmIPygZyKSI/qSkpD2XiP6bm5v+Xl5eGd1V9yg4/uvr6y4n/mNj +; YwrWGf3aKPP+qKioOSbFiIj+goKCKMg3/pmZmf7YzL/+pHI/HuQd5P5sPxH+X1hRKPYZ47iI/tfX1y +; oSIxnLKP59fX3+4ODgKjH+bGxsGdsK+gn+y8vLOf7W1tb+d3d3CtUZ/doo8bKIPybFVf7BwcH+ZmZm +; KMsB/svHwv66lW4fHuId4/5oSCko9xnjLP7z8/OkiP6qqqo3Gc4CDDk+/omJiRnaCvsG/p6env719f +; WbiP6dnZ3+Xl5eCtMZ/dt/7/6UlJT+9vb2JsWTiAM3KM0zK/7XxrX+llsg/odEAOEd4aJc/mJURij3 +; GeKuiP7Hx8cqh4j+f39/GdE3/oyMjP7s7Owu/rOzs/5lZWUZ1wr9wL6IOzn+xsbG/mxsbArSGf3cKO +; wgMf7////G/tTU1DYo0RP+2dXR/rqVbv6HRADgHeD+aUYjKPgZ4v6Hh4f+6urqG/68vLz+ZGRkGdQJ +; Gv78/Pw7/np6ehnVCv3Cp4j+sLCwKo2IKRkK0Rn92yjrIjomxZuI/qSkpP5gYGAo06yI/ru7u/7cy7 +; r+jlAQlNTeHd/+ZE86KPgZ4ST+tbW1/vn5+ZWI/o+PjxnXBv6dnZ3+9vb2I/6ioqIGGdMK/cMZ/oeH +; hz49EP5lZWUK0Bn93H/oN/6xsbEXJsWCiP58fHwo1/6SkpL+3NTL/qx+T/6HRADdHd3+bD8R/l9YUS +; j4GeG9iP7d3d2+iP7MzMz+bm5uGdq8iDsqGjYZ0Qr9xhwx/vz8/CgICs8Z/dx/57eI/t3d3SbFF/64 +; uLj+Y2NjmojZs4j+xMC8/smrjf6OUBAe2x3c/mZKLij5GeEz/vb29lX+oaGhKBncpYj+sLCwKpCI/p +; CQkBnPCv3IN/6Wlpb+8fHxoogRFQrNGf3dKOX+m5ub/vn5+SbFjoj+jIyMNyjbFf6jo6P+3M/D/p1n +; MB7aHdor/mFWTCj5GeAn/s7OzjmAiP56enqCiN8o/oaGhj4M/rq6uv5nZ2cZzFX9y7iI/tbW1v79/f +; 0H/nBwcArNGf3dKOIc/s3NzSbG/svLy/5ra2so376I/tPPyw4e2R3ZNCj5GeEW/u7u7gz+tbW1JBni +; Pv7Dw8M5hIj+gYGBGcsK/cwk/qmpqf74+PiUiP6Tk5MoCssZ/d0o4S3+7+/vJsUu/p2dnf5fX19A4S +; /+s7Oz/trJuP6WWyD+h0QA1x3Xolz+ZE86KPkZ4KiI/r29vf76+vqPiP6Hh4cZ5TcwI6KI/qmpqSQZ +; yAr9zhkE/uLi4hv+vLy8/mhoaArKGf3eKN4k/rq6uhcmxf7b29v+d3d3KOX+ioqK/uDc1/6sfk/+h0 +; QA1h3WK/5fWFEo+Rng/n5+fjO5iP7Hx8f+ampqGei2iP7V1dX+/Pz8/tLS0v5zc3MZxlX90SAIOTP+ +; g4ODCskZ/d8o3LyI/uTk5CbFQP6wsLAkKOewiP7FxcX+yKqM/o5QEJTU1B3V/mVNNCj6Gd83/qqqqv +; 739/eciCwoGeqkiP6np6f++fn5J/6YmJgoGcQK/dIoJf7t7e2piP6rq6v+YmJiCsgZ/d8o2v6kpKQq +; JsWKiP6FhYUo6gb+nJyc/tnNwP6dZy8e0/5wOADT/mw/Ef5hVkwo+VXgIyEqP/50dHQZ7SgX/uPj47 +; eI/sHBwf5qamoZwgr91bOIBzkh/nV1dQrHGf3gKNewiCEmxjH+Z2dnKO25iP7NycX+upVu/o5PEB7R +; HdI0KPoZ4P6Wlpb+8vLyPf6tra0GGfCoiP68vLz+/Pz8iYj+h4eHGcAK/dcV/qGhof729vY2/pqamj +; cKxRn94CjWJf709PQmxTL+lJSUNyjvpogY/tfHtv6WWyD+h0QA0B3Qolz+Y1FAKPoZ3w3+xcXFKoiI +; /oGBgRnzN/6NjY3+7e3tHwX+Y2NjCv3Z/nt7e/7c3Ny/iP7Dw8P+a2trCsV//eAo0wL+wcHBJsb+19 +; fX/nFxcSjz/oSEhP7b19P+s4lfHs8dz/5qRB3+XltXKPkZ4DH+6OjoG/6/v78vGfaxiAc5/tjY2P53 +; d3cK/dkC/rS0tCqMiAsKxBn94SjR/oCAgA23iMUb/qioqBUo9Sv+vr6+/s60m/6OUBAezf5wOADO/m +; VNNCj6Gd8V/rKysgw2/pKSkigZ+Ab+oKCg/vf395qI/p+fn/5eXl4K+VUK2igLHD0j/mVlZQrCGf3i +; KM43/q2trf7+/v4mxYWI/n9/fyj5/pWVlf7Wyr3+rH5PHswdzP5sPxH+X1hRKPkZ4LuI/tvb2yr+z8 +; /P/nBwcBn6VRm9iP7c3Nwq/sjIyP5tbW0K+DsK3K+I/sfHxzn+2tra/nt7ewrCf/3iKMy1iDv+//// +; xRf+vLy8ICj7I/7Hw7/+upRuH5TUyh3L/mZKLij6GeD+np6e/vX19aKI/qSkpCgZ+grCpoj+tLS0/v +; v7+xw4CvZVwArdpIj+mpqa/vLy8sD+oaGh/mBgYArAGf3jKMr+mJiYPSbFNv6Ojo7+Xl5eKP2kiP6m +; pqb+1sW0/pZbIP6HRADJ/nA4AMmlH/5hVkwo+RngsIj+y8vLKoOI/nx8fBn7CsQZ/oiIiBw9/re3t/ +; 5mZmYK81XBCt+7iP7Y2Nj+/Pz8Gv5vb28KGf3lKMaqiP7JyckmxhYYKP3D/n5+fv7V0c3+upVuHsj+ +; cDgAyP5pRiMo+hng/oyMjP7s7OytiP64uLj+YmJiGfsKx62I/sbGxjmBiP59fX0K8jvBCuCmiP6srK +; wMkYgl/l1dXRn95SjE/oWFhf7t7e0mxZmI/qCgoAYo/cU+Lv7ayrn+jlAQHsYdx/5kTzoo+Rngp4j+ +; ubm5/vr6+pGICxn8CsmjiP6YmJgywP6mpqYGCu87wgrhGf6Dg4P+5OTkGwwNGf3lKMGjiP62trb+/v +; 7+JsX+3t7e/nl5eSj9yTj+4t3Z/qx+Tx7F/nA4AMX+bD8R/l9YUSj4GeH+fHx8/uDg4Co8/mxsbBn8 +; Csy5iP7X19f+/Pz8Fv5xcXEK7TvDCuM+/sDAwDkV/oCAgBn95Si6iP7h4eEmxUD+s7OzMyj9yyf+x8 +; fH/siqjf6OUBCU1MMdxP5lTTQo+RngKP6np6f+9/f3nYj+nJycKFX8Cs4G/qurq/76+vqTiDAZCus7 +; wwrko4j+kpKS/u7u7gE+Mxn94/6goKAbJsWMiC0o/c4G/p6env7azsH+nWcvHsIdwiv+YVZMKPgZ4Q +; X+0tLSKv7Z2dn+dnZ2Gf3ACs8Z/oGBgf7k5OQb/r29vQ0K6TvECuYU/tHR0TkD/nNzcxn94K+IAybG +; /sfHxw0o/dG7iP7QzMf+upVu/o5PEJXDwB3B/mhIKSj4GeL+k5OT/vDw8KiI/rCwsP5gYGAZ/cAK0i +; D+v7+/OYeIIgrnO8UK5xX+pKSkLif+mJiY/l5eXhn93SkjrYjFlogdBij906eIBf7YyLf+llsg/odE +; AP5wOACiXP5jUUAo9xniqoj+wsLCG4yIIhn9wQrUKP6Pj4/+7+/vEP6tra3+YmJiCuQ7xgroGf5+fn +; 7+39/fKv7AwMD+ampqGf3aM/6+vr7+////xv7Y2NgyKP3X/oeHh/7d2dX+s4lf/oxiOP5nY2Ao9hnj +; BC8q/sPDww0Z/cEK17SIA/78/Pz+1dXV/nV1dQrjO8YK6RkR/ri4uCqJiB4Z/dj+fX19LybFKv6rq6 +; sVGSj92K6INRsR/oCAgDco8hnjo4j+r6+v/vj4+JmI/pWVlSgZ/cEK2Qb+oqKiLpmI/pycnCgK4DvH +; Cul/wDc4/uvr6y42/mRkZBn91Cj+qamp/v39/SbFLzUZwyj91zf+l5eXJ6uIHP5hYWEo7xnkEB0qA/ +; 5ycnIZ/cIK2xm/iP7f39+8iP7FxcX+a2trCt47yArqGcEJ/srKyjkd/nl5eRn90rOI/tfX1ybFVf7A +; wMAgGcYo/de3iP7R0dE5/tbW1gEo7Bnl/pubm/709PQu/qioqDcZ/cJV3qeI/re3tzmLiP6KiooK3D +; vJAAAAAAAAAAE= ; thumbnail_QOI end ; ; -; thumbnail begin 640x480 35568 -; iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAABn+UlEQVR4Ae29V5Bc15mtmU8d89AP00 -; 96UsxE6GV6YoJzZ6Zn5oYU6kt1q0GBElsULyW2KJEEAYIkCILwrlDwQMEXCt57bwreew8QAOEJ7x1B -; eE9IovRfrl19gKxiZp6Tp3Zm7v/stSO+q75SoZAnq1B71b/3Wiv1xz/+UYrBO++8QwghJeO9996Txo -; 0bS7NmzWTAgAGyceNGuX37toStb7/9Vm7evClHjx6VdevWyaRJk6Rbt27SpEkTVc9PCCHppCj8CCG+ -; 8O6778r7778vH3zwgXTq1Enmzp0rZ86ckb/85S+hQvDRo0dy9uxZ2blzp8ybN08GDRokLVq0MJ9T03 -; tACCGgYAJQ05tACPGLYBrYvHlzGT58uOzatcsIvLD15z//Wa5fvy4HDhyQlStXytixY42QhKjU9PyE -; EGJdAGp6eEKIvwTTwKZNm0r37t1lyZIlcuXKFfnrX/+aUwT+7W9/k/v378vJkydly5YtMnPmTOnbt6 -; 85Wtb0/IQQv7EmADU9NCGEBDRq1Mjc52vZsqVMmDBBDh48KM+ePQudBuJjLl++LHv37jXiEZPEtm3b -; mumipucnhPhJvQWgpoclhJB0Pnj3LfOfmAbiSPjjjz+WiooKWbt2rTF+hC1MC2EkOXbsmGzYsEGmTJ -; kiPXr0MFNFTe8DIcQ/YgtATQ9JCCHptH3vV7L+o38w4P/GfwcRiGkgDCLt27c3R7s45sW9v7D15MkT -; OX/+vLlLuGDBAhkyZIh89tlnNIgQQpwlbwGo6eEIIaQuVY3/SQ43Sz1n78d/Z/674H8PDCKffPKJDB -; 06VLZt22bu/IUtOIlv3Lghhw4dktWrV8v48eOlS5cu5nNpen8IIX6QovgjhPgAjntnffCjWuIvnRUf -; /kCav/sb87HpcTHl5eVSXV0tFy9eNJmAuRYMIg8fPpTTp08b4Thnzhzp16+fcRtreq8IIcknReFHCE -; k6OObFpC+b+EufBvZ8/+Xnfy4wiCDvb8yYMbJv3z55+vRp6DTwT3/6k1y9elX2798vy5cvl5EjR0qH -; Dh3M59P0vhFCkkuKwo8QkmQmf/CPocKvLvgz6QYRTAM//PBD6d27t6xatUq++uqrUBEIg8jdu3fl+P -; HjsmnTJpk2bZr06tXLfB5N7x8hJJmkKPwIIUkkMHrkK/4C0g0iIJgGtmnTRqZOnWqcv5j0hS1MDHF8 -; vGfPHlm0aJG5V9iqVSvGxRBCSkqKwo8QkjTqGj3iEhhEgmlgep8wquAw2cOUL2wFfcJHjhwxETMTJ0 -; 6Url27sk+YEFIyUhR+hJCkAKFW3fSHVsRfFINI586dTS/wuXPn8uoT3rFjh/lzAwcOlE8//VTVe0wI -; SQYpTS+WEEKyAfNGFKNHfaaB/d//8fO/D9NATPAg4GDy2L17tzx+/DhUBNbtEx49erTpE6ZBhBBSTC -; gACSHqyRXvYhv8XXUNImj+QAPIsmXLjPsXcTC5Fgwi9+7de94nPGPGDNMnjCYSTe87IUQvFICEELXA -; pLHto78vmvhLN4hkiouBuWPSpEkmDDpKn/A333xTq0942LBhxmRCgwghpNBQABJCVDKmyUtFF35RDC -; KY4iH8ef369XLr1q1QEQiDCD4OrmL8mcmTJ0v37t3NHUNNXw9CiC4oAAkhqqhvvIttYBDJ1CfcsWNH -; mTVrlpw6dSqSQQT3B2Em2blzp+kTHjx4MPuECSEFgwKQEKIGW/EuhZoGBq8zmAaiAq6qqsq4fh88eB -; AqAiEUETJ98OBB0yc8btw4KSsrM/cMNX2dCCHuQwFICHGesB5fl6aBdQ0imAYi8w8h0JcuXTIGkFwL -; BhKIRUwOt27dKrNnzzZHyp988omqrxkhxG0oAAkhTlMqo0d9poGZDCI4zsVE74svvojcJ3zlyhXTPw -; x38YgRI6Rdu3aMiyGEWIECkBDiLHF6fF2hbp8wjoQ/+ugj6dOnjznevXHjRqgIxLTwzp078uWXX8rG -; jRtNBV3Pnj3ZJ0wIqTcUgIQQ53DN6BGXbH3CmORNnz7dCDsEQ4ctTAwvXLhgwqarq6ulsrJSWrZsSY -; MIISQ2FICEEKdw1egRl1x9wnD6IggaodBhC3ExX3/9tRw+fNj0CU+YMEHKy8vN59L09SWEuAEFICHE -; CQrV4+sK2fqE4fJF7Mv58+eNyMu1YBBBn/CZM2dk+/btMnfuXBkwYIBxG2v6WhNCSg8FICGk5JQ1al -; DQHl+XpoHZ+oRHjRoln3/+uTx58iR0Gohj42vXrhlDyYoVK0yfMHIHaRAhhESFApAQUlI0Gz3ikqlP -; GMYOGDyWL19uxF3UPuETJ07I5s2bzZ1CGExgNNH09SeElAYKQEJISdAW72KbbH3CrVu3NnVwR44cMV -; EwYQt9wsgXxPRw8eLFJngan4N9woSQXFAAEkKKTtKMHnHJZRDB3b4NGzbI7du3Q0Vg0Cd89OhR0yc8 -; adIk6datG/uECSFZoQAkhBQNCB2YITSJtGKQzSCCe31z5swxpo98+4Tnz58vgwYNkhYtWjAuhhDyPS -; gACSFFAeYHTaKs2OTqEx4+fLgRdXAAh630PuFVq1bJ2LFjpXPnzuwTJoTUggKQEFJwNPT4ukKmPuGm -; TZuaI90lS5aYerh8+4RnzZolFRUV5mhZ0/cNIaRwUAASQgqG70aPuGTrE0b7BwKgDxw4YMwfYevZs2 -; dy+fJl0ye8dOnS533CNIgQQigACSEFYUyTl1SJLhfJ1ifct29f0waCZpCwhWkhjCSonYOpZMqUKdKj -; Rw8zVdT0/UQIsQsFICHEKknp8XWF9D5hiEBMA2EQad++vcyYMcPkAEbpE0bAdNAnvHDhQhkyZAj7hA -; nxGApAQog1GO9SGLIZRD755BOprKyUbdu2yf3790NFIAwiN27cMH3Ca9askfHjx0uXLl3YJ0yIh1AA -; EkLqTdJ7fF0hW1xMeXm5mepdvHgxUp/ww4cP5fTp00Y4Imamf//+7BMmxDMoAAkh9cKXHl9XyGYQQd -; 7fmDFjjOEjSp8wWkauXr0q+/fvN/Vz6CLu0KED+4QJ8QQKQEJIbHzs8XWFTAYR9An36tVLVq5cabIA -; o/QJ3717V44fPy6bNm0yfcK9e/dmnzAhHkABSAjJGxo93CDdIAKCaWCbNm1k6tSpphouSp/w06dPzf -; Hxnj17ZNGiRTJ06FD2CROScCgACSF5QaOHW+TqEx44cKCZ7N25cydUBOLu4M2bN+XIkSOybt06mThx -; onTt2tUISk3fn4SQaFAAEkIiwR5ft8lmEEEN3Lx58+Ts2bOR+oRRN4eP3bFjh/lzEJGffvqpqu9VQk -; g4FICEkFDY46sDTAPxtQq+bpgGYoIHAYcWEGQARukTRq7g9evXTeMI7hPCXNKpUyf2CROSICgACSE5 -; YY+vPhDJk6lPGA0gqISD+zfMIIL/HdmCJ0+elC1btsjMmTNNA8nHH3+s6vuXEJIZCkBCSEbY46sbfO -; 0yxcW0atXK3O87dOiQ6QoOW0Gf8N69e2XJkiUyfPhwadu2LQ0ihCiHApAQ8j3Y45sMcCSMr2Vdgwim -; eP369TNmDxg/wlbQJ3zs2DHTJzx58mQzTcQdQ03f14SQF1AAEkKew3iXZAKDSHqfcGAQQfDzrFmzzD -; Fv1D7h8+fPy65du2TBggWmT/izzz5jnzAhCqEAJIQYGO+SbHL1CSP3b/v27fLgwYNQERj0CeMIefXq -; 1TJu3DgpKytjnzAhyqAAJMRzcDxIo4c/ZIuLQeYfQqAvXbpkjnxzLRhEIBaDPuHZs2ebI2WISU3f+4 -; T4DAUgIR5Do4efZOsTxnHu2LFjTT8w2kHCVnqf8LJly2TkyJHSvn179gkTogAKQEI8hT2+JFOfMHqA -; 0QeM4130CYetun3C06ZNM33E6CXW9O+BEN+gACTEM2j0IOlk6hPGkTCiXiDmvvzyy0gGEUwML1y4YP -; qEq6urzb1CRM4wLoYQN6EAJMQjaPQgmcjVJzx48GDZvHmzmfKFLfQJf/3116ZPeO3atSZvsLy8nH3C -; hDgIBSAhHoCNHe0QmkQJKT7ZDCJw+c6fP99EwEDk5VowiKBu7syZM8ZZPHfuXBkwYAD7hAlxDApAQh -; JOWaMGZsKjSYiQ0lG3TzgwiEDAjRo1yhzxPn78OHQaiGPja9euyRdffCErVqyQ0aNHS8eOHWkQIcQR -; KAAJSTA0epC4IBqobp8wjB09e/aU5cuXG3EX1icMg8i9e/fkxIkT5hh5xowZ0qdPH/YJE+IAFICEJB -; DGuxAbwCCSKS6mdevWpg7u8OHDJgombH3zzTcmX/Dzzz+XxYsXy7Bhw6RNmzY0iBBSQigACUkY7PEl -; NslmEMEUr3///qYb+NatW6EiEHcH8XFHjx6V9evXGwHZvXt39gkTUiIoAAlJCNigGe9CCkU2gwju9a -; EJBK0gqIkLW7g/eO7cOdm5c6cxlgwaNEhatGjBPmFCigwFICEJAJf2NYkJopNMfcI4Em7evLk51oWo -; e/jwYagIhFBEyPTBgwdl1apVpn2kc+fORlRq+ndHiGYoAAlRDnt8SbHBNLCuQaRp06bSrVs3c8fv8u -; XLkfuET506JVu3bpVZs2ZJRUWFyR7U9O+PEK1QABKiFBo9SCnJ1ifcsmVLGT9+vBw4cMCYP8IWTCRX -; rlyRffv2ydKlS2XEiBHSrl07xsUQUmAoAAlRCI0exBXS+4QDgwj6hPv27Str1qwxzSBhC9PCO3fumN -; q5jRs3ytSpU03cDKaKmv5dEqIJCkBCFMEe39rsbJKS/R/pes1JJL1PGEfCQZ8wJnnI/jt+/HikPuEn -; T56YPuHdu3fLwoULpbKy0kwUaRAhxD4UgIQogT2+tZn6eko6/CQlo36Zku2Ndb32JJLJIIJp4CeffC -; JDhgwx9/wQCh22gj5hZAxigojjZPQJ43Np+vdKiOtQABLiOOzxrQ2mfr1+lpJW/7VGAPb7eUoGN0jJ -; 0t/reo6kki0upkuXLmaqhwlflD5huImDPuE5c+aYzEG4jTX92yXEZSgACXEY9vjWpvqtGuEXEAjAgH -; GvpWTPB7qeKYlk6xNG3h86gffu3WuOe8MWDCJBnzDq59BF3KFDBxpECLEABSAhjsIe39qMfLW2+Msk -; AINp4Mo/6Hq2pFK3TxjHuOgT7tWrl6xYsUKuX78eqU/47t27pk9406ZNMn36dOndu7cxmmj690yIa1 -; AAEuIYjHepzeZGKSn/5++Lv2wCMGDGGzSIuAAMIphkB9/fwTQQXcBTpkwx1XBR+oSfPn1aq0+4qqrK -; dBKzT5iQeFAAEuIQNHrUZvxrmYVfFAEIYBDZ1EjXMyeRbH3CCH0eOHCgiX5BDEzYwt3BmzdvGtG4bt -; 06mTRpkgmfhqDU9O+cEBegACTEAbAx4vK8pk29kGDqFxg96iMA0w0inAaWnmwGkU6dOsncuXON6SNK -; n/CjR4/k7NmzsmPHDpk3bx77hAmJAQUgISWGPb61mfNmuPDLRwDSIOIWufqEhw8fLrt27TICL2whVx -; B3CNE4snLlShkzZowRkuwTJiQaFICElBD2+L5g34cpqXwluvjLVwDSIOIW2fqEu3fvbirhUA8XpU/4 -; /v37cvLkSdmyZYvMnDnTNJCwT5iQcCgACSkBNHrUZu07NWIuH/EHJv265p5fPiIQTH6dR8IukK1PuF -; WrVjJhwgQ5ePCgPHv2LHQaiI+5fPmyiZdZsmSJmSS2bduWBhFCckABSEiRYY9vbcKMHpnA/cDN/2nu -; gJDDsTGme/mKQE3vU5LJ1Cf88ccfS0VFhTF7wPgRtjAtvH37thw7dkw2bNhgHMY9evRgnzAhWaAAJK -; RIsMe3NrniXXIBwZjp88HtW9UwugDE5FDLe+UDdfuEA4MIgp9xtItj3qh9wufPnzd3CRcsWGBq6D77 -; 7DMaRAipAwUgIUWA8S61ycfokX7fD00guT4vpoHI/4sqAjW8Vz6Rq0946NChphYOd/7CFpzEN27ckE -; OHDsnq1atNnzCq6NgnTMgLKAAJKSA41qLR4wUwevT/ebwjX3QAR/17YPSIciRMV7CbZIuLKS8vl+rq -; arl48WLkPuHTp0/Ltm3bZPbs2aZPGGJS088QQgoFBSAhBYJGj9rU7fGNCqaFcf6+7Y1r7vnlEoDr3n -; H7PfOZbAYRHOeOHTtW9u3bZ9pBwhZaRq5evSr79+83fcIjR46U9u3bs0+YeA8FICEFgD2+tcnU45uP -; 0SMuYQYRRsK4T7pBJOgTRg8w+oBXrVolX331VagIDPqEjx8/bvqEp02bZvqI0Uus6ecKITahACTEIj -; R61Ma20SMu2QwicaeLpLikG0QApnc4EkbUC8QcnL9R+4RxfLxnzx5zlIx7hYicYVwM8REKQEIsQaNH -; baa+Hs/osfztwryeYBpYtx2k1O8TiUauPmFUwW3evNlM+cJW0Cd85MgRWbt2rUycOFG6du3KPmHiHR -; SAhNQTbEjVTX+oajMtJFF7fOsCcwhMIoV+fZgGBkfCVQ1L9z6ReGQziHTu3Fnmz58v586di2QQSe8T -; Rg/xwIED5dNPP1X1s4eQ+kABSEg9wCV1TCY0baCFJE68S32MHnHBNDAwiNAJrA/8m0OHdvDvMDCIQM -; DB5IEj3sePH4dOAzP1CXfs2JF9wsQLKAAJiQnjXV4Qp8c3TryLTTAJpADUDf4N1u0ThrEDDSDLli0z -; 7l9M+3ItGETu3bv3vE94xowZ0qdPH9NEounnESH5QgFISJ4w3qU2OPKN0+M7vsT37yD8GAWjHxhEsv -; UJT5o0yYRBR+kT/uabb+TSpUvP+4SHDRsmbdq0oUGEJBYKQELygD2+tYnT4wuxuNYR0VXVkE7gJJDN -; IIIpHsKf169fL7du3QoVgbg7iI87evSo+TOTJ0+W7t27mzuGmn5OERIFCkBCIoBL54x3eUFcowfyAF -; 16jqqGNdVxLr0mEp9sBhHc60MTyKlTp0xNXNjC/UGYSXbu3Gn6hAcPHsw+YZI4KAAJCQGXzTVtgoUm -; rtEjrMe3FMAIMuqX7r0uEp9MfcI4Em7evLk51oXrFxVxYQtCESHTBw8eNIHT48aNk7KyMhpESGKgAC -; QkBzR6vECj0SOMpb9nFExSwTSwrkEE08Bu3brJokWL5PLly8YAkmvBQPLgwQMzOdy6dauZIlZUVLBP -; mCQCCkBCMkCjR23i9vgiDNrl50IVHJ3AySVXnzAmel988UXkPuErV66Y/mG4i0eMGME+YaIeCkBC6s -; Ae39qUqse3GGxvTCewD6T3CQcGEfQJI+5l9erVcuPGjVARiGnhnTt35Msvv5SNGzfK1KlTpWfPnuwT -; JmqhACTkP2GPb21c6fEtJEEUDCaBWl4ziUd6nzCOhIM+4Xbt2sn06dPl+PHjJhg6bGFieOHCBdm9e7 -; fpE66srJSWLVvSIELUQQFIyDvs8a2Laz2+haSqIaNgfCFbXAzu9A0ZMsQEQSMUOmwhLubrr7+Ww4cP -; y5o1a2TChAlSXl5uPpemn3vEX9q2bUsBSPyGPb61cb3HtxDACTzZ8buKxC7Z4mK6dOliYl8w4YvaJ3 -; zmzBnZvn276RMeMGCAcRtr+hlI/AK/pPTr189cY6AAJN5S1qgBe3zT0NLjaxvkADIKxj+y9Qm3aNFC -; Ro8eLZ9//rk8efIkdBqIY+Nr164ZQ8mKFStk1KhRJneQBhHiGujKRjvOsWPHjLGJApB4CY0eL8DkLq -; 7Rw9V4l3xAFAzuAWp6zcQedfuEMSGBsaNXr15G0EHcRe0TPnHihGzevNncKYTBBEYTTT8XSTLBVYeu -; Xbua72dkWwaLApB4BeNdauOD0SMMOIAZBeM32fqEW7dubergjhw5YiYmYSvoE8b0cPHixVJVVWU+B/ -; uESanALzMjR440Hdd1I48oAIk30OhRG+09vrZgFAwB2QwizZo1M3f7cGfq9u3boSIwvU943bp15sgN -; 4dPsEybFpkOHDjJ//ny5ePFixtBzCkCSePADHZe+NW1GhSQpPb42YRQMCchmEOnUqZPMmTPHmD6i9A -; nDIBL0CWMTHjRokLlfyLgYUmgwvR44cKBpr0GTTbZFAUgSDXt8a5OkHl+bVDVkFAx5Qa4+4eHDh8uu -; XbuMwAtbMIik9wmPHTtWOnfuzD5hUjCQSTlt2jQ5efJkaK4lBSBJLOzxfUESe3xtghiYcQm610jskK -; lPuGnTptK9e3dZsmSJqYeL0id8//79533CM2fONH3COFrW9POUuA3uraKZBs02N2/elCiLApAkDho9 -; aoM7e7i7l6/4c73H1yaMgiHZyNYnjEkLAqAPHDhgzB9h69mzZ8/7hJcuXWr6hBHGy7gYUl/wywSmy/ -; hexPdZ1EUBSBLFmCYvqdpcCk0co4eWHl+bMAqGhJGtT7hv376ydu1a0wwStjAthJEEOWwbNmyQKVOm -; SI8ePcxUUdPPWeIGmErjSsGiRYvMLxdhcUUUgCSRsMe3Nox3yY9NjRgFQ8Kp2yccGETat29vjnaRAx -; ilTxgB0+fPnzd9wgsXLjQ1dOwTJvmA7zv0UMNk9PjxY4mzKACJehjvUps4Rg8cESfd6JELRsGQqGQy -; iAR9wtiQt23bZu78hS04iW/cuCGHDh0yfcLjx483VXTsEyZhtGnTRmbNmiVnz54NrSykACSJhD2+tY -; HRA528NHrEAwJw4e90vWZSOrLFxZSXl0t1dbXJXovSJ/zw4UM5ffq0EY6Imenfvz/7hElG8D2GKwe4 -; PnDnzh2p76IAJCphj29tML2LE+/C6JMXVDWsMYNoes2ktGQziCDvb8yYMcbwEaVPGC0jV69elf3798 -; vy5ctNnzBCfGkQIQH4pQCmo6itNBSAJJGwx7c2cXt8fTN6hMEoGBKXdINI0CcMgwj6hFeuXGmyAKP0 -; Cd+9e1eOHz8umzZtMlluvXv3Zp+w5+CKAabKy5Ytk+vXr4vNRQFI1ECjR21o9LALjn+rGup6zcQd0g -; 0iANM7HAnjvtbUqVNNNVyUyQ36WnF8vGfPHuPuHDp0qLRq1Yp9wh6CHl8Ej6Nbum6PLwUg8QYaPWqD -; jL44Ro/lb+t6zmKCKjg6gUl9yNUnjCo4TPai3N3C3UGE+eK4D33CEydOlK5du5rjZU0/t0l84CyfN2 -; +eXLhwoV5GDwpAohb2+NYmbo8vzCEwiWh61mITRMFs4tE4qSfZDCLIbMOmDvdm1D5hfOyOHTvMn0O/ -; 66effqrqZzjJD/zCMGDAANmyZUskNzkFIEkk7PGtTdweXxo9ooHJH6NgiC0wDcTPsODnWWAQgYAbOX -; KkyQCMkt+GXEHc/ULLA+4TwlzSqVMn9gknkM8++8yEg+MeaJQ8SQpAkkjY4/sC9vgWj6qGFMzELvhZ -; lqlPGA0guNgP928UgwimQSdPnjSTIYROIw7k448/VvVznWQGvxygX3rVqlWRGmVsLQpA4hTs8a0Ne3 -; yLC1zAjIIhtoFBJFNcDMwduN+HMOgoHa74mMuXL8vevXtlyZIlxiCAPmEaRPQCET969Gj54osvInVK -; 21wUgMQZ2ONbmzg9vnAFM94lPoiCGfVLXa+Z6CCbQQQCoF+/fsbscevWrdBNu26f8OTJk830CHcMNf -; 289x1Mg3GUjypAiHp8XYu9KABJyWG8S23ixrsgD1DTc7rI0t+nZHADXa+Z6AIGkUx9wgh+Rr3XqVOn -; It3/wv1B9Anv2rVLFixYIIMHDzZ3yNgn7D6Y/uLrtX37dmP0KdWiACQlhfEutYlr9PC5x9cmjIIhxS -; BXn3BVVZURBg8ePAjdwOEkRsg0jpBXr14t48aNk7KyMvYJO0zr1q1lxowZpv4vihO8kIsCkJQEHIPQ -; 6PEC9vi6wfbGdAKT4oFpYF2DCKaByPxDCPSlS5dCjwZhIIFYDPqEZ8+ebY6UISY17QlJB19bNLvgqB -; 9H+C4sCkBSdGj0qA17fN1h/0c1AhCTQE2vm+glW58wjnMx0UM/cJQWCLSMXLlyxXw83MWImkGYMPuE -; Sw/E+Pjx4+Xw4cPWenxtLApAUlTY41sb9vi6R1VDimtSfLL1Cffp08cc7964cSN0Q8e0EE0jyJHbuH -; Gj6RNGHzEqxTTtE0kBR/tdunQxju1r166Fxv0Ue1EAkqJAo0dt2OPrLnACT2aMDikB6X3CEIFBn3C7 -; du2MmPvyyy8jGUQwMUSFGPqEq6urpbKy0kTO0CBSPJD1iPucCPx+8uSJuLgoAEnBodGjNuzxdRvkAD -; IKhpSKbHExOEaEcxRB0Hfv3g3d3NEfi1BhHDuuXbtWJkyYIOXl5ewTLgIQ7HPmzDEu7UL1+NpYFICk -; YOAHWHXTH6r64VtI2OOrA0TB4B6gptdMkke2PmG4fOfPnx9JXODIETEjZ86cMc7iuXPnmp5Z9gkXBg -; h1GHA2bdok9+7dE9cXBSApCGWNGpjfZDX9wC0k7PHVAxzAjIIhLpCrT3jUqFHy+eefR+4Txh00tE2s -; WLHCNE907NiRBhGLtGjRwoRyRz2md2FRABLr0OjxAkzu4ho9GO9SGhgFQ1wjU58wjB09e/aU5cuXRz -; IYwCCCqdSJEydk8+bNJosOBhP2CdcPiOhu3boZYR3FqOPSogAk1mC8S21o9NALBCCOgjW9ZpJssvUJ -; I1gYk6cjR45EihhB3yzyBTE9XLx4sQwbNkzatGnDPuEYwKWNuJ19+/YVvce3vgsh1BSAxAo0etRm7T -; s1xo18jR5rOXVygqqGPH4n7pGrT7h///6mGzhKnzDuDuLjjh49KuvXr5dJkyaxTzhPcISOCr4oYd2u -; LVwbQF4kBSCpF/hBhMvKmn6IFguIuagTQPb4ugViYMZxEkscJZtBBKIE7tOoNWMQAufOnZOdO3caYw -; lcxrjLxriY7GDqOmjQINm6das8fPhQNC1cE7h+/bqsWbPGPAMFIIkNLidr+qFZCqLcAWSPr3tg+lfV -; UNdrJn6RqU8Y4qR58+bmWBeiLopACfqEDx48KKtWrZKxY8dK586djajUtB8Vg5YtW8r06dPl5MmTJe -; /xzXc9e/ZMjh07JrNmzTLh1DASUQCSWLDHNz+Q4Vf3SJhGD3cJomDoBCauk6lPGCHEMCaggeLy5cuR -; +4RPnTplJlsQCRUVFdKsWTNV+1KhwHsKww0mZ1GO2F1b6B5GfiR+MWjbtq35uuKZKABJXtDoER+Ivc -; pXasTfVDZNOM2mRhSARA/Z+oQxsUIH7YEDByKZFDAlQp8wTA1Lly6VESNGmFBjn+NiIJYwFcWEFO+P -; poUpJTIg0QYDAYvjfbjHA8MPBSCJzJgmL6n6oegiOBJmqLP7QPgxCoZoI71PODCIwKnat29f0waCZp -; CwFfQJI88OfcJTp0414gFTRU37VX3BNBVH4XBKX7161bke37CF439UAY4bN046dOhgmmTw/ZB+v5MC -; kITCHl/iI1UN6QQm+sjWJ9y+fXuT/Xf8+PFIQcXor0WfMLpsFy5caPqEMVH0wSCC92vo0KHmHmWUoG -; 2XFgQ8prgrV640znB8zfBLQKYpLgUgyQnjXYivVDWs6QXW9JoJAZkMIkGfMIQc7vlFqSrDEWLQJ4z7 -; b+gThoEAn0vTPpYPyEScPXu2nD171uke30wLx/yHDh2SadOmmcpAGD0gZrOJdgpAkhH2+BLfQRTMqF -; /qes2EpJMtLgYiDlM9TPii9AnjODHoE0bMDCZLcBtr2tPCgKjFUTmyFO/evSva1s2bN82RPQQ+RCyy -; IcOc3BSA5Huwx5eQGifw4Aa6XjMhdcnWJwxDwJgxY2Tv3r3muDdsoWUEd+HQJ4z6OXQR425ZEgwimJ -; RNnDjRBGNHaVNxaeE4H7E08+bNM2He+LrivmaUo3oKQFIL9vgSUsPKP9AJTJJD3T5hTLzgCO3Vq5e5 -; L4aA4Ch9wpiOoU9406ZNJhOvd+/e5o6Zpn0uAEfjXbt2NYIWWYja1v379809xdGjR5s7noHRI+rzUw -; ASA+NdCKnN9sZ0ApNkAYMITniCn/vBNBBHhnD7Rp2APX36VC5evGj6hBctWiRVVVWmk1hTnzDEL2Ju -; MAHF82haOLbH+4+oHhxbf/bZZ+Z58p3GUgASGj0IycD+j2oEICaBml43IbnI1ieMvLuBAweae2SIgY -; kiQnDv7MiRI7Ju3TrTJ4zwaQhK1/c8TMtwZAoRpa3HF8f1OIafPHmydOrUydzFxHsex51NAegx7PEl -; JDdVDRkFUwwgtjW93iSQzSACUTF37lzjgo1Sd/bo0SPzsTt27DCiCiIS99Bc3PMglAYMGGBaMdB8om -; nheB7H1MhzRGdzq1atjNGjPncwKQA9hT2+hIQDJ/BktrYUjM2NaioRR7xac+Su6bUngWx9wjBF4Hh0 -; 165dRuCFLRgRcIcQjSO4TwhzCYSkS33COCbFMTfuL0bJQXRp4VgewdyIpykvLzdfn6hGj1xQAHrKmF -; //HX/rJiQE5AAyCqYwYLIa9GJX/GuN4xrOa/5cKh5oJRr5nfhO3xvS+4ThKsU9MwQLR+kThikBjlRM -; 2GbOnGnup5W6TxgTsh49esiqVasiNaG4tnAcj9zG4cOH1+rxtfHeUAB6Sp8Gf2d+4PKCOyHZgSDBPU -; BNr9l1IDqCTux0AYj3GYx7jc7rYoDpa/k/17z/mfaIwCCCo0ZEpETtwsXHXL582ZgrlixZ8ly4lMIg -; giNSOGQxmdTY43vu3DljsoFTOzB62HwfKQA9BQIw+IGL38T5Wzch3we/IDEKxh5rv3s/O/yktvirKw -; ABfjml+aZwjH+t9vufbZ8IDCIQUv369TNmDxg/whamhbdv35Zjx46ZYOUpU6aYKVyx+oQxxcQRdHV1 -; tZleauvxxbE7BDSaVzp27GjiXeIaPXJBAegp6QIQ4JiLd3AIqQ2jYOxRV3TkEoABOILnL6f2CO5c1n -; 3/c+0V6QYRBD/PmjXLHPNG7RM+f/68uUu4YMECGTJkiJlkFbJPGEIJfw9MKVHuL7q0IJwRto3japhV -; cvX42oAC0FPqCsDgt27ewSGkNoyCqR/pR435CsDgl1MK8PqTfucyHwEYkN4nPHToUFMLF8VJi6PMGz -; dumI7a1atXy7hx4wrWJ4wsQtw9RG1dFAezSws9vojUQbg23p+wHl8bUAB6SiYByDs4hHyfqoaMgolL -; LtERVQDyl9P6kenOZRwBCNKngWjQwBHrpUuXIvcJnz59WrZt22bcrDhShpi0sZ/hNfXp00fWr18fKc -; PQtXXr1i3TrIJAbYRy2zR65IIC0FNyCUDewSHkBYiBwS9Fml5zqYHo6P/zaOIvigDkL6fxyHbnMq4A -; DAgMIjjOHTt2rOzfvz9Sm0bQJ4yPX7ZsmYwcOdKEMtfniBMicvz48XL48GGVPb4QxTgexx3JoMe3WI -; YZCkBPCROAvINDSA34N1DVUNdrLiXVb0UXfvkKwPRpoKb3pBTkunNZXwEIgj5h3FFDHzDurUXp0w36 -; hI8fP26mXtOmTTMuVzhc8/n7IZJwVIqYmmvXrom2heNz3I1EZmKcHl8bUAB6SlQByDs4xHeCKBj+Ih -; QOMuXyFX9BEHTUn0fp00B+Tb5PlDuXNgQggAjE9A5Hwoh6gZhDYHE+fcJ79uwxR8m4V4jImSjTL0zJ -; hg0bZv4sjCaaFo7LEZOzfPlyqaioMFPUQho9ckEB6Cn5CEDewSE+s6kRo2DCiCM6wPj/PFrHzxXcF8 -; TPmXx+Li38na73qdBEvXNpSwAGpPcJo6Zs8+bNZsoXRQwFfcKoOEPeIO4W5uoTbteunamqg7s47O6h -; awuiF5mEiMXp3Lmz6fEttNEjFxSAnpKvAOQdHOIr+H5nFEx2pr6ev+DAvbTlb3//c0FsVzWM9gspvx -; 4vyPfOpW0BCNINImVlZTJ//nwTZBzFIJLeJwxxhz5huGDTPz8EZv/+/c2x8b1790TTwjPCCQ2TCiJq -; 4FZGtmKpq/IoAD0lrgDkHRziI/iepxO4Ntky5cKAUIFgyfZ5MQ3EvUse/UYjzp3LQgjAgMAgAgEHkw -; eOaR8/fhwqkur2CaPBAyHI+HwwR0yePNkcL2vs8cV9xzlz5pjpZmD0KNXULx0KQE+pjwDkD2LiG1UN -; a0SJptdcSOIeNeYjopFCUPdImL941ibOnctCC0AQTANh7IC7Fffd4P4Na+SAQQTTPQRN4xgZodOTJk -; 0yDSSYoGlbOAZHXiKEMI6ucURebKNHLigAPcWGAORRDPEFRsHUECVTLhOYFO5skv/fhyYWvPdsKqpN -; 3DuXxRKAAcE0EEeeEHKIaonSyYtQZFS44VgYDl9t8S4IocYdxcWLF5t8wkL0+NqAAtBTbAnAAPYJky -; SDyVNVQ12v2TYQHVEy5eoyvp7CmT9XahPnzmWpBCBI7xPGHT50AyP4OGxhGgghpa3HF8fd+/btM4YW -; HGHD6FGIHl8bUAB6im0BCPhbOkkqOI7E97ivBqh8MuUCIBbX8nTAGnHvXJZaAIJ0gwhEEZpAEICsra -; 4t14JQxR3GNWvWGBMLIm1KFe8SFQpATymEAKRBhCSVIAoG/6npddeXuKIDd9M0PafrxL1z6YoADMA0 -; ENMwTMWQ4wfXLyritC8cax89etT0EBerx9cGFICeUigBmG4QYVwMSQpBFIxP9YhxRQdcqZqe02Xi3r -; l0VQCCYBoIJ2y3bt1Mk8fXX3+t7qg3WLdv35YtW7aYHl+EYRerx9cGFICeUmgBSIMISRpVDf2Igomb -; KRfX6EEyE7XHV5sADMDRaMuWLY3TF80YuPOnaeH4+syZM7Jw4ULp2bOniXdx0eiRCwpATymGAAxgnz -; BJAphqJz0KJm6mHIwJmp7TdeLcudQkADEFRFh00OOrbfqHY+vdu3fLuHHjpEOHDs97fF0/8q0LBaCn -; FFMA0iBCkgDEH76PNb3mfIjb47vZs3uRhaSQRg9XBCCOftH7CwEVJSDapYUpJeJpVqxYIf369TMTTN -; eNHrmgAPSUYgvA4EiYfcJEK/jexfewptcchfr2+BI7FNro4YIAxB05OICjVMS5tpBNeOjQIZk2bZrp -; 8dVi9MgFBaCnlEIABtAgQjSSxCgYmz2+JB717fHVIABxPFpRUSEbN2407RjaFkwqyC+srKyUNm3aON -; HjawMKQE8ppQAMpoE+OSqJfnCFAd+7STA2FarHl+SHjR5f1wUgJmVoATl27Ji6Rg/0DqOWbt68ecax -; 7FKPrw0oAD2l1AIwADVPPBImGsD3Kb5ntf/iUoweXxKOrR5fVwUg3LBdu3Y1PcBfffWVaFvoJEZO4a -; hRo0yPb2D0cG0vrw8UgJ7iigAEVQ0ZF0N0UNVQrxDC5C6u0YPxLvaw3eProgCEMWLEiBGyd+9eefr0 -; qWhauJt48eJF41Du27fv8x5frUaPXFAAeopLAjA4El74O04DidtgYj1OofmBRg83KESPr2sCELEo8+ -; fPNyJKW7bfkydPZP/+/ebIulOnTk73+NqAAtBTXBOAAdhcGRdDXEVjFAx7fEtPqeJdiikAIZTQgbt1 -; 61Z58OCBaFrIIcQx9dq1a2XQoEGmxzcpRo9cUAB6iqsCMJgGsk+YuAi+L/E9quG1ssfXDUoZ71IsAY -; g8PMSjnDhxwhgnNC0YU2BQQSNJeXm5Ma0kyeiRCwpAT3FZAKZPAxkXQ1xiU6Oa703Xvy/Z41t64t65 -; 1CQAcS8ONWirV6+WmzdviraFIOqDBw/K+PHj1fX42oAC0FM0CMBgGkiDCHEF16NgIDoqX8lfANDoYR -; cXjR62BSCOSMeMGSMHDhyQZ8+eibaF13zp0iWT7wcBiGq6pLl8w6AA9BQtAjAAEw0aRIgL4PvRxSsK -; uLOHu3v5bv7s8bVLsXt8iy0AcTSKJoxFixaZWjRtPb7BgkHlzp075vgXInDq1KnSo0cPc/zr+v5tCw -; pAT9EmAAH7hIkLVDV0Lwomjuhgj69dXDd62BCAqD5DG8bOnTvV9fhmW3D+nj9/Xnbt2iULFiyQIUOG -; mDuNvANIEotGAQjYJ0xKjUtRMIx3cQMNRo/6CkBUoMEocebMGfnLX/4iSVp4nhs3bpiuX9xnxJFwly -; 5dEn8kTAHoKVoFYAANIqRUYLOvaujG68h3o8cRMY0e9oh751KTAIQpAoHI69evN0em2hYmlQ8fPgzN -; JMRRNj7u9OnTsm3bNpkzZ47079/fZAFq2NPjQAHoKdoFYDANZJ8wKTb4nsP3X6l+AYHoQCdvvps8jR -; 52iXvnUpMAhPiZMGGCHDlyRGWPL6aVy5Ytk+rqajPdi9JKgue8evWqCYRGjd3IkSNNuDWbQEhiSIIA -; DEA4L4+ESbEIomBKcR8V07s4Gzx7fO2i0eiRjwBEjy8y8SCerl+/LtrW/fv3zZ2+sWPHGsMKIl769e -; snq1atitRLjGnh3bt35fjx47Jp0yaTcdi7d29TCadlj48CBaCnJEkAAhhEGBdDigEmf/ieK/b3W9we -; Xxo97KEt3iWOAITIGT58uOzZs8cYJDQt9Pgi2gXCtaKiwvT4opcYLSUwsEAIwu0L52+UiSYmhqi0w3 -; sB1/PQoUNNSwgEspa9PhcUgJ6SNAEIaBAhxaKqYfGmajR6uIGrPb42BWD79u1l3rx5cuHCBSOmNC2I -; VWQSTpkyJWOPL0QbTB0Ie0bd2+bNmyPdacT7gJBrHIOjKm7ixInStWtX87k17fmZoAD0lCQKwAAaRE -; ihwfcYrh4U+u+JIzpwL23524V/bb6AO5da412iAmEEwwNEEY5PNS2YN+DgXbdunQwePFhat26dtccX -; YhD/PaaBOBqG2D137lwkV/OjR4/k7NmzsmPHDvPn0HuM2jgte34mKAA9JckCMH0aqGmjIXpAFAyuHR -; Tq88fNlIM5BIKlkM/uE3HvXGoDUzPcd9PY44vXDccupnItWrSI1OMLQwcmeBBwMHngiDdKriHeH9yJ -; xKRx5cqVpgkF00at9XEUgJ6SdAGYPg3kkTCxDX65wC8ZhfjccTPlaPSwi5YeXxt8/fXXom3BpIG4lh -; EjRki7du3M0W4+uX3BNBCCEQ0guDcI929YswkMIpiSnjx5UrZs2SIzZswwMTmYOrq+79eFAtBTfBGA -; wTSQBhFik0JEwbDH1w2SavTIhaaF41o0dyxevNg4c2H0gHElrjEjmAbC3DFp0iQTFxOl2/ibb76Ry5 -; cvy969e2XJkiXGOAOTiSaDCAWgp/gkAAPYJ0xsgQgYfE/Z+sWCPb5ukLR4l6QJQBzTQnAhm7Bjx47f -; M3rEJTCIYIqHuBiEXt+6dSv09cAgcvv27ed9wpMnT5bu3bubO4au7v3pUAB6io8CELBPmNgAv0jg+8 -; lGEHkc0YEJFeNd7KG5x9cHAYhj12vXrpmatgEDBphpHeJdbIYzpxtEEPw8e/ZsOXXqVCSDCIRpep8w -; zCiYTLreJ0wB6Cm+CkBAgwixQVXDlCz8Xfw/H/eoEXfTSvXMSUR7j2/SBSCOY48ePSozZ840/bwwbk -; CkFUpcYRqIqSKmi1VVVcb1++DBg9DXCaGIkOmDBw8aoTpu3DgpKytz2iBCAegpPgvAAMbFkPoAJ3Bc -; 40Vc0cEeX3skpcc3yQIQx7CIpoEQw/06GD2KIajSp4FwF+O+IQKmo/QJQyxicgiDCqaIOFL+5JNPnN -; r/AygAPYUC8MU0kH3CJA7IAZyeZxYge3zdIEk9vkkUgJimnT59WhYuXCg9e/Y08S71MXrEJTCI4DgX -; E70vvvgicp/wlStXTJ8w3MWImkHItmt9whSAnkIBWBv2CZN8wTWCfI5j2ePrBr4aPbQIQEzQdu/ebX -; p8IZowPYNBo1T36QKDCO4c9unTxxzvIng6bGFaiKaRL7/8UjZu3Ggq6Hr16uVUnzAFoKdQAH4f9gmT -; fMD3StUvon0se3xLj4/xLpoEIBy1mJotX77cHJu2bNnSutEjLhCfeB04Ekbm4PTp042wixKcjYkhqv -; UQNl1dXS2VlZXm2VwwiFAAegoFYGbYJ0yiAjd5xb/m/hj2+LoBjR5uC0CIJJgnMCVDRRsMGIU0esQl -; mAZiKjlkyBATBH3v3r3Q54O4Rdj24cOHTZ8wYmzKy8tL3idMAegpFIC5oUGERAECMNv3CXt8S0/cO5 -; e+UaoF0wSEETL0IKhy9fi6QrpBBC5fxL4gAgYiL+xZ0Sd85swZ2b59u8ydO9dE2kDslupZKAA9hQIw -; HMbFkDBwBFw3V5I9vm7gS4+vVgGI49MTJ04YIdStW7fIPb6ukN4nPGrUKPn888/lyZMnkZ4bmYYwlK -; xYsUJGjx5tQq1LcdRNAegpFIDRYZ8wycakX9e+N8oeXzfwqcdXowDEsSny9SCccKcuMHpo2kMBxCpe -; N4wdcCtD0EHcRekTxnsAAYyYG/QJw2BS7D5hCkBPoQDMD/YJk0xM/01NjBAmd3GNHox3sQeNHm4LQB -; yTwhCB7lwInvr2+LpCMA3EETbq4I4cOWKiYMIW+oSRL4jpIbIGhw0bJm3atCna+0EB6CkUgPFgnzBJ -; Z8l/pGTCazR6uECcO5ekeAIQdWnIxZs4caJ06tTJWo+vKwQGEYRV424f7jWiJzhsQRQj8BptJ+ggnj -; RpkjkSL0afMAWgp1AAxod9wiQA0798xR+MHms5TbaG7z2+rgtAHIdev35d1qxZI4MGDTI9vjjqdC0U -; 2QbpBhHc65szZ44xfUTtEz537pzs3LlT5s+fb94r3IsspECmAPQUCsD6QYMIAZvey08AssfXLox3cV -; sA4hj02LFjMmvWLBN7AsOEJqNHXNL7hIcPH25E3cOHD0Pfr/Q+4VWrVpkwbMTiFMoVTQHoKRSAdmBc -; jN9gEhxVALLH1x5x71yS4glAHH8iJw/32orZ4+sKwTQQghdHurj3iKDrfPqEt27dasRzRUWFef9sv0 -; YKQE+hALQHDSL+AvEfJgBp9LALjR72sbkwxTp79qxpvUD1WVKMHnEJDCJo/xg/frwcOHDAmD/C1rNn -; z4xg3LdvnyxdulRGjBhhHNM2j84pAD2FAtA+NIj4SS4xAmOCpmdxHfb4ui0AccwJRyuETocOHUy8S5 -; KMHnFJ7xPu27evaQNBAHbYwrQQk9T0PmHEzWCqaON1UQB6CgVgYaBBxD/QBlJ3Q2WPr11o9HBbAEKo -; XL16VVauXCn9+/d3qsfXFdL7hNu3b2+y/5ADGKVPGAHTiM/ZvXu3LFy40LSm2OgTpgD0FArAwsE+Yb -; 8Y+ovamynjXexCo4fbAhDHmei4nT59uqlGg9HDxR5fV0jvE66srJRt27bJ/fv3Q99nHK3fuHHDvNdw -; VGPK2qVLl3oFaFMAegoFYOGhQcQPAjMC4l1o9LAHjB6Vr+gSUr4JwJs3b5qjSQgZBBi73uPrCulxMX -; BHY6p38eLFSH3COGZHtAyEI2JmMHGN2ydMAegpFIDFAdNAZMVp2nhJfoz9Vc3xJHt87bH87RpBrUlE -; pdP3X1LSXtHrz3fh2PLkyZMmr6579+7Pe3x9NXrEJTCI4P0bM2aMMXxE6RNGvA6O3NEnvHz5clOphz -; uX+R65UwB6CgVgcZnxBo+EkwodvnbRHu8y5fWUzHxD12vOZ+G4Erl2o0ePNnfZtPb4ukJ6nzBc07hH -; ieDsKH3Cd+/elePHj8umTZvMEXzv3r3N3cuofzcFoKdQABYfGEQYF0NIZrTHu/R4uabhBSawsp/qeu -; 1RFo4n0Vu7bNkyk0uHeBcaPewRTANxlA63L6rhovQJP3361Bwf79mzRxYtWiRVVVWmkzjKNJYC0FMo -; AEsDDSKEfB/tPb647wvhB+b9VtdrjyIAcSyJ48bJkycnssfXFdL7hAcOHGgme3fu3An9+kCc4z7mkS -; NHZN26daZvGeHT+Brl+vsoAD2FArC00CBCSM3xueZ4F0z6cF8xEH8Ak0BNz5BLAOIYEtVkEBWDBw82 -; kyUaPQpLukEENXDz5s0zwdpR+oQfPXpkPnbHjh3mz0FEwpWd7e+iAPQUCsDSwz5h4jNwTGsSSXVB/E -; +68NM6/csmAHH8iADi2bNnS9euXb3p8XWFoE8Y7ztaQJABCIEXtmDQwR1CNI7gPiHMJZjaZhLtFICe -; QgHoDpgG8kiY+EISenxh8qgr/rRO/zIJQBw7ooc2qB/zrcfXFdL7hOG2RiUc6uHCDCL432HWgVMbfc -; wzZ840DSSY3qZ/fgpAT6EAdAv2CRMfSJLRoy44Ctb0LJkEII4Zz507Z8wEcJT63uPrCoFBpFWrVuZ+ -; 36FDh0xXcNjCx1y+fFn27t0rS5YskeHDh0vbtm2ffz0pAD2FAtBN2CdMkor2Hl/Eu2QSftqnf4EAfP -; z4sREKEyZMkI4dO9Lo4RiBQQRTvH79+pl7mTB+hK2gT/jYsWOyYcMGmTJlivTo0cNMFSkAPYUC0F3Y -; J0yShPYeXwi7ukaPJE3/wLVr12TVqlUyYMAAM2VivIubpBtEEPw8a9Ysc8wbtU/4/PnzsmvXLlmwYI -; HpE6YA9BQKQLdhXAxJAtp7fHFXMZfwCxj4b7qeqy4zZswwvbLs8dVBep8wcv+2b98uDx48CBWBQZ8w -; jpBXr15NAegrFIA6YFwM0Yj2Hl/UuMHRG0X8aZ/+AYQP0+ihi/RpIFzauLeJoG4c+eZaQZ/w6dOnKQ -; B9hQJQD+wTJpqASUJ7j++m96KJvyRM/wCNHnoJDCIw7IwdO1b2799v2kHCFmJ+KAA9hQJQH+wTJq6j -; 3eiByV9U4QcgdjU9XzY07V3k+wR9wri7Cfc27nMiwDtsUQB6CgWgTtgnTFxEe7xLQLZ8v2wgDFrT81 -; EAJheIQEwDcSSMqJdp06aZIO9cBhEKQE+hANQLDSLEJbQbPQJ8nf5RACaLdIMI6vs2b94sd+/epQAk -; L6AA1A8NIqSUwOjR/+e6hE4uwnL+kjr9owBMHukGkbKyMpk/f76JgPn2228pAAkFYFJgnzApBdp7fO -; uS7/QPJhFNz0cB6CeBQQTxPqNGjZI9e/aYwG8KQM+hAEwW7BMmxUJ7j28mfJ7+UQAmm2AaCKd3z549 -; Zfny5Sb4G3EwFICeQgGYPNgnTApJUowedfF9+kcB6AfBNLB169YyadIkOXz4MAWgr1AAJhf2CRPbTH -; 1dl6DJB9+nfxSA/pDeJ9y/f38KQF+hAEw27BMmNtDe4xsGp38UgL6RbhChAPQUCsDkQ4MIqQ9JiXfJ -; Be7O+j79owD0E0wDKQA9hQLQHxgXQ/JBe49vVDj9owD0HQpAT6EA9AsaREgUtPf45kO+078kup8pAP -; 2GAtBTKAD9hAYRkg3tPb75wOkfBSChAPQWCkB/oUGEpJN0o0cmOP2jACQUgN5CAeg37BMmwAejR10w -; /cNRN6d/FIC+QwHoKRSABNAg4idJ6/HNh3ynf+M8OBrXtHcRe1AAegoFIAnANHDlH3QJGBKfpPX45g -; OnfxSA5AUUgJ5CAUjqMuMNHgknnaTfZQuD078UBSB5DgWgp1AAkkzAIMK4mOSR1B7ffIgz/Sv7qa5n -; jIumvYvYgwLQUygASTZwJLzwd5wGJoUk9/jmA1o88pn+TfHofdO0dxF7UAB6CgUgCSM4LtMkdsgLfI -; x3yQanf7nRtHcRe1AAegoFIIkC+4R14mO8Sy7ynf7NfEPX89UXTXsXsQcFoKdQAJJ8YFyMDhDv4rvR -; oy75Tv+AT9M/CkB/oQD0FApAki/sE3YbGj0yw+lfOJr2LmIPCkBPoQAkcWGfsHv41OObL5z+haNp7y -; L2oAD0FApAUh/YJ+wGNHrkhtO/aGjau4g9KAA9hQKQ1Bf2CZcWGj3C4fQvGpr2LmIPCkBPoQAktqBB -; pLjA6FH5ii6BUQo4/YuOpr2L2IMC0FMoAIlN2CdcHDDR6vATXeKiVHD6Fx1NexexBwWgp1AAkkLAPu -; HCQaNHdDj9yw9NexexBwWgp1AAkkLBPmG7MN4lfzj9yw9NexexBwWgp1AAkkJCg4gdaPTIn3ynf/N+ -; q+v5CoGmvYvYgwLQUygASTGgQSQeMHr0/7kuEeEKnP7lj6a9i9iDAtBTKABJsWCfcH5Uv6VLPLgEp3 -; /x0LR3EXtQAHoKBSApNpgG8kg4N+zxrR/5Tv96vKzr+QqFpr2L2IMC0FN8FYAwKGh6vUmDfcKZodGj -; /uQ7/VvESetzNO1dxB4UgJ7iiwDE1Gnh71KyqVHNRosJFC7Wa3qGJMI+4RdMfV2XWHAVTv/io2nvIv -; agAPSUJAvAya+HO1AhCKsa6nqupOF7nzB7fO2R7/Rv+du6nq/QaNq7iD0oAD0lSQIQQgITpWDKFxUI -; RIhFTc+aNHw1iDDexS75Tv8G/puu5ys0mvYuYg8KQE/RLgCrGtYIBxsRI/g8ECKanj9p+BIXwx5f+0 -; DMcfpXPzTtXcQeFICekoQJoM3aMWwMNIiUlqT3CbPHtzBw+ld/NO1dxB4UgJ6SlCNgiLZ8j36zERhE -; OA0sLUnsE2aPb2HId/oHsajp+YqFpr2L2IMC0FOSdAfQdu0YDSKlx6awLyWMdyksnP7ZQdPeRexBAe -; gpSXQB27xHBjGJSZSm508a2vuEafQoLJz+2UPT3kXsQQHoKUmNgbHtKsWdNB4JlxZtBhH2+BYHmDny -; EYCIitH0fMVE095F7EEB6ClJD4K2WTuGzYNxMaVFS1wMe3yLA6d/dtG0dxF7UAB6ig9NIFUN7dWO0S -; DiBi73CbPHt3hw+mcXTXsXsQcFoKf4UgUHwWazdgzGBMbFlBabwt4GNHoUl3ynf5ve0/V8pUDT3kXs -; QQHoKb4IwACbtWPsEy49toV9XNjjW3w4/bOPpr2L2IMC0FN8E4CBaLB5jwzTQB4JlxYcCdsS9vlO/d -; jjW3x6vMzpXyHQtHcRe1AAeoqPAjBdNNiMi6FBpLQU2yDCeJfSwelfYdC0dxF7UAB6is8CMBANNu+R -; sU+49BQ6LgbxLjR6lA5O/wqHpr2L2IMC0FN8F4ABNu+RYdOhQaS02Bb26Ue+NHqUlkVvcfpXKDTtXc -; QeFICeQgH4gkIYRDgNLC02hT17fEsPp3+FRdPeRexBAegpFIC1YZ9w8qivsKfRwx3m/TY/Acij+vzQ -; tHcRe1AAegoFYGbYJ5ws4gp7Gj3coeynnP4VGk17F7EHBaCnUADmFg3oALYhAgE+V8W/6noPkkZUYQ -; +jR+UrujbvpMPpX+HRtHcRe1AAegoFYDiY3tk4EsaG1PtfdD17EgkT9uiL7fATXRt30uH0rzho2ruI -; PSgAPYUCMBq4RxbXVZruHKUAdIdMwp5GDzeZ+UZ+AnAcv46x0LR3EXtQAHoKBWB04twjqysoKADdIh -; D2jHdxl/Y/yX/6h4mhpmd0BU17F7EHBaCnUADmT5R7ZNmcoxSA7gFhr2WDdpnm/18Ntj8vp3/FQ9Pe -; RexBAegpFIDxRUO22rFczlEKQDfRsDlr4LP/PyXN/t+a/7Tx+Tj9Ky6a9i5iDwpAT6EArB+YNgRHwl -; GcoxSAbqJhc9bER/9PjRCs7+eZ8np+AhDTwlI8b1LQtHcRe1AAegoFYP3BNHD2f4/mHKUAdBMNm7M2 -; Gv9fNcSdBnL6V3w07V3EHhSAnkIBaIcuEQ0EFIBu4vrGrJWm/3dK3vo/4t0NxHSd07/iomnvIvagAP -; QUCkA7UADqxvWNWTM4Dn79f6uZBubz55DHmI8A5PSv/mjau4g9KAA9hQLQDhSAunF9Y9YO7gP+9n9P -; yR9finYkPPQXnP6VAk17F7EHBaCnUADagQJQN65vzEkAwg8CEEIw7EiY07/SoGnvIvagAPQUCkA7UA -; DqxvWNOUngSPi//2PN/cBM/3vff+H0r1Ro2ruIPSgAPYUC0A4UgLpxfWNOGpgGvvt/pqTRf/n+/zbv -; t5z+lQpNexexBwWgp1AA2oECUDeub8xJBdNACMHg/59v9Aunf3bRtHcRe1AAegoFoB0oAHXj+sacZD -; ANhBDE/51v9Aunf3bRtHcRe1AAegoFoB0oAHXj+sbsC5gAQgQi1JnTv+Kjae8i9qAA9BQKQDtQAOrG -; 9Y3ZN2AEWf42p3/FRtPeRexBAegpFIB2oADUjesbs49gGpitCxhGEU3PogVNexexBwWgp1AA2oECUD -; eub8w+g2lg3VzAHi/regYtaNq7iD0oAD2FAtAOFIC6cX1j9h1MA4N4GE7/CoemvYvYgwLQUygA7eCq -; ABzcICUz3iju36kR1zdmUsPIVzn9KySa9i5iDwpAT6EAtIOLAhBuyv0fpeRws5RsapSSqobF+7u14f -; rGTEgx0LR3EXtQAHoKBaAdXBOAS39fI/zSgRic/Hrx3hNNuL4xE1IMNO1dxB4UgJ5CAWgHVwTgqF+m -; ZM8H3xd/6UAc4mi42O+Ry7i+MRNSDDTtXcQeFICeQgFoBxcE4MLf5RZ+6eAyPcRiKd4rF3F9YyakGG -; jau4g9KAA9hQLQDqUUgJjmQdBFFX/pR8Jz3uQ0ELi+MRNSDDTtXcQeFICeQgFoh1IJQDh88xV+daFB -; xP2NmZBioGnvIvagAPQUCkA7lEIAYnIXdt8vn2mgz3Exrm/MhBQDTXsXsQcFoKdQANqhVBNAiMCVf7 -; AjAgGiY0r9XpYC1zdmQoqBpr2L2IMC0FMoAO1QahMIpndB5l99j4NL9R6WEtc3ZkKKgaa9i9iDAtBT -; KADt4IILGK7ede/UXwT6eBTs+sZMSDHQtHcRe1AAegoFoB1cyQHEkTBy/uozDcS9Qt+cwa5vzIQUA0 -; 17F7EHBaCnUADawbUmENzlixMNE4BMwWK9dy7g+sZMSDHQtHcRe1AAegoFoB1c7AIOpoFxXcE+BUW7 -; vjETUgw07V3EHhSAnkIBaAcXBWAApoFxjoRxn7DYr7VUuL4xa6b3z3S9Xp/RtHcRe1AAegoFoB1cFo -; AA08A4BhFfYmFc35g10/mnKZn7Zs1/anrdPqJp7yL2oAD0FApAO7guAANQ/ZbPNNAXQ4jrG7Nmprye -; kvXvpmTVH/leu46mvYvYgwLQUygA7aBFAALc7cvHIOKDIcT1jVkr7X9cI/7SGf9azX+v6Tl8QdPeRe -; xBAegpFIB20CQAQT5xMfiYpE8BXd+YtRJM/+pS/VZKuv03Xc/iA5r2LmIPCkBPoQC0gzYBGIA7flH6 -; hCEWXXrdtnF9Y9YIpnwb3q0hkwjEkfCwhpwGuoSmvYvYgwLQUygA7aBVAIKofcJJNoS4vjFrBNO/QA -; DmEoI0iLiDpr2L2IMC0FMoAO2gWQAGhPUJJ7kn2PWNWRvp078oIhBHwpqeL6lo2ruIPSgAPYUC0A5J -; EIAgrE8YLmKXX39cXN+YtTHh31Oy8b3oIhBTaE3Pl1Q07V3EHhSAnkIBaIekCECQyyCSVEOI6xuzJj -; D9g/gLyCYCN6TdBdT0fElG095F7EEB6CkUgHZIkgAMyGYQSaIhxPWNWROjf1lbAIYJQcTCaHq+JKNp -; 7yL2oAD0FApAOyRRAIJMfcJJ7Al2fWPWAqZ/m97LLACziUAaQNxB095F7EEB6CkUgHZIqgAMqNsnnL -; SeYNc3Zi2M+WWNAMwlAtOFIJzCmp4v6Wjau4g9KAA9hQLQDkkXgKBun/Dk13W9/ly4vjFrANO/FW+/ -; EIBhQhACMAlh0B1+kpLl3z332u/+bZT/c/zP4wKa9i5iDwpAT6EAtIMPAjAg6BNOUk+w6xuzBka8+n -; 3xl0sEQjRper5M9P/ue2ffhy9+KcL/XfmKrmdIR9PeRexBAegpFIB28EkAgqBPOCk9wa5vzK7T/rsp -; 2Oo/pmTr+ynZ3CiaEByiWCgB/CKULS4JuYaYDGp6HqBp7yL2oAD0FApAO/gmAAGmfxCAVQ11ve5MuL -; 4xuw6mXhB/AVtCROCad3Q9Xzq9fpaSnU3Cm3MghLVNAzXtXcQeFICeQgFoBx8FYEASjoFd35hdJ5j+ -; 1RKBmAZmEYHDG+p6vgBE1oQJv0zh6VqmgZr2LmIPCkBPoQC0g88CMAm4vjG7TN3pX13qHgnDLAHDiK -; ZnhIBb+07+4i99GojJoevPqWnvIvagAPQUCkA7UADqxvWN2WUyTf++Nw1ME4HTlEW/jHw1vvBLBwYR -; 10OvNe1dxB4UgJ5CAWgHCkDduLwpu0zY9C/TNLBMUfAzzBw2xF9gDHH9eTXtXcQeFICeQgFoBwpA3b -; i+MbtKlOlfOjPe0PFcUY0eUcEUUcNza9q7iD0oAD2FAtAOFIC6cX1jdpF8p39Aw/Rv6uv2hB8mnprC -; oTXtXcQeFICeQgFoBwpA3bi+MbtIvtO/uW+6/TyY+kGw2RJ/UxXW3Gnau4g9KAA9hQLQDhSAunF9Y3 -; aNAf+W//Sv+8vuPk+ceBftjt9MaNq7iD0oAD2FAtAOFIC6cX1jdo18p3/Lfu/mcyDexabRY47jU84w -; NO1dxB4UgJ7y0VsNpNcrf69qs3YRCkDduL4xu0Sc6V+ff3HvOTClS+/xrW/Ei+YO4JY//jtp8puXVe -; 1dxB4UgB7T6A9vSdmvfqhqw3YNCkDduL5BuwSmedqnf/1/bk/8ISBaY+9vQPOf/UDe/W4P0LRnEbtQ -; ABJp+cY/8Ug4JhSAunF9k3YFTPLynf5hYujis9gwfbge7BxG09f+SdUeRQoDBSAxNP2PX0n3hv+gav -; N2AQpA3bi+UbtCvtM/3BV0+XkwucO9vThGD03xLnX59L/9g7z/u1+p2ptI4aAAJM/BkXD7X/8jp4F5 -; QAGoG9c3bBeAizff6Z+We3H5HAlrN3p8/It/VLUfkcJDAUi+Bw0i0aEA1I3rm7YLIMcvH/G34V1dz4 -; dp4PK3cxs9+iv+XoHRo/GbDVTtQaQ4UACSjGAa2PG1H6nazEsBBaBuXN+8S02Sp391wb2+utNADT2+ -; uWjW4Eeq9h1SXCgASU4++e3LPBLOAQWgblzfwEtN0qd/dUk3iGjp8c3GB7/+saq9hhQfCkASSuO3f8 -; O4mCxQAOrG9U28lKC/N9/p3wjlogngSFi70eO9//iNqj2GlAYKQBIJHAm3ef0lTgPrQAGoG9c381Iy -; 4w2/pn9J4MNXX1K1r5DSQgFI8gIGEcbFvIACUDeub+ilIs70b+yvdD1jkmC8C4kDBSDJmyAuRtNGXy -; goAHXj+sZeKuJM/9orbsXQDONdSFwoAElsGBdDAagd1zf3UgAhx+mf+yDehUYPUh8oAEm98L1PmAJQ -; N65v8qUgzvQPR8aanlE7NHoQG1AAEiv42idMAagb1zf6YhNn+gfBqOkZtcMeX2ILCkBiDR/7hCkAde -; P6Zl9s8p3+AU7/igONHsQ2FIDEKr71CVMA6sb1Tb+YcPrnLjR6kEJAAUgKgi8GkSgCEMGyE/5d13P5 -; gobNv1hw+uceMHo0+c3Lqn72Ez1QAJKC4UOfcJgARJ1U0Cu68g8pGdxA1/MlHQ0ioBhw+ucezX/2A3 -; n3u5+hmn7mE11QAJKC0+LNHyf2SDiXAESRfHqxPNjeOCWTX9f1jElGgxAoBpN/zemfS9DoQYoBBSAp -; CjCIJDEuJpMARKH8zibfF38B+z9KyZw3OQ10AS2CoJDEmf7NfVPXM2qB8S6kmFAAkqKRRINIXQEIYZ -; dN+NVlU6OUVDXU9bxJQ4swKCRxpn/dX9b1jBqg0YMUGwpAUnSSZBAJBCCmfpsbRRd/daeBmp45SWgS -; CIUgzvRv2e91PaPrwOjBeBdSCigASUlISp8wBOD41/IXfpmmgTwSLj6ahEIhQIVbvgJwwL/pekaXad -; bgR6p+bpNkQQFISgqmgZqPhIf+omaKV18BGEwDaRApLprEgm0w/Vv9x/zEHz5e0zO6DHt8SamhACQl -; R3ufMCZ3696xIwLB0t9zGlgsNAkG28SZ/lW+ousZXYRGD+IKFIDEGbT3CeMun61pIOJiRv1S1/NrRJ -; NwsEmc6d+Gd3U9o4t8+OpLqn4mk2RDAUicQnufMEQbxJutI2EaRAqLJvFgE0zyOP0rHuzxJS5CAUic -; Q7tBBMe3OMa1NQ1kXEzh0CQibMLpX/FgvAtxFQpA4iza42LGvZaSPR/QIOIymoSELTj9Kw7s8SWuQw -; FInEZ7nzCmgegApkHETTQJCltw+ld42ONLNEABSFSgvU94xhv2joQhAjU9u8toEhU2iDP9G/Gqrmcs -; NezxJVqgACRqgEGk/NUfqBIY6cAgYiMuhgLQHpqEhQ04/SscjHch2qAAJKrQ3idswyBCAWgPTQKjvs -; SZ/iErUNMzlgoaPYhGKACJSnw2iFAA2kOTyKgvcaZ/ZT/V9YzFBkaPxm82UPWzk5AACkCilqTExVAA -; lg5NYqM+xJn+4d6qpmcsNuzxJdqhACTq0d4njGlgPkfCFID20CQ46kO+0z/A6V922ONLkgAFIEkEPv -; UJUwDaQ5PoiMuAf+P0zxY0epAkQQFIEgNEoA99whSA9tAkPuKy7Pec/tmAPb4kaVAAksSR9D5hCkB7 -; aBIgceD0r/6wx5ckFQpAkkiSbBChALSHJiESB07/6gfjXUiSoQAkiSaJcTEUgPbQJEbypfvL+Yu/uW -; /qesZCgXgXGj1I0qEAJIknaQYRCkB7aBIl+RJn+gfRqOkZCwGNHsQXKACJNyTFIEIBaA9NwiQf4kz/ -; IBg1PWMhYI8v8QkKQOIVSTCI4JK+ptfsMprEST7gKDdfAQjDiKZntAmNHsRHKACJd2jvEyb20CRSog -; ITB6d/0aHRg/gKBSDxFu0GEVJ/NAmVqGBCnK8ARFWcpme0AYweTX7zsqqfWYTYhAKQeA2mgR1f+5Eq -; 0ULsoUmwRKH9T/IXf6iJ0/SMNvjk5z+Ud7/7t6/pZxUhtqEAJOQ7Wrz5Yx4Je4gm0RIFTv/CodGDkB -; ooAAn5T2AQ0RwXQ/JHk3AJI870b8O7up6xPjDehZDaUAASkgYNIn6hScCEwelfdtjjS8j3oQAkJAM0 -; iPiBJhGTC07/MvPZT/+e8S6EZIECkJAsaO8TJuFoEjO5mPzr/AXgiFd1PWO+NGvwI1U/bwgpNhSAhI -; SAaSCPhJOJJkGTDU7/vg97fAkJhwKQkAho7xMmyRWAuPu3LU8BOPZXup4xKjR6EBIdCkBC8kB7nzBJ -; lgDs8N3073CzlBz4KCU7m0Sf/qEtRNNzRoHxLoTkBwUgIXmivU+YJEcAznmzRgCCQx+nZM8H4dNA3B -; fU9IxhsMeXkHhQABISA8bFJANNQqcuwfSvLl98Nw3c3tiP6R97fAmJDwUgIfWAcTG60SR26pI+/asL -; poG7P/i+AMR9QU3PmA32+BJSfygACakn7BPWiybRk0626V9d9n9Y+0g4CdO/5j/7AXt8CbEABSAhFm -; jSpIkMavN7GfQrTgM1oUn4pDP19WgCEBz8bhq4q0kypn80ehBiDwpAQupJq1atZPr06XLq1Cm5enKf -; LOz2uioR5DOaxE9A1OlfOpveq/lzmp4zHca7EGIfCkBCYvL+++9Lr169ZO3atXLr1i0J1jeP7sn60a -; 2k8vX/WZUY8hFNIiggn+kfwF1BTc9XFxo9CCkMFICExKBZs2Yybtw4OXjwoPzpT3+STOvSoc0y+g// -; qypB5BuahBDAFA95f1GE374PU9Jf4TMGwOjR+M0Gqn4uEKIJCkBC8uDdd9+VsrIyWbJkiVy9elX+9r -; e/Sa4VTAM1iSKf0CSIwMhXo4m/6rd0PVdd2ONLSOGhACQkIh988IEMHTpUdu3aJY8fP5Z8FqaBPBJ2 -; D02iKOr0DyJR03PVhT2+hBQHCkBCItC2bVuZPXu2nDt3Tr799luJszANpEHELTQJo7Dp3+ZGKSn/Z1 -; 3PlA6NHoQUFwpAQnLQuHFjqaiokI0bN8rdu3fFxtpbPZTTQEfQJJByTf9gDNH0LHX58NWXVP1cICQJ -; UAASkoVPP/1UJk2aJEePHs1q9Ii7bpw9KJM++i+qxFIS0SKQsk3/MPXr9TM9z1EX9vgSUjooAAmpw3 -; vvvSddu3aV5cuXy1dffSWFWjSIlB4tQinT9I/xLoSQ+kABSEgaH330kYwYMUL27t0rT58+lWIsxsWU -; Dg1Cqe70D/Eula/oeO2ZYI8vIW5AAUjIf9KhQweZP3++XLx4Uf76179KMRcNIqVBg2BKn/6tfUd3ow -; d7fAlxBwpA4j3o8R04cKBs3bpVHjx4IKVcNIgUF9cFU/r0b/xr7r/eXLDHlxC3oAAkXtOyZUuZNm2a -; nDhxQv785z+LC4sGkeLhumjC9I9GD0JIIaAAJF7SqFEj6dmzp6xevVpu3rwpri32CRcHl4UT7vnR6E -; EIKRQUgMQ7Pv74YxkzZowcOHBAnj17Ji4vGkQKi8viSXOoM3t8CXEfCkDiDejx7dy5syxatEiuXLkS -; 2uPrysI0cMXARqqElRY0iSotsMeXEB1QABIvQI9vZWWl7NixQx49eiSaFhzJV69elRkDW0m/X/xPqg -; SW62gSVhpgjy8heqAAJImnTZs2MnPmTDlz5oz85S9/EU3rm2++kcOHD8v06dOlrKxMWn/wH1L+7/+L -; KpHlMprElcuwx5cQfVAAksTy/vvvS9++fWX9+vVy584d0bZgTtm0aZMMHTrUiNhmzZqZZ2r0x7ek/a -; //Ufo0+DtVYstFNIksV2GPLyE6oQAkiaR58+YyYcIEOXLkiPUe30IvxNGcOnXKhFL36NFDWrRoIU2b -; NjUVdenP+NFbDaR7w39QJbhcQ5PQcg3GuxCiGwpAkiggksrLy2XZsmVy/fp10bbu378vu3btMi7l9u -; 3byyeffCKNGzfO+ryN/lAzDdQkulxCk+ByCca7EKIfCkCSGD788EMZPny47NmzR548eSKa1rfffiuX -; Ll0ywrWiosIEVKOXGHmFUZ4d00AeCVMAFhrEu9DoQUgyoAAkiQDTsrlz58qFCxeMmNK0nj59ajIJp0 -; yZIp06dTLH13AtI7Ymn/cA08CyX/1QlQArNZrEV6mh0YOQZEEBSFSD49H+/fvL5s2bzfGppoUcwhs3 -; bsi6detk8ODB0rp1axNSDaNHfd6Tlm/8E6eBFIBWYY8vIcmDApCo5bPPPjNTs+PHjzvT4xt1wZiC1z -; 1nzhzp2rXrc6NHvlO/bDT9j1/RIEIBWG9o9CAkuVAAEnXgXlz37t1l1apV8vXXX4u2dffuXdm+fbuM -; HDlS2rVrZ+Jdchk9Yr9Pf2BcDAVgfGj0ICTZUAASVeCIdPTo0bJ//34TkqxpIYT6/PnzsnjxYundu7 -; eZYMK4UjfexTYwiPR65e9VCTMKwNIBo0eT37zs3L99QohdKACJCnA0CoPEwoUL5fLly6YeTdN6/Pix -; 7Nu3z2QTduzY0Rg9mjRpYu3INwxMAzu+9iNV4owCsPg0/9kP5N3vvldc+rdPCCkMFIDEeSCUYJLAse -; nDhw9F04JQvXbtmqxevVoGDhworVq1MlPMqPEutmnx5o95JEwBmBEaPQjxCwpA4jRwxs6YMUNOnz6t -; rsf32bNncvToUdND3KVLF/n0009jxbvYBgYRxsVQAAYw3oUQP6EAJE6CKBTck1u7dq3cvn1btK1bt2 -; 6ZaJqqqipp27bt8x5fV95fGkQoAAGNHoT4CwUgcQ6IpXHjxsmhQ4fU9fhiSnnmzBlzV7Fnz54m3qUY -; Ro+4+G4Q0STWbAKjR+M3Gzj5PUkIKQ4UgMQZcDRaVlYmS5YsMffmEJSsaT148EB2794tY8eOlQ4dOj -; zv8S31kW8YPvcJaxJttmjW4EdOfz8SQooDBSBxAoQg47gUAkpbjy+MHleuXJEVK1ZIv3798u7xdQUf -; +4Q1CTcbsMeXEBJAAUhKDu7IoREDGXkae3wPHjwoU6dOlc6dOztj9IiLb33CmsRbfaDRgxBSFwpAUj -; JwPIqJ2caNG+XevXuiaeF4Gi0kGzZskMrKSmnTpo2VHl9X8KVPWJOIi8uHr76k6nuPEFIcKABJScCk -; bNKkSXLs2DF1Pb54vSdOnJC5c+dKt27drPf4uoIPfcKahFycqR97fAkh2aAAJEUF9+IgmnBf7saNG6 -; JtYVK5Y8cOGTVqlOnxDYwemr4GeX29Em4Q0STo8oHxLoSQMCgASdGAMWLkyJGmEk1bjy/uJl64cEGW -; Ll0qffr0ed7jq83oEftrl9C4GE2iLgrs8SWERIUCkBQFxKIsWLBALl26pK7HF67k/fv3myNr9BEXu8 -; fXFZLYJ6xJ3IXBHl9CSD5QAJKCAqE0aNAg2bp1q7oeXxg9rl+/btpI8AxBj29SjB5xSVKfsCaBlwv2 -; +BJC8oUCkBQM5OFNmzZNTp48qa7HFw0kMKjMmjVLysvLjWkliUaPuMAgUv7qD1SJvSQKQMa7EELiQg -; FIrIMJGWrQ1qxZIzdv3hRtC93DmFgOHz7cyR5fV0hCn7AmsVcXGj0IIfWBApBYBWIJVWgIR3727Jlo -; WphSnj17Vqqrq6VXr17PjR6u9vi6gmaDiCbBF8AeX0KIDSgAiRVwNIomjEWLFplaNG09vrif+Pnnn8 -; v48eOf9/j6aPSIi1aDiCbhB9jjSwixBQUgqTeoPkMbxs6dO+Xx48eiacGRfPXqVVm5cqX0799fbY+v -; K3zy25dVHQlrEn/s8SWE2IQCkNQLVKDBKIGjU209vsgiPHz4sEyfPl3KysrU9/i6QuO3f6OmT1iD8K -; PRgxBSCCgASSxgiujbt6/pwr1z545oWzCnoIN46NChRsTS6GEXHAm3ef0l56eBros/9vgSQgpFikdd -; JF8QhDxx4kQ5evSoiUvRtNDje+rUKZk/f7507979eY8vjR6FAQYRl/uEXRV+7PElhBSaFI68MPngsR -; cJAyIJmXjLli0zAcna1v379809xTFjxkj79u0T3+PrCi73Cbso/hjvQggpBikcgQUbIacgJBuIQ0Eu -; HpyyT58+FU0LdxNRQQfhWlFRYeJdaPQoPi7Gxbgk/BDvQqMHIaRYpNDSMHPmTBN9wWkgyQSmZfPmzZ -; MLFy6o7PH94osvZPLkyV73+LoCpoEuGURcEX80ehBCik0KmyQuxK9bt85MR9B1ymkgARBKAwYMkC1b -; tpjjU00LOYRfffWV+b4ePHiwtG7dmj2+DtHyjX9ywiDigvhjjy8hpBSkgg0TrQ1ob5gwYYIpvcfmzy -; Myf8Ex6dSpU+X48ePGOKFpwZiC1z179mzp2rXrc6MHp35ugT7hUhtESj31o9GDEFIqUukbJ4730OKw -; dOlS45DEpskjYb+A6O/Ro4esWrVKvv76a9G2EEmzbds2GTFihLRr187Eu9Do4fD3W4n7hEsl/mj0II -; SUmlSmTfTRo0eya9cus4kGd6Z4JJx8cEQ6evRoc2cOIcmaFnp8z58/b6roevfuzR5fZZTKIFJs4Qej -; R5PfvKzqa0MISSapXBsq2h3mzp1rLs/TIJJc8DXF17i6ulouX76szuiB+rm9e/ea6wsdO3ak0UMppe -; gTLqb4a/6zH8i73z2jpq8JISS5pMI2VxypoTFh4MCBz4/TOFVJDhD2Q4YMkR07dpjJr6YFoXrt2jVZ -; vXq1Mavg7iqmmLy7qpsWb/64aEfCxRJ/NHoQQlwjFSXTDZfq0fowZcoUU5tFg0gygDMWEUCnT582E1 -; 9NC6alI0eOyIwZM6RLly7s8U0YMIgUIy6m0MKP8S6EEFdJnThxQu7evRt67IdYDbQ/rFixQnr16mXu -; V2EayA1XHzjKxz05RKTcvn1btK1bt27J5s2bpaqqij2+CaYYBpFCij/2+BJCXCa1fPlyc+kfR2lRel -; 0RrIv7VjALIF6D00BdoPVl/PjxcvjwYZU9vphWLliwwDiV8f1Ho0fyKaRBpBDC77Of/j3jXQghzpPq -; 37+/zJkzR7Zv3y5nzpyRhw8fmmlfroVqLbRCLFy40By/0SDiPhBJ+Foh4gdiP+xr7Np68OCB7N69W8 -; aOHVurx5ffc35QqD5h2+KvWYMfqXpfCSH+ksImWl5ebhyUa9asMZMh5L9B5IWte/fuydatW6WyspJ9 -; wg6DPMdhw4bJnj17zARX08L3IZzJmFT369dPWrZsyR5fj8E00OaRsE3xxx5fQogmUvh/MEXBxgohh6 -; keJi2Y8EURCziWQ+sCLuMjeBfTQGzOnMy4Ab4miPJBRl4UUe/SgkEJ7TRoJOncubOJd6HRg9jsE7Yh -; /Gj0IIRoJJX+/8GkqGfPnmbDRfTLl19+aWJgouTCYWqICWLfvn3NhIbTwNKC9x8Ts02bNplJraaF4+ -; kbN27I+vXrTUQNe3xJJmz0CddX/DHehRCilVTd/wLTO0yN0AKC+2L79u0z9XBRDANojzhw4IAxGWCi -; SINIaYA5YvLkyUbAa+zxhTMdU8tu3bqxx5fkpL59wvWZ+tHoQQjRTCrb/4BojYqKCpk1a5a553fq1C -; lzET/MPIBpIe5sLV682Gzg7BMuHhDbeM9XrlxpJmjaFuKIYEYaOXKk+SUkuFeq6WtAik994mLiiD/2 -; +BJCkkAq1/8I4Ya7V3Berlq1ytzH+uqrryKFBsNNvHPnTmM+YJ9w4cGx+6hRo8zEVmOPL+6cLlmyRP -; r06cMeXxKLOHEx+Qg/9vgSQpJEKuwDMLnDMdygQYNk/vz5RtSdO3fO9K9G2diR24aYGXS0Mi6mMOC9 -; RTbepUuXVPb4QrROnDiRPb6k3uTbJxxV/LHHlxCSNFJRPxDirXv37uZuGS7noxoOjQxRnKX4uA0bNg -; gyB3GZnwYRO0AoQZhv27bNTFw1raBZBsYh9Eyzx5fYJGqfMI0ehBBfSeXzwRBtqN7CsS7u+H3++edm -; 6hTlyBGX+5ExCAEJVycNIvUDgmn69Oly8uRJlT2++AUC90vZ40sKRZQ+Yca7EEJ8JRXnD2FSg7tayP -; 5DJytcm4gaidInjBYKhPoibgb3vHgknB94v9DFvHbtWjNZ1bbQPbxlyxbzS0Tbtm3Z40sKSphBhEYP -; QoivpOL+QUzvcGcLncArVqx43iccJXYE977QSgHTAqY/nAZGA2Jp3LhxxoyDKZqmhSnl2bNnTdA4xD -; +NHqSYZDOIZDJ6NH6zgapnI4SQOKTq+wkg4AYMGGBy24I+4UePHkXqE0Y7BYwlZWVlNIjkAO8J3iMc -; u1+9elVdjy/uJ0LwQ7x26NDBxLvQ6EGKTaY+4Vbs8SWEeErKxifBZh70CeNoMp8+YWS/4Uhw8ODB7B -; POAITx0KFDZdeuXZGc1y4tXAlAiDgmxDAAsceXuEB6nzB7fAkhvpKy9Ykg2mBMgFiprq42Ex9ku6HP -; NWzh2BitFdOmTWOfcBq4Izd79mwTu6OtxxfGoEOHDpmvKaaXNHoQlwj6hGn0IIT4Ssr2J8S9LpgUsP -; Gjh/b48eOR+4QRMr169WpjMAn6hH0UDHhutLAgOgcTUm0L0190SVdWVhrXOHt8CSGEELdIFeKTYnrX -; vn17U+m1bNky2b9/v7m7FqVPGBNDfDzaR2AU8M0ggkkZQpERkxLl/XJpYZKLWJp58+aZzMigx5dH+o -; QQQohbpAr5yXGnr1+/fuYYE2HFaAWJ2ieMfMFFixZJ165dvTCIQCThWRGRg0motnX//n3TEgNXOMQ/ -; e3wJIYQQd0kV+i+ACMAdMDhAcbyLe2E3btyIFF4MsQhncVVV1XPnaBKnSTg2HzFihOzduzfSnUmXFu -; 4mXrx4UZYuXSp9+/Y1U1saPQghhBC3SRXjL8HkDsIATl901sLRigiYKK5WHCueOnXKtEYgQiRp00A8 -; E6JwIKK09fg+efLE5D+i3aVTp07s8SWEEEKUkCrmXwbx1qNHDyMYYHA4duyYaYaIInxu3rwp69atM0 -; fKSegThlBCBy4icDDp1LRwhI9jakT+QNSj2o9GD0IIIUQP/wMgz7W7tKzNVgAAAABJRU5ErkJggg== +; thumbnail begin 640x480 46696 +; iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAACIlUlEQVR4Ae29Z3AeWZaeiV/zY6WIlX +; 5NbIQmQqH5I+2uerUrrZF6Z6Z7prt6WD2trmm1UVd3VbHoQRD0FqABvSdB770FvfcEvQe9t6C3IAn6 +; KlZ1XdV7MQlmwn6Z383MezLfG/FEV1eRH/J+yJv3zXPPOW/Op59+qrKlc+fOavXq1erFixfKGX/605 +; /U3r17VZcuXdQf//jHev/uH/7wB/33T5w4odzj+fPnatOmTfq/mbhGEi/4PRNCkk+fPn3UmTNn1Fdf +; faWf5d99953eGzZv3qzatGnT4N9t2rSpGj58uDp58qT65ptvqveDly9fqvnz56uWLVvq/UTC90CI7e +; Rks6l/8cUXatiwYbUWa2VlpZo5c6Zq0aJFxp/VrFkztXjxYvXmzZvqz3n//r06ffq0GjlypPr888+t +; EjSkcSQtBEJIdnz55Zdq7ty56sGDBzoAgIF9oby8XE2YMEF99tlnGX8WAgdr1qzRe4kzICQPHz6sev +; ToofcDm78LQiQQWADm5eWpBQsWqEePHnkid3jzKywsDCTY8IDA2yMeGO6BB8qyZctUbm6uFcKGNIyk +; BUAIyZ5OnTqpAwcOqNevX1c/t/HPBw8e1P8tyGc2b95cTZw4UV2+fFmLP2c8fvxYjR8/Xv93274HQi +; ThWwA6Ig3Hu06IHwORu5UrV2qRhg/ORjzgM3Bc8P77CKAz3r17px8m/fv3z+rzSThIueEJIebAfgAx +; hpd25xQI0b+HDx+qefPm6ZOdbD+/oKBA7dixQ719+7Z6P/j666/Vli1bVIcOHXxFFgkhH/AlAJF/MW +; XKFHXjxg1PhO7WrVtq6NChOn/DlKBABHHMmDHqyZMnyp1XiAfNrFmz9Ntf1CKHUPQRQqpAPt/GjRv1 +; Ma0ToUNQAKdA/fr1M/6z5syZo+7cuePZe7AXDRo0SB8/x/U9ECKVnEw3+m7duunF7g7x440Pb2YdO3 +; ZssNAjG4GBN7xDhw55Fj0Sirdv365zQaIWPWlH0s1NSGM0++NvRF2vLQwcOFBdvHhRR+Kc/DwU7iFv +; r3Xr1qH8TOScQ+wdOXKk+uc6R80lJSX657JAhJDMaVQAIqo3evRodfbs2erEXoyKigo1efLkSCJxWP +; h4+0MlmFt8XrhwQRUXFxuNPBIKP5J8On3+sdre6l9q8M+Srj1O8LxHsR7y8Jz94P379+ratWt6n4ji +; GhBwWL58uXr69KknMIBOEjguxn4RxXUQIp0GBWD79u3V0qVLay20srIy1bNnT517EZUAwZtdr1691K +; VLlzzXggcRWtC0a9cuFmGUZCTdyIRkytgv/6M6nZtTzdE2f6b/naQ5xAFOgY4dO+bp1PDq1Su1e/du +; vVdEeS3ILRw7dqw6f/68+vbbb6uv59mzZ2ratGm6A0WU10OIROoUgMi/GzBggC65d4fasdjx9odcQP +; zlOAQJfjaOGdwFKPjno0ePqsGDB4dyFJ0mJN28hPgBx72LWvwbj/hzs77ln6u2n30iak5RgIja1KlT +; 1e3bt6vFFv737t27asaMGfoEJo7rwrMeaUAoGKyZmrRr1y7dQ5YFIoTUTy0BiDwK9PCrmWx79epVLQ +; rxMIhbpGBRo+jk/v371deHHBQ8oJxmoXFfozQk3bSE+AXHvIj01Sf+3NHAoi/+RtTcwgTtvpDn7U6/ +; QTUujltxImPDNbZq1UpNnz69VvswCFQ0lc62EpmQpFItAPE2hWPdbdu2ecrtEV2DI0d+fr5V0TVcPK +; 4Jxw/uHlF4E8TbH3oR2nKttiLpRiUkKLOa/9tGhV9N8HfSXiAC8XTlypXqdlzI+UM6EHqyOqdAtoBT +; K1Qe79+/33M6hL0MJ0YQsiwQIcSLFoB4Qxo3bpzOr3OLKfRyQp4F/rutIgYLH8cT7o7xOJ7AgwtFKm +; gPYOu1x4Gkm5OQbHAKPfyKP4e0FohA3K1YsULn0zmFHkgFQkNmnLzYfO0ICiBNqaZBAXIF0b+WBSKE +; fCAHXdrRwLmm5Q66uiPpV0JOHa6xa9eu2jbOPfC2umHDBt2J3vY5hI2km5KQbKlZ6BEUp0AkLdFAnJ +; ycOnVKN9539gK03dq6datq27atiDngpX/EiBF6Hm6LUswDVnW2RS8JiYuc48eP11okaLnix8fXFoGD +; FgXoB1WzYzy8inGckTY/YUk3IiEmgFBb0fJfGRF/aSoQgWhCg33kVbsLPdDkf9KkSSK9dxEUWLdund +; 7TnIGIphPcoJ8wSTs57ojZuXPndJhcslBCgUhRUZEuCHEPPNiWLFmiO8pLmg+FHyGZgeKNTAo9sokG +; Dm36n0V9J5mAZvuw9kSXB2eg1Qu6QHTp0kXUXGqCQAZSgZAS5B5OehP9hEmayXEnymbr42uTAMJxBY +; 4t3NFNzBNJwkgWljQfCj9CGqah9i6mwc9KwpEwUmcggq5fv+7x8UX+3MKFCxMjjhAU6N27ty4OrKvA +; ET0M2S6GpJEcp1Q+icUSiGSiuMXdyBoPOPhHon9VEvyEJd1shJgGRRp7W/+zyMSfu0BEcrsYtPtav3 +; 59LR9fFEv0799f1FwyBUGBefPmqXv37nmigU6Ls7j6GRISFzlolpnk5smYJIpA0CjaPZzEZuSCSJoP +; hR8hVUxu9u8jF35JKBCBwEO6j9MuBQIQQhCCMCwfX1uAyBsyZIh2NHn/T+1tMByTA/QUZLsYkhZyPv +; 30U5UGsPDRJNqd54IHAB6EY8aMsaLBNUUfIY2TbXsX06BAREK7GJx4LFiwQB/xOu1dcPSLExEcBadJ +; +DjdL9Dqxj0gDNHgmgUiJA2kRgAC5HmgzQFC/u6BByIeBmgWauN1S7qhCAkTU+1dwooG2vq9oZgDRR +; 01fXxR/IEiEFuvO0yc/rcXLlyoFsQYFRUVunCEfsIk6aRKADpiysl/cfsc4zjkyJEjauDAgdYciUu6 +; kQgJk8Z8fG2KBtp0JIxIFtq4oJ2Lu70LuiKg7Qtyv2251jjAsx4RPzhgucXx++9Ph2CBh0ghC0RIUk +; mdAHTAgxHNQtEOwBnIhcGDEs1C4+qDKOnmISQK4ir0yCYaaEOBCIoeIGyQ7+wUeqDBMxok4yTEht+t +; LSAoMHPmTHXz5k3P6RD2A7if0E+YJJHUCkBHbKEFwL59+zyLHkcjpaWl+s0wymshhHgJ4uNrC3H6CU +; O0wLrNOeXAESfy3WDxRieMukEeOApkDh486PETRmQQKUIQ1CwQIUki1QLQAQsfbWHcHeORHA1v5AkT +; JoTaIkfSzUJIVNhW6BGUqP2EIe6WLVum89icvLb33x9nohEy2n3Z9Du2FQQFli5dqh4/fuwJDJw5c0 +; b3E6SfMEkKFID/BN7sunfvrquC3QMPUtgJIVHa1M+SdIMQEjW2FnoEJap2MTixgLWnu9nxy5cvdS4b +; Ctxs+z3bDF76R48erUWfkzuJgXY5yJ1kFJUkAQrAGsIMuX84JnHM0DFwjIIHK45VkBCczecTQuomLB +; 9fWwjLTxgtrnCCgab+7kIP2GFOnTqVEassQFBgw4YNWkg7A5FVpA3Ba5gFIkQyFIB1gEWNamA8UN0D +; /x/NQpEwTOFHiDl6fv6TUH18bYoGmvQTxnElLM5q+viinx2a3Nv4u5YGon0Q0teuXfPsB6ikRpSQBS +; JEKhSADYg2HJvg+MR9BICHK3pn9enTh8KPEANILvQIigk/YYgPiJL3/+RogcjUkydP9EtqUnx8bQFB +; ATzz9+zZ4zkdwj8jQpifn89oIBEHBWAjoF3MxIkT1fPnz6sXPQQhDNSnTZum3/4o/Ajxj7T2LqYJ6i +; cMu7I1a9boZ5LT3gVpKhcvXlSDBg2y+ncuHQQF4KaC6J97oGCwqKiIfsJEFBSAGYACEXgmIw/QPfAA +; 3rx5s+6yL+mXTkjcJK3QIyh+C0T69eunCxPcPr7oXrBp0ybVpk0b63/vSQAib9iwYerEiRMeP2HkCS +; 5cuJB+wkQMFIAZgi8L0b5Fixap169fVy96PADwQB41ahSTrQlpBAgdFENIEmlR0FiBCJ498+bN043r +; 3T6+5eXlavz48Tx+jAG8+K9evdpzOoQBR6kePXrQT5hYDwWgT5xcEBiou8eDBw/U8uXL2W6BkHpA8Y +; MkURY19fkJ4/QBzYndL5745wMHDmirMim//ySCXEv0isURsHMcj4FcTPx7+gkTm6EA9Am+NIT3cdyy +; ceNGzxEAEoIPHTqkBgwYwCMAQlxI8PG1BcdPGC+bEBGI8iHa5xR64GUTdpVp9/G1BTzrCwoKdMGguw +; cj9gZY8XXs2JERWmIlFIA+hF9NEOJHJZ67Yzwe0PCTnD17Nt/+SOpJe6FHUBAN3Dmz0OPji7w/pJv0 +; 7dtX1D2QFhAUwHMf/RfdAwJ+8ODBFOzEOigAAwg/N3j7g0sIjmjcw+nA37NnTxE3AiGmmdzs34sSXT +; Zyb3lH9c3rZzrPDPlmKDCQdA+kDeSBo4fs4cOHq32YnSN7WPRBJPJ0iNgCBWBA4VcTLHy8/bk7xuPY +; Bq0Zxo0bx7c/khqS4uNrC+f7/29q9sgeou6BtIOggOPJ7B4nT55UhYWFLBgkVkABmKXwc4M8D0T8IP +; rcAwnB6NuFrv223giEmIDtXcKhvgIRYi+o3B4zZoz2l3ebCSCaO336dPoJk9ihADQg/GqCYxoc1zi9 +; upz8nbKyMjVkyBC2ByCJI+k+vrYQlp8wCQ+0hEGfRrddHwTh7t27dSsZFoiQuEi9AAzri4XIg9ir2T +; H+zp07upM8/IRtuxkICUJafHxtAd91EAcREh8ICiDqV7N92L1799SIESPoJ0xiIbUCMIovF8m+7dq1 +; 02btTvNWJyEYb3+9e/e26mYgxC9p9PG1BXz32foJk+hAUABOLvv27fOcDqF92Nq1a/VewQIREiWpE4 +; BxfMlI+J0yZYqqrKz0HAFcvXpV/3u+/RFpsNDDDvA7wO9C0r2TdiD04CgFVxf3uHDhgm7xwwIREhWp +; EYBxf9F4s+vatas6deqUZ9E/e/ZMN5RGt38bbghCGoOFHnbh10+YxA/8hHH0i/3AafKNgb6PsPyjnz +; CJgsQLQNu+cFgHLV26VL1586Z60aNfFB4EeCDw7Y/YCn187YYFIvJAUADHv+7TITT+Rl/Z7t27s2CQ +; hEpiBaDNXzoWdVFRkbp165YnGoiCkZKSEtW2bVurr5+kD/r4ygDRQPyuJN1baQeOUZMmTVJXrlzx+A +; k/evRI95BF0EDSfIgcEicApXzxCO9D6G3dutVzBAAvSZi8QyDyCIDYAH185bGi5V+o5p/9Vj9DbEHS +; PR81aAWDosDS0lKPnzBOh7Zs2aJ7yLJdDDFNYgSgpC/dDaKBxcXFno7xqBhGu4CZM2fST5jEBn18ZY +; PfXf+mf2OVCKSYbJjc3FydA3j37l3P6dC1a9e0xRwdpYhJxAtASV92feCB1rFjR3XkyBHPokdC8LZt +; 23QuiKT5EPnQxzcZ4Eh4crMfWBcNpGisH+SBDx48WB09elS9f/9euduHIX8cPWQlzYfYi1gBKOlLzh +; S83eHtz90xHg+A8+fP6yghKsckzYfIg+1dksmGVn+uOn/+c1GCLQ6sWoudOqkVK1boThHucfz4cdWr +; Vy8WDJKsEScAJX25QUCeB8zCkRDsHkgIXrVqle4hJWk+RA5s75JsdLuYZv9JlCCzkSjXJHrE4uUfPQ +; LdZgJPnz7VPWSZIkSyQYwAlPSlZgseMgjzr1u3TicBOwPd43EsgOMBJgQTU6C9Cws90gOigXmffyJK +; dEkhjPWJz0XEDwWDOAZ2BooHd+7cqXvIcj8gQRAhACV9oSZBgcjw4cM9HePRJuD27dvVzUIlzYfYBw +; s90gmigUktELGVbNcqggIzZsxQN2/e9JwOYT8YNmwYHaWIb6wWgJK+yLDAgwMtAPbu3evpEYU8QXgM +; FxQUiJoPsQf6+JLZ398DLBCJFz9rFkGB/v3761Zhbj9htI5BihBai5kQmyQdWCkAJX2BUYGE3+nTp+ +; vKYGfAT/jy5cu6iSjf/kimsNCDuNne+l+yQMRCGlrDCAosWbJEPX782BMNPHv2rO4nyAIRkglWCUBJ +; X1wcIM8DLWGwyN0DCcHr16/XrWQkzYdEDws9SF04BSKMBtqNey2ja8SoUaPU6dOna/kJz549W7Vs2V +; LMc4nEgxUCUNIXZgNY2MuXL6/VMf7EiRM6Z5D+kaQmKPRY0fJfiRIlJHpYICKPbt26qQ0bNqiXL19W +; 7weoGN6/f7/+b9wPSH3ELgAlfVk2gUU9YMAAdefOHU808N69e2rx4sW6o7yk+ZDw6Pn5T3SER5IQIf +; GBe2XYl/9ZlAhKOwgKoC3M1atXPfvBgwcP1JgxY+gnTOokNgEo6UuyFSz8vLw8tWPHDp0P6Iw3b96o +; ffv2qX79+omaDzEPCz1IUNAaiEfCckBQoE+fPmr37t3q3bt3yt0+bOPGjfQTJrWIXABK+nKkgITfCR +; MmeDrG4wjg+vXrunCEb3/pg+1diAlQIMJ2MbJAUGD+/Pnq/v37nmggCgZRQUxHKeIQmQCU9KVIBAsf +; DUHLyso8i76yslJt2bJFde3aVdR8SHDo40tMwgIReaBABL0BYRv33uUnjPZhixYt0j1kEQ10/x1Jzz +; hihkgEoKQvRDpoB7Nw4UJPx3g8AFA5jFwQvv0l+Hf/x9+wvQsJDRaIyANBAfQHfP78uScwAEepnj17 +; 6tOj+v6upGcfCUaoAlDSF5EknFwQHAG7BxxFYC6OIwJJ8yGNM7TpfxYlJohM6CcsD6QAjR8/Xl28eN +; HjJ/zkyRPdQxZ+wo19hqRnIcmcUASgpC8gqWDRtmnTRif/uo8AkBx8+PBhNXDgQCYEJwT6+JKoQTSQ +; R8JywLMerlHbt2/XRYLu0yH8O/SQReAg08+T9Hwk9WNUAEqaeFrAokazUHfHeFjKwU9y7ty5bBYqGB +; Z6kDihn7A8EBSYNWuWunXrlud0CPvBkCFDdApRkM+V9NwkHzAmACVNOm1ggXbo0EH7R7oHGofu3LlT +; 9erVS9R8CAs9iD3QT1gWyAPHCdChQ4e0gYAzEBlEihB6yNYsEPGDpOdo2slaAEqabNrBwsfbn7tjPC +; yELl26pNvIoHJM0nzSiPbxbc1CD4cDzXNUWWtZ15xE6CcsDwQFSkpKVEVFhScwcOrUKVVYWNhggUim +; SHq2ppHAAlDSJMkH8GaH6q8LFy54Fj0SgteuXasfCpLmkwachymS7yWJgrCZ+0mO6v7DHDXp5zlqXz +; NZ155EWCAiDxz5ojsEukS4zQTQPmzGjBk6Rcjkz5P03E0DgQSgpAmS2mAhog8U2gO4O8bjOAB9o4YO +; HUr/SAt+Rw44XlvR8i9EiYEwQdRvwI9zVMf/r0oADv1Jjhr1UY5a+3tZ80gqbBcjjx49eqhNmzbpPo +; HOgCDcs2eP7iHrp0AkUyQ9j5NKDoVfesGiHjx4sPYPdg/4C6NZKBKGJc1HMvU9JHt98VP6+LpY+bsq +; 4efgCECHab/IUYdbyJpTEqGfsDwQFJg2bVqt9mHYH0aOHKnbyYT1syU9q5NEDoVfusHiy8/PV6WlpZ +; 4eUWgkvXfvXt1PUNJ8JH3vjTGbPr4eJn7sFX91CUAnGrjxD7LmllToJywL5P3BQx7PfvfpEP553bp1 +; ql27dlkViGSCpOe4dHIo/gjAwp88ebKnYzyOAK5du6amTp1KP2EDZPoARDI927t8YFfTHNX7r2uLv/ +; oEoMOCX7FAxAZQIIJIdtjihZgDQg+OUg8ePPBEA9FMGgIRBYVRXIek57tEcij8iAPe7Lp06aJOnjzp +; WfQQhcgPwX+TNB8b8PvAY6GHl+m/qFv4ZSIAAQpESpvKmnMSoZ+wPNAVYvjw4Xo/QLcIZ6CLxPz58/ +; WRcZTXI+m5L4Ucij9SE0T7lixZUqtj/OnTp3VTaUQLJc0nSoI+3LAxInle0qYeJoj6OYUe2QhAd4EI +; o4HxwwIReaAIZM2aNboy2BkwE0AfQRSPmGgX4wdJ+4Ht5FD4kbpAgQhC/egQ7x44Eli2bJlq27atqP +; mESbYPNCTLS9rEw2bJrxsXfn4EIAtE7ILtYuQBv+CJEyeqy5cva/HnDDhMwWc4Ez/hMJC0T9hIDoUf +; qQ8sMAi9LVu2eI4AkBB88OBB1b9//9QuQlMPMPr4fuBYqxw15meZiz+/ApAFInZBP2FZIEUIDaLhHv +; X27Vvlbh+GPcKvn7BpJO0ftpAj6WJJPCDEP3bsWE/HeFQMl5eXa2cRvP1Jmk9QTD6sWOjhZetnVWLO +; j/gDs35ZlefnRwSC2Z/wSNgG6CcsD1jFwUce7cLcA+1jBg0apHMH47w+SXtK3FAAkozAwurUqZM6fP +; iwZ9G/ePFCbd++XeeCSJqPn3mbZnKzH4japMOmsUKPukB+4K5/Ku6AkMOxMaJ7fkWgpO8pydBPWBao +; AkYP2aNHj3r8hNE+DPZy6CEbdruYTJC018QBBSDxBRY+3v7cHeNxPAxrueLi4kT4CYf1MELUjz6+H2 +; iovUtDQDDW9Xmo9i1ukrkARORQyneVBugnLA8EBZYvX66ePn3qCQycOHFCFRQURF4gUh+S9p8ooQAk +; vkGeBxY3EoLdAwnBq1ev1o2lJc0HhP0AYnsXL34KPdz5fnACaehzEQ1E/79MRaCE7ypNsEBEHugagZ +; f/8+fPe8wEnj17pp1FTPsJZ4ukfSlsKABJILCQWrdurdauXau++uqr6kWPfz527Jg+HsARgO1zCP3h +; +P2xFgs9PoBCj2E/CXbkCw/gTH8OCj0yORJmVbCdsF2MPHr27KmLQXAM7D4d2rVrl+rcuXOsBSJ1Yf +; PeFBUUgCQrsKiHDRvm6RiPNgG3b9+ubhZq0/VG+YBhoYeXmj6+mYJoYZCft69ZVZ5fQwJw22d2f2dp +; hgUi8kBQYMaMGbpA0D1QMIKm0s2aNbPyum3ao6KEApBkDRZQ+/bttX+ku0cU3gTx9te7d28rrjFK6O +; PrpS4fXz+FHkFprECELWHshwUiskDeX1FRkTpw4IDndAitY5AilJeXZ0WBSF3EvU9FDQUgMQYKRJDz +; 4e4YDz/hK1euaJ9hvP1FeT1xPEBY6OHFdKFHUOorEAkaXSTRwgIReSAoAEepR48eeaKB586dU3379o +; 3MTzgIUe5TcUIBSIyCN7vu3burM2fOeBY9qsQ2bNigq8bC/PlxPjRY6OFl7ifBCj3WfxrO9TjRwJru +; IHF/TyQz6CcsD7z0jxw5UtuIus0E0D5szpw5kfsJByGqvTMOKACJcbBo0BwalnE1O8afOnVKjRgxwq +; ifcNwPCGxIK1r+hajNNEwy9fGtCYpDUCQS9vUhGugcCRc3ie97IsFggYg8unXrptavX6+FnzNQMYxj +; YgQMbCsQqYu499UwoAAkoYFFDbs4FIS4x71799TSpUt1R/lsPt+GhwKS1BGZkLSBhkmQ9i7ZFHoEBd +; FAp0CElcDywJqDh7YNzwCSGWgHg1Sgq1evevaDhw8faqcptJORMA9b9lcTUACSUMGCQdLvtm3bdD6g +; OyF4//79ql+/fr4/zxbY3uUDQXx8g7R3MQkigRSAssEa5JGwHBAU6NOnjy4OdJ8OoVhk06ZNOm/Q1g +; KRmti65/qBApBEAo58x48fr5uDuo8Abty4odsGNOYnbNPCZ3sXLzjyDeLjOz3m/DsIP7aCkQ8KRNgu +; RhZt27bVbcJwGuQeKBgcMGBA7H7CfpCw/9YHBSCJDLzZoSEoGkW7B/JCtm7dqnNB3H/exsVOH18vQX +; x8IRa3WiK6ipuwEjgJsEBEHhB5Q4cOVWVlZer9+/fV+wFsRhcvXqx7CkqJBgIp+7AbCkASOagMW7Bg +; gadjPB4AaA+AXBAb3/6QdM72Lh8IWuiBfoA2zaO4SZV1nE3XRILDAhF5ICiwcuVKz+kQBgIFvXr1ss +; ZPOFMk7cUUgCQWkAuCBtHXrl3zLHr0jMLDoF27dtYsaCSbS9oEwyZooUdjPr5xgEKQST+377pIcOgn +; LA8UgCBF6OLFix4/4YqKCl04ghQhSfMBEvZhCkASG1gkbdq00e0B0CLGnRB85MgRNWjQoNjbA7DQ4w +; MSCz0aY+3v2QomqSAayCNhOeC4t6CgQBcMvnnzxnM6tGPHDiv9hDPB5j2YApBETs0FghA/moW6O8bD +; Uu7WrVtq7ty5sTQLZaGHl6A+vmgGbfO8YAXHSuDkQj9heSAoMGvWLP38dw/8f+QM2uon3Bg27sUUgC +; QyGlocePvr0KGDbg3jHkgILi0t1W+GUS1U+vh6icvHNwr2NWMlcBqgn7AsEBRANfChQ4c8fsKIDCJF +; CD1kJRWIuLFpT6YAJKHjZ3HAH3LmzJmejvGwELp06ZKaOHFiqG9/9PH1YouPb5g4rWAQCZRyzSQY9B +; OWB4ICJSUl6smTJ57AAKxGkUNus59wY9iwN1MAklDIZmEgz6Nnz57q/PnznkWPhOB169apjh07Gl+M +; 9PH1YpuPb5gUN2ErmLTAdjHywEv/6NGjtehzmwlUVlbqo2IJfsINEec+TQFIjGJyYWBhI9z/7t276k +; WPYpHjx4+rYcOGGWkPQB9fL7b7+IYBKoFnW56rSMzCdjHyQJ/YjRs3qpcvX1bvBxCEe/fuVV27dhVZ +; IOIQ135NAUiMENbCgMhDNfDdu3c90UD8fzQLRS5I0M/u9cVP6ePrQoqPr2nQB5CtYNIH/YTlgaDA1K +; lT1fXr1z37wf3799WoUaPE+AnXR9T7NgUgCUxUiwLJvugLuHPnTs8RABKC8fbXt29f35/JQo8PIHIX +; tNDD1vYufkArGOQBSrpmYg76CcsCkT488/Hsd58O4Z/RUiw/P19sgYhDVHs4BSDxTVyLAgm/kyZNUs +; +fP/ccAaCZ9LRp0zJqFsr2Ll7SUOjRGKgAZiuYdEM/YXkgKABHqQcPHniigSgY7N+/v+gCEYew93IK +; QJIxNiwIvNl16dJFnThxwrPoIQo3b96sc0Hq+7ss9PAi3cfXFGwFQwALROQB29Dhw4fr/cDtJ4w8QY +; hDHBkzGlg/FICkUWxcFMj1QA5gzY7xqBRDxZj77Q8PdCR9S9qMwiQpPr4mYSsY4sACEXkgKLBmzRpd +; GewMmAnAUQodJaT5CddFGHs7BSCpF9sXBHJB+vXrp8rLyz3RQBwJLF++XOXl5dHHtwZJ8vE1SXETto +; IhH6CfsDyQAjRhwgR9BAzx54zHjx/rfy/RT7guTO7xFICkFpIWA8L7qATetGmT5wgACcGHhvxE1KYT +; Jkn08TUJ2sBMS1BeIzED/YRlgf2gsLBQewe/fftWuduHwWMYPWQlt4txMLXXUwASjaSbvy4Q4h8zZo +; zuGP/2zkl1sfe/FrXRhAly9pC751f82e7jaxK2giH1QT9heSAoMHv2bHXnzh3P6dCNGzfU4MGDxfoJ +; 1yTbfZ8CMOVIutkbA29/izr9/6I2l7AJUughxcfXJGwFQxqDfsKyQB44esgiDxARQGe8fv1aLVu2TL +; SfsJuge//QoUMpANOKpBs8E+jj64XtXfxR2pStYEjj0E9YHjj2heB7+vSpJxp48uRJfVychAIRkOne +; jwJKWKoiZYoCMGVIuqEzhe1dvAQp9MARcdILPRqCrWBIprBARB448h07dqw6d+6cx0zg2bNnavr06a +; ply5ai5tMQDe3/PXr00H1znUEBmBIk3cCZQh9fLyj0gCcvCz2CAQG44reyrpnEB9vFyAMtYdAvFsfA +; zvjmm2/U7t27dSuZJBSIgJr7P466586d65k3BWAKkHTT+oE+vl4QvQvS3oWtTz5Q3KSqGETSNZN4YY +; GIPFq3bq2jfigIcQ/4y48YMUK8n7AbaAA4phw7dkzVNSgAE4qkm9Qv9PH1EtTHN22FHo3BVjAkKCwQ +; kQXy/oqKitT+/fvVV199VS2I0Dpm7dq1uodsEgpEcOyNY+76BgVgwpB0c/qFhR5eWOhhFhz/FjeRdc +; 3EHlggIo/8/Hy1aNEi9fDhQ48wOn/+vOrbt69YP2FEObdu3erJd6QATDCSbs4gsNDDC3r0BSn0WP+p +; rHlGCazgWAlMsoF+wvKAn/DIkSPVqVOndD6gM168eKHz5uAnLGk+ffr0Ubdv31aZDApA4Ui6MYNAH1 +; 8vQX18URyCIhFJc40apxVMKY/GSZawQEQe3bp10+1RIPycAUu5gwcPqu7du1tfIAIhu3TpUo8DCgVg +; grH5ZjQBfXy9BPXxZaFHZiDyx1YwxBSIBuIZJumZm3bQDmby5MnqypUrHj9hHBEXFxdbWyDSqVMnde +; bMGeV3UAAKxMYb0DSLWvwbUQ/7MKGPb3QUN6FgJmbBs4xHwnJApK93796qtLS0lp8wWsh06NDBqmgg +; BGtlZaUKMigABWHLDRcmSKLe2/qfiXrAhwl9fKMFVcBsBUNMgwIRtouRRdu2bdW8efN0exj3uHr1qh +; o4cKA+co3z+mBlh/6F2QwKQAHEeZNFyeRmPxD1UA+bID6+qApme5fgoBXMpJ/LumYiAxaIyANVwEOG +; DNF99GCd5oxXr16pJUuW6GrbONrFwOP4wYMHKttBAWgxUd9UccH2Ll6CtndBP0BJ87SRtb/PUaM+kn +; XNRBYoEGG7GGF7VOfOasWKFbV66pWVlalevXpF5ieMHMRVq1bp4+hsx7t37ygAbSSKG8kW2N7FS9BC +; jzT7+JqErWBIFNBPWB4QX+PGjVMXLlxQf/rTn6qF1NOnT9WUKVNC9xNGJfLly5eViXHv3j3d+oYC0D +; KiupljX0zfH4Ow0OMD9PG1g33NWAlMogPRQB4JywHHvYj4ocnymzdvqgUV+gfu3LlTRwpNF4jgZ86e +; PVsfO2c70Bh6z5491b7HFICWEOdNHTUs9PBCH197KGtdJQARCZR03UQu9BOWR5s2bdTMmTPVzZs3PQ +; ILDZiHDRummjVrZuTnwJLu0KFDysR4/vy59kB2RyopAGMm7hs5aujj64U+vvZR3ITimkQP/YRlgby/ +; /v3760bRNf2EkaeHKuJsCkRGjRqlKioqlIkBl5PCwsJauYoUgDFh040cBSz08EIfX3tBJfBsttEhMU +; A/YXm0b99eO3A8fvzYI7rQmBm2bH79hGE9t2nTJo8tXdCBY+rly5frljF1iVEKwBiw8SYOExZ6eKGP +; r92gDyBbwZC4YLsYeeDId/To0Vr0Ic/OGWjQjPy9TP2ECwoKah0rBx3l5eW6hU1Dx9EUgBFi680bFn +; iArWj5F6IevmFCH18ZoBUM8gAlXTNJHvQTlgcqdTds2KBevnxZLcRQMbxv3z7tNVxfgQiihIsWLfIU +; lgQd6Fe4bds21bFjx0YLUigAI8DmGzYsen3xU/0mK+mBGyb08ZUDKoDZCobYAP2E5YEiC7SFuXbtmk +; eYoXHzmDFjavkJw1ru5MmTysR48uSJmjhxomrRokVG10oBGCI236RhwkKPDyByF7TQg+1d4oGtYIht +; 0E9YFoi89e3bV7dcQcNlZ6BYZOPGjSo/P1/n5E2YMEFX55oYR44cUT179vTVlJoCMCRsv0HDgO1dvL +; DQQy4QgDgKlnTNJNnQT1geaOOyYMECdf/+fY9Yu3LlihZs3333ncp2oD/gwoULdZ6h36pjCkAKPyOw +; 0MPL1s+qCjf8FnpsZdTJCoqb8Pid2AcLROTx5Zdf6t6Ax48f9/gJmxhwBkErGr+Vxg4UgBR+WYEHEZ +; KVJT1EowJiLtMIIH187QJtYKYxEksshQUi8sDxLCJ/JgaOklFs4hwlB70mCkCKv8AgOVnSQzMOMskB +; pI+vfSD6V9xE1jWTdEE/YTl07dpVewibGCgmQcuZmsUkQaAApPALBH18/YEefjWPhFnoYS9OKxhWAh +; PboZ+w3cyYMcPTFiboyKSdjF8oACn+fMFCj+BA7I35WZX4m0unCaspbUoBSORAP2H7gBXcgQMHlInh +; t6F0plAAUvhlzORmPxD1ULQRHAmzqbP9QPixFQyRBv2E7WD48OG1rOGCjrNnz6revXsHLvRoCApACr +; 9GoY8vSSPFTVgJTORBP+H4QBPo9evXG6v2raio0GISlcRhXC8FIMVfg7C9C0krxU2qfIElXTMhgAUi +; 0dOrVy9148YNZWJ85+oPiEbRyCOEuDR9zRSAFH51Qh9fknbQCmbSz2VdMyFu2C4mfOC8MX/+fPX69W +; uV7fjmm2903uCuXbt0g2dnfPvtt9pVBNXEpgpAAAUgxV8t6ONLSFUl8KiPZF0zITWhn3B4oA9fWVmZ +; MjGePn2qPYQR6UOxx7Rp02pFFO/du6dGjhxppAUMoACk8PNAH19Cqtj4B1YCk+RAP2GzFBcXq2fPni +; kTAyISR8huH1/8c79+/XTrF7efMP553bp1ql27dlk1gQYUgBR/GrZ3IcTLvmasBCbJAgUiOOGRtDfZ +; RuvWrdW2bdt0X75sB455lyxZoj+zPjEHobdo0SL18OFDz99FY2kIxGyqgykAUy78AAs9CKlNWesqAY +; hIoKTrJqQh6CccHAiuO3fuKBPj6tWrauDAgRlV+OLPjBgxQp08eVLnCToDDaaRfxi0P2DqBaAtN1Yc +; 0MeXkIYpbsJWMFEAsS3pepMAC0QyBwKspKTEcxQbdMDHd/PmzapDhw6+CzpQBLJ27VrdGNoZqBg+dO +; iQ6tGjh+cIORNSKwBturnigD6+hDQOKoFn07UlNHY1rbJEnPBx1ZG7pGtPAmwX0zidO3fWzZhNDBzj +; IncwmyKOFi1aqEmTJqnLly972sU8evRIjR8/Xv/3TD8rlQLQppsrLqb88s/41k1II6APIFvBhAMiq4 +; 4v9pC/q6q4RuU1n0vRAVeiid+Lb0l7V6T75JQp6sWLFyrbAaF28OBB1b17dyNtXPAZcAcpLS1Vb9++ +; rf45X3/9tdqyZUvG0cVUCUDbbq44GfTTP9MPXCa4E1I/ECTIA5R0zbYD0eF4YrsFIL5nMO0XrLyOAk +; Rfe/911fcvae+KgtzcXLV3715lYkBAzp0717iPr3Od+OyaeYnXr19XgwYNajS/MDUC0KabywYgAJ0H +; Lt7E+dZNSG3wgsRWMObY+v332f2HXvFXUwACvJyy+CY8pv/C+/1L2rvCZsiQIbUqboOO8+fPq759+4 +; bi4+uAzx48eLA6evSojgA6A42ply5dqtq0aVNvhXHiBaBNN5ZNuAUgwDEXc3AI8cJWMOaoKToaEoAO +; OILny6k5nJzLmt+/pL0rLJA7t2bNGo+ICjpwLIvPysvLy7pXX6Z06tRJrVixolZvwhMnTqiCgoI6C0 +; QSLQBtuKlspaYAdN66mYNDiBe2gskO91GjXwHovJxSgGePO+eSAtALKmjRlsXEuHv3rm7ZYsqtww/4 +; mSgyQeTR3acQLiNTp06t5SecSAEYxw0kjboEIHNwCKlNcRO2gglKQ6IjUwHIl9PsqCvnkgKwChRKzJ +; kzx+O7G3SgP9/u3btVly5djPr1+gURx549e+piELc/Ma4PRSOoanauL3ECMK4vXRoNCUDm4BDyAbSB +; wUuRpGuOG4iOYT/JTPxlIgD5chqM+nIuKQCrHDaOHDmiTAwcu06fPr1WhC1O4C4yY8YMVV5e7rlWFI +; wMHz5cNWvWLFkC0JYvXgKNCUDm4BBSBdZAcRNZ1xwnK3+XufDzKwDd0UBJ30kcNJRzmXYBOHr0aH0s +; amLAnaOwsNB3E+YowDX1799fHThwQDegdgZyFFevXp0MAWjbly6BTAUgc3BI2nFawfBFqHHQU86v+H +; MaQWf6PHJHA/k7qU0mOZdpFYBoxQIXjm+//VZlO3C8umzZMt2KJapCj6C0b99eew6jWbR7iBeANn/p +; NuNHADIHh6SZ0qZsBdMYQUQHmP5PR+t4riBfEM8ZP8+lFb+V9T2FTaY5l2kUgIjS3bp1S5kYN27c0K +; 1XcIwqZf641pEjR6rTp09X+wmLFYBSvnRb8SsAmYND0grud7aCqZ+5n/gXHMhLW/9p7c+C2C5uktkL +; KX8fH/Cbc5kmAYg+eYsXL/Y4ZgQdaBGzdetW1bFjx1gLPbKhW7duav369bpBtUgBKOnLtpWgApA5OC +; SN4J5nJbCX+nrKNQaECgRLfZ+LaCDyLnn0mxlBci7TIgAh1E6dOqVMjMePH6sJEyb48tq1FRSrwOZO +; nACU9CXbTDYCkA9ikjaKm1SJEknXHCZBjxr9iGh0Iah5JMwXTy9Bci7TIgAnTpyoKisrVbYDPr6HDx +; /WrVVsLPQICiKYYgSgpC9WAiYEII9iSFpgK5gqMukpVxeIFB5o7v/nwYkF3z2dirwEzblMgwCE9Rn6 +; 3UG4ZTtevnypFixYoFuq2F7oEQQRAlDSFyoFUwLQgX7CJMkg8lTcRNY1mwaiI5OecjWZnqVw5nPFS5 +; Ccy7QIwIEDB6r79+8rE+PSpUuqqKgoVB/fuLFeAEr6MiVhWgACvqWTpILjSNzjaS2A8tNTzgFicStP +; B4wRNOcyDQIQFmgrV6709LoLOt69e6eLJPLz8xMZ9XNjrQCU9CVKJAwByAIRklScVjD4X0nXnS1BRQ +; dy0yTN03aC5lymQQB27dpVR+tMDEQPR40aFYuPbxxYKQAlfYFSCUsAugtE2C6GJAWnFUya7BGDig5U +; pUqap80EzblMgwBEdG7mzJk6Ty/bgcbQe/fu1WJSanuXIFgnACV9eZIJWwCyQIQkjeIm6WgFE7SnXN +; BCD1I3mfr4plEA5uXlqYMHDyoTA5XCs2bN0i4hkr4DE1gjACV9aUkgCgHoQD9hkgQQ1U56K5igPeVQ +; mCBpnrYTJOcyLQJwxIgR6smTJ8rEgCtG7969E13o0RBWCEBJX1hSiFIAskCEJAGIP9zHkq7ZD0F9fH +; elLC8yTMIs9JAuANG8eMOGDdU2ZtmMN2/e6KKRtm3bJr7QoyFiF4CSvqwkEbUAdI6E6SdMpIJ7F/ew +; pGvOhGx9fIkZwi70kCwACwoKVHl5uTIx4Ac8dOhQUT6+YRGrAJT0RSWNOASgAwtEiESS2ArGpI8vCU +; a2Pr5JFoBw3li4cKGO2GU73r9/r3bs2KE6deqUqkKPhohFAEr6gpJKnALQiQamqaKSyAcpDLh3k1DY +; FJaPL/GHCR/fpArA9u3bq+PHjysTo6KiQk2ePFkfI9s417iIXABK+nKSTNwC0AE2TzwSJhLAfYp7Vv +; qLSxQ+vqRxTPn4JlEAjh8/Xj1//lyZGMeOHVO9evVKlI+vKSIVgJK+mKRjiwAExU3YLobIoLiJXCGE +; yF3QQg+2dzGHaR/fJAlA+PjimPZPf/qTyna8evVKLV68OLE+viaITABK+lLSgE0C0DkSXvFbRgOJ3S +; BiPU1g8QMLPewgDB/fpAhA+O7evXtXmRhXrlzRvsBffvmlFXOzlUgEoKQvJC3YJgAdsLmyXQyxFYmt +; YOjjGz9xtXeRIABRjbt8+XLtwZvtgBfwpk2bdP4gCz0aJ1QBKOmLSBu2CkAnGkg/YWIjuC9xj0q4Vv +; r42kGc7V1sF4BdunRR586dUybGw4cP1dixY1Pj42uC0ASgpC8hjdgsAN3RQLaLITZR2rTq3rT9vqSP +; b/wEzblMiwCcNm2aevHihcp2IF/wwIEDqnv37oz6+SQUASjpC0grEgSgEw1kgQixBdtbwUB0jPmZfw +; HAQg+z2FjoYYsAhPvGvn37lIkBATlnzpxU+viawLgAlDT5NCNFADogosECEWIDuB9tTFFAzh5y9/xu +; /vTxNUvUPr6SBCAcOB49eqRMDBwd9+3bN7U+viYwKgAlTTztSBOAgH7CxAaKm9jXCiaI6KCPr1lsL/ +; SIUwC2aNFCrV27Vn399dcq2/H27Vu1evVqlZeXx/YuWWJEAEqaMKlCogAE9BMmcWNTKxi2d7EDCYUe +; cQnAnj17qmvXrikT486dO2rEiBEs9DBE1gJQ0mTJB6QKQAcWiJC4wGZf3MSO6/C70eOImIUe5giac5 +; kGAYiCjLlz56rXr1+rbMc333yjdu3apauGWehhjqwEoKSJEi/SBaATDaSfMIka3HO4/+J6AYHogCev +; 302ehR5mCZpzmQYB2K5dO3X06FFlYjx79kxXDNPH1zyBBaCkSZLaJEEAOqA5L4+ESVQ4rWDiyEdF9C +; 7IBk8fX7NILPSISgCiFx9Em4lx4sQJVVhYSB/fkAgkACVNkNRNkgQgQIEI28WQKEDkD/dc1PdbUB9f +; FnqYQ1p7lygFIFqxbN26VX377bcq24Fj45KSEu0NzEKP8PAtACVNjtRP0gQgYIEIiYriJtFF1VjoYQ +; e2+vjaIAD79Omjbt++rUyM69evq8GDB2uLOFv3z6SQQ/GXTpIoAB1YIELCBvcYUg/C/jlBRAfy0tZ/ +; Gv61pQXkXEpt7xK2APzyyy/V0qVLdWuWbAdaxCCC2LFjRxZ6REQOxV86SbIAdEcDJW00RA5oBYO0g7 +; A+P2hPORSHQLCEOfc0ETTnMg0CsFOnTurMmTPKxEBz6PHjx+t+gTbvm0kjh8IvnSRdALqjgTwSJqbB +; ywVeMsL47KA95VjoYRYpPr5xCMDJkyeryspKle347rvv1OHDh3WvQBZ6RE8OxV86SYsAdKKBLBAhJg +; mjFQx9fO0gqYUeJgRgbm6u2r17txZu2Y6XL1+q+fPnq9atW7PQIyZyKP7SSZoEoAP9hIkp0AIG95Sp +; Fwv6+NpB0tq7mBSAgwYNUg8ePFAmxsWLF1VRUZHOIbR9r0wyORR/6SSNAhDQT5iYAC8SuJ9MNCIPIj +; oQoWJ7F3NI9vENWwDCdm3VqlVGfHzfvXun1q1bp/Lz8xn1s4Acir90klYBCFggQkxQ3CRHrfht8L8f +; 9KgRuWlxzTmJSPfxDVMAduvWTV2+fFmZGPfu3VMjR46kj69F5FD8pZM0C0AHtosh2YBK4KCFF0FFB3 +; 18zZEUH98wBCCic7NmzVKvXr1S2Q40ht6zZ4/q2rUr27tYRg7FXzqhAPwQDaSfMAkC+gDO99kLkD6+ +; dpAkH1/TAjAvL08dOnRImRjPnz9XM2bM0C4hUvbGNJFD8ZdOKAC90E+Y+AVpBH6OY+njawdpLfTIRA +; COGjVKVVRUKBPj1KlTqnfv3qpp06ai9sY0kSPpYok5KABrQz9h4gfcK8V/n9mfpY9v/KSxvUumtGzZ +; Um3atEl98803Ktvx5s0btWLFCtW2bVsWelgOBWBKoQCsG/oJk0xBNfmQv2v4z9DH1w5Y6NEwN2/eVC +; YGPmfo0KH08RUCBWBKoQBsGBaIkEyAAKzvPqGPb/wEzblMG9mO9+/fq+3bt2t7OBZ6yIECMKVQADYO +; 28WQxsARcM2+kvTxtYO0+PjGLQCfPHmiJk2apI+RJe2BhAIwtVAAZg79hEl9zPqlN2+UPr52kCYf3z +; gF4NGjR1WvXr3o4ysUCsCUQgHoD/oJk7qY/49VbYQQuQta6MH2LuZgoUc0AhD9ARctWkQfX+FQAKYQ +; eDAO//h/EiXAbIF+wsTNmv+eo2b8goUeNhAk55L4F4BwBhkwYAB9fBMABWCKgAXP/Pnz1cOHD9WYX/ +; 7PooSXTdBPmDgg+udX/KHQYyujycZIu49vVALwq6++Uhs3blTt27dnoUdCoABMCV26dNHd3V+/fq0X +; 85hP/oUo0WUbLBAhoPQLfwKQPr5mYXuXaATggwcP1JgxY1SLFi1E7XukYSgAEw7e1CZOnKj7MzlNPv +; /0pz99HwGkADQB28WkG0SCMxWA9PE1R9CcS+JPAGKv2L9/v+revTsLPRIIBWCCyc3NVZs3b1YvXrxQ +; 3333XXUY/8yZM2rEz/+ZKKFlMywQSS8Q/40JQBZ6mIWFHtEIQOwbs2fPpo9vgqEATCiDBw9Wly5dUl +; 9//bVezBCAMOZes2aNrtxiFbB5WCCSThoSIyhMkDQX26GPbzQC8OzZs6pPnz708U04FIAJA804ly5d +; qptzInyPgS7t165d00bfzp+jAAwHFoikD7iB1NxQ6eNrFhZ6RCMA3759q1atWqXy8vLY3iUFUAAmiB +; 49eqiysjK9iJ3x8uVLtWvXLl255f6zFIDhQT/hdDH2772bKdu7mIWFHtEIwNu3b6vhw4frbhFS9jyS +; HRSACQBh+mnTpqk7d+6ob7/9Vi9m/O/du3fVjBkz6gzjUwCGDwtE0oFTjID2Liz0MAcKPcb8TJaQkk +; ppaanuFMH2LumCAlA4+fn5evEi0ucO4x8/flz17Nmz3r9HARgNiAaiV5ykjZf4Y+o/VB1P0sfXHOs/ +; rRLUkkSUm8F/m6O6Cbp++vimEwpAwSCn7+rVqzrHzynZr6ioUCUlJY0uaArAaFnwKx4JJxVW+JpFen +; uXOZ/kqIW/knXNEvY7Yh4KQIGgiheJuqjqddq7oNoXVb9Dhw7N6DMoAKMHBSJsF0NI3Uhv71L0oyqH +; FxSBFfyVrGu3fc8j4UABKAyU5p8+fVq9e/euur0L+jVt3bpVtW3bNuPPoQCMBxaIEFIb6T6+yPeF8A +; Mlv5F17YAVv+mEAlAIzZo1U3PmzFH379/3FHrA4WPSpEm+k3cpAOOFBSKEVB2fS27vgkgf8hUd8QcQ +; CZQ0B0cAUgSmDwpAAXTq1Enb8Tg+vhhv3rxRhw8f1pVbQT6TAjB+6CdM0gwqpiWJpJqg/Y9b+EmN/r +; kFIEVguqAAtBgsxnHjxqkbN254fHwfPXqkFixYkFW/JgpAe0A0kEfCJC0kwccXRR41xZ/U6F9NAUgR +; mB4oAC0FPr4bN25UlZWVHh/fc+fOqaKioqw/nwLQLugnTNJAkgo9aoKjYElzaUgAUgimAwpACxk4cK +; C6cOGCx8cXQnDdunWqTZs2Rn4GBaCd0E+YJBXpPr5o71KX8JMe/WtIAFIEJhsKQIto0aKFWrRokXr8 +; +HG1jy+Ofq9fv67Gjh1r9GdRANoL/YRJkpDu4wthV7PQI0nRv8YEIEVgcqEAtIRu3bqpo0eP6uIOZ7 +; x69Urt3btXdezY0fjPowC0G7aLIUlAuo8vchUbEn4OI34qa15+BSBFYDKhAIyZL774Qk2ZMkUbcbvb +; u9y7d0/NmjVLffnll6H8XApAGbBdDJGIdB9f2LihojcT8Sc9+pepAKQITB4UgDHSrl07tX37do+PLx +; o8nzx5UhUWFob6sykA5UA/YSIJFElI9/Et/SIz8ZeE6J8fAUgRmCwoAGNi+PDh6sqVKx4f36dPn6oV +; K1aoVq1ahf7zKQDlQT9hYjvSCz0Q+ctU+AGIXUnzMyEAKQKTAwVgxEDcQeRB7DmFHhCBEIMQhVFdBw +; WgTOgnTGxEensXh/r6+9UHmkFLmp8pAUgRmAwoACMEx7o43nV8fDFw/ItjYBwHR3ktFIByYYEIsQnp +; hR4OaY3+BRWAFIHyoQCMABRyoKADhR3uQg8UfqAABIUgUV8TBaB8WCBC4gSFHsN+IkvoNERjff6SGv +; 3LRgBSBMqGAjBkOnTooFu5oKWL28cXLV/Q+iWu66IATAb0EyZxIN3HtyZ+o38oEpE0vzAFIEWgXCgA +; QwTNm9HE2e3jiybPaPaMps9xXhsFYLKgnzCJCuk+vnWR5uifCQFIESgTCsAQgF0bbNvcPr6wdYO9G2 +; zebLhGCsDkQT9hEiZJKfSoSdqjf6YEIEWgPCgADVNUVKTOnTunvvrqK4+P78aNG1Vubq4110kBmFzo +; J0xMM/cTWYLGD2mP/pkUgBSBsqAANETz5s3VggUL1KNHjzw+vjdu3FDjxo2zbmFQACYb+gkTE0j38W +; 0MRv/MC0CKQDlQABqgS5cu6vDhwx4f39evX6v9+/erTp06WXnNFIDJhwUiJBuS0t6lIZA7m/boXxgC +; kCJQBhSAWfD555+rSZMmqZs3b3rauzx48EDNmTNHNWvWzNprpwBMD2wXQ/wg3cc3Uxj9owBMOxSAAW +; nbtq3aunWrevHiRXWhBxo8nz59WvXp08f666cATBcsECGZIN3H1w9+o39JrH4OUwBSBNoPBWAAhgwZ +; oi5duqQre51Cj+fPn6tVq1ap1q1bi5gDBWA6YYEIqQ/pPr5+YPQvGgFIEWg3FIA+aNmypSopKVEVFR +; UeH9+rV6+qUaNGiZoLBWB6YYEIcZP0Qo+6YPQvOgFIEWgvFIAZ0rNnT3X8+HH19u1bj49vaWmpys/P +; FzUXQAGYbugnTEAaCj1qgugfjroZ/YtOAFIE2gkFYCM0bdpUzZgxQ929e9dT6HHnzh01ffp0/d8lzc +; eBApAAFoikk6T5+PrBb/RvWgqOxqMQgBSB9kEB2ADt27fXET5E+pyBCGBZWZnq0aOHqLnUhAKQOCAa +; uPEPsgQMCU7SfHz9wOgfBSD5AAVgPSCn79q1azrHz/HxRe7f0qVLdS6gpLnUBQUgqcmCX/FIOOkkPZ +; etMRj9y4lVAFIE2gUFYA1Qxbt69Wpd1ev28UXVL6p/Jc2lISgASV2gQITtYpJHUn18/RAk+lfwV7Lm +; GJQoBSBFoD1QALro27evOnPmjMfHF33+Nm/erPv+SZpLY1AAkvrAkfCK3zIamBSS7OPrB7h4+In+zU +; nR90YBmE4oAL8Hjh1z587VDh5uH184fEycOFE7fkiaTyZQAJLGcI7LJIkd8oE0tnepD0b/GiZqAUgR +; aAepF4CdO3dWBw4c0N69bh/fQ4cOaY9fSXPxAwUgyQT6Ccskje1dGsJv9G/hr2TNL1viEIAUgfGTWg +; GIqN748eNVeXm5jvY5hR4PHz5U8+fPV82bNxc1H79QABI/sF2MDNDeJe2FHjXxG/0DaYr+xSkAKQLj +; JZUCMDc3V23atElVVlZWF3og7+/s2bOqqKhI1FyCQgFI/EI/YbthoUfdMPrXOBSA6SR1AnDQoEHq4s +; WLtXx8165dK8bH18j3QAFIAkI/YftIk4+vXxj9a5w4BSBFYHzkQPSk4RfQokULtXjxYvXkyROPjy96 +; /Y0ZM0bUXExAAUiygX7CdsBCj4Zh9C8zKADTSc6FCxe0z20SK10dunfvro4dO6bevHlTXejx6tUrtX +; v3btWhQwdRczEFBSDJFvoJxwsLPRqH0b/MiFsAUgTGQw7EEKzOZs2aJdbXtj4wn6lTp6rbt297fHzv +; 3bunZs6cqb788ktR8zEJBSAxBQtEogWFHmN+JktgxAGjf5ljgwCkCIyeHOUaaIfSsWPHRPwi2rVrp3 +; bs2FHLx/fEiROqoKBA1FzCgAKQmIR+wtGAiFb3H8oSF3HB6F/mUACmkxw0O3YqYTEeP36sfXC/+OIL +; URNxM2LECHXlyhWPj+/Tp0/V8uXLE+HjawIKQBIG9BMODxZ6ZA6jf/6wRQBSBEZLzsCBA9Xhw4er7c +; +c4oiNGzfqdimSfiGtWrVSK1euVM+ePfP4+F6+fFkNGzZMzDyigAKQhAX9hM3C9i7+YfTPHxSA6US3 +; gcFxKYQTmiC7x/Xr17U/roRoYO/evdWpU6fUu3fvqq8fPr7btm1TeXl51l9/1FAAkjBhgYgZWOjhH7 +; /Rv5LfyJpfGNgkACkCo6O6DyAKItAO5ezZs9VHp44t2sKFC611xsB1z549W92/f99T6HHr1i01efJk +; 0UfZYUIBSKKABSLBQKHHsJ/IEhG2wOiff2wTgBSB0VCrEXS3bt3Uli1btEuGe5SVlWlvXJt+MShY2b +; t3r27p4gy0ejly5Ijq2rWrNddpIxSAJCroJ+yPlb+TJR5sgtG/YFAAppM6nUDQNHnGjBn6CNhpmoyB +; 3LoJEybE3i4GN0dxcbG6ceOGx8cXBSyIVuL647w+CVAAkqhBNJBHwg1DH9/s8Bv9K/qRrPmFhY0CkC +; IwfBq0goMv7v79+z0NlHG8ivYqyBuM4xfUpk0btX79eo+PLwo9zp8/rwYMGBD59UglrQIQBQqSrjdp +; 0E+4bljokT1+o3+rGGmthgIwnTTqBYxK4CVLlujmye5x584dLbiizLHDz4PQcyqWIQAhBDds2KCFYZ +; RfnHTSIgARdVrx2xxV2rRqo0UECon1kuaQROgn/IG5n8gSC7bC6F9wbBWAFIHhkpPJH4LIGz58uDp5 +; 8qSOtrkbK69YsSL03nooQMHR7qNHj6qPpHH0iyNgHAXzJvHPoI+SKwBnf9J4BSoEYXETWfNKGmn3E6 +; aPrzn8Rv/WfyprfmFDAZhOcvz84U6dOuloG5oquwcqh3v06BGKnzCKOdCnsKaP7759+3QRSFRfVJLA +; okqSAISQQETJifJlCgQixKKkuSaNtBaIsL2LWfxG/0b8VNb8wsZmAUgRGB45fv8ConGTJk3SThtO2x +; Wn5x4KR0wViCDqiJ+Ddi7u9i5o94K2L2n28c0GZ0FJF4DFTaqEg4kWI/gcCBFJ808aaWkXQx9f80DM +; MfqXHRSA6SQn6F8sLCxUu3bt8rRgQU4eInMdOnTI6peGxs1o4AxR6RR6oMEzGj2j4XOcX5hk3AsqCR +; FAk7Zj2BhYIBIvSfcTpo9vODD6lz0UgOkkJ5u/3Lp1azV//nx1+/Ztj58wHEXgxxukQGTo0KHaus3J +; NcTnov0MnEpg9Rb3FyaVmgsqKUfAEG1+j37rwykQYTQwXpLoJ0wf33DwG/2DWJQ0v6iwXQBSBIZDTr +; YfgLy/wYMHq6NHj3r8hCHg1q1bp6tzM/nloZBk2bJlOr/QKfSAIwmOmiEmbfviJFHXYkpSDqBp2zEW +; iMSPSWEfJ2zvEi6M/pmBAjCd5Jj6oPz8fLV69WrdjNk9IOBwbNtQgUivXr3UiRMndFWxM16+fKl27t +; ypP9emL0wa9S2mJFYBm8wjg5hEJErS/JOGdD9hFnqEC6N/5pAgACkCzZNj8sNQmIG2LOjV5zh0OFW7 +; OCpu1qxZrT+PwpG7d+96Cj3QY3Dq1KmxO45Ip6GFlNQ2MKarSpGTxiPheJFWIEIf32hAMYcfAYhWMZ +; LmFyUSxB8FoHlywvhQtIRxijjcA8fEaCWDX2T79u3V7t27PUUkiAAeO3ZM/33bvihpNLaQktwH0BEN +; JgtE2C4mXqS0i6GPbzQw+mcW24UfRWA45IT1wcjpmzVrliovL/f4CVdUVOij4mvXrukcPwz89ydPnm +; jHEfr4miHtAhAUNzFnO8YCETuw2U+YPr7RweifWSQIPwpA8+SE+eH4ZfXv318dOHDAk9+Hyl63j+/F +; ixfVoEGDrPtypJLJIkqDAAQQbCZtx1CYwHYx8WJS2JuAhR7R4jf6V/qFrPnFge2ijwIwHHKi+CHo64 +; eegU7Ezy0Ejx8/rtq1a2fVlyKZTBdRWgSgg0nbMfoJx49pYR8U+vhGD6N/5rFd9FEEhkPoAhDOIfPm +; zdO9Ad1Hwc6AxVtJSQmPfg3gZwGlTQA6osFkHhmigTwSjhccCZsS9n6jfvTxjZ6iHzH6Fwa2Cz4KwH +; AIVQB27txZHTx4UL1+/bpa8MHR4+bNm7q5s3ucPn1adevWLRQ/4TTgdwGlUQC6RYPJdjEsEImXqAtE +; 2N4lPhj9CwebxR4FYHiEIgAh4iZMmKALQJx2MIj+PXr0SC1atEi1bdtWt3lBIYjbT7iyspLtXwIQZA +; GlWQA6osFkHhn9hOMn7HYxaO/CQo/4YPQvPGwWexSB4WFcAObm5qrNmzd7fHzhEHLmzBnVr18/z5/t +; 06eP2rNnjydCCKGI9jBoE8NfcmZQAAbHZB4ZNh0WiMSLaWHvPvJloUe8rPodo39hYbPQowAMD6MCEJ +; ZwqOh1+/g+f/5crVmzRvsG1/V3YBW3cOFC3fzZPe7fv699gYP4CaeJoIuHAvADYRSIMBoYLyaFPX18 +; 44fRv3CxWehRAIaHEQGInn9Lly7VvfzcPr444h09enSjfx8iD2KvrKzM4yeMf3bEI3/htclm8VAAeq +; GfcPLIVtiz0MMeSn7jTwDyqN4ftoo8isBwyVoAwrUDws3d5w/uHmj7gmNcP5/VoUMHtXbtWi0k3QNR +; xYKCAhaIuMh24VAA1g39hJNFUGHPQg97KPgrRv/CxlaBRwEYLoEFIAo1pk2bpo9u3T6+8PWFv2/QQg +; 74BY8fP16LPref8MuXL9WcOXO0f3DcX1rcmFg4FIANiwZ4AJsQgQCfNeTvZH0HSSNTYY9CjzE/k7V5 +; Jx1G/8LHVoFHARgugQRgfn6+Ki0t1aLMGYgAoqlzr169jFwYPmfnzp2en4Fx6NAh1bFjx1T/8ikAow +; HROxNHwtiQBv6trLknkcaEPfxiu/9Q1saddBj9iwYbxR0FYPj4FoAjR45UV69e9fj4wt8XzZyRC2jy +; 4vB5iPqhb6BTUYyBI+IxY8aksl2MqYVDAZgZyCMLWlXqrhylALSHuoQ9Cz3sZOGv/AnAafw9BsJGcU +; cRGD4ZC8BWrVqpVatW6apet4/vpUuXdAFHWBeIX/DAgQN15A9NpJ0BAbpp0ybdUzAtN4HJRUMBmDlB +; 8shqCgoKQLtwhD3bu9hLtx/6j/4hYihpjrZgm6ijAIyGjAQg+vWdOnWqWoBBAKLP39atW7UAi+JC4R +; e8YsUKbSnnHjdu3FB9+/ZNfLsY04uGAtA/meSR1Vc5SgFoHxD2UjZom8n7f6ow/bmM/kWHjcKOAjB8 +; GhSAKMiYPXu27snnLvS4deuWmjRpUuRVuSgAwdHv2bNnq4+gMdBIevHixYn2E6YAtEc01Gc71lDlKA +; WgnUjYnCXQ/v/NUbn/d9X/mvg8Rv+ixUZhRwEYPvUKwE6dOql9+/Z5XDrevHmjDh8+rLp27RrrRcMz +; eMuWLfo42j1OnDihunTpkrh2MWEsGgrA7EC0wTkSzqRylALQTiRszpJo/R+rhGC2nzPnE38CENHCOO +; abFGwTdRSB0ZBTl9gYN26cPlqt6eO7YMEC1bx5cysuHNG+6dOnq+vXr3v8hCEKEZ1MSoFIWAuGAjB7 +; EA1c/N8yqxylALQTCZuzNJr9n1UEjQYy+hc9tgk6CsBo8AhA2LJt2LBBVVZWenx8z507p/r372/lBO +; AvjEglopPOgCBECxm0q5F+c1AA2k1hhgUEFIB2YvvGLJWW/1eO+t3/Hiw3ENF1Rv+ixTZBRwEYDdUC +; EJW258+f9/j4QgiuW7dOC0ObJ5Gbm6uWLFmi7t275zkSRlPqQYMGiS0QCXPBUACagQJQNrZvzJLBcf +; An/7YqGujn76Efox8ByOhf9tgm6CgAoyEHR6mLFi1Sjx8/rvbxxdEvjlbHjh0r5suFyBs+fLjOA3RE +; LAYql1euXKnb2Ei6UcJeMBSAZqAAlI3tG7N0kA/4m/81R/3xB5kdCY/9e0b/4sAmMUcBGB05R48e9R +; yfwsd379692m1D0kQcULyyfv163ZzaPRDd7Nmzp5gCEQpAGVAAysb2jTkJQPhBAEIINnYkzOhfPNgk +; 5igCoyPH3d4FR6izZs0S77eLQhUUgly+fNnjJ4zehTNnzrR+flEsFgpAM1AAysb2jTlJ4Ej4v/27qv +; zAuv774L9l9C8ubBJyFIDRkeMck548eVIVFhaKuvjGwHzgWYyopnvs37/fWj/hqBYLBaAZKABlY/vG +; nDQQDfz8/8hRTf9D7f9W8htG/+LCFhFHARgtOc+ePdMOG8iRk3ThmYJ5zZs3T92+fdvjJwxHEfga29 +; YuhgJQFhSAsrF9Y04qiAZCCDr/32/rF0b/zGKTkKMAjI4cFE5IuuAgIO9v8ODBCvmOaGvjDBSLIF8Q +; VcQ23EBRLhYKQDNQAMrG9o05ySAaCCGIf/bb+oXRP7PYIuIoAKMl549//KNKC+gLuGrVKt3U2j2uXr +; 2q/Y7jbBcT9WKhADQDBaBsbN+Y0wIigBCBaOrM6F/02CLiKACjJVUCEKAABO1t0Ny6pp8wnE7gfxzH +; dUW9WCgAzUABKBvbN+a0gUKQ9Z8y+hc1tog4isBoSZ0AdOjRo4fatm2brgx2j2PHjqnOnTtH2i4mjo +; VCAWgGCkDZ2L4xpxFEA+vzAkahiKS5SMEWAUcBGC2pFYCgZcuWui0MfI+dJtgYT58+VePHj4+sQCSO +; hUIBaAYKQNnYvjGnGUQDa/YFLPqRrDlIwRYBRwEYLakWgAA3TVFRkTpw4IB6+/ZttQhE/0BECPPy8k +; K9seJaKBSAZqAAlI3tG3PaQTTQaQ/D6F94xC3cKADjIfUC0KFt27aqpKRE3b9/33MkjPYxAwYMCKVA +; JM6FQgFoBlsF4KiPctSCX0X7MyVi+8ZMqpj4MaN/YWKDeKMAjB4KQBc48kVvwFOnTnn8hBEZXLZsmT +; 4yNnmTxblQKADNYKMARDVlWescdTo3R5U2zVHFTaL72dKwfWMmJAriFm4UgPFAAVgHKALZuHGjQpNs +; 9zhz5ozq3r27kQKRuBcKBaAZbBOAa39fJfzcQAzO/iS670QStm/MhESBDeKNAjB6KADrAX7CU6ZM0T +; 0CHb9kjMrKSjVt2rSsC0TiXigUgGawRQBO+nmOOtyitvhzA3GIo+GovyObsX1jJiQKbBBvFIDRQwHY +; CGgQvXv3bt0n0BmwlNuzZ49q3759oJvOhoVCAWgGGwTgit82LPzcIJkeYjGO78pGbN+YCYkCG/YkCs +; DooQDMgDZt2qiFCxeqO3fuePyEHzx4oIYNG+a7QMSGhUIBaIY4BSCieRB0mYo/95Hwkl8zGghs35gJ +; iQIb9iQKwOihAMwQ5P0NGTJElZWVefyE8c9r167VIjGTG9CWhUIBaIa4BCAqfP0Kv5qwQMT+jZmQKL +; BlX6IAjBYKQJ/g2HfNmjXq8ePHngKRy5cvq8LCwkajgbYsFApAM8QhABG5ayzfz080MM3tYmzfmAmJ +; Alv2JQrAaKEADAD8guEUcuHCBd0w2hkvX75Uc+fO1X7Ddf09mxYKBaAZ4ooAQgRu/IMZEQjQOibu7z +; IObN+YCYkCm/YmCsDooADMgp49e6odO3Zo4ecehw8fVp06dap1Q9q0UCgAzRB3EQiid07Pv2yPg+P6 +; DuPE9o2ZkCiwaW+iAIwOCsAsQXPoOXPmqPLyco+f8JMnT9TYsWOr28XYtlAoAM1gQxUwqnq3fZa9CE +; zjUbDtGzMhUWDb/kQBGA0UgAbAjTdw4EB16NAh9e7du2oR+P79e7VlyxZtM4ciEpsWCgWgGWzpA4gj +; YfT5yyYaiLzCtFUG274xExIFNu1NFIDRQQFokLy8PLV8+XLdHsY9bt68qYqKinSBiC0LhQLQDLY5gS +; CXL0hrGAf0FIzqu7MB2zdmQqLAln2JAjBaKAANgyPf0aNHa9s4RACd8ebNG7VkyRLVokULKxYKBaAZ +; bPQCdqKBQauC09Qo2vaNmZAosGFPogCMHgrAkOjatavavHmzev78uScaePLkSf3f4j4SpgA0g40C0A +; HRwCBHwsgnjPpa48L2jVkyA38s63rTjA3ijQIweigAQwTRPvgGX7t2zeMnDFE4efJk3S4mroVCAWgG +; mwUgQDQwSIFIWtrC2L4xS6bXX+Wopb+u+l9J151GbBBvFIDRQwEYIs5N2bdvX7V37159DOwMVAyXlp +; bqxtJxRAMpAM1guwB0gPWbn2hgWgpCbN+YJTPnkxy1/fMctemP/K5tJ27hRgEYDxSAIeK+MXNzc9Xi +; xYvV3bt3PUfC9+7dU4MHD9a5g1EuFApAM0gRgAC5fX4KRNJQEGL7xiyVbv+lSvy5mf6Lqn8vaR5pIU +; 7RRgEYHxSAIVHXzQmRN3z4cHX8+HH19ddfV4tAtI5ZtWqVat26dWQLhQLQDJIEIPDTLgZ/JulRQNs3 +; Zqk40b+arPxdjur7N7LmkgbiEmwUgPFCARgSDd2kHTt2VOvWrVMVFRWeaCCs5Xr16hVJuxgKQDNIE4 +; AOyPHLxE8YYtGm6zaN7RuzRBDl2/F5FXWJQBwJj2vCaKBNRC3UKADtgAIwJBq7UZs3b64mTpyoLl26 +; VMtPeNasWaEXiFAAmkGqAASZ+gknuSDE9o1ZIoj+OQKwISHIAhF7iFqoUQDaAQVgCPi5YQsKCnQxyK +; tXrzzRwAMHDuhIYVgFIhSAZpAsAB0a8xNOsk+w7RuzNNzRv0xEII6EJc0vqUQhzqJAkk6wAQrAEPB7 +; 0yL3b+7cuerWrVvqu+++qxaBjx490k2lwygQoQA0QxIEIGjMTxhVxDZff1Bs35ilMeO/5qidX2QuAh +; GFljS/pBKFOIsCSTrBBigAQyDIjYtI36BBg9SRI0fUV199VS0CUSyyYcMG437CFIBmSIoABA0ViCS1 +; IMT2jVkSiP5B/DnUJwJ3uHIBJc0vyUQt1CgA7YAC0DDZ3sD5+flq5cqV6uHDh54j4evXr+t+gqYKRC +; gAzZAkAehQX4FIEgtCbN+YJTH5514B2JgQRFsYSfNLMlGKNApAe6AANIyJm7hZs2Zq7Nix6ty5cx4/ +; 4devX6uFCxfqAhIKQDtIogAEdfkJJ9En2PaNWQqI/pV+UbcArE8EsgDEHqIWahR/dkABaBDTN3T37t +; 3V1q1bVWVlpScaWFZWprp06ZLVkTAFoBmSKgAdavoJJ80n2PaNWQpTfl4lABsSgW4hiEphSfNLOnEI +; NgrA+KEANEgYN3XLli3VzJkz1Y0bN7R9nDOePXumJkyYELhdDAWgGZIuAEFNP+HZn8i6/oawfWOWAK +; J/Gz79IAAbE4IQgEloBt39hzlq/ffz3vr92uj918E/xwbiEGwUgPFDAWiQMG/uoqIitX//fvX27dtq +; Efjtt9+qHTt2qHbt2vmOBlIAmiENAtDB8RNOkk+w7RuzBCZ8XFv8NSQCIZokza8uhn1/7xxr9eGlCP +; 885mey5uAmTuFGARgfFICGiOIGRyXw0qVLtX+we9y5c0cNGDDAV7sYCkAzpEkAAsdPOCk+wbZvzLbT +; 7fso2OY/5qg9X+aoXU0zE4KjBQslgBeh+toloa8hIoOS5gPiFG4UgPFBAWiIqG5yiLwRI0aokydPev +; yEERlcvny5atWqFQVghKRNAAJE/yAAi5vIuu66sH1jth1EvSD+HHY3IgK3fCZrfm4G/DhHHWjeuHMO +; hLC0aGDc4o0CMB4oAA0R9c3euXNn3R/w6dOnnmjg2bNnVY8ePRo9EqYANEMaBaBDEo6Bbd+YbceJ/n +; lEIKKB9YjA8U1kzc8BLWsaE351NU+XEg2MW7xRAMYDBaAB4rrhW7RooSZPnqyuXLmi8wGd8eLFCzVj +; xowGC0QoAM2QZgGYBGzfmG2mZvSvJjWPhFEsgYIRSXOEgNv6mX/x544GInJo+zzjFG4Uf/FBAWiAuG +; /+3r17q927d3v8hGEpt3fvXtWhQ4c6o4EUgGagAJSN7RuzzdQV/asVDXSJwHnCWr9M/Di48HODAhHb +; m17HvYdRAMYDBaABbFgAbdq0UQsWLFC3b9/2+AnDUWT48OG1CkQoAM1AASgbmzdlm2ks+ldXNLBAUO +; NnFHOYEH9OYYjt87VhD6MAjB4KQAPYsghgEzdkyBB17Ngxj58w/nndunUqNze3OhpIAWgGCkDZ2L4x +; 20om0T83C34lY16ZFnpkCqKIEuZtyx5GARgtFIBZYuNiaN++vVq9erV6/Pixp0AEuYKFhYVaKFIAmo +; ECUDa2b8w24jf6ByRE/+Z+Yk74IeIpqTm0jfsYBWD4UABmia0LAn7C48aNU+fPn1fffPNNtQhEnuD8 +; +fPVYApAI1AAysb2jdlG/Eb/lv7a7vkg6gfBZkr8zRVoc2frPkbxFy4UgFli++Lo2bOn2r59u64Mdo +; +R//DPRW3UtkIBKBvbN2bbGP5T/9G/fj+ydz5B2rtIr/itC6QH2b6XUQCahwIwC6QsEDSHnj17tiov +; L6/2Ex7zyb8QtVHbCgWgbGzfmG3Db/Rv3e/tnAfau5gs9FhieZSzMdAxol+/fmL2NApAM1AAZoGkRY +; LiD9jFHTx4ULuG3Dq1S03+w78WtVnbCAWgbGzfmG0iSPRv0N/aNw9E6dw+vtm2eJHsAdzrJ/9Cndmz +; WveRvXbtmpo+fbpq2bKlqL1NkmawDQrALJC0SBzatWunli1bph48eKDevXquVvT9RNSGbRsUgLKxfY +; O2CUTzpEf/hv3EnPhDg2iJvr8O+X/3v6jD+/d4UoOeP3+uNm/erLp16yZiP5OkF2yEAjALJCyQuoBD +; yKhRo9Tp06fV+/fv1dGVY3kkHBAKQNnYvknbAiJ5fqN/iBjaOBcTRR+2N3ZujJa/+E96L4Cb1JIlS9 +; SbN2+qRSD2hDNnzqjRo0c36CZlA5L0go1QAGaBzQsjE7p27ao2bdqknj17ph5eO6lmtf4PojZvG6AA +; lI3tG7Ut+I3+IVfQ5vkgcoe8vSCFHpLau9Sk3d/8S/Xlb//Bsw+gLVhRUZHOEXcPmAgsX75cnxrZuo +; dJ0gs2QgEYEFsXhF/wBjht2jSd//HmRYXaPrkjo4E+oACUje0btg2gitdv9E9KXpyfI2HphR5t/v7f +; 1bsPIEc8Ly9PH/8iAuiMd+/eqUOHDqmBAwfWaSkaN5I0g41QAAbEtoWQLX379tWVYK9fv2aBiA8oAG +; Vj+6ZtA+jj50f87fhc1vwQDVz/acOFHsME3ysd/sufqWa//iijfQCWoWPHjlVPnjypFoHoHHHz5k01 +; Z84c3VHClj1Lkl6wFQrAgNiyCEyCXlCLFi1Sd+/e1QUiG0Y0FbWZxwEFoGxs37zjJsnRv5ogr69mNF +; CCj29D5H70l773AUT6OnfurA4fPuw5En758qXasWOH6tWrlxX7lSS9YCsUgAGxYQGEAd4Ahw0bpo4f +; P649hC8fWM0j4QagAJSN7Rt43CQ9+lcTd4GIFB/f+mjxy/+S1V6AApC5c+dq4ecMuEpduHBBjR8/Xj +; Vv3jzWvUqSXrAVCsCAxHnjR0HHjh3V2rVr9VFA5cNytoupBwpA2di+iccJ/Hv9Rv8mCBdNAEfC0gs9 +; vvjv/2hkH0CBCPzjL1265IkGYl9Ys2aN6tChQyz7kyStYDMUgAGI44aPA7zhTZgwQS/+15VP1L55/R +; gNrAEFoGxs38zjZMGv0hX9SwKtPv6B8X0AR8Jt2rTRAQGcCjkD/1xWVqaGDh2qhWKUe5MkvWAzFIAB +; iPJGt4GCggK1c+dOfRSAAhG2i/kABaBsbN/Q4yJI9G/qP8iaY5Koq72LaZz0oPv371eLwO+++07duX +; NHLVy4UIvEqPYkSXrBZigAAxDVTW4TqP5CPgiqwd6+fKbbxUja6MOCAlA2tm/scREk+tdNsCuGZBpq +; 72IaRANx7Ltnzx4t/pyB7hH4d3369InkOiTpBZuhAAxAFDe4jWDxDxo0SFeHoT8U28VQAErH9s09Di +; DkGP2zH7R3ybbQIygoEEH/2MrKymoRCD/hq1evqilTpuj+smH9bElawXYoAAMQ5UKzkfz8fLVixQrd +; KT7tfsIUgLKxfZOPgyDRPxwZS5qjdEwWegQFAYHu3btr2zj3gLPUxo0bVZcuXUL5uZK0gu1QAAYg6o +; VmI82aNVNjxoxRZ8+eTbWfMAWgbGzf6KMmSPQPglHSHKXj+PjaQsuWLdWyZcvU27dvq0Xg119/rU6d +; OqVGjhxp3E9YklawHQrAAMS10GwEb4BbtmzRRwFp9BOmAJSN7Zt91PiN/gFG/6IhikKPoKBAZMCAAe +; r27dueaCAKRkpKSrTNnImfI0knSIAC0CdxLzQbwRvgjBkz1PXr19WbF09T5SdMASgb2zf9KGH0z16i +; LPQICo6E27Vrp7Zt26YbRjsDkcEDBw6o/v37Z/0zJGkFCVAA+sSGhWYrRUVFat++ferNmzepKRDJRA +; CiseyM/yprXmlBwuYfFYz+2QcKPZr/449E7QOIBsIp5OnTpx4/4Rs3bqhZs2bpgEGQz5WkE6RAAegT +; 2xabbbRt21YtWbJE3bt3LxV+wo0JQNhJOb6iG/+Qo0Z9JGt+SUeCCIgCRv/sI+/Hf64+/8PvRD3/HR +; w/4aNHj3qOhF+8eKEjhD169PD9mZJ0ghQoAH1i42KzDbwBjhgxQp08eVInA5/ZOiexR8INCUAYybuN +; 5cG+Zjlq9iey5phkJAiBKJj9S0b/bMK2Qo+gwE1qwYIF6tWrV9UiEEWD58+fV8XFxbqYMNPPkqQTpE +; AB6BObF5tt4A1w/fr1qqKiQheIJLFdTF0CEIbyB5rXFn8OZa1z1JJfMxpoA1IEQZgEif4t/bWsOUrB +; hvYupoFNXO/evXWPQPd49OiRWrVqlWrfvn2jnyFJI0iCAtAnEhacTaAh6OTJk9WVK1fU68qKxBWI1B +; SAEHb1Cb+alDbNUcVNZM03aUgRBmESJPrX70ey5igBCYUeQcGRcG5urg4I4FTI7SeMY+LBgwc36Ccs +; SSNIggLQJxIWm40UFhaqXbt26aOAJBWIOAIQUb9dTTMXfzWjgZLmnCQkCYQwCBL9W/d7WXO0HRR62N +; rexTRID0JvQJgIuP2E0T5m/vz5qnXr1rX+jiR9IA0KQJ9IWWg2gsWNRY7F/u5VMvyEIQCn/8K/8Ksr +; Gsgj4eiRJBTCABZufgXg8J/KmqPN5H70l6Ke4SZw/IT379/v8RNGcABBAgQL3H9ekj6QBgWgT6QsMl +; tBmH/IkCE67I/wP6KBko+Ex/59VRQvWwHoRANZIBItksSCaRD92/xHf+IPf17SHG0mLh9fW4BDyMyZ +; M3VlsDPQP/Dy5ctq0qRJuoAEf06SPpAGBaBPJC0wm0Hi7+rVq3UisHQ/YUTutn1mRgSCtb9nNDAqJA +; kG0wSJ/o35maw52kgSCz2Cgmhgz5491blz5zwFIigcRL5gp06dROkDaVAA+kTS4rIdtABAKwC0BEiC +; nzBy+UxFA9EuZtLPZc1fIpKEg0mCRP92fC5rjjbS6uMfiHpGR0WrVq3UypUr1bt37zx+widOnFDDhw +; /XJ0eSdIIUKAB9ImlRSQFvgGgOiqMA6X7CEG0Qb6aOhFkgEi6SxINJEMlj9C86bPbxtQUUiAwaNEjd +; vXvXEw2EqQDMBVBFLEkrSIAC0CeSFpQkYA8Em6Dy8nL19uVT0QUiOL7FMa6paCDbxYSHJBFhEkb/oi +; PJ7V1MgyPh/Px8tXPnTvXtt99Wi0DYi8JmtF+/fqL0gu1QAPpE0mKSCAzDYRwOA3Hp7WKm/SJHHW7B +; AhGbkSQkTMHoXzRI9PG1BUQDUQjy/Plzj5/w9evX1fTp03V/WUm6wVYoAH0iaRFJJS8vT5WUlKj79+ +; +L9xNGNBAewCwQsRNJgsIUjP6Fj2QfXxvAXotoYJcuXXQeoHtAFG7ZskV169ZNlHawEQpAn0haRJJB +; iwA0DD116lQi/IQX/MrckTBEoKS524wkUWGCING/CR/LmmPcJMXHNy5q7rdoB7N48WL1+vVrj5/w2b +; Nn1ZgxY/ReIUU/2AYFoE8kLaQkgDfATZs2qWfPnukCkUVdfiRKYLhBgYiJdjEUgOaQJCxMwOhfeLC9 +; ixnq2ndRBYz8vxs3bniigXAUWbFihWrXrp0YDWETFIA+kbSQkgLyPaZOnarNxKX7CZsoEKEANIckgZ +; EtQaJ/6BUoaY5xwUIPMzS297Zt21YHBBABdAZaxxw6dEgNHDiw0c8gXigAfSJpMSWNPn36qD179uij +; gDQXiFAAmkOSyMiWING/gr+SNceoQaFHs19/JOo5ajOZ7MEoEMHR7+PHjz1+wjdv3lRz5szRHSVs1x +; G2QAHoE0mLKYm0adNGLVy4UN25c0cXiCShXQwFYHxIEhvZECT6h7xVSXOMmjT6+IaJ330YLiGI/LnH +; y5cvdQuZXr16Wa8lbIAC0CeSFlRSQT7I0KFDVVlZWSL8hBEN9HMkTAFoDkmCIxv8Rv8Ao3/1k3Yf3z +; AIsh+jAARRPwg/t5/wxYsX1YQJE7TblM16Im4oAH0iaUElnQ4dOqi1a9eqJ0+epMpPmALQHJJER1CG +; /5TRP1Ow0CMcstmTERAoKCjQos89sC9gf8A+YaueiBsKQJ9IWlRpAC0C8KaHxf/q+ZNU+AlTAJpDkv +; gIyrrfM/pnAvr4hoeJfbl169ZqzZo1+lTIGfhnnBThxIh+wrWhAPSJpEWVJpDzsWPHDn0UkHQ/YQpA +; c0gSIEFg9C976OMbLib3Zyc9CCYC7oGc8UWLFukccht1RVxQAPpE0sJKG61atdL5IKgGe/vyWWILRC +; gAzSFJiASB0b/sYHuXcAlrj27fvr3avXu3to9zBrpHoIsEuknYpiviggLQJ5IWVxqBfRD6QaE6DP2h +; ktguhgLQHJLEiF/6/ci/+Fv6a1lzDAu0d2GhR/iEuVejXQz6x1ZWVlaLwG+//VZdu3ZN/3ukD9mmL6 +; KGAtAnkhZXmkFneHSIR6f4pBWIUACaQ5Io8UuQ6B9Eo6Q5hgELPaIhiv0aAQF4BsNS1D3gLIWG0nCa +; sklfRA0FoE8kLbC0gxYBo0ePVmfOnNGd45NSIEIBaA5JwsQPQaJ/EIyS5hgG9PGNjij3bDSHLikpUW +; /evKkWgfCYP336tBo1apSOFtqkM6KCAtAnkhYYqQJvgJs3b1bPnz9PRIEIkvQlXbPNSBInfsBRrl8B +; iIIRSXM0CQs9oiWOvRsFIv3791e3bt3yRAMfPHigli1bpvLy8qzRGVFBARgASQuNVIE3wOnTp+v8jz +; cvnor2EybmkCRSMgVFHIz+ZQ4LPaIl7r0bQm/r1q26YbQz3r59qw4ePKgFYtzXGAU4Gp8yZQoFYBAk +; LTbipV+/fmrfvn36KEB6gQjJHklCJVMQIfYrAGEVJ2mOJkChR/N//JGo51cSsGEPx5FvcXGxqqioqB +; aBqBguLy9Xs2bNSrSfcNu2bbVdHvZACsAASFpspDZYAIsXL1Z3797VBSIbRjQVJVqIOSQJlkzo9kP/ +; 4g82cZLmaIK2P/kL9fkffifquZUEbNvHO3furI4cOeI5En7x4oXavn276tGjh1XXa4Jhw4apGzduVE +; c/KQADIGnBkbrBG+Dw4cPViRMndDLwma1zeCScQiSJlkxg9K9xWOgRHzbu5/ALnjdvnnr16pXHT/j8 +; +fM6SohiQhuv2w+IaK5cuVK3xPnuu++qW+JQAAZA0oIjDdOpUye1fv16fRSAAhHJ7WKIfyQJl8YIEv +; 3b8bmsOWYD27vEi817OgpEevfura5cueKJBj5+/FitXr1aN5a2+fobAi5ZZ8+e9VjkIefxwIEDFIBB +; kLToSOOgIeikSZPU5cuX1evKChaIpAhJAqYxGP2rH/r4xouUfR1WcevWrdOnQm4/4WPHjqkhQ4bo4g +; kJcwE45Zo9e7Z69OhRtSMK/hf/f+7cuTqySQGYxc1CkkVhYaEqLS3VRwEsEEkHkkRMQzD6Vzft/+qf +; s72LBUja2xENRHoQTAScgWPT27dvq/nz56vWrVtbPwdELPfv368jfe6+h+fOndP7nPPnKAADImnxkc +; zB4kY+CHpFSfcTJo0jScw0xOxf+heAEz6WNUe/5H70l6KePUlF0r7u3t87dOig9u7dW50zh4HgwK5d +; u/Rxsa3XPnbsWL1/OYUeuH4UtuAou1WrVp4/SwGYxQ1CkgneAAcPHqyOHj2qw/+IBvJIOJlIEjT1we +; hfbejjaweS9vS6wDHqjBkztIBy+wkjV3Dy5MlW+QkjeIF8dohUR7RCBKK1zYgRI+r8OxSAAZG0CEkw +; 8vPz1apVq3TOhHQ/YZJcAYjcv70+BeDUf5A1x0xhoYddSNrT6wN5f927d9eFFO7x9OlTtWHDBl1IGP +; c1or/txYsXteWpM9DnD9HKhhxOKAADImkRkuCgRQBC6sidSIKfMEmWAOz+ffTvdG6OOtE6Rx1onnn0 +; D24hkuaZCWzvYheS9vNM9nscn65YsaJWXt3Jkyd1hC0OP2HsTwsWLNBdLNyFHrC3mzZtmj7NaujvUw +; BmeVOQdICmoLAPwlGAdD9hkhwBuOTXVQIQnGqTow63aDwaiHxBSXNsDPr42oekfdwPEFQDBgxQd+7c +; 8UQD7927p5YuXapNBqK6FjSxRprSu3fvPBXLEKTdunXL6DMoALNA0oIk2YNmmjNnztSd1OknnAwkCZ +; 2aONG/mhz/Phq4r1k6on/08bUPSXt40H2/Xbt2aseOHTof0N1bD5W3RUVFof98tC2Dk5Xz8xH1e/bs +; mRahLVq0yPizKACz/EWQ9AHDcDTRxIJnuxjZSBI7NXFH/2qCaOChFrUFIPIFJc2xPujjay+S9vBswJ +; HvxIkTtfByBoTY9evXdeGIHyGWKbm5uWrbtm3q9evX1YUeSE26evWqGjRokO/PowDMAkmLkpgFibUl +; JSXq/v379BMWjCTR46a+6F9Nylp5j4STEP3L+/Gf08fXUiTt36Y0QJcuXVRZWZnnSBiWa0gZQvGIqZ +; +FRtTXrl2rLvSAAIQQ3LJlixaGQT6TAtDADUDSCTqpI/kXORf0E5aJJOHjZu4nmQlAcPL7aODB5smI +; /rHQw14k7dumQTHGokWLtCBzBoQaKodRRJiNnzAiicuWLVPPnz+vLvTA0S/yECdMmJDVd08BmCWSFi +; gJB7wBbty4UbcFoJ+wLCSJH4dMo39uSr+o+nuS5umG7V3sRtKeHRYoEOnTp48+AnYPOIqsXLlS5w36 +; /UwUH546dcrj44uij8OHDxtpP0MBmCWSFikJD7ylTZkyRedi0E9YDpJEkIOf6B9ArqCk+dWEhR72I2 +; nPDlsP4DgWAYGafsIQbcjTy8RPGGISeYRo5+Ju7/LkyRNtR4eIo4nrpQA08AsnxAFvgLt379ZHASwQ +; sR9JQgggiod+f5kIv2OtctQwgXN0QKFHs19/JGr9pxFJ+3VUQMCNGjVKmwg4Azl7sGibO3duLUs2N4 +; gU7tmzRzdydvcbvHDhgt5fTF4nBaABJC1WEj5t2rTRzTlhHo4CEfoJ24skQQQmfpyZ+Fv5O1nzqgl9 +; fGUgaZ+OQxd07NhRd4xwj5cvX6qdO3eqgoKCWn8HovHmzZseH1/8+bVr12qrN9PXSAFo6BdNiBu8AQ +; 4dOlQdO3aMfsIWI0kUZRr9g0iUNK+a0MdXBpL26DhBAcisWbO0kHMGBN6lS5d0EQeOcxERhMjDn3H7 +; +EIMjh49OrRrowA0gKRFS6IFb3TOwqefsH1IEkaNRf92Nc1Rvf9a1pzcsNBDDpL2ZxtA3l+vXr30Ma +; 57IKcPDaXx7905g+gxu3fvXu1HH+Z1UQAaQtLiJeGDJqE4BnbncTiDfsL2IEkgNRT9Q2GIpLnUpNXH +; PxC1vtOOpL3ZFvC9IdK3atUqj30bijtq+vjCcaoxH18TUAAa/OUSAjp06KCOHz+uGhr0E7YDKQKpvu +; gfon4DfixnHjWhj688JO3LNgJhN3Xq1OqGzm4hiN5+QRw9gkIBaAhJC5iEx/jx43XDzkwGC0TiR4pQ +; qiv6x/YuJGok7cm2gj0CQs/tI+wMnBjh6Ldv376RXAsFoEEkLWRiFlT+IpfDCeX7GWwXEx8ShFLN6B +; /au4z5mYxrrwv6+MpE0l5sI+gPuHnzZo+PLwo9YCf64sWL6v0AwhCWb9OmTQvFT9gNBaBBJC1mYo6i +; oiJ19+5dlc1ggUg8SBBM7ujf1s9kO3rQx1cmkvZhGxk4cKC6cuWKx8cX0b7t27drX/muXbtqS1H3wE +; kSBCP+W1jXRQFoGEmLmmQHyveXL1/uSegNOvDWB1ggEi22CyZ39G/6L+y/3oagj69MJO2/ttG8eXO1 +; ZMkS9ezZM4+P771799SkSZM83y+iffiz7sJBCMYzZ87oVjAoLDR9fRSAhpG0sElw4P977tw5ZWLAKx +; J9ovC2hwcFC0Siw3bRhOgfCz1IXEjae22jW7du6sSJE54AAf4ZvWGxf9T1d1Ag0q9fP93/zz1QGYxg +; A6KFJq+RAtAwkhY3CQZyM9w5G0EH3gjRJR6G31j4eAPEZyP/480L+glHgc3CCXl+LPQgcSJp77UF9P +; zDcxy5fU6hB571FRUVauHChY36+OJ7h9BDQMBdKQzxePDgQTVgwABjvxsKwBCQtMBJ5mBRokLLxICA +; hCck7H1q/hxUgOHn4CiABSLhYrN4ktzUmT6+8pG059oC9ojS0lL97HYKPSDi4PqBXHE/n4Uj37Fjx+ +; pm0e6gQXl5uZo9e7Zq2bJl1tdLARgCkhY5yYxhw4Z5jL2zGTg6hsiDRVB9Pw8VY4sWLdLFJSgQ2TCi +; qShhJQVJokoK9PGVj6T91haGDx+ubty44fHxRcXvhg0bdJeIIJ+J30WnTp3U4cOHPXsI3KXQdaJnz5 +; 5ZXTMFYEhIWuykfvCWBY9Gt01P0AF7nzVr1qh27drpY4LGfjbeACE80VQaP//M1jk8EqYAtBr6+MpH +; 0j5rA467R2Vlpae9y+3bt3UEz8TPQLAAJ0avXr2q3k/wM2AhN27cuEaPleuDAjAkJC14Ujd4u0I+no +; mBxp8jRozQeX5+r6Njx45q3bp1+igABSJsF0MBaBv08U0GkvZYGygoKFBnz56t5eO7f/9+7Qhl8mch +; aFBYWKguX77s2VseP36sAwvt27f3/ZkUgCEiaeGTD6AgY968eZ63raADb2m7du3SVV/43KDXhHYCEy +; dO1LkkryufsECEAtAa6OObDCTtrXGDiNycOXN0WpDbxxf/H/l5YbRscTQF8sZxKvXVV19V7zP4Z1QX +; DxkyRAvFTD+PAjBEJC1+UkV+fr46evSoMjHQ0mX69On6iMDU9eGNE0nGyAFBgQjbxVAAxgXbuyQHSf +; tq3CCyhwgfIn1uAYZIIJ7PUVwDgglID0J7GGfg+BknTQsWLNAiMZPPoQAMGUkPgbSDfA2INhMD/Z8Q +; rseboOnrhKBEPsitW7fU25fP6CdMARg5bO+SHCTtp3GDPQK5fe5CD+T+IQfQRFWuX22BY989e/ZU5x +; 5ioPBk9+7dqnfv3o1+BgVgBL8kYjd4W9q6dWud5tx+BxZfSUmJruLNpNAjKPjsQYMG6eow9IdCNJBH +; whSAYYP2Liz0SA6S9tI4QRXvxo0ba/n4ouoX1b9xXhuCDOg7CCHqDOxlV69eVZMnT9bpQ/X9XQrACJ +; D0QEgbffr00W90Jsb169fV4MGDdUVWVNePI+uVK1dqNxH6CVMAhgkLPZKFpD00TtC/D7nXNX18kYpj +; 2pkjKAgIdO/eXdvGuQdOtCBcO3fuXOffowCMAEkPhbSAJN6lS5d68jiCDlSAIYKIfk3ZFHoEBYITRx +; PIQcFDin7CFICmoY9vspC0f8YFImdw7nj69KnHxxcOH1OnTvVVbBEVOIZetmyZZ1/D/nTq1CndhaJm +; cQoFYERIejgkHQi106dPKxMDJfjjx4/XCy/ueeENEEIURwH0E6YANAELPZKHpH0zLtC1AVW1NX18kd +; sNj1+brx1BiP79+9c62YJwRXpS27Ztq/8sBWBESHpAJBnkRLhzJYIOHAMg/w69AsMo9AgKhOiMGTP0 +; cfSbF0/ZLoYCMDAs9EgekvbMOEBUD3vEvXv3PD6+OEpdsmRJg/l0NoHfNY6nt23bVl2wgoHIIPznHV +; s6CsCIfykkHvDWg8ood7VU0IEWLE6pva3zxQJHqwL6CVMA+gWFHs3/8UfW3tskGJL2yjjAHrF9+/Za +; Pr5XrlxRAwcOFDUXBwQncEKFY2xnQNCieGXmzJkUgFEi6WGRJFCY4e6XlM24ePGiFlcN+fjaAh5oyH +; PE2yz9hCkAMyHvx3+uPv/D76y/t4k/JO2TcYAGyjg1qenju3nz5sA+vraA3z+KQHCk7R4vXrygAIzj +; l0GiAbZr6M9kwscX+R+wY0PVbZjtXUyDN0Ak/548eZJ+whSADcJCj2QiaX+MGqTMLF++3OPji6NfNF +; RG5EzSXBoDxYI4uXI7XFEARoykB4dkUBBR0zMx6EAEbdSoUYF8fG0Bb4AbNmzQRwH0E6YAdMP2LslF +; 0t4YNT169NDFgG5LNbzoHzp0SPuvS5pLpiB4gQbRjsc9BWAMSHqASAM3+KxZs4z4+OJNcO/evbrqK4 +; 72LqaBgEWCM3JaXldWsECEApCFHglF0n4YNTgVQf4beqe6fXzR0QEe8EjvkTQfv+D+wLH2+vXrKQDj +; /CUQs7Rr106/vZkYz58/1w8Jkz6+toA3wF27dmmRnPYCEUlizSQo9Gj2649E3bckMyTtg1GDFB681N +; fsk3f+/PmMrNOSBIIaFIAxIemBIgEc0VZUVCgTA8cCeBhIKPQICiqYkQ+CXlHvXqXXT1iSaDNF7kd/ +; KepeJZkjaQ+MmtGjR6ubN296Cj3Q0WHNmjX6RV/SXExBARgjkh4stoKFu2nTJk+vo6AD5f8rVqzQ1b +; OSCj2CgjdAVL8dPXpU58Gk0U9YknAzAX18k4ukvS9K8LKLAj6IPbePL8TgyJEjRc3FNBSAMSPpAWMb +; hYWFehGbGPicoUOH6kafkr4DE7Rv316tXr1a58CkzU9YknjLBhZ6JBtJe16U9O3bV124cMHTCQIv+u +; gJi5QhSXMJAwrAmJH0kLEFJPEuWrRIL+RsBxp9ovknqmSTUOgRFLQIGDdunM6FwdtxWvyEJYm4oLT6 +; +Aei7kXiD0n7XVTgeTZ//nz15MkTT6EH+sHCKQnPeknzCQsKQAuQ9LCJmw4dOmg/RhMDDwdUxdrg42 +; sLsLaDIEaT0DT4CUsSckGifvTxTTaS9rmogNf7kSNHPD6+SHE5deqUbg8maS5hQwFoCZIeOnExYcIE +; XZ1rYqAreq9evazy8bUF5FWilU55ebl6+/JpogtEJAk6P7C9S7KRtLdFuYdij7h7967Hxxd7xrJly3 +; QbLEnziQIKQEuQ9PCJmtzcXLVz587qUH42A61PcHyMPkhpKPQICr6bAQMGqIMHD+qWCUltFyNJ1GUC +; fXyTj6R9LSqwR2zZskXbt7l9fNHwGFagkuYSJRSAFiHpIRQV/fv3104cJgYaIEPUID9E0ncQJ3l5ef +; rt+f79+4n0E5Yk7hqDPr7JR9J+FhWDBg1SV69e1YIPw/Hx3bZtmxaGkuYSNRSAliHpYRQmqMZFSxZ3 +; HkfQgfyPjRs36vxBRv38g36I6LOI/ohJ8xOWJPAagj6+yUfSPhYFONJdunSpevbsWfXpEI5+cQQ8ce +; JEfmcZQAFoIZIeSmHQtWtXXbpvYqDqa+zYsaJ9fG36vaDnIh64KBBZ1OVHosReEgUg27skH0l7V1TA +; nvPkyZO1fHzR0xQdHSTNJU4oAC1E0sPJNNOnT9cNO7MdeCPcv3+/rvpKc3sX00BIT506VefWJMFPWJ +; LYqwkLPZKPpH0rCvAsxx6BF3t3exe4QMHZCOk9kuYTNxSAliLpIWUC5JpBsJkYaGEyZ84c3QFe0ncg +; CTRY3bNnj861kVwgIknwOdDHNx1I2q+iAHsEPMzd/V+R93fx4kXVr18/UXOxBQpAi5H0sMqGYcOGaR +; cKE+Ps2bNanCTZx9cWkGC9cOFCdefOHbEFIpKEH6CPbzqQtE9FwYgRI3RbKrePLzo6rF+/Xr/oS5qL +; TVAAWo6kh5Zf0IAZHo1O9VY2A61KYGeGt0QWekQH+ijCQq+srEzn41w+sFrUkbAk8Ucf3+QjaW+KAv +; QkxXMdpzpuH99bt26pMWPGiJqLjVAACkDSAyxT0IT5+vXrysRABGr48OGp9PG1BVRYr127VrurVD4s +; F+MnLEH4sdAjHUjak6IAXu/nzp3z+PjiRX/fvn3av1zSXGyFAlAIkh5kDYEkXng0Incs24E3wdLSUt +; WlSxcWelgABDg68SMn59XzJ2rfvH7WRwNtF3/08U0HkvaisEH6zty5c3VakLvQ49GjR9qhCKcOkuZj +; MxSAQpD0MKuP/Px8bcFmYjx9+lRXo+KIQNJ3kAYQ3YVzC6q5USBis5+wrcKPPr7pQNIeFAUdO3asdh +; 9yBlJLzpw5o58rkuYiAQpAQUh6sNWkuLhY948zMY4fP64KCgro42sxEOaoxL5586Z6+/KZtX7CNoo/ +; tndJB5L2nigYN26cun37drWPL3L+Kisr1cqVK3W+uKS5SIECUBiSHnAAnruw5HEWdTYDx8bo/E4fXx +; ngdzRw4EB16NAh3aTVxnYxNgk/tHdhoUfykbTfRAGe52gw7/bxRXrPjRs3dIcISXORBgWgQKQ86NCO +; BQUaJgYaD8PzkT6+8sDRP2z9Hj58qNvF2FQgYov4Y6FHOpC0z0QBvN4vX77s8fFFnz+kkLRt21bUXC +; RCASgUmx9yEGklJSWePI6gAxVgW7Zs0bkhLPSQCxK70bYBuTx42B9dOdaKAhEbxB99fNOBpP0lbFAw +; tnjxYp3L7fbxvXfvnpoyZYo+PZA0H6lQAArGxoccfBjRjNnEQNXX+PHj6eObIODhCUH//Plz7Sccd4 +; FI3FE/FnokH0l7ShTAUxx9Q5EW4gz8M3K78d8kzUU6FIDCselBhzc3NOzMduAYAHljPXr0YNQvgSCh +; G36e6AP55sXTWP2E4xJ/LPRIB5L2krBBVA97BKJ8Tk44on+IAiIaiKigpPkkAQrABBD3Qw65GvCFNT +; HQOmTevHn08U0B8O9EU1fk/MRVIBK18EOhR/N//JGo3xPxj6T9IwqwR+zYsUOvdafQA6kgyP9DHqCk +; uSQJCsCEENeDbsiQITq538S4cOGCFgX08U0P2BiWLFmiowJx+AlHKf7yfvzn6vM//E7U74f4R9K+EQ +; Wo5EVFr9vHFxW/qPxFBbCkuSQNCsAEEeVDDnl58Gh02/QEHcj/gI0YqkXZ3iV9oJ8jrPxOnDih76cz +; W+dEdiQclfhjoUfykbRXRAFSPdDDD738nKgfjn7R6w89/yTNJalQACaMKB50yM27cuWKMjEQ+Rk5ci +; QLPYjq1KmTWr9+vaqoqNAFIlG0iwlb+LG9S/KRtD9ERc+ePXXFP1w8nIGuEHD5QEcHSXNJMhSACSSs +; Bx2ic3B3ePXqlcp24E1w9+7duuqLhR7EAYngkyZN0rlBrysrQi8QCVP80cc3+UjaF6IA0Xz49aKDg9 +; vHF76+8PdFeo+k+SQdCsCEYvpBh+PZw4cPKxMDLUBmzJhBH19SL4WFhWrXrl36ZSPMApEwhF/7v/rn +; bO+ScCTtBVHRvn17XdTl7v+KlI5z587p9SxpLmmBAjDhmHjYjR49Wh/LmRinTp1SvXv3po8vaRRUgq +; Mi/NatW6H5CZsWf7kf/aWo75j4R9LzPyrQ5B3r1F3ogY4OyBPHi76kuaQJCsAUEPRBhw148+bN1Ys6 +; m4Hy/+XLl+uqTxZ6kExBesDgwYPV0aNHdT4RooEmj4RNij/6+CYbSc/8qMAegbxdROrdPr7l5eVqxI +; gRouaSRigAU4Lfhx2idHijMzHwMEC7GPr4kqDgeGnVqlU6t8ikn7AJ4cdCj2Qj6TkfJWjZdfHixWof +; X+dFH6kb7dq1EzWXtEIBmCIyedghSRdd2bGQsx14MGzbtk1Xd7LQg2QLXiCKi4t1TpEpP+FsxR/buy +; QbSc/3qMA6XLBggXry5Imn0OPBgwfa4QfPeknzSTMUgCmjoYcdyvORo2di4OEwceJE3QtKwoOeyAFt +; iPBiAdvBbP2Es4n6sdAjuUh6pkcJvN6PHDni8fFFasbJkydV9+7dRc2FUACmlpoPPIg1VOeaGHhAoA +; 8UCz1IWODFAu0m4DCQjZ9wEPFHH9/kIukZHvV+gfZMd+/e9fj4Pnv2TJWUlOg+rpLmQ6qgAEwxWNS5 +; ubmqtLS0OoE3m4Gqr0WLFunEYBZ6kCiAj+iBAwd064kg7WL8CD/6+CYXSc/tqMEegYg77NvcPr5Xr1 +; 5VgwYNEjUX4oUCMMUMGDBA3b9/X5kYjqk3fXxJ1OTl5ekoBO5lv37CmYo/+vgmF0nP7KhBBf61a9eq +; Cz0cH9+tW7dqYShpLqQ2FIApBEm88Gh02/QEHfiMDRs26CpNRv1IXODFA5aCyGH14yfMQo/0IumZHT +; U40l22bJlOC3IKPXD0iyPgCRMm8PtLCBSAKQPWayjdNzEQcUEDUPr4Elvo0qWL2rhxo85NysRPmO1d +; 0oek53UcoJgDL1LuAAGKPpDbjSIQSXMhDUMBmCJgv4Y8vWwH3ghh+YMHBdu7ENvAC8mUKVN0jlJjfs +; Is9EgPkp7VcYBnOfYItHNxt3dBR4f58+frkyNJ8yGNQwGYApAjhUR5E6OyslJXX9LHl9hOnz591O7d +; u3XOUn0FInUVejT79Uei5kkaRtKzOi7QuBlrxd3/FakUFy5cUH379hU1F5I5FIAJB3Y8jx8/VibGmT +; Nn9KbKQg8ihTZt2qiFCxeqO3fu6AKRmn7CHenjm1gkPafjZNSoUermzZseH19Yu61bt053dJA0F+IP +; CsCEgj5pKM5w2/QEHXgrhA0XIoks9CDSwNHW0KFD1bFjx2r5CVcXenzyQ1FzIvUj6TkdJzjFWbNmjU +; 4Lcvv4QgyOHj1a1FxIMCgAE0ivXr10g1wTA37Aw4YNU82bNxe1CRBSkw4dOugNDxFxx0945Gf/QZUd +; KFWFhYXMZxWOpGd03MDr/fz58/qY1xnopbl3716Vn58vai4kOBSACQIbGJJ1kfOU7UDkcOfOnbrqC1 +; E/52dI2hAIqQleZMaPH69zm5wjLwwcec2bN08nukuaD6Hw8wPSd3Cf4yXIXejx8OFDNXPmTO3eJGk+ +; JDsoABMC3tqOHz+uTIyKigpdRYlj5Lp+lqTNgZC6QJR8x44dtariDx8+XP3SI2k+aUTKs9kW4PV+6N +; ChWj6+p0+f1tadkuZCzEABmADGjRtnzMe3rKxMFRQU6GhiYz9X0mZBSE2QAzV79mxVXl5eHQ3BQNuL +; 4uJiellbioRnsm0g6o1CKMfHFzl/6OiwfPnyel/0SfKhABQMKrS2b9/u2byCDhyBLVmyRFdN+n3ISt +; o8CHGDSN/AgQNrRUaQArFlyxYWPlmE7c9jG8HzfPPmzR4fX6Q+XL9+XRdGSZoLMQ8FoFD69eunbXlM +; DDTMxSaYTaNPSRsJITVBHzREQ9AE1z1QEVlUVMQCkRix+TlsM3imX7lyxePji44OCBq0bdtW1FxIOF +; AACgNJvPBodEcrgg7kf+DtENWR7kKPbJC0sRDiBmsL7S/Q79LdPgmb5tKlS2l5GCG2Pn8lgEInnObA +; DtHt43vv3j01efJkY896Ip//AYs5mPzCJ5rXAAAAAElFTkSuQmCC ; thumbnail end ; ; @@ -1229,8 +1664,8 @@ ; top infill extrusion width = 0.42mm ; first layer extrusion width = 0.50mm -M73 P0 R1 -M73 Q0 S1 +M73 P0 R6 +M73 Q0 S6 M201 X4000 Y4000 Z200 E2500 ; sets maximum accelerations, mm/sec^2 M203 X300 Y300 Z40 E100 ; sets maximum feedrates, mm / sec M204 P4000 R1200 T4000 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2 @@ -1249,7 +1684,7 @@ M862.5 P2 ; g-code level check M862.6 P"Input shaper" ; FW feature check M115 U6.0.4+14924 -M555 X118.1 Y94.1 W32 H17.8 +M555 X114.916 Y91.1 W32 H23.8005 G90 ; use absolute coordinates M83 ; extruder relative mode @@ -1295,8 +1730,6 @@ G29 A ; activate mbl ; prepare for purge M104 S230 G0 X0 Y-4 Z15 F4800 ; move away and ready for the purge -M73 P2 R1 -M73 Q2 S1 M109 S230 G92 E0 @@ -1307,21 +1740,13 @@ M569 S0 E ; set spreadcycle mode for extruder ; G92 E0 ; reset extruder position G1 E2 F2400 ; deretraction after the initial one before nozzle cleaning -M73 P3 R1 -M73 Q3 S1 G0 E7 X15 Z0.2 F500 ; purge G0 X25 E4 F500 ; purge -M73 P7 R1 -M73 Q7 S1 +M73 P1 R6 +M73 Q1 S6 G0 X35 E4 F650 ; purge -M73 P9 R1 -M73 Q9 S1 G0 X45 E4 F800 ; purge -M73 P10 R1 -M73 Q10 S1 G0 X48 Z0.05 F8000 ; wipe, move close to the bed -M73 P11 R0 -M73 Q11 S1 G0 X51 Z0.2 F8000 ; wipe, move quickly away from the bed G92 E0 @@ -1337,226 +1762,1126 @@ M107 ;Z:0.2 ;HEIGHT:0.2 G1 E-.7 F2100 -M73 P12 R0 +M73 P2 R6 G1 Z.8 F720 -G1 X119.793 Y100.346 F18000 +G1 X116.784 Y97.356 F18000 G1 Z.2 F720 -M73 Q13 S1 +M73 Q2 S6 G1 E.7 F1500 -M73 P13 R0 -M73 Q13 S0 M204 P500 ;TYPE:Skirt/Brim ;WIDTH:0.5 G1 F2400 -G3 X122.509 Y98.796 I3.45 J2.89 E.12138 -G1 X127.541 Y98.802 E.19125 -G3 X131.211 Y102.562 I-.798 J4.45 E.21308 -G1 X131.243 Y107.123 E.17335 -G3 X127.321 Y111.223 I-4.475 J-.354 E.23343 -M73 P14 R0 -M73 Q14 S0 -G1 X122.82 Y111.238 E.17107 -G3 X118.777 Y107.321 I.41 J-4.469 E.23126 -G1 X118.762 Y102.82 E.17107 -G3 X119.755 Y100.392 I4.481 J.416 E.10117 +G3 X119.696 Y95.774 I3.447 J2.874 E.12901 +G1 X130.29 Y95.773 E.40264 +G1 X131.3 Y96.002 E.03936 +G3 X134.21 Y99.561 I-1.572 J4.255 E.18323 +G1 X134.247 Y105.376 E.22101 +G3 X134.251 Y109.244 I-12.717 J1.947 E.14757 +G3 X134.02 Y111.24 I-6.469 J.261 E.07668 +G3 X130.13 Y114.242 I-4.274 J-1.516 E.19736 +G1 X119.874 Y114.242 E.38979 +G3 X115.787 Y110.424 I.363 J-4.485 E.22938 +G2 X115.626 Y107.993 I-15.768 J-.176 E.09269 +G3 X115.752 Y104.868 I13.615 J-1.014 E.11913 +G1 X115.772 Y99.722 E.19558 +G3 X116.746 Y97.403 I4.459 J.508 E.09689 M204 P4000 M204 T4000 -G1 X119.755 Y100.392 F18000 -G1 X120.108 Y100.68 +G1 X116.746 Y97.403 F18000 +G1 X117.1 Y97.69 M204 P500 G1 F2400 -G3 X122.539 Y99.253 I3.133 J2.553 E.10944 -G1 X127.511 Y99.259 E.18897 -M73 Q15 S0 -G3 X130.754 Y102.593 I-.772 J3.995 E.18817 -M73 P15 R0 -G1 X130.786 Y107.104 E.17145 -G3 X127.248 Y110.771 I-4.013 J-.33 E.20969 -G1 X122.842 Y110.781 E.16746 -G3 X119.229 Y107.248 I.384 J-4.007 E.20763 -M73 Q16 S0 -G1 X119.219 Y102.842 E.16746 -M73 P16 R0 -G3 X120.071 Y100.727 I4.022 J.391 E.08785 -M204 P4000 -G1 X120.071 Y100.727 F18000 -G1 X120.429 Y101.01 +G3 X119.764 Y96.227 I3.127 J2.536 E.11845 +G1 X130.216 Y96.224 E.39724 +G3 X133.759 Y99.639 I-.446 J4.009 E.20115 +G1 X133.79 Y105.389 E.21854 +G3 X133.795 Y109.214 I-12.556 J1.928 E.14593 +G3 X133.592 Y111.079 I-6.173 J.271 E.07158 +G3 X130.109 Y113.785 I-3.839 J-1.345 E.17717 +G1 X119.893 Y113.785 E.38827 +G3 X116.243 Y110.386 I.34 J-4.024 E.20431 +G2 X116.08 Y107.943 I-15.804 J-.174 E.09315 +G3 X116.209 Y104.882 I13.557 J-.962 E.11669 +G1 X116.224 Y99.793 E.19342 +G3 X117.063 Y97.737 I4.003 J.433 E.08551 +M73 P3 R6 +M73 Q3 S6 +M204 P4000 +G1 X117.063 Y97.737 F18000 +G1 X117.421 Y98.019 M204 P500 G1 F2400 -G3 X122.571 Y99.71 I2.811 J2.22 E.09729 -G1 X127.424 Y99.709 E.18444 -G1 X127.911 Y99.835 E.01912 -G3 X130.297 Y102.625 I-1.178 J3.423 E.14633 -G1 X130.328 Y107.084 E.16947 -G3 X127.178 Y110.319 I-3.55 J-.306 E.18585 -M73 P17 R0 -M73 Q17 S0 -G1 X122.865 Y110.324 E.16392 -G3 X119.681 Y107.178 I.356 J-3.546 E.18394 -G1 X119.676 Y102.865 E.16392 -G3 X120.392 Y101.057 I3.564 J.365 E.07485 -M204 P4000 -G1 X120.392 Y101.057 F18000 -G1 X120.754 Y101.333 +G3 X119.83 Y96.68 I2.802 J2.203 E.10755 +G1 X130.144 Y96.676 E.392 +G1 X130.964 Y96.853 E.03188 +G3 X133.308 Y99.714 I-1.219 J3.389 E.14762 +G1 X133.333 Y105.403 E.21622 +G3 X133.339 Y109.186 I-12.403 J1.909 E.14433 +G3 X133.155 Y110.944 I-5.777 J.284 E.06744 +G3 X130.089 Y113.328 I-3.396 J-1.203 E.15589 +G1 X119.913 Y113.328 E.38675 +G3 X116.699 Y110.348 I.316 J-3.564 E.17929 +G2 X116.535 Y107.892 I-15.833 J-.172 E.09365 +G3 X116.666 Y104.896 I13.528 J-.906 E.11421 +G1 X116.677 Y99.863 E.19129 +G3 X117.384 Y98.066 I3.546 J.359 E.07432 +M204 P4000 +G1 X117.384 Y98.066 F18000 +G1 X117.748 Y98.342 M204 P500 G1 F2400 -G3 X122.659 Y100.16 I2.479 J1.891 E.08697 -G1 X127.338 Y100.159 E.17783 -M73 Q18 S0 -G3 X129.84 Y102.658 I-.588 J3.091 E.14272 -M73 P18 R0 -G1 X129.871 Y107.064 E.16746 -G3 X127.11 Y109.867 I-3.088 J-.281 E.16199 -G1 X122.889 Y109.867 E.16042 -G3 X120.133 Y107.111 I.328 J-3.084 E.16018 -G1 X120.133 Y102.889 E.16046 -G3 X120.718 Y101.381 I3.1 J.335 E.06219 -M73 P19 R0 -M73 Q19 S0 -M204 P4000 -G1 X120.718 Y101.381 F18000 -G1 X121.086 Y101.651 +G3 X119.894 Y97.133 I2.471 J1.877 E.09626 +G1 X130.119 Y97.133 E.38861 +G3 X132.856 Y99.79 I-.341 J3.09 E.15609 +G1 X132.876 Y105.416 E.21382 +G3 X132.882 Y109.159 I-12.256 J1.891 E.1428 +G3 X132.718 Y110.809 I-5.389 J.296 E.06327 +G3 X130.067 Y112.871 I-2.952 J-1.06 E.1347 +G1 X119.933 Y112.871 E.38516 +G3 X117.155 Y110.308 I.293 J-3.104 E.15433 +G2 X116.989 Y107.842 I-15.855 J-.17 E.09403 +G3 X117.123 Y104.909 I13.535 J-.849 E.11181 +G1 X117.129 Y99.93 E.18923 +G3 X117.712 Y98.39 I3.09 J.289 E.06334 +M204 P4000 +G1 X117.712 Y98.39 F18000 +G1 X118.08 Y98.659 M204 P500 G1 F2400 -G3 X122.745 Y100.609 I2.141 J1.568 E.07626 -G1 X127.254 Y100.609 E.17137 -G1 X127.622 Y100.702 E.01443 -G3 X129.391 Y102.745 I-.878 J2.547 E.10759 -G1 X129.414 Y107.043 E.16335 -G3 X127.046 Y109.414 I-2.627 J-.256 E.13798 -G1 X122.955 Y109.414 E.15548 -G3 X120.586 Y107.046 I.258 J-2.627 E.13791 -M73 P20 R0 -M73 Q20 S0 -G1 X120.586 Y102.955 E.15548 -G3 X121.051 Y101.7 I2.641 J.264 E.05142 -M204 P4000 -G1 X121.051 Y101.7 F18000 -G1 X121.425 Y101.961 +G3 X119.955 Y97.586 I2.136 J1.557 E.08458 +G1 X130.095 Y97.59 E.38538 +G3 X132.404 Y99.863 I-.313 J2.628 E.13252 +G1 X132.419 Y105.428 E.21151 +G3 X132.536 Y108.217 I-13.288 J1.956 E.10628 +G2 X132.386 Y110.287 I12.583 J1.956 E.07897 +G3 X130.045 Y112.414 I-2.609 J-.519 E.12874 +G1 X119.954 Y112.414 E.38352 +G3 X117.611 Y110.267 I.269 J-2.645 E.12946 +M73 P4 R6 +M73 Q4 S6 +G2 X117.443 Y107.791 I-15.866 J-.167 E.09442 +G3 X117.581 Y104.922 I13.578 J-.788 E.10937 +G1 X117.586 Y99.953 E.18885 +G3 X118.046 Y98.708 I2.63 J.263 E.05099 +M204 P4000 +G1 X118.046 Y98.708 F18000 +G1 X118.42 Y98.968 M204 P500 G1 F2400 -G3 X122.828 Y101.059 I1.796 J1.254 E.06503 -G1 X127.172 Y101.059 E.1651 -G3 X128.941 Y102.828 I-.405 J2.174 E.10106 -G1 X128.957 Y107.021 E.15936 -G3 X127.022 Y108.957 I-2.165 J-.229 E.1125 -M73 P21 R0 -M73 Q21 S0 -G1 X122.978 Y108.957 E.1537 -G3 X121.043 Y107.022 I.231 J-2.165 E.11246 -G1 X121.043 Y102.978 E.1537 -G3 X121.391 Y102.01 I2.178 J.237 E.03946 -M204 P4000 -G1 X121.391 Y102.01 F18000 -G1 X121.772 Y102.261 +G3 X119.977 Y98.043 I1.792 J1.243 E.07098 +G1 X130.07 Y98.048 E.3836 +G3 X131.952 Y99.933 I-.285 J2.166 E.10887 +G1 X131.962 Y105.441 E.20934 +G3 X132.081 Y108.179 I-13.223 J1.945 E.10434 +G2 X131.937 Y110.198 I11.688 J1.849 E.07702 +G3 X130.024 Y111.957 I-2.156 J-.424 E.10569 +G1 X119.977 Y111.957 E.38185 +G3 X118.061 Y110.18 I.241 J-2.182 E.10638 +G2 X117.898 Y107.741 I-14.955 J-.226 E.09301 +G3 X118.038 Y104.936 I13.665 J-.725 E.10693 +G1 X118.043 Y99.976 E.18851 +G3 X118.386 Y99.017 I2.169 J.235 E.03907 +M204 P4000 +G1 X118.386 Y99.017 F18000 +G1 X118.768 Y99.267 M204 P500 G1 F2400 -G3 X122.907 Y101.51 I1.444 J.95 E.05316 -G1 X127.093 Y101.51 E.15909 -G1 X127.306 Y101.561 E.00832 -G3 X128.49 Y102.907 I-.543 J1.671 E.0715 -G1 X128.5 Y107 E.15556 -M73 P22 R0 -M73 Q22 S0 -G3 X127 Y108.5 I-1.706 J-.206 E.0869 -G1 X123 Y108.5 E.15203 -G3 X121.5 Y107 I.206 J-1.706 E.0869 -G1 X121.5 Y103 E.15203 -G3 X121.74 Y102.311 I1.716 J.211 E.02794 -M204 P4000 -G1 X121.74 Y102.311 F18000 -G1 X122.129 Y102.549 +G3 X120.001 Y98.5 I1.44 J.941 E.05697 +G1 X130.045 Y98.505 E.38174 +G3 X131.5 Y99.999 I-.257 J1.705 E.08512 +G1 X131.505 Y105.453 E.20729 +G3 X131.625 Y108.141 I-13.174 J1.936 E.10244 +G2 X131.487 Y110.113 I10.9 J1.755 E.07523 +G3 X130 Y111.5 I-1.701 J-.333 E.08259 +G1 X120 Y111.5 E.38006 +G3 X118.511 Y110.099 I.213 J-1.718 E.08312 +G2 X118.352 Y107.691 I-13.253 J-.334 E.09185 +G3 X118.495 Y104.949 I13.805 J-.657 E.10453 +G1 X118.5 Y99.999 E.18813 +G3 X118.736 Y99.318 I1.708 J.209 E.0276 +M204 P4000 +G1 X118.736 Y99.318 F18000 +G1 X119.126 Y99.553 M204 P500 G1 F2400 -G3 X122.981 Y101.962 I1.083 J.66 E.0405 -G1 X127.019 Y101.962 E.15347 -G3 X128.038 Y102.981 I-.238 J1.257 E.05818 -M73 P23 R0 -M73 Q23 S0 -G1 X128.043 Y106.977 E.15187 -G3 X126.977 Y108.043 I-1.248 J-.182 E.06141 -G1 X123.023 Y108.043 E.15028 -G3 X121.957 Y106.977 I.182 J-1.248 E.06141 -G1 X121.957 Y103.023 E.15028 -G3 X122.099 Y102.601 I1.255 J.186 E.01701 -M204 P4000 -G1 X122.099 Y102.601 F18000 -G1 X122.498 Y102.82 +G3 X120.023 Y98.957 I1.081 J.654 E.04229 +G1 X130.019 Y98.962 E.37991 +G3 X131.043 Y100.022 I-.233 J1.249 E.05976 +G1 X131.048 Y105.466 E.20691 +G3 X131.207 Y107.516 I-31.589 J3.478 E.07816 +G1 X131.17 Y108.102 E.02232 +G2 X131.006 Y110.163 I16.658 J2.361 E.07863 +G3 X129.978 Y111.043 I-1.22 J-.385 E.05419 +M73 P5 R6 +G1 X120.023 Y111.043 E.37835 +G3 X118.962 Y110.024 I.188 J-1.257 E.05963 +M73 Q5 S6 +G2 X118.807 Y107.641 I-12.657 J-.369 E.0909 +G3 X118.927 Y105.383 I8.774 J-.663 E.08618 +G1 X118.957 Y100.022 E.20376 +G3 X119.096 Y99.605 I1.25 J.185 E.01679 +M204 P4000 +G1 X119.096 Y99.605 F18000 +G1 X119.496 Y99.823 M204 P500 G1 F2400 -G3 X123.045 Y102.414 I.716 J.394 E.02671 -G1 X126.999 Y102.423 E.15028 -G3 X127.586 Y103.045 I-.22 J.795 E.03417 -M73 P24 R0 -M73 Q24 S0 -G1 X127.586 Y106.955 E.1486 -G3 X126.955 Y107.586 I-.8 J-.169 E.03588 -G1 X123.045 Y107.586 E.1486 -G3 X122.414 Y106.955 I.169 J-.8 E.03588 -G1 X122.414 Y103.045 E.1486 -G3 X122.471 Y102.874 I.8 J.169 E.00686 -M204 P4000 -G1 X122.471 Y102.874 F18000 -G1 X122.889 Y103.07 +G3 X120.045 Y99.414 I.718 J.391 E.02685 +G1 X129.999 Y99.423 E.37832 +G3 X130.586 Y100.045 I-.22 J.795 E.03417 +G1 X130.591 Y105.478 E.20649 +G3 X130.75 Y107.497 I-37.494 J3.97 E.07698 +G1 X130.714 Y108.064 E.02159 +G2 X130.575 Y110.007 I11.081 J1.767 E.07413 +G3 X129.955 Y110.586 I-.793 J-.228 E.03386 +G1 X120.045 Y110.586 E.37664 +G3 X119.415 Y109.958 I.169 J-.8 E.03575 +G2 X119.261 Y107.591 I-12.236 J-.393 E.09029 +G3 X119.383 Y105.426 I8.399 J-.614 E.08264 +G1 X119.415 Y100.045 E.20452 +G3 X119.47 Y99.877 I.799 J.169 E.00673 +M204 P4000 +G1 X119.47 Y99.877 F18000 +G1 X119.888 Y100.072 M204 P500 G1 F2400 -G3 X123.113 Y102.871 I.343 J.162 E.01171 -G1 X126.887 Y102.871 E.14344 -G3 X127.129 Y103.113 I-.119 J.361 E.01349 -G1 X127.129 Y106.887 E.14344 -M73 P25 R0 -G3 X126.887 Y107.129 I-.361 J-.119 E.01349 -M73 Q25 S0 -G1 X123.113 Y107.129 E.14344 -G3 X122.871 Y106.887 I.119 J-.361 E.01349 -G1 X122.871 Y103.127 E.1429 -M204 P4000 -G1 E-.7 F2100 -G1 X122.871 Y103.127 F18000 +G3 X120.113 Y99.871 I.344 J.161 E.01179 +G1 X129.887 Y99.871 E.37147 +G3 X130.128 Y100.113 I-.119 J.362 E.01346 +G1 X130.159 Y105.971 E.22264 +G3 X130.142 Y109.017 I-9.023 J1.471 E.11631 +G1 X130.128 Y109.89 E.03318 +G3 X129.887 Y110.129 I-.36 J-.123 E.01337 +G1 X120.113 Y110.129 E.37147 +G3 X119.872 Y109.888 I.12 J-.362 E.01343 +G2 X119.715 Y107.541 I-12.243 J-.364 E.08954 +G3 X119.838 Y105.469 I8.033 J-.564 E.07911 +G1 X119.871 Y100.129 E.20296 +M204 P4000 +G1 E-.7 F2100 +G1 X119.871 Y100.129 F18000 M486 S0 -G1 X126.093 Y103.907 Z.258 +G1 X124.447 Y103.681 Z.301 G1 Z.2 F720 G1 E.7 F1500 M204 P500 -;TYPE:Perimeter +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F2400 +G1 X124.478 Y103.598 E.0025 +M204 P4000 +G1 X124.478 Y103.598 F18000 +G1 X125.516 Y103.086 +M204 P500 +G1 F2400 +G1 X125.316 Y103.059 E.00571 +G1 X125.124 Y103.179 E.0064 +M204 P4000 +G1 E-.7 F2100 +G1 X125.124 Y103.179 F18000 +G1 X126.482 Y104.348 Z.231 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.423608 +G1 F2400 +G1 X126.457 Y104.151 E.00629 +;WIDTH:0.464296 +G1 X126.431 Y103.953 E.007 +;WIDTH:0.473508 +G1 X126.427 Y103.003 E.03401 +;WIDTH:0.47903 +G1 X126.424 Y102.957 E.00167 +M204 P4000 +G1 X126.427 Y103.356 F18000 +G1 X127.307 Y102.397 +M204 P500 +;WIDTH:0.38292 +G1 F2400 +G1 X127.287 Y101.814 E.01649 +M204 P4000 +G1 X127.287 Y101.814 F18000 +G1 X127.936 Y102.397 +M204 P500 +G1 F2400 +G1 X127.307 Y102.397 E.01778 +G1 X127.287 Y102.497 E.00288 +G1 X127.287 Y104.345 E.05225 +M204 P4000 +G1 X127.338 Y103.949 F18000 +G1 E-.7 F2100 +G1 X128.172 Y107.642 Z.266 F18000 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.43666 +G1 F2400 +G1 X128.323 Y107.58 E.00534 +;WIDTH:0.40979 +G1 X128.474 Y107.518 E.00498 +;WIDTH:0.38292 +G1 X128.984 Y107.56 E.01447 +M73 P6 R6 +M204 P4000 +G1 E-.7 F2100 +G1 X128.984 Y107.56 F18000 +G1 X125.344 Y108.217 Z.265 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +G1 F2400 +M73 Q6 S6 +G1 X125.344 Y106.367 E.0523 +M204 P4000 +G1 X125.344 Y106.767 F18000 +G1 E-.7 F2100 +G1 X123.805 Y102.838 Z.274 F18000 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.447755 +G1 F2400 +G1 X124.04 Y102.807 E.00798 +;WIDTH:0.494158 +G1 X124.299 Y102.773 E.0098 +;WIDTH:0.507532 +G1 X124.521 Y102.665 E.00954 +G1 X124.406 Y103.016 E.01427 +;WIDTH:0.489033 +G1 X124.488 Y103.175 E.00664 +;WIDTH:0.486005 +G1 X124.659 Y103.224 E.00655 +;WIDTH:0.46885 +G2 X124.555 Y103.379 I.444 J.407 E.00664 +;WIDTH:0.441877 +G1 X124.499 Y103.479 E.0038 +G1 X124.469 Y103.42 E.0022 +;WIDTH:0.46885 +G1 X124.438 Y103.361 E.00236 +;WIDTH:0.489033 +G1 X124.116 Y103.24 E.01276 +;WIDTH:0.527703 +G1 X123.864 Y103.221 E.01019 +;WIDTH:0.566372 +G1 X123.612 Y103.202 E.011 +G1 X123.512 Y103.416 E.01028 +G1 X123.39 Y103.487 E.00614 +G1 X123.26 Y103.46 E.00578 +G1 X123.177 Y103.308 E.00754 +G1 X123.164 Y102.875 E.01885 +G1 X123.219 Y102.717 E.00728 +G1 X123.341 Y102.653 E.006 +G1 X123.519 Y102.683 E.00786 +G1 X123.604 Y102.845 E.00796 +G1 X123.648 Y102.844 E.00192 +;WIDTH:0.525117 +G1 X123.692 Y102.843 E.00176 +;WIDTH:0.483862 +G1 X123.736 Y102.842 E.00161 +;WIDTH:0.442607 +G1 X123.745 Y102.842 E.0003 +M204 P4000 +G1 X124.142 Y102.793 F18000 +G1 E-.7 F2100 +G1 X123.342 Y106.457 Z.265 F18000 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.451187 +G1 F2400 +G1 X123.358 Y106.436 E.0009 +;WIDTH:0.46291 +G1 X123.338 Y106.667 E.0081 +;WIDTH:0.505207 +G2 X123.341 Y107.156 I1.663 J.233 E.01886 +;WIDTH:0.460671 +G1 X123.365 Y107.413 E.00897 +;WIDTH:0.448346 +G1 X123.305 Y107.309 E.00405 +;WIDTH:0.480558 +G1 X123.244 Y107.205 E.00439 +;WIDTH:0.505207 +G2 X122.862 Y107.094 I-.558 J1.204 E.01535 +;WIDTH:0.462045 +G1 X122.707 Y107.067 E.00548 +;WIDTH:0.466495 +G1 X122.625 Y107.069 E.00289 +;WIDTH:0.514108 +G1 X122.542 Y107.07 E.00325 +;WIDTH:0.561721 +G1 X122.459 Y107.072 E.00358 +G1 X122.36 Y107.281 E.00998 +G3 X122.102 Y107.333 I-.2 J-.322 E.0116 +G1 X122.03 Y107.216 E.00593 +G1 X122.009 Y106.743 E.02042 +G1 X122.06 Y106.586 E.00712 +G1 X122.184 Y106.521 E.00604 +G1 X122.364 Y106.552 E.00788 +G1 X122.451 Y106.715 E.00797 +G1 X122.541 Y106.712 E.00388 +;WIDTH:0.514108 +G1 X122.631 Y106.708 E.00353 +;WIDTH:0.466495 +G1 X122.721 Y106.705 E.00317 +;WIDTH:0.450322 +G1 X122.972 Y106.661 E.00863 +;WIDTH:0.481761 +G1 X123.223 Y106.617 E.0093 +G1 X123.29 Y106.527 E.00409 +;WIDTH:0.451187 +G1 X123.306 Y106.506 E.0009 +M204 P4000 +G1 X123.33 Y106.905 F18000 +G1 X124.383 Y107.569 +M204 P500 +;WIDTH:0.360125 +G1 F2400 +G1 X124.48 Y107.422 E.00465 +G1 X124.538 Y107.407 E.00158 +;WIDTH:0.363177 +G1 X124.545 Y106.367 E.0277 +;WIDTH:0.408785 +G1 X124.559 Y106.302 E.00202 +;WIDTH:0.454392 +G1 X124.573 Y106.237 E.00227 +;WIDTH:0.499999 +G1 X124.586 Y106.172 E.00252 +G1 X124.76 Y106.139 E.00673 +G1 X125.224 Y106.174 E.01769 +;WIDTH:0.462123 +G1 X125.288 Y106.214 E.00263 +;WIDTH:0.432249 +G1 X125.344 Y106.367 E.00527 +;WIDTH:0.422522 +G1 X125.305 Y106.248 E.00395 +;WIDTH:0.462123 +G1 X125.266 Y106.128 E.0044 +;WIDTH:0.499999 +G1 X125.207 Y105.61 E.01981 +;WIDTH:0.538502 +G1 X125.239 Y105.548 E.00288 +;WIDTH:0.577005 +G1 X125.271 Y105.486 E.0031 +;WIDTH:0.615508 +G1 X125.303 Y105.425 E.00328 +;WIDTH:0.654011 +G1 X125.335 Y105.363 E.00355 +G1 X125.874 Y105.316 E.02749 +;WIDTH:0.650923 +G1 X126.042 Y105.3 E.00853 +G1 X126.091 Y105.338 E.00313 +;WIDTH:0.613192 +G1 X126.14 Y105.376 E.00294 +;WIDTH:0.575461 +G1 X126.189 Y105.414 E.00275 +;WIDTH:0.53773 +G1 X126.238 Y105.452 E.00255 +;WIDTH:0.499999 +G1 X126.292 Y105.637 E.00732 +G1 X126.227 Y106.07 E.01664 +G1 X126.547 Y106.036 E.01223 +G1 X126.996 Y106.255 E.01899 +G1 X127.249 Y106.474 E.01272 +G1 X127.281 Y106.534 E.00258 +G1 X127.499 Y106.301 E.01213 +G1 X127.996 Y106 E.02208 +G1 X128.145 Y105.97 E.00578 +G1 X128.684 Y106.056 E.02074 +G1 X129.021 Y106.351 E.01702 +;WIDTH:0.505396 +G1 X129.221 Y106.85 E.02067 +G1 X129.249 Y107.361 E.01968 +;WIDTH:0.519318 +G1 X129.217 Y107.52 E.00642 +G1 X129.155 Y107.546 E.00266 +;WIDTH:0.489229 +G1 X129.094 Y107.573 E.00248 +;WIDTH:0.487675 +G1 X129.159 Y107.632 E.00325 +;WIDTH:0.516211 +G1 X129.223 Y107.692 E.00345 +;WIDTH:0.520295 +G1 X129.19 Y107.989 E.01186 +G1 X128.877 Y108.417 E.02105 +;WIDTH:0.499999 +G1 X128.652 Y108.535 E.00966 +G1 X128.099 Y108.594 E.02114 +G1 X127.867 Y108.488 E.00969 +G1 X127.368 Y108.153 E.02284 +G1 X127.263 Y108.051 E.00556 +G1 X127.074 Y108.266 E.01088 +G1 X126.599 Y108.521 E.02049 +G1 X126.493 Y108.547 E.00415 +G1 X126.087 Y108.379 E.0167 +G1 X125.944 Y108.435 E.00584 +G1 X125.43 Y108.432 E.01954 +G1 X125.401 Y108.361 E.00291 +;WIDTH:0.460973 +G1 X125.373 Y108.289 E.00269 +;WIDTH:0.421947 +G1 X125.344 Y108.217 E.00245 +G1 X125.316 Y108.289 E.00243 +;WIDTH:0.460973 +G1 X125.287 Y108.361 E.0027 +;WIDTH:0.499999 +G1 X125.258 Y108.432 E.00291 +G1 X124.739 Y108.435 E.01973 +G1 X124.563 Y108.381 E.007 +G1 X124.404 Y108.493 E.00739 +G1 X124.215 Y108.52 E.00726 +G3 X123.942 Y108.5 I-.109 J-.391 E.01061 +G1 X123.511 Y108.193 E.02011 +G1 X123.384 Y107.96 E.01009 +G1 X123.34 Y107.68 E.01077 +;WIDTH:0.467562 +G1 X123.356 Y107.494 E.00659 +;WIDTH:0.464521 +G1 X123.25 Y107.751 E.00975 +;WIDTH:0.499999 +G1 X123.143 Y108.008 E.01058 +G1 X122.713 Y108.436 E.02306 +G1 X122.462 Y108.571 E.01083 +G3 X121.794 Y108.627 I-.472 J-1.605 E.02565 +G1 X121.385 Y108.48 E.01652 +;WIDTH:0.502168 +G1 X120.975 Y108.103 E.02127 +;WIDTH:0.52159 +G1 X120.825 Y107.803 E.01335 +G1 X120.757 Y107.454 E.01415 +;WIDTH:0.50575 +G1 X120.731 Y106.845 E.02346 +;WIDTH:0.50628 +G1 X120.787 Y106.273 E.02214 +G1 X120.97 Y105.806 E.01932 ;WIDTH:0.499999 +G1 X121.334 Y105.447 E.01943 +G1 X121.794 Y105.256 E.01893 +;WIDTH:0.540664 +G1 X122.331 Y105.219 E.02228 +;WIDTH:0.58055 +G1 X122.505 Y105.233 E.0078 +G1 X122.763 Y105.41 E.01399 +;WIDTH:0.540275 +G1 X123.021 Y105.586 E.01292 +;WIDTH:0.499999 +G1 X123.051 Y105.763 E.00682 +G1 X123.242 Y105.985 E.01113 +G1 X123.298 Y106.156 E.00684 +;WIDTH:0.464647 +G1 X123.353 Y106.327 E.0063 +G1 X123.45 Y106.233 E.00474 +;WIDTH:0.499999 +G1 X123.547 Y106.139 E.00513 +G1 X124.065 Y106.139 E.01969 +G1 X124.184 Y106.172 E.00469 +G1 X124.198 Y106.238 E.00256 +;WIDTH:0.454392 +G1 X124.211 Y106.303 E.00227 +;WIDTH:0.408785 +G1 X124.225 Y106.368 E.00202 +;WIDTH:0.363177 +G1 X124.228 Y107.258 E.0237 +;WIDTH:0.361437 +G1 X124.237 Y107.564 E.00811 +;WIDTH:0.360125 +G1 X124.323 Y107.567 E.00227 +M204 P4000 +G1 X124.545 Y107.234 F18000 +G1 E-.7 F2100 +G1 X125.313 Y102.221 Z.289 F18000 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.499999 +G1 F2400 +G3 X125.49 Y102.195 I.425 J2.303 E.0068 +G1 X126.07 Y102.361 E.02293 +G1 X126.3 Y102.562 E.01161 +G1 X126.369 Y102.684 E.00533 +;WIDTH:0.493713 +G1 X126.424 Y102.957 E.01044 +;WIDTH:0.499999 +G1 X126.399 Y101.805 E.04379 +G1 X126.42 Y101.702 E.004 +G1 X126.634 Y101.57 E.00956 +G1 X127.167 Y101.586 E.02027 +G1 X127.231 Y101.598 E.00247 +;WIDTH:0.498678 +G1 X127.25 Y101.67 E.00282 +;WIDTH:0.460092 +G1 X127.268 Y101.742 E.00257 +;WIDTH:0.421506 +G1 X127.287 Y101.814 E.00234 +G1 X127.307 Y101.743 E.00232 +;WIDTH:0.460092 +G1 X127.326 Y101.671 E.00258 +;WIDTH:0.498678 +G1 X127.346 Y101.599 E.00283 +;WIDTH:0.499999 +G1 X127.975 Y101.515 E.02412 +G1 X128.193 Y101.613 E.00908 +G1 X128.23 Y101.772 E.0062 +G1 X128.155 Y102.327 E.02129 +G1 X128.082 Y102.35 E.00291 +;WIDTH:0.460973 +G1 X128.009 Y102.374 E.00267 +;WIDTH:0.421947 +G1 X127.936 Y102.397 E.00241 +;WIDTH:0.428617 +G1 X128.027 Y102.418 E.003 +;WIDTH:0.474313 +G1 X128.118 Y102.44 E.00336 +;WIDTH:0.499999 +G1 X128.284 Y102.898 E.01851 +G1 X128.349 Y103.415 E.0198 +G1 X128.29 Y104.496 E.04115 +G1 X128.212 Y104.648 E.00649 +G1 X128.05 Y104.703 E.0065 +G1 X127.364 Y104.555 E.02667 +G1 X127.339 Y104.485 E.00283 +;WIDTH:0.460973 +G1 X127.313 Y104.415 E.0026 +;WIDTH:0.421947 +G1 X127.287 Y104.345 E.00235 +G1 X127.264 Y104.416 E.00235 +;WIDTH:0.460973 +G1 X127.24 Y104.487 E.00261 +;WIDTH:0.499999 +G1 X127.217 Y104.558 E.00284 +G1 X126.642 Y104.565 E.02186 +G1 X126.548 Y104.532 E.00379 +;WIDTH:0.480044 +G1 X126.515 Y104.44 E.00355 +;WIDTH:0.431482 +G1 X126.482 Y104.348 E.00316 +;WIDTH:0.421947 +G1 X126.445 Y104.417 E.00247 +;WIDTH:0.460973 +G1 X126.408 Y104.486 E.00272 +;WIDTH:0.499999 +G1 X126.371 Y104.555 E.00298 +;WIDTH:0.548095 +G1 X126.182 Y104.61 E.00827 +;WIDTH:0.59619 +G1 X125.992 Y104.665 E.0091 +;WIDTH:0.644285 +G1 X125.803 Y104.72 E.00984 +;WIDTH:0.649416 +G1 X125.181 Y104.77 E.03147 +;WIDTH:0.648928 +G1 X125.055 Y104.69 E.00752 +;WIDTH:0.599285 +G1 X124.929 Y104.61 E.0069 +;WIDTH:0.549642 +G1 X124.804 Y104.53 E.00625 +;WIDTH:0.499999 +G1 X124.56 Y104.341 E.01173 +G1 X124.396 Y104.057 E.01246 +;WIDTH:0.496761 +G1 X124.386 Y103.98 E.00293 +;WIDTH:0.499999 +G1 X124.122 Y104.327 E.01657 +G1 X123.68 Y104.654 E.0209 +G1 X123.496 Y104.72 E.00743 +;WIDTH:0.526801 +G1 X122.955 Y104.771 E.02186 +;WIDTH:0.556261 +G1 X122.569 Y104.674 E.01699 +G1 X122.369 Y104.515 E.01091 +;WIDTH:0.52813 +G1 X122.169 Y104.357 E.01028 +;WIDTH:0.499999 +G1 X121.808 Y103.775 E.02603 +G1 X121.699 Y103.13 E.02486 +G1 X121.777 Y102.486 E.02465 +G1 X122.102 Y101.873 E.02637 +G1 X122.423 Y101.601 E.01599 +G1 X122.865 Y101.399 E.01847 +G1 X123.348 Y101.356 E.01843 +G1 X123.699 Y101.429 E.01363 +G1 X124.106 Y101.669 E.01796 +G1 X124.414 Y102.137 E.02129 +;WIDTH:0.509868 +G1 X124.547 Y102.449 E.01317 +;WIDTH:0.518886 +G1 X124.558 Y102.599 E.00595 +M73 P7 R6 +G1 X124.804 Y102.377 E.01311 +;WIDTH:0.499999 +G3 X125.254 Y102.233 I.934 J2.147 E.01799 +M204 P4000 +G1 X125.654 Y102.254 F18000 +G1 E-.7 F2100 +G1 X121.753 Y104.701 Z.28 F18000 +G1 Z.2 F720 +M73 Q7 S6 +G1 E.7 F1500 +M204 P500 +;TYPE:Perimeter +;WIDTH:0.653912 +G1 F2400 +G2 X120.979 Y105.045 I1.28 J3.922 E.04311 +G2 X120.958 Y104.356 I-4.499 J-.207 E.03505 +;WIDTH:0.611633 +G1 X120.937 Y104.167 E.00899 +;WIDTH:0.569353 +G1 X120.916 Y103.977 E.00837 +;WIDTH:0.527073 +G1 X120.895 Y103.788 E.00766 +;WIDTH:0.484793 +G1 X120.875 Y102.984 E.02955 +;WIDTH:0.491105 +G1 X120.9 Y102.517 E.01743 +;WIDTH:0.538909 +G1 X120.924 Y102.05 E.01929 +;WIDTH:0.578634 +G1 X120.944 Y101.898 E.00683 +;WIDTH:0.618359 +G1 X120.964 Y101.745 E.00738 +;WIDTH:0.658084 +G1 X120.984 Y101.593 E.00784 +;WIDTH:0.697809 +G1 X121.005 Y101.441 E.00836 +;WIDTH:0.721386 +G1 X121.017 Y101.018 E.02387 +G1 X122.327 Y101.018 E.0739 +G1 X122.179 Y101.088 E.00924 +G1 X121.801 Y101.366 E.02647 +G1 X121.618 Y101.601 E.0168 +;WIDTH:0.697809 +G1 X121.56 Y101.744 E.0084 +;WIDTH:0.658084 +G1 X121.503 Y101.886 E.00783 +;WIDTH:0.618359 +G1 X121.446 Y102.029 E.00737 +;WIDTH:0.578634 +G1 X121.389 Y102.171 E.00682 +;WIDTH:0.538909 +G1 X121.361 Y102.299 E.0054 +;WIDTH:0.510529 +G1 X121.333 Y102.426 E.00506 +;WIDTH:0.482148 +G1 X121.275 Y103.191 E.02802 +;WIDTH:0.48696 +G1 X121.337 Y103.777 E.02176 +;WIDTH:0.528698 +G1 X121.39 Y103.931 E.00658 +;WIDTH:0.570436 +G1 X121.443 Y104.085 E.00714 +;WIDTH:0.612174 +G1 X121.496 Y104.239 E.00771 +;WIDTH:0.653912 +G1 X121.549 Y104.393 E.00827 +G1 X121.72 Y104.651 E.01573 +M204 P4000 +G1 E-.7 F2100 +G1 X121.72 Y104.651 F18000 +G1 X122.782 Y100.928 Z.268 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.542338 +G1 F2400 +G1 X123.045 Y100.915 E.01093 +;WIDTH:0.515085 +G1 X123.307 Y100.901 E.0103 +;WIDTH:0.498884 +G1 X123.429 Y100.907 E.00463 +M204 P4000 +G1 E-.7 F2100 +G1 X123.429 Y100.907 F18000 +G1 X126.96 Y105.111 Z.296 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.674754 +G1 F2400 +G1 X127.331 Y105.112 E.01949 +;WIDTH:0.633925 +G1 X127.502 Y105.115 E.0084 +;WIDTH:0.593096 +G1 X127.673 Y105.118 E.00782 +;WIDTH:0.552266 +G1 X127.933 Y105.122 E.01101 +;WIDTH:0.505874 +G1 X128.194 Y105.127 E.01005 +;WIDTH:0.499121 +G1 X128.285 Y105.103 E.00357 +;WIDTH:0.53876 +G1 X128.377 Y105.078 E.00393 +;WIDTH:0.578399 +G1 X128.468 Y105.054 E.00419 +;WIDTH:0.618038 +G1 X128.56 Y105.03 E.00455 +G1 X128.6 Y104.906 E.00623 +;WIDTH:0.575453 +G1 X128.639 Y104.783 E.00571 +;WIDTH:0.532868 +G1 X128.679 Y104.66 E.00527 +;WIDTH:0.490283 +G1 X128.719 Y104.537 E.00481 +;WIDTH:0.447698 +G1 X128.762 Y103.927 E.02058 +;WIDTH:0.418698 +G1 X128.764 Y103.415 E.016 +;WIDTH:0.447147 +G1 X128.719 Y102.883 E.01795 +;WIDTH:0.479719 +G1 X128.67 Y102.61 E.01007 +;WIDTH:0.51229 +G1 X128.622 Y102.336 E.01086 +G1 X128.672 Y101.832 E.01977 +;WIDTH:0.518145 +G1 X128.633 Y101.586 E.00984 +;WIDTH:0.558255 +G1 X128.594 Y101.339 E.01072 +G1 X128.476 Y101.298 E.00535 +;WIDTH:0.515813 +G1 X128.358 Y101.257 E.00491 +;WIDTH:0.473371 +G1 X128.24 Y101.216 E.00447 +;WIDTH:0.430929 +G1 X128.122 Y101.174 E.00404 +;WIDTH:0.388487 +G1 X128.004 Y101.133 E.00359 +;WIDTH:0.349043 +G1 X127.927 Y101.137 E.00196 +;WIDTH:0.382482 +G3 X126.519 Y101.182 I-1.068 J-11.158 E.0398 +;WIDTH:0.425039 +G1 X126.433 Y101.223 E.00303 +;WIDTH:0.469554 +G1 X126.348 Y101.263 E.00333 +;WIDTH:0.514069 +G1 X126.262 Y101.304 E.00373 +;WIDTH:0.558584 +G1 X126.177 Y101.344 E.00403 +;WIDTH:0.603099 +G1 X126.091 Y101.385 E.00444 +;WIDTH:0.647614 +G1 X126.006 Y101.425 E.00472 +;WIDTH:0.692129 +G1 X125.92 Y101.466 E.00514 +G1 X125.864 Y101.716 E.01383 +G1 X125.619 Y101.657 E.0136 +;WIDTH:0.69225 +G1 X125.301 Y101.648 E.01718 +;WIDTH:0.721332 +G1 X124.838 Y101.748 E.02672 +G1 X124.58 Y101.357 E.02643 +G1 X124.275 Y101.085 E.02305 +G1 X124.143 Y101.018 E.00835 +G2 X125.626 Y101.003 I.348 J-39.966 E.08367 +;WIDTH:0.692129 +G1 X125.773 Y100.98 E.00803 +;WIDTH:0.646784 +G1 X125.919 Y100.958 E.00741 +;WIDTH:0.601439 +G1 X126.065 Y100.935 E.00686 +;WIDTH:0.556094 +G1 X126.211 Y100.912 E.00631 +;WIDTH:0.510749 +G1 X126.358 Y100.89 E.00578 +;WIDTH:0.465404 +G1 X126.504 Y100.867 E.00519 +;WIDTH:0.420059 +G1 X126.65 Y100.844 E.00463 +;WIDTH:0.386419 +G2 X128.004 Y100.83 I.448 J-21.602 E.03868 +;WIDTH:0.388487 +G1 X128.164 Y100.851 E.00464 +;WIDTH:0.430929 +G1 X128.325 Y100.873 E.00524 +;WIDTH:0.473371 +G1 X128.486 Y100.894 E.00581 +;WIDTH:0.515813 +G1 X128.646 Y100.915 E.00635 +;WIDTH:0.558255 +G2 X129.064 Y100.936 I.311 J-1.985 E.01797 +G2 X129.085 Y101.527 I6.052 J.085 E.02535 +;WIDTH:0.518145 +G1 X129.106 Y101.861 E.01322 +;WIDTH:0.478034 +G1 X129.122 Y102.859 E.03611 +;WIDTH:0.447147 +G1 X129.138 Y103.415 E.0187 +;WIDTH:0.418698 +G1 X129.137 Y103.945 E.01656 +;WIDTH:0.452272 +G1 X129.121 Y104.623 E.02308 +;WIDTH:0.493714 +G1 X129.101 Y104.778 E.00586 +;WIDTH:0.535155 +G1 X129.08 Y104.933 E.0064 +;WIDTH:0.576597 +G1 X129.059 Y105.089 E.00699 +;WIDTH:0.618038 +G2 X129.048 Y105.684 I2.867 J.353 E.02851 +G2 X128.581 Y105.53 I-.428 J.512 E.02408 +;WIDTH:0.578399 +G1 X128.456 Y105.533 E.00557 +;WIDTH:0.53876 +G1 X128.331 Y105.535 E.00515 +;WIDTH:0.499121 +G1 X128.206 Y105.538 E.00474 +;WIDTH:0.505874 +G1 X127.977 Y105.561 E.00886 +;WIDTH:0.552266 +G1 X127.749 Y105.585 E.00971 +;WIDTH:0.593096 +G1 X127.594 Y105.654 E.00776 +;WIDTH:0.633925 +G1 X127.439 Y105.723 E.00834 +;WIDTH:0.674754 +G1 X127.284 Y105.791 E.00889 +G1 X126.833 Y105.565 E.0265 +G1 X126.821 Y105.437 E.00675 +G1 X126.656 Y105.109 E.01929 +G1 X126.9 Y105.11 E.01282 +M204 P4000 +G1 E-.7 F2100 +G1 X126.9 Y105.11 F18000 +G1 X129.006 Y108.992 Z.277 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.701232 +G1 F2400 +G1 X128.821 Y109.013 E.01019 +;WIDTH:0.660837 +G1 X128.636 Y109.033 E.00956 +;WIDTH:0.620441 +G1 X128.451 Y109.053 E.00894 +;WIDTH:0.580046 +G1 X128.266 Y109.073 E.00831 +;WIDTH:0.56184 +G1 X127.96 Y109.062 E.01321 +M204 P4000 +G1 X127.96 Y109.062 F18000 +G1 X127.288 Y108.647 +M204 P500 +;WIDTH:0.463067 +G1 F2400 +G1 X127.394 Y108.728 E.00466 +;WIDTH:0.512454 +G1 X127.677 Y108.895 E.01283 +;WIDTH:0.56184 +G1 X127.96 Y109.062 E.01418 +G1 X127.839 Y109.077 E.00526 +;WIDTH:0.531795 +G1 X127.557 Y109.094 E.01148 +;WIDTH:0.497431 +G1 X127.275 Y109.111 E.01068 +;WIDTH:0.508694 +G1 X127.057 Y109.089 E.00849 +;WIDTH:0.55432 +G1 X126.839 Y109.066 E.00932 +;WIDTH:0.599946 +G1 X126.621 Y109.043 E.01015 +G1 X126.717 Y108.99 E.00508 +;WIDTH:0.560297 +G1 X126.814 Y108.936 E.00478 +;WIDTH:0.520647 +G1 X127.051 Y108.792 E.01102 +;WIDTH:0.491857 +G1 X127.237 Y108.678 E.00814 +M204 P4000 +G1 E-.7 F2100 +G1 X127.237 Y108.678 F18000 +G1 X121.653 Y109.078 Z.298 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.53026 +G1 F2400 +G1 X121.539 Y109.096 E.00468 +;WIDTH:0.49445 +G1 X121.425 Y109.114 E.00433 +;WIDTH:0.45864 +G1 X121.311 Y109.132 E.00399 +;WIDTH:0.42283 +G1 X120.856 Y109.14 E.01438 +;WIDTH:0.40606 +G1 X120.834 Y108.547 E.01792 +G1 X121.146 Y108.814 E.0124 +;WIDTH:0.44746 +G1 X121.315 Y108.902 E.00641 +;WIDTH:0.48886 +G1 X121.484 Y108.99 E.00707 +;WIDTH:0.53026 +G1 X121.6 Y109.05 E.00529 +M204 P4000 +G1 E-.7 F2100 +G1 X121.6 Y109.05 F18000 +G1 X124.069 Y109.044 Z.243 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.597824 +G1 F2400 +G1 X123.847 Y109.069 E.01031 +;WIDTH:0.548582 +G1 X123.626 Y109.093 E.00935 +;WIDTH:0.499339 +G1 X122.765 Y109.093 E.03268 +;WIDTH:0.505098 +G1 X122.239 Y109.09 E.02021 +G1 X122.561 Y109.017 E.01269 +;WIDTH:0.500313 +G1 X122.87 Y108.881 E.01284 +;WIDTH:0.499339 +G1 X123.241 Y108.555 E.01874 +G1 X123.69 Y108.881 E.02106 +;WIDTH:0.548582 +G1 X123.879 Y108.962 E.00865 +;WIDTH:0.597824 +G1 X124.014 Y109.02 E.00678 +M204 P4000 +G1 E-.7 F2100 +G1 X124.014 Y109.02 F18000 +G1 X122.239 Y109.09 Z.231 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.53026 +G1 F2400 +G1 X121.653 Y109.078 E.02375 +M204 P4000 +G1 E-.7 F2100 +G1 X121.653 Y109.078 F18000 +G1 X125.278 Y108.828 Z.263 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.371873 +G1 F2400 +G1 X125.465 Y108.828 E.00511 +;WIDTH:0.376214 +G1 X126.069 Y108.807 E.01675 +;WIDTH:0.418748 +G1 X126.154 Y108.856 E.00307 +;WIDTH:0.461282 +G1 X126.239 Y108.904 E.0034 +;WIDTH:0.503815 +G1 X126.324 Y108.952 E.00374 +;WIDTH:0.546349 +G1 X126.409 Y109 E.00409 +;WIDTH:0.588882 +G1 X126.493 Y109.049 E.00441 +G1 X126.411 Y109.07 E.00384 +;WIDTH:0.544933 +G1 X126.329 Y109.092 E.00354 +;WIDTH:0.500984 +G1 X126.247 Y109.114 E.00323 +;WIDTH:0.457035 +G1 X126.165 Y109.136 E.00292 +;WIDTH:0.413086 +G1 X125.944 Y109.157 E.00683 +;WIDTH:0.3765 +G1 X124.739 Y109.157 E.03342 +;WIDTH:0.42175 +G1 X124.649 Y109.132 E.00294 +;WIDTH:0.471629 +G1 X124.558 Y109.107 E.00336 +;WIDTH:0.521508 +G1 X124.468 Y109.082 E.00372 +;WIDTH:0.571387 +G1 X124.378 Y109.057 E.0041 +;WIDTH:0.621266 +G1 X124.287 Y109.032 E.00454 +G1 X124.352 Y108.994 E.00362 +;WIDTH:0.573254 +G1 X124.417 Y108.957 E.0033 +;WIDTH:0.525241 +G1 X124.482 Y108.919 E.00302 +;WIDTH:0.477228 +G1 X124.547 Y108.881 E.00272 +;WIDTH:0.429215 +G1 X124.611 Y108.843 E.00239 +;WIDTH:0.381202 +G1 X124.739 Y108.828 E.00363 +;WIDTH:0.371873 +G1 X125.218 Y108.828 E.0131 +M204 P4000 +G1 X125.218 Y108.828 F18000 +G1 X124.287 Y109.032 +M204 P500 +;WIDTH:0.621266 G1 F2400 -G1 X126.093 Y105 E.04154 -G1 X126.093 Y106.093 E.04154 -G1 X123.907 Y106.093 E.08308 -M73 P26 R0 -G1 X123.907 Y103.907 E.08308 -M73 Q26 S0 -G1 X126.033 Y103.907 E.0808 -M204 P4000 -G1 X126.55 Y103.45 F18000 +G1 X124.069 Y109.044 E.0105 +M204 P4000 +G1 E-.7 F2100 +G1 X124.069 Y109.044 F18000 +G1 X124.653 Y105.588 Z.261 +G1 Z.2 F720 +G1 E.7 F1500 +M204 P500 +;WIDTH:0.696293 +G1 F2400 +G1 X124.385 Y105.659 E.01506 +G1 X124.236 Y105.602 E.00867 +G1 X123.637 Y105.583 E.03256 +G1 X123.462 Y105.281 E.01896 +G1 X123.86 Y105.182 E.02228 +M73 P8 R6 +G1 X124.318 Y104.87 E.03011 +G2 X124.808 Y105.183 I1.263 J-1.437 E.03171 +G1 X124.755 Y105.258 E.00499 +G1 X124.671 Y105.531 E.01552 +M73 Q8 S6 +M204 P4000 +G1 E-.7 F2100 +G1 X124.671 Y105.531 F18000 +G1 X129.553 Y109.55 Z.31 +G1 Z.2 F720 +G1 E.7 F1500 M204 P500 ;TYPE:External perimeter +;WIDTH:0.499999 G1 F2400 -G1 X126.55 Y105 E.05891 -G1 X126.55 Y106.55 E.05891 -G1 X123.45 Y106.55 E.11782 -G1 X123.45 Y103.45 E.11782 -G1 X126.49 Y103.45 E.11554 -M73 P27 R0 -M73 Q27 S0 +G1 X120.448 Y109.55 E.34605 +G2 X120.421 Y108.487 I-15.344 J-.133 E.04042 +;WIDTH:0.519268 +G1 X120.294 Y107.468 E.04067 +;WIDTH:0.50575 +G1 X120.283 Y106.784 E.02633 +;WIDTH:0.513833 +G1 X120.415 Y105.515 E.04996 +;WIDTH:0.499999 +G1 X120.444 Y105.001 E.01957 +G1 X120.45 Y100.45 E.17297 +G1 X129.55 Y100.45 E.34586 +G2 X129.566 Y105.705 I469.139 J1.236 E.19972 +G1 X129.62 Y106.326 E.02369 +;WIDTH:0.504913 +G1 X129.702 Y107.03 E.02723 +G1 X129.694 Y107.449 E.0161 +;WIDTH:0.513692 +G3 X129.606 Y108.479 I-6.39 J-.031 E.04051 +;WIDTH:0.499999 +G2 X129.555 Y109.492 I9.977 J1.013 E.03857 M204 P4000 -G1 X126.549 Y103.846 F18000 +G1 X129.159 Y109.549 F18000 G1 E-.7 F2100 -G1 X124.836 Y104.09 Z.23 F18000 +G1 X126.255 Y106.99 Z.268 F18000 G1 Z.2 F720 G1 E.7 F1500 M204 P500 -;TYPE:Solid infill -;WIDTH:0.570672 +;WIDTH:0.399689 G1 F2400 -G1 X125.704 Y104.958 E.05387 -G1 X125.704 Y105.704 E.03274 -G1 X124.296 Y104.296 E.08738 -G1 X124.296 Y105.042 E.03274 -G1 X125.164 Y105.91 E.05387 +G1 X126.32 Y106.927 E.00269 +G1 X126.459 Y106.957 E.00422 +G1 X126.534 Y107.139 E.00584 +;WIDTH:0.401208 +G1 X126.533 Y107.47 E.00986 +G1 X126.443 Y107.621 E.00524 +G1 X126.292 Y107.612 E.00451 +G1 X126.174 Y107.416 E.00682 +G1 X126.195 Y107.048 E.01098 +;WIDTH:0.399689 +G1 X126.212 Y107.032 E.00069 M204 P4000 +G1 X126.6 Y107.131 F18000 M106 S127.5 ;LAYER_CHANGE ;Z:0.4 @@ -1564,1947 +2889,7714 @@ M106 S127.5 ;BEFORE_LAYER_CHANGE G92 E0.0 ;0.4 -M201 X3999.96 Y3999.96 +M201 X3999.92 Y3999.92 G1 E-.7 F2100 -G1 X125.164 Y105.91 Z.2 F18000 -M73 Q28 S0 -G1 X126.368 Y103.632 Z.4 F9625.247 +G1 X126.6 Y107.131 Z.2 F18000 +G1 X129.368 Y109.368 Z.4 F13019.812 ;AFTER_LAYER_CHANGE ;0.4 -M74 W0.0364665 +M74 W0.0791103 M104 S225 ; set temperature -G1 X126.368 Y103.632 F18000 -M73 P28 R0 +G1 X129.368 Y109.368 F18000 G1 Z.4 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y106.368 E.09261 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y103.632 E.09261 -G1 X126.308 Y103.632 E.09058 -G1 X126.775 Y103.225 F18000 -M73 P29 R0 +G1 F2088 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X126.775 Y106.775 E.12016 -G1 X123.225 Y106.775 E.12016 -M73 Q29 S0 -G1 X123.225 Y103.225 E.12016 -G1 X126.715 Y103.225 E.11813 -G1 X126.774 Y103.621 F18000 -M73 P30 R0 -M73 Q30 S0 -G1 E-.7 F2100 -G1 X123.978 Y103.978 Z.449 F18000 +G1 F2088 +G1 X129.1 Y109.775 E.02285 +G1 X120.9 Y109.775 E.27756 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y109.1 E.02285 +G1 X120.225 Y100.9 E.27756 +G1 X120.225 Y100.225 E.02285 +G1 X120.9 Y100.225 E.02285 +G1 X129.1 Y100.225 E.27756 +G1 X129.775 Y100.225 E.02285 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y109.1 E.27756 +G1 X129.775 Y109.715 E.02082 +G1 X129.379 Y109.774 F18000 +M73 P9 R6 +G1 E-.7 F2100 +G1 X123.022 Y100.795 Z.592 F18000 +G1 Z.4 F720 +M73 Q9 S6 +G1 E.7 F1500 +M204 P1500 +;TYPE:Bridge infill +;WIDTH:0.450572 +G1 F3000 +G1 X121.149 Y101.86 E.07303 +G2 X120.978 Y102.304 I1.956 J1.008 E.01616 +G1 X120.978 Y102.426 E.00414 +G1 X123.524 Y100.978 E.09928 +G1 X124.349 Y100.978 E.02796 +G1 X120.978 Y102.895 E.13145 +G1 X120.978 Y103.364 E.0159 +G1 X124.905 Y101.131 E.15313 +G1 X125.021 Y101.237 E.00533 +G1 X125.569 Y101.209 E.0186 +G1 X125.732 Y100.978 E.00958 +G1 X125.998 Y100.978 E.00902 +G1 X120.978 Y103.833 E.19575 +G2 X121.11 Y104.227 I1.433 J-.261 E.01413 +G1 X126.823 Y100.978 E.22278 +G1 X127.648 Y100.978 E.02796 +G1 X120.978 Y104.771 E.26009 +G1 X120.978 Y105.24 E.0159 +G1 X128.472 Y100.978 E.29223 +G1 X128.841 Y100.978 E.01251 +G1 X128.973 Y101.163 E.0077 +G1 X120.978 Y105.709 E.31175 +G1 X120.978 Y106.178 E.0159 +G1 X129.022 Y101.604 E.31366 +G1 X129.022 Y102.072 E.01586 +G1 X120.978 Y106.647 E.31368 +G1 X120.978 Y107.115 E.01586 +G1 X129.022 Y102.541 E.31366 +G1 X129.022 Y103.01 E.0159 +G1 X120.978 Y107.584 E.31366 +G1 X120.978 Y108.053 E.0159 +G1 X129.022 Y103.479 E.31366 +G1 X129.022 Y103.948 E.0159 +G1 X120.978 Y108.522 E.31366 +G1 X120.978 Y108.991 E.0159 +G1 X129.022 Y104.417 E.31366 +G1 X129.022 Y104.886 E.0159 +G1 X121.749 Y109.022 E.2836 +G1 X122.574 Y109.022 E.02796 +G1 X129.022 Y105.355 E.25144 +G1 X129.022 Y105.824 E.0159 +G1 X123.398 Y109.022 E.2193 +G1 X124.223 Y109.022 E.02796 +G1 X129.022 Y106.293 E.18713 +G1 X129.022 Y106.762 E.0159 +M73 P10 R6 +G1 X125.048 Y109.022 E.15496 +G1 X125.872 Y109.022 E.02793 +M73 Q10 S6 +G1 X129.022 Y107.231 E.12283 +G1 X129.022 Y107.7 E.0159 +G1 X126.697 Y109.022 E.09066 +G1 X127.522 Y109.022 E.02796 +G1 X129.022 Y108.169 E.05849 +G1 X129.022 Y108.638 E.0159 +G1 X128.024 Y109.205 E.03891 +M204 P4000 +G1 E-.7 F2100 +G1 X128.024 Y109.205 F18000 +G1 X121.14 Y101.05 Z.586 G1 Z.4 F720 G1 E.7 F1500 ;TYPE:Solid infill -G1 F1200 -G1 X126.022 Y103.978 E.06919 -G1 X126.022 Y106.022 E.06919 -G1 X123.978 Y106.022 E.06919 -G1 X123.978 Y104.182 E.06228 -G1 X124.385 Y104.385 E.01539 -G1 X125.615 Y104.385 E.04163 -G1 X125.615 Y105.615 E.04163 -M73 P31 R0 -M73 Q31 S0 -G1 X124.385 Y105.615 E.04163 -G1 X124.385 Y104.589 E.03473 -G1 X124.794 Y104.794 E.01549 -;WIDTH:0.454205 -G1 X125.206 Y104.794 E.01409 -G1 X125.206 Y105.206 E.01409 -G1 X124.794 Y105.206 E.01409 -G1 X124.794 Y104.998 E.00711 -M106 S255 +;WIDTH:0.631622 +G1 F2088 +G2 X121.05 Y101.14 I-.068 J.022 E.0143 +G1 X121.121 Y101.121 E.0036 +M106 S252.45 ;LAYER_CHANGE ;Z:0.6 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;0.6 -M201 X3999.96 Y3999.96 +M201 X3999.89 Y3999.89 G1 E-.7 F2100 -G1 X124.794 Y104.998 Z.4 F18000 -G1 X126.368 Y103.632 Z.6 F7921.103 +G1 X121.121 Y101.121 Z.4 F18000 +G1 X129.368 Y109.368 Z.604 ;AFTER_LAYER_CHANGE ;0.6 -M74 W0.0405131 +M74 W0.103924 -G1 X126.368 Y103.632 F18000 +G1 X129.368 Y109.368 G1 Z.6 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y106.368 E.09261 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y103.632 E.09261 -M73 P32 R0 -M73 Q32 S0 -G1 X126.308 Y103.632 E.09058 -G1 X126.775 Y103.225 F18000 +G1 F2588 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X126.775 Y106.775 E.12016 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y103.225 E.12016 -M73 P33 R0 -M73 Q33 S0 -G1 X126.715 Y103.225 E.11813 -G1 X126.774 Y103.621 F18000 -G1 E-.7 F2100 -G1 X123.978 Y106.022 Z.664 F18000 +G1 F2588 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +M73 P10 R5 +G1 E-.7 F2100 +G1 X121.011 Y108.911 Z.747 F18000 G1 Z.6 F720 G1 E.7 F1500 ;TYPE:Solid infill -G1 F1200 -G1 X123.978 Y103.978 E.06919 -G1 X126.022 Y103.978 E.06919 -M73 P34 R0 -G1 X126.022 Y106.022 E.06919 -M73 Q34 S0 -G1 X124.182 Y106.022 E.06228 -G1 X124.385 Y105.615 E.01539 -G1 X124.385 Y104.385 E.04163 -G1 X125.615 Y104.385 E.04163 -G1 X125.615 Y105.615 E.04163 -G1 X124.589 Y105.615 E.03473 -G1 X124.794 Y105.206 E.01549 -;WIDTH:0.454205 -G1 X124.794 Y104.794 E.01409 -G1 X125.206 Y104.794 E.01409 -G1 X125.206 Y105.206 E.01409 -G1 X124.998 Y105.206 E.00711 +;WIDTH:0.548766 +G1 F2588 +G2 X121.072 Y108.928 I.017 J.059 E.01322 +;WIDTH:0.449999 +G1 X120.978 Y108.278 E.02223 +G1 X121.722 Y109.022 E.03561 +G1 X122.298 Y109.022 E.0195 +G1 X120.978 Y107.702 E.06319 +G1 X120.978 Y107.126 E.0195 +G1 X122.874 Y109.022 E.09076 +G1 X123.45 Y109.022 E.0195 +G1 X120.978 Y106.551 E.11831 +G1 X120.978 Y105.975 E.0195 +G1 X124.025 Y109.022 E.14586 +G1 X124.601 Y109.022 E.0195 +G1 X120.978 Y105.399 E.17343 +G1 X120.978 Y104.823 E.0195 +G1 X125.177 Y109.022 E.201 +G1 X125.752 Y109.022 E.01946 +G1 X120.978 Y104.248 E.22853 +G1 X120.978 Y103.672 E.0195 +G1 X126.328 Y109.022 E.2561 +G1 X126.904 Y109.022 E.0195 +M73 P11 R5 +M73 Q11 S6 +G1 X120.978 Y103.096 E.28367 +G1 X120.978 Y102.521 E.01946 +G1 X127.479 Y109.022 E.3112 +G1 X128.055 Y109.022 E.0195 +G1 X120.978 Y101.945 E.33877 +G1 X120.978 Y101.369 E.0195 +G1 X128.631 Y109.022 E.36634 +G1 X129.109 Y109.079 E.01629 +;WIDTH:0.38292 +G1 X129.09 Y109.15 E.00208 +G1 X129.161 Y109.131 E.00208 +G1 X129.15 Y109.09 E.0012 +;WIDTH:0.41646 +G1 X129.086 Y108.964 E.00439 +;WIDTH:0.449999 +G1 X129.022 Y108.837 E.00481 +G1 X121.163 Y100.978 E.37621 +G1 X121.036 Y100.914 E.00481 +;WIDTH:0.41646 +G1 X120.91 Y100.85 E.00439 +;WIDTH:0.38292 +G1 X120.921 Y100.891 E.0012 +G1 X120.85 Y100.91 E.00208 +G1 X120.869 Y100.839 E.00208 +G1 X120.869 Y100.839 F18000 +G1 X121.738 Y100.978 +;WIDTH:0.449999 +G1 F2588 +G1 X129.022 Y108.262 E.34868 +G1 X129.022 Y107.686 E.0195 +G1 X122.314 Y100.978 E.32111 +G1 X122.89 Y100.978 E.0195 +G1 X129.022 Y107.11 E.29354 +G1 X129.022 Y106.535 E.01946 +G1 X123.465 Y100.978 E.26601 +G1 X124.041 Y100.978 E.0195 +G1 X129.022 Y105.959 E.23844 +G1 X129.022 Y105.383 E.0195 +G1 X124.617 Y100.978 E.21086 +G1 X125.193 Y100.978 E.0195 +G1 X129.022 Y104.807 E.18329 +G1 X129.022 Y104.232 E.01946 +G1 X125.768 Y100.978 E.15577 +G1 X126.344 Y100.978 E.0195 +G1 X129.022 Y103.656 E.12819 +G1 X129.185 Y103.196 E.01652 +;WIDTH:0.38292 +G1 X129.166 Y103.267 E.00208 +G1 X129.237 Y103.248 E.00208 +G1 X129.226 Y103.207 E.0012 +;WIDTH:0.41646 +G1 X129.124 Y103.143 E.00374 +;WIDTH:0.449999 +G1 X129.022 Y103.08 E.00406 +G1 X126.92 Y100.978 E.10062 +G1 X126.857 Y100.876 E.00406 +;WIDTH:0.41646 +G1 X126.793 Y100.774 E.00374 +;WIDTH:0.38292 +G1 X126.804 Y100.815 E.0012 +G1 X126.733 Y100.834 E.00208 +G1 X126.752 Y100.763 E.00208 +;WIDTH:0.449999 +G1 X127.495 Y100.978 E.02618 +G1 X129.022 Y100.978 E.05169 +G1 X129.022 Y102.505 E.05169 +G1 X127.639 Y101.122 E.0662 +G1 X127.639 Y101.122 F18000 +G1 X128.559 Y101.366 +;WIDTH:0.530084 +G1 F2588 +G2 X128.618 Y101.382 I.017 J.056 E.01225 ;LAYER_CHANGE ;Z:0.8 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;0.8 -M201 X3999.95 Y3999.95 +M201 X3999.86 Y3999.86 G1 E-.7 F2100 -M73 P35 R0 -G1 X124.998 Y105.206 Z.6 F18000 -M73 Q35 S0 -G1 X126.368 Y103.745 Z.8 F7642.212 +G1 X128.618 Y101.382 Z.6 F18000 +G1 X129.368 Y109.368 Z.8 ;AFTER_LAYER_CHANGE ;0.8 -M74 W0.0445597 +M74 W0.129063 -G1 X126.368 Y103.745 F18000 +G1 X129.368 Y109.368 G1 Z.8 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y104.381 E.02153 -G1 X126.293 Y104.418 E.00283 -G1 X126.176 Y104.691 E.01005 -G1 X126.168 Y105.176 E.01642 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.832 Y105.176 E.00491 -G1 X123.826 Y104.591 E.0198 -G1 X123.695 Y104.294 E.01099 -G1 X123.632 Y104.263 E.00238 -G1 X123.632 Y103.745 E.01753 -G1 X123.787 Y103.832 E.00602 -G1 X124.243 Y103.832 E.01544 -G1 X124.502 Y103.775 E.00898 -G1 X124.616 Y103.632 E.00619 -G1 X125.372 Y103.632 E.02559 -G1 X125.441 Y103.751 E.00466 -G1 X125.597 Y103.814 E.00569 -G1 X126.213 Y103.832 E.02086 -M73 P36 R0 -G1 X126.316 Y103.774 E.004 -M73 Q36 S0 -G1 X126.316 Y103.774 F18000 -G1 X126.341 Y103.352 +G1 F2211 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +M73 P12 R5 +M73 Q12 S6 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X126.388 Y103.326 E.00182 -G1 X126.416 Y103.225 E.00355 -G1 X126.775 Y103.225 E.01215 -G1 X126.775 Y104.587 E.0461 -G1 X126.591 Y104.709 E.00747 -G1 X126.575 Y105.176 E.01582 -G1 X126.61 Y105.29 E.00404 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.379 Y104.55 E.00464 -G1 X123.225 Y104.477 E.00577 -G1 X123.225 Y103.225 E.04238 -G1 X123.584 Y103.225 E.01215 -G1 X123.612 Y103.326 E.00355 -M73 P37 R0 -G1 X123.787 Y103.425 E.00681 -G1 X124.312 Y103.413 E.01778 -G1 X124.444 Y103.225 E.00778 -M73 Q37 S0 -G1 X125.543 Y103.225 E.0372 -G1 X125.643 Y103.398 E.00676 -G1 X125.695 Y103.419 E.0019 -G1 X126.213 Y103.425 E.01753 -G1 X126.289 Y103.382 E.00296 -G1 X126.657 Y103.225 F18000 -G1 X126.022 Y104.182 +G1 F2211 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X128.687 Y108.425 Z.826 F18000 +G1 Z.8 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G2 X126.022 Y105.751 I1.328 J.784 E.05571 -G1 X126.022 Y106.022 E.00917 -G1 X124.178 Y104.178 E.08827 -G2 X124.783 Y103.978 I.084 J-.761 E.02225 -G1 X125.204 Y103.978 E.01425 -G2 X125.736 Y104.178 I.499 J-.519 E.01978 -G1 X125.822 Y104.178 E.00291 -G1 X123.978 Y106.022 E.08827 -G3 X124.178 Y105.344 I2.325 J.317 E.02402 -M73 P38 R0 -G2 X124.12 Y104.348 I-3.037 J-.324 E.03392 -M73 Q38 S0 -G1 X124.049 Y104.221 E.00492 +G1 F2211 +G2 X129.022 Y108.176 I-.403 J-.892 E.01424 +G1 X129.022 Y108.654 E.01618 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X128.496 Y108.496 E.02518 +G3 X127.273 Y108.156 I-.332 J-1.173 E.04519 +G3 X126.19 Y108.485 I-.78 J-.623 E.04073 +G2 X125.986 Y108.494 I-.098 J.09 E.00789 +G1 X125.428 Y108.494 E.01889 +G2 X125.261 Y108.494 I-.084 J.02 E.00779 +G1 X124.702 Y108.494 E.01892 +G1 X124.632 Y108.424 E.00335 +G3 X123.298 Y107.887 I-.458 J-.787 E.05611 +G3 X121.566 Y108.434 I-1.182 J-.727 E.06702 +G1 X120.978 Y109.022 E.02815 +G1 X120.978 Y108.654 E.01246 +G1 X121.346 Y109.022 E.01762 +M73 Q12 S5 +G1 X123.346 Y109.022 E.0677 +G1 E-.7 F2100 +G1 X122.958 Y107.042 Z.835 F18000 +G1 Z.8 F720 +G1 E.7 F1500 +G1 F2211 +G1 X123.259 Y106.741 E.01441 +G1 X123.259 Y106.741 F18000 +G1 X122.493 Y105.783 +;TYPE:Solid infill +;WIDTH:0.397545 +G1 F2211 +G1 X122.844 Y105.994 E.01208 +;WIDTH:0.399178 +G1 X123.031 Y106.325 E.01126 +G1 X122.72 Y106.415 E.00959 +G1 X122.554 Y106.201 E.00802 +;WIDTH:0.392852 +G1 X122.265 Y106.057 E.0094 +;WIDTH:0.362992 +G1 X121.891 Y106.082 E.00998 +;WIDTH:0.373165 +G1 X121.621 Y106.271 E.00905 +;WIDTH:0.399449 +G1 X121.447 Y106.692 E.0135 +G1 X121.45 Y107.201 E.01509 +;WIDTH:0.397184 +G1 X121.596 Y107.584 E.01207 +;WIDTH:0.38205 +G1 X121.857 Y107.8 E.00955 +;WIDTH:0.369376 +G1 X122.229 Y107.836 E.01015 +;WIDTH:0.384857 +G1 X122.508 Y107.704 E.00878 +;WIDTH:0.401321 +G1 X122.698 Y107.397 E.01076 +G1 X123.023 Y107.424 E.00972 +;WIDTH:0.391871 +G1 X123.191 Y107.504 E.0054 +;WIDTH:0.39058 +G1 X123.085 Y107.541 E.00325 +;WIDTH:0.397648 +G3 X122.752 Y107.943 I-2.282 J-1.545 E.01542 +;WIDTH:0.382997 +G1 X122.411 Y108.129 E.01098 +;WIDTH:0.368207 +G1 X122.03 Y108.158 E.01033 +;WIDTH:0.369376 +G1 X121.723 Y108.09 E.00854 +;WIDTH:0.380518 +G1 X121.394 Y107.873 E.01106 +;WIDTH:0.405198 +G1 X121.17 Y107.515 E.01272 +G1 X121.099 Y107.236 E.00867 +;WIDTH:0.399449 +G1 X121.091 Y106.684 E.01637 +G1 X121.193 Y106.335 E.01078 +;WIDTH:0.39918 +G1 X121.353 Y106.077 E.00899 +;WIDTH:0.373165 +G1 X121.69 Y105.814 E.01174 +;WIDTH:0.365871 +G1 X122.117 Y105.726 E.01171 +;WIDTH:0.373185 +G1 X122.292 Y105.753 E.00486 +G1 E-.7 F2100 +G1 X122.292 Y105.753 F18000 +G1 X123.743 Y106.455 Z.828 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.386917 +G1 F2211 +G1 X123.676 Y106.455 E.00192 +G1 X123.634 Y106.535 E.00258 +;WIDTH:0.388611 +G1 X123.635 Y107.338 E.02308 +;WIDTH:0.406742 +G3 X123.537 Y107.488 I-.152 J.008 E.0058 +G1 X123.629 Y107.596 E.00429 +;WIDTH:0.399229 +G1 X123.728 Y107.928 E.01026 +;WIDTH:0.387474 +G1 X123.836 Y108.044 E.00454 +;WIDTH:0.432624 +G1 X123.937 Y108.064 E.00334 +;WIDTH:0.482293 +G1 X124.038 Y108.084 E.00376 +;WIDTH:0.517995 +G1 X124.124 Y108.064 E.00349 +;WIDTH:0.553697 +G1 X124.21 Y108.044 E.00375 +;WIDTH:0.589398 +G1 X124.296 Y108.024 E.00401 +G1 X124.199 Y107.996 E.00459 +;WIDTH:0.546363 +G1 X124.15 Y107.931 E.00341 +;WIDTH:0.498575 +G1 X124.101 Y107.866 E.00308 +;WIDTH:0.450786 +G1 X124.052 Y107.801 E.00276 +;WIDTH:0.406742 +G1 X123.973 Y107.494 E.00959 +G1 X123.978 Y106.626 E.02626 +;WIDTH:0.386917 +G1 X123.947 Y106.455 E.00497 +G1 E-.7 F2100 +G1 X123.947 Y106.455 F18000 +G1 X125.549 Y105.989 Z.829 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.435488 +G1 F2211 +G1 X125.519 Y106.322 E.01091 +;WIDTH:0.449999 +G1 X125.39 Y106.454 E.00625 +G1 X125.28 Y106.486 E.00388 +G1 X124.825 Y106.507 E.01542 +G3 X124.763 Y107.706 I-6.678 J.259 E.04069 +;WIDTH:0.481293 +G1 X124.693 Y107.804 E.00439 +;WIDTH:0.512586 +G1 X124.622 Y107.902 E.00473 +;WIDTH:0.54208 +G1 X124.529 Y107.97 E.00478 +G1 X124.66 Y107.998 E.00556 +;WIDTH:0.50393 +G1 X124.791 Y108.025 E.00513 +;WIDTH:0.46578 +G1 X124.881 Y108.087 E.00384 +;WIDTH:0.449999 +G1 X125.092 Y108.087 E.00714 +G1 X125.224 Y107.997 E.00541 +G1 X125.483 Y108.002 E.00877 +G2 X125.822 Y108.083 I.263 J-.348 E.01214 +;WIDTH:0.483904 +G1 X125.894 Y108.042 E.00304 +;WIDTH:0.517808 +G1 X125.966 Y108.001 E.00327 +;WIDTH:0.551712 +G1 X126.039 Y107.959 E.00356 +;WIDTH:0.590464 +G1 X126.14 Y107.944 E.00465 +G1 X126.073 Y107.877 E.00431 +;WIDTH:0.549192 +G1 X126.006 Y107.81 E.00399 +;WIDTH:0.50792 +G1 X125.941 Y107.626 E.00755 +;WIDTH:0.47896 +G1 X125.875 Y107.443 E.00705 +;WIDTH:0.449999 +G1 X125.907 Y106.962 E.01632 +;WIDTH:0.490239 +G1 X125.984 Y106.845 E.00521 +;WIDTH:0.530478 +G1 X126.06 Y106.729 E.00562 +;WIDTH:0.570717 +G1 X126.137 Y106.612 E.00615 +;WIDTH:0.606072 +G1 X126.219 Y106.573 E.00425 +G1 X126.163 Y106.539 E.00307 +;WIDTH:0.572731 +G1 X126.107 Y106.506 E.00286 +;WIDTH:0.53939 +G1 X126.051 Y106.473 E.00268 +;WIDTH:0.506049 +G1 X125.988 Y106.399 E.00374 +;WIDTH:0.470769 +G1 X125.926 Y106.326 E.00341 +;WIDTH:0.435488 +G1 X125.896 Y105.824 E.01642 +;WIDTH:0.386411 +G1 X125.843 Y105.771 E.00214 +G1 X125.568 Y105.787 E.00787 +G1 X125.568 Y105.787 F18000 +G1 X125.614 Y106.74 +;WIDTH:0.366983 +G1 F2211 +G1 X125.514 Y107.023 E.00809 +;WIDTH:0.375048 +G1 X125.505 Y107.44 E.01152 +;WIDTH:0.375681 +G1 X125.548 Y107.644 E.00577 +G2 X125.157 Y107.635 I-.22 J1.111 E.01088 +G1 X125.174 Y107.426 E.0058 +;WIDTH:0.375048 +G1 X125.19 Y106.852 E.01586 +;WIDTH:0.366983 +G2 X125.421 Y106.805 I.025 J-.467 E.00642 +G1 X126.219 Y106.573 F18000 +;WIDTH:0.606072 +G1 F2211 +G1 X126.373 Y106.55 E.00729 +;WIDTH:0.584682 +G1 X126.439 Y106.527 E.00315 +;WIDTH:0.558258 +G1 X126.506 Y106.504 E.00304 +;WIDTH:0.531833 +G1 X126.577 Y106.5 E.00289 +;WIDTH:0.4959 +G1 X126.648 Y106.496 E.00268 +;WIDTH:0.459966 +G1 X126.719 Y106.491 E.00247 +;WIDTH:0.424032 +G1 X126.784 Y106.508 E.00213 +;WIDTH:0.397539 +G1 X126.849 Y106.524 E.00197 +;WIDTH:0.391384 +G1 X127.044 Y106.738 E.00839 +;WIDTH:0.420692 +G1 X127.115 Y106.792 E.0028 +;WIDTH:0.449999 +G1 X127.187 Y106.845 E.00303 +G1 X127.464 Y106.825 E.0094 +G1 X127.717 Y106.576 E.01202 +G1 X127.939 Y106.472 E.0083 +G1 X128.096 Y106.462 E.00533 +G1 X128.468 Y106.522 E.01275 +G1 X128.663 Y106.668 E.00825 +G1 X128.827 Y106.979 E.0119 +G1 X128.868 Y107.32 E.01163 +G1 X128.817 Y107.374 E.00251 +G1 X128.746 Y107.591 E.00773 +G1 X128.794 Y107.771 E.00631 +G1 X128.712 Y107.89 E.00489 +G1 X128.455 Y108.078 E.01078 +G1 X128.162 Y108.125 E.01004 +G1 X127.818 Y108.062 E.01184 +G1 X127.449 Y107.752 E.01631 +M73 Q13 S5 +G1 X127.138 Y107.737 E.01054 +G1 X127.002 Y107.883 E.00675 +;WIDTH:0.408729 +G1 X126.867 Y108.029 E.00605 +;WIDTH:0.403484 +G1 X126.776 Y108.055 E.00284 +;WIDTH:0.439509 +G1 X126.686 Y108.082 E.0031 +;WIDTH:0.477978 +G1 X126.611 Y108.071 E.00274 +;WIDTH:0.516447 +G1 X126.535 Y108.06 E.00302 +;WIDTH:0.554916 +G1 X126.46 Y108.048 E.00323 +;WIDTH:0.593384 +G1 X126.385 Y108.037 E.00347 +G1 X126.444 Y107.994 E.00334 +;WIDTH:0.556655 +G1 X126.503 Y107.95 E.00314 +;WIDTH:0.519926 +G1 X126.562 Y107.906 E.00292 +;WIDTH:0.483197 +G1 X126.835 Y107.483 E.01843 +;WIDTH:0.449999 +G1 X126.825 Y107.079 E.01368 +;WIDTH:0.434363 +G1 X126.747 Y106.919 E.00579 +;WIDTH:0.43035 +G1 X126.679 Y106.831 E.00358 +;WIDTH:0.468363 +G1 X126.611 Y106.743 E.00393 +;WIDTH:0.506375 +G1 X126.543 Y106.655 E.00429 +;WIDTH:0.556224 +G1 X126.381 Y106.614 E.00713 +;WIDTH:0.606072 +G1 X126.277 Y106.587 E.00503 +G1 X126.277 Y106.587 F18000 +G1 X127.362 Y107.287 +;WIDTH:0.541617 +G1 F2211 +G1 X127.52 Y107.263 E.00663 +;WIDTH:0.568213 +G1 X127.677 Y107.239 E.00694 +;WIDTH:0.577127 +G1 X128.058 Y106.936 E.02162 +G1 X128.261 Y106.955 E.00906 +M73 P13 R5 +G1 X128.37 Y107.121 E.00882 +G1 X128.274 Y107.63 E.02301 +G1 X128.057 Y107.627 E.00964 +G1 X127.676 Y107.345 E.02106 +;WIDTH:0.568213 +G1 X127.563 Y107.324 E.00502 +G1 X127.563 Y107.324 F18000 +G1 X126.355 Y108.019 +;WIDTH:0.607864 +G1 F2211 +G1 X126.14 Y107.944 E.0107 +G1 E-.7 F2100 +G1 X126.14 Y107.944 F18000 +G1 X124.529 Y107.97 Z.828 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.589398 +G1 F2211 +G1 X124.296 Y108.024 E.01087 +G1 E-.7 F2100 +G1 X124.296 Y108.024 F18000 +G1 X124.209 Y102.859 Z.89 +G1 Z.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2211 +G3 X123.547 Y102.804 I-.29 J-.529 E.02381 +G2 X123.391 Y102.553 I-.352 J.045 E.01032 +G2 X122.975 Y102.975 I-.089 J.329 E.02434 +G1 X123.484 Y103.454 E.02366 +G3 X123.867 Y103.093 I.447 J.09 E.01899 +G1 X124.459 Y103.281 E.02102 +G2 X124.525 Y103.255 I.033 J-.015 E.00376 +G1 X124.344 Y102.914 E.01307 +G1 X124.388 Y102.816 E.00364 +G1 X124.209 Y102.859 E.00623 +G1 X124.209 Y102.859 F18000 +G1 X123.824 Y101.991 +;TYPE:Solid infill +;WIDTH:0.404145 +G1 F2211 +G1 X124.047 Y102.192 E.00902 +G1 X124.191 Y102.445 E.00874 +;WIDTH:0.403378 +G1 X123.878 Y102.543 E.00983 +G1 X123.674 Y102.297 E.00958 +;WIDTH:0.380516 +G1 X123.422 Y102.186 E.00773 +;WIDTH:0.364934 +G1 X123.056 Y102.211 E.00982 +;WIDTH:0.379233 +G1 X122.778 Y102.395 E.00932 +;WIDTH:0.399449 +G1 X122.604 Y102.822 E.01367 +G1 X122.607 Y103.331 E.01509 +;WIDTH:0.397184 +G1 X122.753 Y103.714 E.01207 +;WIDTH:0.382049 +G1 X123.014 Y103.929 E.00954 +;WIDTH:0.369373 +G1 X123.386 Y103.965 E.01014 +;WIDTH:0.3867 +G1 X123.666 Y103.834 E.00884 +;WIDTH:0.423866 +G1 X123.743 Y103.743 E.00378 +;WIDTH:0.461032 +G1 X123.819 Y103.652 E.00412 +;WIDTH:0.498197 +G1 X123.896 Y103.56 E.00454 +G1 X124.078 Y103.634 E.00744 +;WIDTH:0.467054 +G1 X124.26 Y103.707 E.00692 +;WIDTH:0.47177 +G1 X124.19 Y103.727 E.0026 +;WIDTH:0.507629 +G2 X124.094 Y103.805 I-.004 J.093 E.00523 +;WIDTH:0.470498 +G1 X124.069 Y103.862 E.00221 +;WIDTH:0.433367 +G1 X124.045 Y103.919 E.00201 +;WIDTH:0.396236 +G1 X123.919 Y104.07 E.00578 +;WIDTH:0.386989 +G1 X123.595 Y104.252 E.01063 +;WIDTH:0.369313 +G1 X123.187 Y104.287 E.01111 +G1 X122.88 Y104.22 E.00853 +;WIDTH:0.380517 +G1 X122.551 Y104.003 E.01106 +;WIDTH:0.405201 +G1 X122.327 Y103.645 E.01272 +G1 X122.256 Y103.366 E.00867 +;WIDTH:0.399449 +G1 X122.248 Y102.814 E.01637 +G1 X122.289 Y102.635 E.00544 +;WIDTH:0.394604 +G1 X122.492 Y102.218 E.01356 +;WIDTH:0.378877 +G1 X122.799 Y101.971 E.01101 +;WIDTH:0.36781 +G1 X123.171 Y101.866 E.01044 +G1 X123.497 Y101.878 E.00881 +;WIDTH:0.371788 +G1 X123.632 Y101.924 E.0039 +G1 E-.7 F2100 +G1 X123.632 Y101.924 F18000 +G1 X125.366 Y103.037 Z.836 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.531889 +G1 F2211 +G1 X125.641 Y103.053 E.0112 +G1 X125.751 Y103.197 E.00737 +;WIDTH:0.553264 +G1 X126.001 Y103.433 E.01459 +;WIDTH:0.6025 +G1 X126.133 Y103.492 E.00673 +;WIDTH:0.651736 +G1 X126.264 Y103.551 E.00727 +G1 X126.138 Y103.621 E.0073 +;WIDTH:0.6025 +G1 X126.012 Y103.691 E.00671 +;WIDTH:0.553264 +G1 X125.674 Y103.703 E.01435 +;WIDTH:0.531889 +G1 X125.236 Y103.795 E.0182 +G2 X125.418 Y103.298 I-.884 J-.605 E.02174 +G1 X125.405 Y103.237 E.00254 +G1 E-.7 F2100 +G1 X125.405 Y103.237 F18000 +G1 X127.112 Y102.543 Z.832 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.370515 +G1 F2211 +G1 X127.12 Y102.876 E.00907 +;WIDTH:0.41909 +G1 X127.073 Y103.005 E.00429 +;WIDTH:0.467665 +G1 X127.027 Y103.134 E.00484 +;WIDTH:0.516239 +G1 X126.98 Y103.263 E.0054 +;WIDTH:0.562386 +G1 X126.808 Y103.353 E.00838 +;WIDTH:0.608533 +G1 X126.635 Y103.443 E.00917 +;WIDTH:0.65468 +G1 X126.462 Y103.533 E.00992 +G1 X126.585 Y103.571 E.00655 +;WIDTH:0.607312 +G1 X126.708 Y103.608 E.00603 +;WIDTH:0.559944 +G1 X126.92 Y103.752 E.01102 +;WIDTH:0.516239 +G3 X127.372 Y103.68 I.385 J.964 E.01816 +G1 X127.397 Y103.421 E.01024 +;WIDTH:0.467665 +G1 X127.421 Y103.163 E.00915 +;WIDTH:0.41909 +G1 X127.445 Y102.904 E.00814 +;WIDTH:0.370515 +G1 X127.39 Y102.379 E.01438 +;WIDTH:0.327855 +G3 X127.107 Y102.34 I-.08 J-.462 E.00688 +;WIDTH:0.36857 +G1 X126.992 Y102.279 E.00352 +;WIDTH:0.409285 +G1 X126.877 Y102.218 E.00397 +;WIDTH:0.449999 +G1 X126.761 Y102.157 E.00444 +G1 X126.761 Y102.777 E.02099 +G1 X126.691 Y102.931 E.00573 +G1 X126.298 Y103.044 E.01384 +G1 X126.149 Y102.981 E.00548 +G1 X125.969 Y102.713 E.01093 +G1 X125.681 Y102.607 E.01039 +G1 X125.31 Y102.592 E.01257 +G1 X125.022 Y102.672 E.01012 +G2 X124.812 Y102.9 I.358 J.541 E.01059 +G1 X124.947 Y103.198 E.01107 +G1 X124.953 Y103.383 E.00627 +G1 X124.701 Y103.684 E.01329 +;WIDTH:0.442003 +G1 X124.538 Y103.745 E.00578 +;WIDTH:0.449999 +G1 X124.703 Y103.876 E.00713 +G1 X124.821 Y104.107 E.00878 +G1 X124.993 Y104.22 E.00697 +G1 X125.272 Y104.25 E.0095 +G1 X125.721 Y104.148 E.01559 +G2 X126.132 Y104.217 I.415 J-1.215 E.01417 +G1 X126.25 Y104.082 E.00607 +G1 X126.498 Y104.041 E.00851 +G3 X126.773 Y104.217 I-.1 J.461 E.01128 +G1 X127.029 Y104.217 E.00867 +G1 X127.197 Y104.115 E.00665 +G1 X127.358 Y104.114 E.00545 +G1 X127.623 Y104.217 E.00962 +G1 X127.806 Y104.211 E.0062 +G1 X127.812 Y102.629 E.05355 +G1 X127.736 Y102.468 E.00603 +G3 X127.812 Y102.1 I1.553 J.132 E.01275 +G1 X127.792 Y101.933 E.00569 +G1 X127.545 Y101.933 E.00836 +G1 X127.391 Y102.033 E.00622 +G1 X127.216 Y102.036 E.00592 +G1 X126.951 Y101.933 E.00962 +G1 X126.761 Y101.954 E.00647 +G1 E-.7 F2100 +G1 X126.761 Y101.954 F18000 +G1 X126.462 Y103.533 Z.828 +G1 Z.8 F720 +G1 E.7 F1500 +;WIDTH:0.65468 +G1 F2211 +G1 X126.264 Y103.551 E.01011 +G1 E-.7 F2100 +G1 X126.264 Y103.551 F18000 +G1 X129.022 Y103.346 Z.848 +G1 Z.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2211 +G1 X129.022 Y101.346 E.0677 +G1 X128.654 Y100.978 E.01762 +G1 X129.022 Y100.978 E.01246 +G1 X128.22 Y101.78 E.03839 +G1 X128.22 Y102.334 E.01875 +G2 X128.22 Y102.46 I.015 J.063 E.00584 +G1 X128.22 Y104.374 E.06479 +G1 X127.969 Y104.624 E.01199 +G1 X127.376 Y104.624 E.02007 +G2 X127.198 Y104.624 I-.089 J.022 E.00822 +G1 X126.605 Y104.624 E.02007 +G1 X126.451 Y104.471 E.00735 +G1 X126.338 Y104.624 E.00644 +G1 X125.77 Y104.624 E.01923 +G2 X125.522 Y104.617 I-.128 J.131 E.00923 +G1 X125.344 Y104.656 E.00617 +G1 X123.921 Y106.079 E.06812 +G1 X124.102 Y106.079 E.00613 +G1 X124.353 Y106.33 E.01202 +G1 X124.365 Y107.551 E.04133 +G2 X124.418 Y107.265 I-.36 J-.215 E.01005 +G1 X124.417 Y106.33 E.03165 +G1 X124.668 Y106.079 E.01202 +G1 X125.177 Y106.079 E.01723 +G1 X125.177 Y105.647 E.01462 +G1 X125.412 Y105.412 E.01125 +G1 X124.267 Y104.267 E.05481 +G3 X122.522 Y104.457 I-.993 J-1.012 E.06407 +G3 X122.11 Y102.11 I1.026 J-1.39 E.089 +G1 X120.978 Y100.978 E.05419 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +M106 S255 ;LAYER_CHANGE ;Z:1 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;1 -M201 X3999.95 Y3999.95 +M201 X3999.84 Y3999.84 G1 E-.7 F2100 -G1 X124.049 Y104.221 Z.8 F18000 -G1 X126.368 Y103.684 Z1 F8946.585 +G1 X120.978 Y103.346 Z.8 F18000 +G1 X129.368 Y109.368 Z1 ;AFTER_LAYER_CHANGE ;1 -M74 W0.0482789 +M74 W0.147529 -G1 X126.368 Y103.684 F18000 +G1 X129.368 Y109.368 G1 Z1 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 G1 F1200 -G1 X126.368 Y104.381 E.02359 -G1 X126.293 Y104.418 E.00283 -G1 X126.192 Y104.618 E.00758 -G1 X126.168 Y105.176 E.01891 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.829 Y104.619 E.02376 -G1 X123.704 Y104.304 E.01147 -G1 X123.632 Y104.263 E.0028 -G1 X123.632 Y103.685 E.01956 -G1 X123.935 Y103.832 E.0114 -G1 X124.462 Y103.827 E.01784 -G1 X124.749 Y103.713 E.01045 -M73 P39 R0 -G1 X124.79 Y103.632 E.00307 -M73 Q39 S0 -G1 X125.2 Y103.632 E.01388 -G1 X125.241 Y103.713 E.00307 -G1 X125.505 Y103.824 E.00969 -G1 X126.07 Y103.832 E.01913 -G1 X126.314 Y103.711 E.00922 -G1 X126.314 Y103.711 F18000 -G1 X126.194 Y103.358 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +M73 Q14 S5 +G1 X129.368 Y100.632 E.2957 +M73 P14 R5 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter G1 F1200 -G1 X126.232 Y103.344 E.00137 -G1 X126.272 Y103.225 E.00425 -G1 X126.775 Y103.225 E.01703 -G1 X126.775 Y104.587 E.0461 -G1 X126.617 Y104.665 E.00596 -G1 X126.583 Y104.732 E.00254 -G1 X126.575 Y105.176 E.01503 -G1 X126.61 Y105.29 E.00404 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.382 Y104.554 E.00447 -G1 X123.225 Y104.477 E.00592 -G1 X123.225 Y103.225 E.04238 -M73 P40 R0 -G1 X123.733 Y103.225 E.0172 -M73 Q40 S0 -G1 X123.773 Y103.345 E.00428 -G1 X123.935 Y103.425 E.00612 -G1 X124.476 Y103.404 E.01833 -G1 X124.589 Y103.225 E.00717 -G1 X125.402 Y103.225 E.02752 -G1 X125.514 Y103.404 E.00715 -G1 X126.003 Y103.425 E.01657 -G1 X126.138 Y103.378 E.00484 -G1 X126.507 Y103.225 F18000 -G1 X126.022 Y104.182 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z1.106 F18000 +G1 Z1 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 G1 F1200 -G2 X126.022 Y105.751 I1.327 J.784 E.05572 -G1 X126.022 Y106.022 E.00917 -G1 X124.175 Y104.175 E.08841 -G2 X125.012 Y103.978 I.123 J-1.352 E.02962 -G2 X125.822 Y104.178 I.673 J-.987 E.02885 -G1 X123.978 Y106.022 E.08827 -G3 X124.178 Y105.344 I2.327 J.318 E.02402 -M73 P41 R0 -M73 Q41 S0 -G1 X124.174 Y104.587 E.02562 -G2 X124.042 Y104.221 I-.909 J.122 E.01327 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 Q15 S5 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +M73 P15 R5 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:1.2 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;1.2 -M201 X3999.94 Y3999.94 +M201 X3999.83 Y3999.83 G1 E-.7 F2100 -G1 X124.042 Y104.221 Z1 F18000 -G1 X126.336 Y103.65 Z1.2 F8889.382 +G1 X120.978 Y103.346 Z1 F18000 +G1 X129.368 Y109.368 Z1.2 ;AFTER_LAYER_CHANGE ;1.2 -M74 W0.0520099 +M74 W0.158699 -G1 X126.336 Y103.65 F18000 +G1 X129.368 Y109.368 G1 Z1.2 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 G1 F1200 -G1 X126.368 Y103.636 E.00118 -G1 X126.368 Y104.382 E.02525 -G1 X126.293 Y104.419 E.00283 -G1 X126.177 Y104.683 E.00976 -G1 X126.168 Y105.176 E.01669 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.832 Y104.679 E.02173 -G1 X123.757 Y104.385 E.01027 -G1 X123.632 Y104.264 E.00589 -M73 P42 R0 -G1 X123.632 Y103.632 E.02139 -G1 X123.977 Y103.823 E.01335 -M73 Q42 S0 -G1 X124.084 Y103.832 E.00363 -G1 X124.691 Y103.811 E.02056 -G1 X124.978 Y103.632 E.01145 -G1 X125.22 Y103.782 E.00964 -G1 X125.357 Y103.823 E.00484 -G1 X125.927 Y103.832 E.0193 -G1 X126.281 Y103.675 E.01311 -G1 X126.281 Y103.675 F18000 -G1 X126.063 Y103.364 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter G1 F1200 -G1 X126.078 Y103.358 E.00055 -G1 X126.129 Y103.225 E.00482 -G1 X126.775 Y103.225 E.02187 -G1 X126.775 Y104.587 E.0461 -G1 X126.608 Y104.678 E.00644 -G2 X126.61 Y105.29 I1.658 J.299 E.02083 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -M73 P43 R0 -M73 Q43 S0 -G1 X123.425 Y105.176 E.00907 -G1 X123.4 Y104.581 E.02016 -G1 X123.225 Y104.477 E.00689 -G1 X123.225 Y103.225 E.04238 -G1 X123.881 Y103.225 E.0222 -G1 X123.933 Y103.358 E.00483 -G1 X124.048 Y103.422 E.00445 -G1 X124.531 Y103.425 E.01635 -G1 X124.679 Y103.362 E.00544 -G1 X124.733 Y103.225 E.00498 -G1 X125.26 Y103.225 E.01784 -G1 X125.315 Y103.362 E.005 -G1 X125.41 Y103.418 E.00373 -G1 X125.927 Y103.425 E.0175 -G1 X126.008 Y103.389 E.003 -G1 X126.372 Y103.224 F18000 -G1 X126.022 Y104.178 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +M73 Q16 S5 +G1 X129.775 Y109.715 E.32122 +M73 P16 R5 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z1.306 F18000 +G1 Z1.2 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 G1 F1200 -G2 X126.022 Y105.751 I1.355 J.786 E.05578 -G1 X126.022 Y106.022 E.00917 -G1 X124.177 Y104.177 E.08832 -G2 X124.997 Y104.043 I.222 J-1.214 E.02868 -G2 X125.822 Y104.178 I.616 J-1.175 E.02879 -G1 X123.978 Y106.022 E.08827 -M73 P44 R0 -G3 X124.178 Y105.344 I2.327 J.318 E.02402 -M73 Q44 S0 -G2 X124.065 Y104.224 I-2.535 J-.309 E.03842 -G1 X124.063 Y104.222 E.0001 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:1.4 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;1.4 -M201 X3999.94 Y3999.94 +M201 X3999.82 Y3999.82 G1 E-.7 F2100 -G1 X124.063 Y104.222 Z1.2 F18000 -G1 X126.21 Y103.659 Z1.4 F8391.09 +G1 X120.978 Y103.346 Z1.2 F18000 +G1 X129.368 Y109.368 Z1.4 ;AFTER_LAYER_CHANGE ;1.4 -M74 W0.0557451 +M74 W0.169868 -G1 X126.21 Y103.659 F18000 +G1 X129.368 Y109.368 G1 Z1.4 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 G1 F1200 -G1 X126.368 Y103.632 E.00543 -G1 X126.368 Y104.383 E.02542 -G1 X126.291 Y104.421 E.00291 -G1 X126.17 Y104.744 E.01168 -G1 X126.168 Y105.176 E.01462 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -M73 P45 R0 -M73 Q45 S0 -G1 X123.832 Y105.321 E.01281 -G2 X123.793 Y104.465 I-6.942 J-.115 E.02902 -G1 X123.7 Y104.3 E.00641 -G1 X123.632 Y104.267 E.00256 -G1 X123.632 Y103.632 E.02149 -G1 X123.736 Y103.632 E.00352 -G1 X124.045 Y103.803 E.01195 -G1 X124.232 Y103.832 E.00641 -G1 X124.871 Y103.8 E.02166 -G1 X124.998 Y103.689 E.00571 -G1 X125.125 Y103.8 E.00571 -G1 X125.321 Y103.832 E.00672 -G1 X125.909 Y103.819 E.01991 -G1 X126.157 Y103.687 E.00951 -G1 X126.157 Y103.687 F18000 -G1 X125.929 Y103.359 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +M73 Q17 S5 +G1 X129.368 Y109.308 E.29367 +M73 P17 R5 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter G1 F1200 -G1 X125.986 Y103.225 E.00493 -G1 X126.775 Y103.225 E.02671 -G1 X126.775 Y104.587 E.0461 -G1 X126.594 Y104.702 E.00726 -G2 X126.61 Y105.29 I1.966 J.241 E.01998 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -M73 P46 R0 -M73 Q46 S0 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.381 Y104.552 E.00455 -G1 X123.225 Y104.477 E.00586 -G1 X123.225 Y103.225 E.04238 -G1 X124.03 Y103.225 E.02725 -G1 X124.091 Y103.368 E.00526 -G1 X124.17 Y103.415 E.00311 -G1 X124.712 Y103.422 E.01835 -G1 X124.857 Y103.312 E.00616 -G1 X124.878 Y103.225 E.00303 -G1 X125.119 Y103.225 E.00816 -G1 X125.139 Y103.312 E.00302 -G1 X125.256 Y103.414 E.00525 -G1 X125.784 Y103.425 E.01788 -G1 X125.879 Y103.386 E.00348 -G1 X126.244 Y103.223 F18000 -G1 X126.022 Y104.144 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z1.506 F18000 +G1 Z1.4 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 G1 F1200 -G2 X125.825 Y104.709 I2.893 J1.327 E.02028 -G2 X126.022 Y105.751 I1.686 J.22 E.0365 -G1 X126.022 Y106.022 E.00917 -G1 X124.17 Y104.17 E.08865 -G2 X124.995 Y104.125 I.288 J-2.269 E.02812 -M73 P47 R0 -M73 Q47 S0 -G2 X125.825 Y104.175 I.576 J-2.658 E.02826 -G1 X123.978 Y106.022 E.08841 -G3 X124.178 Y105.344 I2.325 J.317 E.02402 -G2 X124.12 Y104.344 I-3.709 J-.286 E.03401 -G1 X124.026 Y104.178 E.00646 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +M73 P18 R5 +M73 Q18 S5 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:1.6 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;1.6 -M201 X3999.94 Y3999.94 +M201 X3999.81 Y3999.81 G1 E-.7 F2100 -G1 X124.026 Y104.178 Z1.4 F18000 -G1 X126.063 Y103.637 Z1.6 F8001.666 +G1 X120.978 Y103.346 Z1.4 F18000 +G1 X129.368 Y109.368 Z1.6 ;AFTER_LAYER_CHANGE ;1.6 -M74 W0.0594874 +M74 W0.181038 -G1 X126.063 Y103.637 F18000 +G1 X129.368 Y109.368 G1 Z1.6 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 G1 F1200 -G1 X126.065 Y103.632 E.00018 -G1 X126.368 Y103.632 E.01026 -G1 X126.368 Y104.385 E.02549 -G1 X126.288 Y104.425 E.00303 -G1 X126.198 Y104.6 E.00666 -G1 X126.168 Y105.176 E.01952 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -M73 P48 R0 -M73 Q48 S0 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.816 Y104.541 E.02641 -G1 X123.705 Y104.305 E.00883 -G1 X123.632 Y104.27 E.00274 -G1 X123.632 Y103.632 E.0216 -G1 X123.958 Y103.632 E.01103 -G1 X124.125 Y103.776 E.00746 -G1 X124.38 Y103.832 E.00884 -G1 X124.881 Y103.829 E.01696 -G1 X125 Y103.754 E.00476 -G1 X125.058 Y103.82 E.00297 -G1 X125.211 Y103.832 E.00519 -G1 X125.835 Y103.8 E.02115 -G1 X126.032 Y103.687 E.00769 -G1 X126.032 Y103.687 F18000 -G1 X125.783 Y103.357 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter G1 F1200 -G1 X125.842 Y103.225 E.00489 -G1 X126.775 Y103.225 E.03158 -G1 X126.775 Y104.587 E.0461 -G1 X126.615 Y104.667 E.00606 -G1 X126.585 Y104.726 E.00224 -G2 X126.61 Y105.29 I2.218 J.183 E.01916 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -M73 P49 R0 -M73 Q49 S0 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.176 E.00907 -G1 X123.407 Y104.596 E.01964 -G1 X123.225 Y104.477 E.00736 -G1 X123.225 Y103.225 E.04238 -G1 X124.179 Y103.225 E.03229 -G1 X124.295 Y103.406 E.00728 -G1 X124.84 Y103.424 E.01846 -G1 X125 Y103.237 E.00833 -G1 X125.14 Y103.421 E.00783 -G1 X125.64 Y103.425 E.01692 -G1 X125.737 Y103.389 E.0035 -G1 X126.101 Y103.224 F18000 -G1 X126.022 Y104.111 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +M73 P19 R5 +M73 Q19 S5 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z1.706 F18000 +G1 Z1.6 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 G1 F1200 -G3 X125.867 Y104.492 I-.62 J-.03 E.01419 -G2 X126.022 Y105.751 I1.678 J.433 E.04396 -G1 X126.022 Y106.022 E.00917 -G1 X124.144 Y104.144 E.0899 -G2 X125 Y104.161 I.481 J-2.785 E.02909 -M73 P50 R0 -M73 Q50 S0 -G2 X125.855 Y104.145 I.373 J-2.982 E.02904 -G1 X123.978 Y106.022 E.08985 -G3 X124.178 Y105.344 I2.325 J.317 E.02402 -G2 X124.095 Y104.288 I-2.617 J-.327 E.0361 -G1 X123.993 Y104.115 E.0068 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +M106 S252.45 ;LAYER_CHANGE ;Z:1.8 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;1.8 -M201 X3999.93 Y3999.93 +M201 X3999.79 Y3999.79 G1 E-.7 F2100 -G1 X123.993 Y104.115 Z1.6 F18000 -G1 X125.91 Y103.634 Z1.8 F7551.422 +G1 X120.978 Y103.346 Z1.6 F18000 +G1 X129.368 Y109.368 Z1.8 ;AFTER_LAYER_CHANGE ;1.8 -M74 W0.0632498 +M74 W0.192207 -G1 X125.91 Y103.634 F18000 +G1 X129.368 Y109.368 G1 Z1.8 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X125.911 Y103.632 E.00008 -G1 X126.368 Y103.632 E.01547 -G1 X126.368 Y104.387 E.02556 -G1 X126.285 Y104.43 E.00316 -G1 X126.18 Y104.667 E.00877 -G1 X126.168 Y105.176 E.01723 -G1 X126.274 Y105.519 E.01215 -G1 X126.368 Y105.569 E.0036 -G1 X126.368 Y106.368 E.02705 -G1 X123.632 Y106.368 E.09261 -M73 P51 R0 -M73 Q51 S0 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.829 Y104.613 E.02397 -G1 X123.71 Y104.312 E.01096 -G1 X123.632 Y104.273 E.00295 -G1 X123.632 Y103.632 E.0217 -G1 X124.117 Y103.632 E.01642 -G1 X124.217 Y103.746 E.00513 -G1 X124.529 Y103.832 E.01095 -G1 X125.042 Y103.832 E.01736 -G1 X125.613 Y103.821 E.01933 -G1 X125.879 Y103.698 E.00992 -G1 X125.884 Y103.688 E.00038 -G1 X125.884 Y103.688 F18000 -G1 X125.622 Y103.345 +G1 F1454 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +M73 Q20 S5 +G1 X129.775 Y109.775 F18000 +M73 P20 R5 ;TYPE:External perimeter -G1 F1200 -G1 X125.699 Y103.225 E.00483 -G1 X126.775 Y103.225 E.03642 -G1 X126.775 Y104.587 E.0461 -G1 X126.59 Y104.711 E.00754 -G2 X126.61 Y105.29 I2.123 J.217 E.01967 -G1 X126.775 Y105.377 E.00631 -G1 X126.775 Y106.775 E.04732 -G1 X123.225 Y106.775 E.12016 -M73 P52 R0 -M73 Q52 S0 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.384 Y104.556 E.00439 -G1 X123.225 Y104.477 E.00601 -G1 X123.225 Y103.225 E.04238 -G1 X124.327 Y103.225 E.0373 -G1 X124.425 Y103.396 E.00667 -G1 X124.957 Y103.425 E.01803 -G3 X125.042 Y103.425 I.042 J.023 E.00353 -G1 X125.582 Y103.407 E.01829 -G1 X125.589 Y103.395 E.00047 -G1 X125.952 Y103.227 F18000 -G1 X126.022 Y104.112 +G1 F1454 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z1.906 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G3 X125.894 Y104.423 I-.439 J.001 E.01168 -G2 X126.022 Y105.751 I1.584 J.517 E.04646 -G1 X126.022 Y106.022 E.00917 -G1 X124.049 Y104.049 E.09445 -M73 P53 R0 -M73 Q53 S0 -G2 X124.521 Y104.178 I.526 J-.997 E.0167 -G1 X125.504 Y104.178 E.03327 -G2 X125.927 Y104.073 I-.023 J-.994 E.01487 -G1 X123.978 Y106.022 E.0933 -G3 X124.178 Y105.344 I2.327 J.318 E.02402 -G2 X124.124 Y104.358 I-3.695 J-.292 E.03352 -G1 X123.994 Y104.115 E.00933 +G1 F1454 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +G1 E-.7 F2100 +G1 X120.45 Y105.745 Z1.843 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 +;TYPE:Ironing +;WIDTH:0.40161 +;HEIGHT:0.0075 +G1 F900 +M73 Q21 S5 +G1 X120.45 Y104.3 E.0018 +G1 X120.35 Y104.3 E.00012 +G1 X120.35 Y105.745 E.0018 +G1 E-.7 F2100 +M73 P21 R5 +G1 X123.05 Y100.495 Z1.903 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 +G1 F900 +G1 X123.05 Y100.25 E.00031 +G1 X122.95 Y100.25 E.00012 +G1 X122.95 Y100.45 E.00025 +G1 X122.85 Y100.45 E.00012 +G1 X122.85 Y100.25 E.00025 +G1 X122.75 Y100.25 E.00012 +G1 X122.75 Y100.45 E.00025 +G1 X122.65 Y100.45 E.00012 +G1 X122.65 Y100.25 E.00025 +G1 X122.55 Y100.25 E.00012 +G1 X122.55 Y100.45 E.00025 +G1 X122.45 Y100.45 E.00012 +G1 X122.45 Y100.25 E.00025 +G1 X122.35 Y100.25 E.00012 +G1 X122.35 Y100.45 E.00025 +G1 X122.25 Y100.45 E.00012 +G1 X122.25 Y100.205 E.00031 +G1 E-.7 F2100 +G1 X127.75 Y100.495 Z1.896 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 +G1 F900 +G1 X127.75 Y100.25 E.00031 +G1 X127.65 Y100.25 E.00012 +G1 X127.65 Y100.45 E.00025 +G1 X127.55 Y100.45 E.00012 +G1 X127.55 Y100.25 E.00025 +G1 X127.45 Y100.25 E.00012 +G1 X127.45 Y100.45 E.00025 +G1 X127.35 Y100.45 E.00012 +G1 X127.35 Y100.25 E.00025 +G1 X127.25 Y100.25 E.00012 +G1 X127.25 Y100.45 E.00025 +G1 X127.15 Y100.45 E.00012 +G1 X127.15 Y100.25 E.00025 +G1 X127.05 Y100.25 E.00012 +G1 X127.05 Y100.45 E.00025 +G1 X126.95 Y100.45 E.00012 +G1 X126.95 Y100.205 E.00031 +G1 E-.7 F2100 +G1 X129.75 Y105.381 Z1.903 F18000 +G1 Z1.8 F720 +G1 E.7 F1500 +G1 F900 +G1 X129.75 Y104.576 E.001 +G1 X129.65 Y104.576 E.00012 +G1 X129.65 Y105.381 E.001 ;LAYER_CHANGE ;Z:2 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;2 -M201 X3999.93 Y3999.93 +M201 X3999.78 Y3999.78 G1 E-.7 F2100 -G1 X123.994 Y104.115 Z1.8 F18000 -G1 X125.761 Y103.632 Z2 F7050.553 +G1 X129.65 Y105.381 Z1.8 F18000 +G1 X129.368 Y109.368 Z2 F14534.154 ;AFTER_LAYER_CHANGE ;2 -M74 W0.0670353 +M74 W0.203414 -G1 X125.761 Y103.632 F18000 +G1 X129.368 Y109.368 F18000 G1 Z2 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y103.632 E.02055 -G1 X126.368 Y104.322 E.02336 -G1 X126.284 Y104.365 E.00319 -M73 P54 R0 -M73 Q54 S0 -G1 X126.169 Y104.691 E.0117 -G1 X126.168 Y105.2 E.01723 -G1 X126.279 Y105.595 E.01389 -G1 X126.368 Y105.641 E.00339 -G1 X126.368 Y106.368 E.02461 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.811 Y104.521 E.02709 -G1 X123.715 Y104.319 E.00757 -G1 X123.632 Y104.277 E.00315 -G1 X123.632 Y103.632 E.02183 -G1 X124.275 Y103.632 E.02176 -G1 X124.318 Y103.715 E.00316 -G1 X124.52 Y103.811 E.00757 -G1 X124.677 Y103.832 E.00536 -G1 X125.354 Y103.832 E.02292 -G1 X125.686 Y103.734 E.01172 -G1 X125.725 Y103.678 E.00231 -G1 X125.725 Y103.678 F18000 -G1 X125.487 Y103.351 +G1 F1225 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.123 Y105.879 E.00916 +G1 X121.132 Y105.407 E.01598 +G1 X121.126 Y104.146 E.04268 +G1 X120.998 Y103.847 E.01101 +G1 X120.632 Y103.671 E.01375 +G1 X120.632 Y100.632 E.10287 +G1 X121.534 Y100.632 E.03053 +G1 X121.606 Y100.865 E.00825 +G1 X121.866 Y101.081 E.01144 +G1 X122.11 Y101.132 E.00844 +G1 X123.183 Y101.132 E.03632 +G1 X123.378 Y101.1 E.00669 +G1 X123.629 Y100.938 E.01011 +G1 X123.75 Y100.632 E.01114 +G1 X126.221 Y100.632 E.08364 +G1 X126.342 Y100.938 E.01114 +G1 X126.511 Y101.066 E.00718 +G1 X126.788 Y101.132 E.00964 +G1 X127.894 Y101.132 E.03744 +G1 X128.139 Y101.081 E.00847 +G1 X128.399 Y100.865 E.01144 +G1 X128.47 Y100.632 E.00824 +G1 X129.368 Y100.632 E.0304 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.912 Y104.276 E.00541 +G1 X128.868 Y104.628 E.01201 +G1 X128.878 Y105.519 E.03016 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X125.556 Y103.225 E.00486 -G1 X126.775 Y103.225 E.04126 -G1 X126.775 Y104.521 E.04387 -G1 X126.614 Y104.603 E.00612 -M73 P55 R0 -M73 Q55 S0 -G1 X126.584 Y104.664 E.0023 -G2 X126.612 Y105.361 I3.308 J.215 E.02366 -G1 X126.775 Y105.446 E.00622 -G1 X126.775 Y106.775 E.04499 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.386 Y104.559 E.00427 -G1 X123.225 Y104.477 E.00612 -G1 X123.225 Y103.225 E.04238 -G1 X124.476 Y103.225 E.04234 -G1 X124.557 Y103.386 E.0061 -G1 X124.677 Y103.425 E.00427 -G1 X125.277 Y103.425 E.02031 -G1 X125.452 Y103.395 E.00601 -G1 X125.814 Y103.224 F18000 -G1 X125.858 Y104.468 +G1 F1225 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.68 Y104.101 E.00459 +M73 Q22 S5 +G1 X120.521 Y104.025 E.00597 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X121.907 Y100.225 E.05693 +M73 P22 R5 +G1 X121.907 Y100.521 E.01002 +G1 X121.942 Y100.636 E.00407 +G1 X122.11 Y100.725 E.00644 +G1 X123.183 Y100.725 E.03632 +G1 X123.288 Y100.696 E.00369 +G1 X123.387 Y100.521 E.00681 +G1 X123.387 Y100.225 E.01002 +G1 X126.585 Y100.225 E.10825 +G1 X126.585 Y100.521 E.01002 +G1 X126.639 Y100.66 E.00505 +G1 X126.788 Y100.725 E.0055 +G1 X127.894 Y100.725 E.03744 +G1 X128.062 Y100.636 E.00644 +G1 X128.097 Y100.521 E.00407 +G1 X128.097 Y100.225 E.01002 +G1 X129.775 Y100.225 E.0568 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.29 Y104.428 E.00771 +G1 X129.275 Y104.628 E.00679 +G1 X129.289 Y105.482 E.02891 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z2.106 F18000 +G1 Z2 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -M73 P56 R0 -M73 Q56 S0 -G2 X126.022 Y103.978 I-.611 J-.477 E.01783 -G1 X123.978 Y106.022 E.09785 -G1 X126.022 Y106.022 E.06919 -G1 X123.978 Y103.978 E.09785 -G1 X123.978 Y104.089 E.00376 -G1 X124.145 Y104.424 E.01267 +G1 F1225 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.716 Y100.978 E.01036 +G1 X128.702 Y101.026 E.00169 +G1 X129.022 Y101.346 E.01532 +G1 X129.022 Y103.759 E.08168 +G2 X128.533 Y104.355 I.191 J.655 E.02773 +G2 X128.588 Y105.758 I4.186 J.539 E.04775 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.288 Y100.978 E.01049 +G1 X121.301 Y101.023 E.00159 +G1 X120.978 Y101.346 E.01546 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:2.2 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;2.2 -M201 X3999.92 Y3999.92 +M201 X3999.77 Y3999.77 G1 E-.7 F2100 -G1 X124.145 Y104.424 Z2 F18000 -G1 X125.654 Y103.632 Z2.2 F6609.862 +G1 X120.978 Y103.346 Z2 F18000 +G1 X129.368 Y109.368 Z2.2 ;AFTER_LAYER_CHANGE ;2.2 -M74 W0.0705597 +M74 W0.215146 -G1 X125.654 Y103.632 F18000 +G1 X129.368 Y109.368 G1 Z2.2 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y103.632 E.02417 -G1 X126.368 Y104.182 E.01862 -G1 X126.247 Y104.288 E.00545 -G1 X126.168 Y104.589 E.01053 -G1 X126.168 Y105.383 E.02688 -M73 P57 R0 -M73 Q57 S0 -G1 X126.29 Y105.749 E.01306 -G1 X126.368 Y105.788 E.00295 -G1 X126.368 Y106.368 E.01963 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G1 X123.832 Y104.679 E.02173 -G1 X123.738 Y104.352 E.01152 -G1 X123.632 Y104.277 E.0044 -G1 X123.632 Y103.632 E.02183 -G1 X124.361 Y103.632 E.02468 -G1 X124.406 Y103.719 E.00332 -G1 X124.584 Y103.806 E.00671 -G1 X124.76 Y103.832 E.00602 -G1 X125.316 Y103.828 E.01882 -G1 X125.606 Y103.716 E.01052 -G1 X125.624 Y103.681 E.00133 -G1 X125.624 Y103.681 F18000 -G1 X125.384 Y103.353 +G1 F1240 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +M73 Q23 S5 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.156 E.05467 +G1 X120.998 Y103.847 E.01135 +G1 X120.632 Y103.671 E.01375 +M73 P23 R5 +G1 X120.632 Y100.632 E.10287 +G1 X121.685 Y100.632 E.03564 +G1 X121.769 Y100.886 E.00906 +G1 X122.023 Y101.085 E.01092 +G1 X122.259 Y101.132 E.00815 +G1 X123.327 Y101.132 E.03615 +G1 X123.422 Y101.125 E.00322 +G1 X123.664 Y101.03 E.0088 +G1 X123.878 Y100.632 E.0153 +G1 X126.096 Y100.632 E.07508 +G1 X126.31 Y101.03 E.0153 +G1 X126.374 Y101.068 E.00252 +G1 X126.647 Y101.132 E.00949 +G1 X127.492 Y101.132 E.0286 +G1 X127.986 Y101.085 E.0168 +G1 X128.241 Y100.885 E.01097 +G1 X128.324 Y100.632 E.00901 +G1 X129.368 Y100.632 E.03534 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.902 Y104.304 E.00641 +G2 X128.878 Y105.519 I8.102 J.764 E.04117 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X125.45 Y103.225 E.00487 -G1 X126.775 Y103.225 E.04485 -G1 X126.775 Y104.387 E.03933 -G1 X126.617 Y104.466 E.00598 -G1 X126.575 Y104.589 E.0044 -G2 X126.616 Y105.505 I4.532 J.257 E.03109 -M73 P58 R0 -M73 Q58 S0 -G1 X126.775 Y105.584 E.00601 -G1 X126.775 Y106.775 E.04031 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.76 E.01899 -G1 X123.393 Y104.57 E.00652 -G1 X123.225 Y104.477 E.0065 -G1 X123.225 Y103.225 E.04238 -G1 X124.559 Y103.225 E.04515 -G1 X124.642 Y103.387 E.00616 -G1 X124.701 Y103.416 E.00223 -G1 X125.248 Y103.425 E.01852 -G1 X125.346 Y103.393 E.00349 -G1 X125.708 Y103.224 F18000 -G1 X125.824 Y103.99 +G1 F1240 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.68 Y104.101 E.00459 +G1 X120.521 Y104.025 E.00597 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.055 Y100.225 E.06194 +G1 X122.055 Y100.521 E.01002 +G1 X122.096 Y100.643 E.00436 +G1 X122.259 Y100.725 E.00618 +G1 X123.327 Y100.725 E.03615 +G1 X123.403 Y100.71 E.00262 +G1 X123.531 Y100.521 E.00773 +G1 X123.531 Y100.225 E.01002 +G1 X126.443 Y100.225 E.09857 +G1 X126.443 Y100.521 E.01002 +G1 X126.535 Y100.691 E.00654 +G1 X126.647 Y100.725 E.00396 +G1 X127.75 Y100.725 E.03734 +G1 X127.914 Y100.643 E.00621 +G1 X127.954 Y100.521 E.00435 +G1 X127.954 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06164 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.286 Y104.437 E.00212 +G2 X129.289 Y105.482 I12.891 J.488 E.03538 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z2.306 F18000 +G1 Z2.2 F720 +G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G3 X125.252 Y104.178 I-.61 J-.891 E.02065 -G3 X124.183 Y103.992 I-.269 J-1.616 E.03743 -M73 P59 R0 -M73 Q59 S0 -G1 X123.978 Y103.978 E.00696 -G1 X126.022 Y106.022 E.09785 -G1 X123.978 Y106.022 E.06919 -G1 X126.022 Y103.978 E.09785 -G1 X125.845 Y104.375 E.01471 +G1 F1240 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P24 R5 +M73 Q24 S5 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.759 E.08168 +G2 X128.531 Y104.37 I.196 J.66 E.02822 +G2 X128.588 Y105.758 I4.18 J.524 E.04724 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:2.4 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;2.4 -M201 X3999.92 Y3999.92 +M201 X3999.76 Y3999.76 -G1 X125.845 Y104.375 Z2.2 F18000 -G1 X126.868 Y105.006 Z2.4 F4873.872 +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z2.2 F18000 +G1 X129.368 Y109.368 Z2.4 ;AFTER_LAYER_CHANGE ;2.4 -M74 W0.074219 +M74 W0.226893 -G1 X126.868 Y105.006 F18000 -G1 Z2.4 F720 -;TYPE:External perimeter -;WIDTH:0.38292 -G1 F1200 -G1 X126.769 Y105.006 E.0028 -G1 E-.7 F2100 -G1 X126.769 Y105.006 F18000 -G1 X125.808 Y103.632 Z2.429 +G1 X129.368 Y109.368 G1 Z2.4 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -M73 Q60 S0 -G1 X126.368 Y103.632 E.01896 -G1 X126.368 Y104.038 E.01374 -M73 P60 R0 -G1 X126.307 Y104.067 E.00229 -G1 X126.185 Y104.312 E.00926 -G1 X126.168 Y104.895 E.01974 -G1 X126.209 Y105.006 E.00401 -G1 X126.173 Y105.041 E.0017 -G1 X126.168 Y105.521 E.01625 -G1 X126.305 Y105.906 E.01383 -G1 X126.368 Y105.936 E.00236 -G1 X126.368 Y106.368 E.01462 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y105.723 E.02183 -G1 X123.715 Y105.681 E.00315 -G1 X123.832 Y105.321 E.01281 -G2 X123.806 Y104.503 I-10.046 J-.092 E.02771 -G1 X123.713 Y104.316 E.00707 -G1 X123.632 Y104.275 E.00307 -G1 X123.632 Y103.632 E.02176 -G1 X124.212 Y103.632 E.01963 -G1 X124.251 Y103.71 E.00295 -G1 X124.467 Y103.813 E.0081 -G1 X124.679 Y103.832 E.0072 -G1 X125.466 Y103.828 E.02664 -G1 X125.773 Y103.702 E.01123 -G1 X125.781 Y103.686 E.00061 -G1 X125.781 Y103.686 F18000 -G1 X125.536 Y103.355 +G1 F1236 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.129 Y104.167 E.05429 +G1 X121.015 Y103.869 E.0108 +G1 X120.632 Y103.671 E.01459 +G1 X120.632 Y100.632 E.10287 +G1 X121.836 Y100.632 E.04075 +G1 X121.932 Y100.904 E.00976 +G1 X122.163 Y101.081 E.00985 +G1 X122.407 Y101.132 E.00844 +G1 X123.471 Y101.132 E.03602 +G1 X123.608 Y101.116 E.00467 +G1 X123.834 Y101.012 E.00842 +G1 X124.026 Y100.632 E.01441 +G1 X125.951 Y100.632 E.06516 +G1 X126.143 Y101.012 E.01441 +G1 X126.239 Y101.071 E.00381 +G1 X126.506 Y101.132 E.00927 +G1 X127.492 Y101.132 E.03337 +G1 X127.835 Y101.088 E.01171 +G1 X128.084 Y100.903 E.0105 +G1 X128.179 Y100.632 E.00972 +G1 X129.368 Y100.632 E.04025 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.893 Y104.33 E.00733 +G2 X128.878 Y105.519 I10.324 J.724 E.04027 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X125.599 Y103.225 E.00489 -G1 X126.775 Y103.225 E.03981 -G1 X126.775 Y104.253 E.0348 -M73 P61 R0 -M73 Q61 S0 -G1 X126.621 Y104.326 E.00577 -G1 X126.581 Y104.408 E.00309 -G1 X126.575 Y104.895 E.01649 -G1 X126.672 Y104.951 E.00379 -;WIDTH:0.41646 -G1 X126.769 Y105.006 E.00346 -G1 X126.673 Y105.048 E.00325 -;WIDTH:0.449999 -G1 X126.577 Y105.091 E.00356 -G1 X126.575 Y105.521 E.01456 -G1 X126.621 Y105.649 E.0046 -G1 X126.775 Y105.723 E.00578 -G1 X126.775 Y106.775 E.03561 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.523 E.04238 -G1 X123.386 Y105.441 E.00612 -G1 X123.425 Y105.321 E.00427 -G1 X123.425 Y104.679 E.02173 -G1 X123.385 Y104.558 E.00431 -G1 X123.225 Y104.477 E.00607 -G1 X123.225 Y103.225 E.04238 -G1 X124.416 Y103.225 E.04031 -G1 X124.496 Y103.384 E.00602 -G1 X124.618 Y103.425 E.00436 -G1 X125.397 Y103.425 E.02637 -G1 X125.494 Y103.392 E.00347 -M73 P62 R0 -M73 Q62 S0 -G1 X125.857 Y103.224 F18000 -G1 E-.7 F2100 -G1 X124.144 Y104.432 Z2.437 F18000 +G1 F1236 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +M73 Q25 S5 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.686 Y104.109 E.00427 +G1 X120.521 Y104.025 E.00627 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.204 Y100.225 E.06699 +M73 P25 R5 +G1 X122.204 Y100.521 E.01002 +G1 X122.249 Y100.649 E.00459 +G1 X122.407 Y100.725 E.00593 +G1 X123.471 Y100.725 E.03602 +G1 X123.549 Y100.71 E.00269 +G1 X123.675 Y100.521 E.00769 +G1 X123.675 Y100.225 E.01002 +G1 X126.302 Y100.225 E.08892 +G1 X126.302 Y100.521 E.01002 +G1 X126.385 Y100.685 E.00622 +G1 X126.506 Y100.725 E.00431 +G1 X127.607 Y100.725 E.03727 +G1 X127.766 Y100.649 E.00597 +G1 X127.811 Y100.521 E.00459 +G1 X127.811 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06648 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.283 Y104.446 E.00244 +G2 X129.289 Y105.482 I15.373 J.433 E.03507 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X129.022 Y103.346 Z2.512 F18000 G1 Z2.4 F720 G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G3 X123.978 Y103.978 I.674 J-.503 E.0166 -G1 X125.974 Y105.974 E.09555 -G3 X125.834 Y105.005 I1.453 J-.704 E.03367 -G3 X125.902 Y104.098 I1.832 J-.319 E.0311 -G1 X123.978 Y106.022 E.0921 -G1 X125.838 Y106.022 E.06296 +G1 F1236 +G1 X129.022 Y101.346 E.0677 +G1 X128.654 Y100.978 E.01762 +G1 X129.022 Y100.978 E.01246 +G1 X120.978 Y109.022 E.38506 +G1 X120.978 Y108.654 E.01246 +G1 X121.346 Y109.022 E.01762 +G1 X128.654 Y109.022 E.24737 +G1 X129.022 Y108.654 E.01762 +M73 P25 R4 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:2.6 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;2.6 -M201 X3999.92 Y3999.92 +M201 X3999.74 Y3999.74 G1 E-.7 F2100 -M73 P63 R0 -M73 Q63 S0 -G1 X125.838 Y106.022 Z2.4 F18000 -G1 X125.021 Y103.121 Z2.6 F11136.775 +G1 X120.978 Y103.346 Z2.4 F18000 +G1 X129.368 Y109.368 Z2.6 ;AFTER_LAYER_CHANGE ;2.6 -M74 W0.0778736 +M74 W0.238595 -G1 X125.021 Y103.121 F18000 +G1 X129.368 Y109.368 G1 Z2.6 F720 G1 E.7 F1500 -;TYPE:External perimeter -;WIDTH:0.38292 -G1 F1200 -G1 X125.021 Y103.2 E.00223 -G1 X125.021 Y103.23 E.00085 -G1 X125.021 Y103.23 F18000 -G1 X125.965 Y103.639 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X125.968 Y103.632 E.00026 -G1 X126.368 Y103.632 E.01354 -G1 X126.368 Y103.894 E.00887 -G1 X126.325 Y103.913 E.00159 -G1 X126.188 Y104.168 E.0098 -G1 X126.168 Y104.322 E.00526 -G1 X126.188 Y104.925 E.02042 -G1 X126.277 Y105.007 E.0041 -G1 X126.188 Y105.089 E.0041 -G1 X126.168 Y105.659 E.01931 -G1 X126.322 Y106.065 E.0147 -G1 X126.368 Y106.086 E.00171 -G1 X126.368 Y106.368 E.00955 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y106.302 E.00223 -G1 X123.832 Y105.821 E.01763 -G1 X123.832 Y104.179 E.05558 -G1 X123.77 Y103.911 E.00931 -G1 X123.652 Y103.745 E.00689 -G1 X123.632 Y103.632 E.00388 -G1 X124.051 Y103.632 E.01418 -G1 X124.072 Y103.68 E.00177 -M73 Q64 S0 -G1 X124.314 Y103.81 E.0093 -M73 P64 R0 -G1 X124.922 Y103.832 E.02059 -G1 X125.021 Y103.796 E.00357 -G1 X125.053 Y103.828 E.00153 -G1 X125.546 Y103.832 E.01669 -G1 X125.765 Y103.791 E.00754 -G1 X125.936 Y103.69 E.00672 -G1 X125.936 Y103.69 F18000 -G1 X125.687 Y103.356 +G1 F1240 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 Q26 S5 +G1 X121.017 Y106.128 E.01464 +M73 P26 R4 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.122 Y104.119 E.05592 +G1 X120.999 Y103.848 E.01007 +G1 X120.632 Y103.671 E.01379 +G1 X120.632 Y100.632 E.10287 +G1 X121.986 Y100.632 E.04583 +G1 X122.093 Y100.92 E.0104 +G1 X122.296 Y101.074 E.00862 +G1 X122.556 Y101.132 E.00902 +G1 X123.615 Y101.132 E.03585 +G1 X123.803 Y101.102 E.00644 +G1 X124.007 Y100.989 E.00789 +G1 X124.174 Y100.632 E.01334 +G1 X125.806 Y100.632 E.05524 +G1 X125.973 Y100.989 E.01334 +G1 X126.107 Y101.075 E.00539 +G1 X126.365 Y101.132 E.00894 +G1 X127.464 Y101.132 E.0372 +G1 X127.689 Y101.089 E.00775 +G1 X127.928 Y100.918 E.00995 +G1 X128.034 Y100.632 E.01032 +G1 X129.368 Y100.632 E.04515 +G1 X129.368 Y103.949 E.11228 +G1 X128.991 Y104.137 E.01426 +G1 X128.886 Y104.357 E.00825 +G2 X128.878 Y105.519 I13.187 J.668 E.03935 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X125.748 Y103.225 E.00489 -G1 X126.775 Y103.225 E.03476 -G1 X126.775 Y104.12 E.03029 -G1 X126.627 Y104.185 E.00547 -G1 X126.582 Y104.271 E.00329 -G1 X126.575 Y104.771 E.01693 -G1 X126.775 Y105.007 E.01047 -G1 X126.582 Y105.192 E.00905 -G1 X126.575 Y105.659 E.01581 -G1 X126.626 Y105.795 E.00492 -G1 X126.775 Y105.861 E.00552 -G1 X126.775 Y106.775 E.03094 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y106.024 E.02542 -G1 X123.365 Y105.966 E.00513 -G1 X123.425 Y105.821 E.00531 -G1 X123.425 Y104.179 E.05558 -G1 X123.365 Y104.034 E.00531 -M73 P65 R0 -M73 Q65 S0 -G1 X123.225 Y103.976 E.00513 -G1 X123.225 Y103.225 E.02542 -G1 X124.273 Y103.225 E.03547 -G1 X124.341 Y103.374 E.00554 -G1 X124.422 Y103.418 E.00312 -G1 X124.922 Y103.425 E.01693 -G1 X124.972 Y103.328 E.00369 -;WIDTH:0.41646 -G1 X125.021 Y103.23 E.0034 -G1 X125.06 Y103.327 E.00325 -;WIDTH:0.449999 -G1 X125.098 Y103.424 E.00353 -G1 X125.546 Y103.425 E.01516 -G1 X125.643 Y103.389 E.0035 -G1 X126.007 Y103.224 F18000 +G1 F1240 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.693 Y104.119 E.00388 +G1 X120.521 Y104.025 E.00663 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.352 Y100.225 E.072 +G1 X122.352 Y100.521 E.01002 +G1 X122.402 Y100.654 E.00481 +G1 X122.556 Y100.725 E.00574 +G1 X123.615 Y100.725 E.03585 +G1 X123.701 Y100.706 E.00298 +G1 X123.819 Y100.521 E.00743 +G1 X123.819 Y100.225 E.01002 +G1 X126.161 Y100.225 E.07927 +G1 X126.161 Y100.521 E.01002 +G1 X126.234 Y100.677 E.00583 +G1 X126.365 Y100.725 E.00472 +G1 X127.464 Y100.725 E.0372 +G1 X127.619 Y100.654 E.00577 +G1 X127.667 Y100.521 E.00479 +G1 X127.667 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07135 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.281 Y104.455 E.00274 +G2 X129.289 Y105.482 I17.831 J.373 E.03477 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 G1 E-.7 F2100 -G1 X124.32 Y104.159 Z2.634 F18000 +G1 X123.346 Y109.022 Z2.706 F18000 G1 Z2.6 F720 G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G1 X125.027 Y104.166 E.02393 -G2 X125.846 Y104.126 I.299 J-2.268 E.02791 -G1 X124.178 Y105.822 E.08052 -G1 X124.178 Y104.178 E.05565 -G1 X125.905 Y105.905 E.08267 -M73 Q66 S0 -G1 X125.95 Y106.022 E.00424 -M73 P66 R0 -G1 X124.111 Y106.012 E.06225 +G1 F1240 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P27 R4 +M73 Q27 S5 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.759 E.08168 +G2 X128.55 Y104.267 I.182 J.642 E.02468 +G2 X128.588 Y105.758 I4.068 J.642 E.05076 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +M73 Q27 S4 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:2.8 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;2.8 -M201 X3999.91 Y3999.91 +M201 X3999.73 Y3999.73 G1 E-.7 F2100 -G1 X124.111 Y106.012 Z2.6 F18000 -G1 X126.084 Y103.61 Z2.8 F11462.502 +G1 X120.978 Y103.346 Z2.6 F18000 +G1 X129.368 Y109.368 Z2.8 ;AFTER_LAYER_CHANGE ;2.8 -M74 W0.0815626 +M74 W0.250343 -G1 X126.084 Y103.61 F18000 +G1 X129.368 Y109.368 G1 Z2.8 F720 G1 E.7 F1500 ;TYPE:Perimeter -;WIDTH:0.395439 -G1 F1200 -G1 X126.105 Y103.589 E.00087 -;WIDTH:0.364268 -G1 X126.411 Y103.589 E.00818 -G1 X126.411 Y103.705 E.0031 -;WIDTH:0.395439 -G1 X126.306 Y103.865 E.00561 -;WIDTH:0.42661 -G1 X126.202 Y104.025 E.00609 -;WIDTH:0.449999 -G1 X126.168 Y104.188 E.00564 -G1 X126.18 Y104.764 E.0195 -G1 X126.33 Y105.007 E.00967 -G1 X126.181 Y105.25 E.00965 -G1 X126.168 Y105.798 E.01855 -G1 X126.368 Y106.237 E.01633 -G1 X126.368 Y106.368 E.00443 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y106.044 E.01097 -M73 Q67 S0 -G1 X123.683 Y106.021 E.00189 -M73 P67 R0 -G1 X123.832 Y105.621 E.01445 -G1 X123.832 Y104.783 E.02837 -G1 X123.8 Y104.185 E.02027 -;WIDTH:0.448873 -G1 X123.678 Y103.98 E.00805 -;WIDTH:0.44111 -G1 X123.628 Y103.956 E.00184 -;WIDTH:0.440966 -G1 X123.628 Y103.628 E.01086 -G1 X123.909 Y103.628 E.0093 -G1 X123.929 Y103.674 E.00166 -;WIDTH:0.448873 -G1 X124.162 Y103.807 E.00906 -;WIDTH:0.449999 -G1 X124.379 Y103.832 E.00739 -G1 X124.941 Y103.811 E.01904 -G1 X125.026 Y103.72 E.00421 -G1 X125.112 Y103.811 E.00424 -G1 X125.187 Y103.826 E.00259 -G1 X125.695 Y103.832 E.0172 -G1 X125.922 Y103.776 E.00791 -;WIDTH:0.42661 -G1 X126.013 Y103.682 E.00417 -;WIDTH:0.395439 -G1 X126.042 Y103.653 E.0012 -G1 X126.042 Y103.653 F18000 -G1 X125.84 Y103.358 -;TYPE:External perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X125.897 Y103.225 E.0049 -G1 X126.775 Y103.225 E.02972 -G1 X126.775 Y103.986 E.02576 -G1 X126.635 Y104.044 E.00513 -G1 X126.583 Y104.133 E.00349 -G1 X126.575 Y104.641 E.0172 -G1 X126.668 Y104.812 E.00659 -G1 X126.775 Y104.843 E.00377 -G1 X126.775 Y105.172 E.01114 -G1 X126.668 Y105.203 E.00377 -G1 X126.587 Y105.304 E.00438 -G1 X126.575 Y105.798 E.01673 -G1 X126.634 Y105.941 E.00524 -G1 X126.775 Y106 E.00517 -G1 X126.775 Y106.775 E.02623 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.823 E.03222 -M73 P68 R0 -M73 Q68 S0 -G1 X123.375 Y105.755 E.00557 -G1 X123.425 Y105.621 E.00484 -G1 X123.425 Y104.379 E.04204 -G1 X123.375 Y104.245 E.00484 -G1 X123.225 Y104.177 E.00557 -G1 X123.225 Y103.225 E.03222 -G1 X124.131 Y103.225 E.03067 -G1 X124.197 Y103.373 E.00549 -G1 X124.333 Y103.425 E.00493 -G1 X124.835 Y103.418 E.01699 -G1 X124.986 Y103.225 E.00829 -G1 X125.026 Y103.225 E.00135 -G1 X125.217 Y103.418 E.00919 -G1 X125.695 Y103.425 E.01618 -G1 X125.791 Y103.387 E.00349 -G1 X126.156 Y103.223 F18000 -G1 E-.7 F2100 -G1 X125.898 Y106.01 Z2.849 F18000 +G1 F1241 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.131 Y104.19 E.05351 +G1 X121.03 Y103.891 E.01068 +G1 X120.632 Y103.671 E.01539 +G1 X120.632 Y100.632 E.10287 +G1 X122.137 Y100.632 E.05094 +G1 X122.254 Y100.934 E.01096 +G1 X122.431 Y101.068 E.00751 +G1 X122.704 Y101.132 E.00949 +G1 X123.759 Y101.132 E.03571 +G1 X123.927 Y101.108 E.00574 +G1 X124.185 Y100.959 E.01008 +G1 X124.323 Y100.632 E.01201 +G1 X125.66 Y100.632 E.04526 +G1 X125.798 Y100.959 E.01201 +G1 X125.976 Y101.08 E.00729 +G1 X126.224 Y101.132 E.00858 +G1 X127.321 Y101.132 E.03713 +G1 X127.56 Y101.083 E.00826 +G1 X127.772 Y100.932 E.00881 +G1 X127.889 Y100.632 E.0109 +G1 X129.368 Y100.632 E.05006 +G1 X129.368 Y103.949 E.11228 +G1 X128.99 Y104.137 E.01429 +G1 X128.88 Y104.383 E.00912 +G2 X128.878 Y105.519 I16.794 J.595 E.03846 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1241 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +M73 Q28 S4 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.699 Y104.13 I-27.335 J-.395 E.05556 +M73 P28 R4 +G1 X120.521 Y104.025 E.007 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.501 Y100.225 E.07704 +G1 X122.501 Y100.521 E.01002 +G1 X122.554 Y100.659 E.005 +G1 X122.704 Y100.725 E.00555 +G1 X123.759 Y100.725 E.03571 +G1 X123.865 Y100.695 E.00373 +G1 X123.963 Y100.521 E.00676 +G1 X123.963 Y100.225 E.01002 +G1 X126.02 Y100.225 E.06963 +G1 X126.02 Y100.521 E.01002 +G1 X126.082 Y100.667 E.00537 +G1 X126.224 Y100.725 E.00519 +G1 X127.321 Y100.725 E.03713 +G1 X127.471 Y100.658 E.00556 +G1 X127.524 Y100.521 E.00497 +G1 X127.524 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07619 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.316 Y104.382 E.00616 +G1 X129.279 Y104.464 E.00305 +G1 X129.289 Y105.482 E.03446 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z2.906 F18000 G1 Z2.8 F720 G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G1 X124.04 Y106.022 E.06289 -G1 X124.076 Y105.924 E.00353 -M73 Q69 S0 -G1 X125.822 Y104.178 E.08358 -G2 X125.901 Y105.007 I1.772 J.25 E.02845 -M73 P69 R0 -G2 X125.822 Y105.822 I1.837 J.59 E.02793 -G1 X124.142 Y104.151 E.08021 -G3 X124.178 Y105.647 I-10.184 J.994 E.0507 -G1 X124.146 Y105.733 E.00311 +G1 F1241 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.759 E.08168 +G2 X128.522 Y104.497 I.21 J.68 E.0326 +G1 X128.522 Y105.431 E.03161 +G2 X129.022 Y106.14 I.703 J.035 E.03164 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:3 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;3 -M201 X3999.91 Y3999.91 +M201 X3999.72 Y3999.72 G1 E-.7 F2100 -G1 X124.146 Y105.733 Z2.8 F18000 -G1 X126.275 Y103.679 Z3 F10943.52 +G1 X120.978 Y103.346 Z2.8 F18000 +G1 X129.368 Y109.368 Z3 ;AFTER_LAYER_CHANGE ;3 -M74 W0.0852567 +M74 W0.262091 -G1 X126.275 Y103.679 F18000 +G1 X129.368 Y109.368 G1 Z3 F720 G1 E.7 F1500 ;TYPE:Perimeter -;WIDTH:0.485096 -G1 F1200 -G1 X126.188 Y103.871 E.00775 -;WIDTH:0.46487 -G1 X126.168 Y104.054 E.00646 -;WIDTH:0.449999 -G1 X126.177 Y104.616 E.01903 -G1 X126.368 Y105.008 E.01476 -G1 X126.22 Y105.26 E.00989 -G1 X126.178 Y105.397 E.00485 -G1 X126.168 Y105.936 E.01825 -M73 Q70 S0 -G1 X126.362 Y106.368 E.01603 -G1 X123.632 Y106.368 E.09241 -M73 P70 R0 -G1 X123.632 Y105.83 E.01821 -G1 X123.706 Y105.793 E.0028 -G1 X123.832 Y105.421 E.01329 -G1 X123.832 Y104.989 E.01462 -G1 X123.799 Y104.38 E.02064 -G1 X123.69 Y104.187 E.0075 -G1 X123.632 Y104.16 E.00217 -G1 X123.632 Y103.632 E.01787 -G1 X123.707 Y103.632 E.00254 -G1 X123.973 Y103.792 E.01051 -G1 X124.19 Y103.832 E.00747 -G1 X124.773 Y103.818 E.01974 -G1 X125.031 Y103.659 E.01026 -G1 X125.291 Y103.818 E.01032 -G1 X125.844 Y103.832 E.01872 -;WIDTH:0.46487 -G1 X126.093 Y103.787 E.00888 -;WIDTH:0.485096 -G1 X126.223 Y103.709 E.00557 -G1 X126.223 Y103.709 F18000 -G1 X125.989 Y103.358 -;TYPE:External perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.046 Y103.225 E.0049 -G1 X126.775 Y103.225 E.02468 -G1 X126.775 Y103.852 E.02122 -G1 X126.644 Y103.901 E.00473 -G1 X126.584 Y103.994 E.00375 -G1 X126.575 Y104.51 E.01747 -G1 X126.632 Y104.65 E.00512 -G1 X126.775 Y104.712 E.00528 -G1 X126.775 Y105.304 E.02004 -G1 X126.632 Y105.365 E.00526 -G1 X126.592 Y105.424 E.00241 -M73 Q71 S0 -G1 X126.575 Y105.936 E.01734 -G1 X126.643 Y106.088 E.00564 -G1 X126.775 Y106.139 E.00479 -G1 X126.775 Y106.775 E.02153 -M73 P71 R0 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.623 E.03899 -G1 X123.383 Y105.545 E.00596 -G1 X123.425 Y105.421 E.00443 -G1 X123.425 Y104.641 E.0264 -G1 X123.378 Y104.448 E.00672 -G1 X123.225 Y104.377 E.00571 -G1 X123.225 Y103.225 E.03899 -G1 X123.988 Y103.225 E.02583 -G1 X124.047 Y103.366 E.00517 -G1 X124.118 Y103.412 E.00286 -G1 X124.641 Y103.425 E.01771 -G1 X124.806 Y103.34 E.00628 -G1 X124.844 Y103.225 E.0041 -G1 X125.219 Y103.225 E.01269 -G1 X125.257 Y103.34 E.0041 -G1 X125.349 Y103.412 E.00395 -G1 X125.844 Y103.425 E.01676 -G1 X125.94 Y103.387 E.00349 -G1 X126.305 Y103.224 F18000 -G1 E-.7 F2100 -G1 X124.182 Y106.022 Z3.061 F18000 +G1 F1240 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 Q29 S4 +G1 X121.017 Y106.128 E.01464 +M73 P29 R4 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.072 Y103.964 E.0092 +G1 X121.001 Y103.85 E.00455 +G1 X120.632 Y103.672 E.01387 +G1 X120.632 Y100.632 E.1029 +G1 X122.287 Y100.632 E.05602 +G1 X122.414 Y100.947 E.0115 +G1 X122.719 Y101.117 E.01182 +G1 X122.852 Y101.132 E.00453 +G1 X123.903 Y101.132 E.03558 +G1 X124.136 Y101.086 E.00804 +G1 X124.366 Y100.92 E.0096 +G1 X124.473 Y100.632 E.0104 +G1 X125.513 Y100.632 E.0352 +G1 X125.62 Y100.92 E.0104 +G1 X125.849 Y101.086 E.00957 +G1 X126.083 Y101.132 E.00807 +G1 X127.177 Y101.132 E.03703 +G1 X127.43 Y101.078 E.00876 +G1 X127.618 Y100.944 E.00781 +G1 X127.744 Y100.632 E.01139 +G1 X129.368 Y100.632 E.05497 +G1 X129.368 Y103.949 E.11228 +G1 X128.99 Y104.138 E.01431 +G1 X128.876 Y104.408 E.00992 +G2 X128.878 Y105.519 I21.118 J.504 E.03761 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1240 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.705 Y104.14 I-35.718 J-.375 E.05522 +G1 X120.521 Y104.025 E.00734 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.649 Y100.225 E.08205 +G1 X122.649 Y100.521 E.01002 +G1 X122.706 Y100.663 E.00518 +G1 X122.852 Y100.725 E.00537 +G1 X123.903 Y100.725 E.03558 +G1 X124.009 Y100.695 E.00373 +G1 X124.107 Y100.521 E.00676 +G1 X124.107 Y100.225 E.01002 +G1 X125.879 Y100.225 E.05998 +G1 X125.879 Y100.521 E.01002 +G1 X125.928 Y100.654 E.0048 +G1 X126.083 Y100.725 E.00577 +G1 X127.177 Y100.725 E.03703 +G1 X127.324 Y100.662 E.00541 +G1 X127.381 Y100.521 E.00515 +G1 X127.381 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08103 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.302 Y104.402 E.0069 +G2 X129.289 Y105.482 I8.131 J.639 E.03659 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z3.106 F18000 G1 Z3 F720 -M73 P72 R0 -M73 Q72 S0 G1 E.7 F1500 ;TYPE:Internal infill ;WIDTH:0.45 -G1 F1200 -G1 X125.847 Y106.022 E.05636 -G1 X125.822 Y105.822 E.00682 -G1 X124.178 Y104.178 E.0787 -G2 X125.031 Y104.085 I.249 J-1.675 E.02936 -G2 X125.822 Y104.178 I.571 J-1.436 E.02727 -G1 X123.978 Y106.022 E.08827 -G1 X124.182 Y106.022 E.00691 +G1 F1240 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P30 R4 +M73 Q30 S4 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.759 E.08168 +G2 X128.522 Y104.498 I.213 J.683 E.03261 +G1 X128.522 Y105.431 E.03158 +G2 X129.022 Y106.14 I.703 J.035 E.03164 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:3.2 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;3.2 -M201 X3999.9 Y3999.9 +M201 X3999.71 Y3999.71 G1 E-.7 F2100 -G1 X124.182 Y106.022 Z3 F18000 -G1 X126.19 Y103.791 Z3.2 F11091.904 +G1 X120.978 Y103.346 Z3 F18000 +G1 X129.368 Y109.368 Z3.2 ;AFTER_LAYER_CHANGE ;3.2 -M74 W0.088802 +M74 W0.273838 -G1 X126.19 Y103.791 F18000 +G1 X129.368 Y109.368 G1 Z3.2 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -M73 Q73 S0 -G1 X126.168 Y104.007 E.00735 -G1 X126.182 Y104.51 E.01703 -G1 X126.28 Y104.731 E.00818 -M73 P73 R0 -G1 X126.368 Y104.777 E.00336 -G1 X126.368 Y105.24 E.01567 -G1 X126.28 Y105.285 E.00335 -G1 X126.189 Y105.48 E.00728 -G1 X126.168 Y106.075 E.02015 -G1 X126.314 Y106.368 E.01108 -G1 X123.632 Y106.368 E.09078 -G1 X123.632 Y105.617 E.02542 -G1 X123.723 Y105.57 E.00347 -G1 X123.832 Y105.221 E.01238 -G1 X123.832 Y104.779 E.01496 -G1 X123.727 Y104.436 E.01214 -G1 X123.632 Y104.371 E.0039 -G1 X123.632 Y103.645 E.02457 -G1 X124.012 Y103.831 E.01432 -G1 X124.5 Y103.832 E.01652 -G1 X124.753 Y103.777 E.00876 -G1 X124.923 Y103.632 E.00756 -G1 X125.151 Y103.632 E.00772 -G1 X125.174 Y103.683 E.00189 -G1 X125.467 Y103.823 E.01099 -G1 X125.993 Y103.832 E.01781 -G1 X126.131 Y103.804 E.00477 -G1 X126.131 Y103.804 F18000 -G1 X126.127 Y103.414 +G1 F1240 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.086 Y103.995 E.00807 +G1 X121.002 Y103.852 E.00561 +G1 X120.632 Y103.672 E.01393 +G1 X120.632 Y100.632 E.1029 +G1 X122.437 Y100.632 E.0611 +G1 X122.574 Y100.958 E.01197 +G1 X122.836 Y101.109 E.01024 +G1 X123.001 Y101.132 E.00564 +G1 X124.047 Y101.132 E.03541 +G1 X124.321 Y101.067 E.00953 +G1 X124.551 Y100.866 E.01034 +G1 X124.624 Y100.632 E.0083 +G1 X125.365 Y100.632 E.02508 +G1 X125.437 Y100.866 E.00829 +G1 X125.668 Y101.067 E.01036 +G1 X125.941 Y101.132 E.0095 +G1 X127.034 Y101.132 E.037 +G1 X127.298 Y101.072 E.00916 +G1 X127.464 Y100.955 E.00687 +G1 X127.599 Y100.632 E.01185 +G1 X129.368 Y100.632 E.05988 +G1 X129.368 Y103.949 E.11228 +G1 X128.989 Y104.139 E.01435 +G1 X128.872 Y104.432 E.01068 +G1 X128.878 Y105.519 E.03679 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -;WIDTH:0.570578 -G1 F1200 -G1 X126.174 Y103.413 E.00206 -G1 X126.235 Y103.285 E.00622 -;WIDTH:0.569467 -G1 X126.715 Y103.285 E.02102 -G1 X126.715 Y103.678 E.01721 -;WIDTH:0.570578 -G1 X126.567 Y103.763 E.00749 -M73 Q74 S0 -G1 X126.57 Y103.815 E.00229 -;WIDTH:0.530385 -G1 X126.572 Y103.868 E.00215 -;WIDTH:0.490192 -G1 X126.575 Y103.92 E.00194 -;WIDTH:0.449999 -G1 X126.595 Y104.467 E.01853 -G1 X126.775 Y104.581 E.00721 -G1 X126.775 Y105.436 E.02894 -G1 X126.612 Y105.52 E.00621 -M73 P74 R0 -G1 X126.587 Y105.568 E.00183 -G1 X126.575 Y106.075 E.01717 -G1 X126.656 Y106.237 E.00613 -G1 X126.775 Y106.277 E.00425 -G1 X126.775 Y106.775 E.01686 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y105.423 E.04576 -G1 X123.389 Y105.338 E.00625 -G1 X123.425 Y105.221 E.00414 -G1 X123.425 Y104.955 E.009 -G1 X123.39 Y104.664 E.00992 -G1 X123.225 Y104.577 E.00631 -G1 X123.225 Y103.225 E.04576 -G1 X123.846 Y103.225 E.02102 -G1 X123.895 Y103.356 E.00473 -G1 X124.048 Y103.425 E.00568 -G1 X124.584 Y103.407 E.01815 -G1 X124.702 Y103.225 E.00734 -G1 X125.372 Y103.225 E.02268 -G1 X125.44 Y103.375 E.00557 -M73 Q75 S0 -G1 X125.621 Y103.425 E.00636 -;WIDTH:0.490192 -G1 X125.805 Y103.421 E.00684 -;WIDTH:0.530385 -G1 X125.99 Y103.417 E.0075 -;WIDTH:0.570578 -G1 X126.067 Y103.416 E.00338 -G1 X126.445 Y103.285 F18000 -G1 E-.7 F2100 -G1 X125.039 Y104.005 Z3.228 F18000 +G1 F1240 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +M73 P31 R4 +M73 Q31 S4 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.71 Y104.151 I-47.579 J-.357 E.05484 +G1 X120.521 Y104.025 E.00769 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.797 Y100.225 E.08706 +G1 X122.797 Y100.521 E.01002 +G1 X122.859 Y100.667 E.00537 +G1 X123.001 Y100.725 E.00519 +G1 X124.047 Y100.725 E.03541 +G1 X124.169 Y100.685 E.00435 +G1 X124.251 Y100.521 E.00621 +G1 X124.251 Y100.225 E.01002 +G1 X125.738 Y100.225 E.05033 +G1 X125.738 Y100.521 E.01002 +G1 X125.82 Y100.685 E.00621 +G1 X125.941 Y100.725 E.00431 +G1 X127.034 Y100.725 E.037 +G1 X127.177 Y100.666 E.00524 +G1 X127.238 Y100.521 E.00532 +G1 X127.238 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08587 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.297 Y104.412 E.00722 +G2 X129.289 Y105.482 I9.388 J.605 E.03624 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z3.306 F18000 G1 Z3.2 F720 -M73 P75 R0 G1 E.7 F1500 -;TYPE:Solid infill -;WIDTH:0.449999 -G1 F1200 -G1 X125.152 Y104.078 E.00455 -G2 X125.828 Y104.178 I.605 J-1.748 E.02326 -G1 X125.856 Y104.619 E.01496 -G1 X126.022 Y104.948 E.01247 -G3 X125.849 Y105.433 I-1.641 J-.312 E.0175 -G1 X125.829 Y106.022 E.01995 -G1 X123.978 Y106.022 E.06265 -G3 X124.034 Y105.704 I.552 J-.067 E.01109 -G1 X124.178 Y105.174 E.01859 -G1 X124.163 Y104.642 E.01801 -G1 X123.978 Y104.171 E.01713 -G2 X124.813 Y104.117 I.229 J-2.945 E.02842 -G1 X124.857 Y104.096 E.00165 -G1 X125.026 Y104.478 E.01414 -G1 X125.435 Y104.569 E.01418 -G1 X125.581 Y105.008 E.01566 -G1 X125.462 Y105.283 E.01014 -G1 X125.435 Y105.615 E.01127 -G1 X124.488 Y105.615 E.03205 -M73 Q76 S0 -G1 X124.585 Y105.172 E.01535 -G1 X124.571 Y104.596 E.0195 -M73 P76 R0 -G1 X124.829 Y104.53 E.00901 -G1 X125.125 Y104.982 E.01829 -;WIDTH:0.586528 -G2 X125.107 Y105.048 I-.063 J.019 E.01525 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1240 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.759 E.08168 +G2 X128.522 Y104.5 I.216 J.686 E.03266 +G1 X128.522 Y105.431 E.03151 +G2 X129.022 Y106.14 I.703 J.035 E.03164 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:3.4 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;3.4 -M201 X3999.9 Y3999.9 +M201 X3999.69 Y3999.69 G1 E-.7 F2100 -G1 X125.107 Y105.048 Z3.2 F18000 -G1 X126.168 Y103.831 Z3.4 F6300.572 +G1 X120.978 Y103.346 Z3.2 F18000 +G1 X129.368 Y109.368 Z3.4 ;AFTER_LAYER_CHANGE ;3.4 -M74 W0.0927406 +M74 W0.285586 -G1 X126.168 Y103.831 F18000 +G1 X129.368 Y109.368 G1 Z3.4 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.168 Y104.248 E.01411 -G1 X126.23 Y104.517 E.00934 -G1 X126.368 Y104.619 E.00581 -G1 X126.368 Y105.399 E.0264 -G1 X126.247 Y105.469 E.00473 -G1 X126.168 Y105.77 E.01053 -G1 X126.168 Y106.213 E.015 -G1 X126.255 Y106.368 E.00602 -G1 X123.632 Y106.368 E.08879 -G1 X123.632 Y103.693 E.09055 -G1 X123.906 Y103.832 E.0104 -M73 Q77 S0 -G1 X124.358 Y103.832 E.0153 -G1 X124.624 Y103.771 E.00924 -M73 P77 R0 -G1 X124.744 Y103.632 E.00622 -G1 X125.339 Y103.632 E.02014 -G1 X125.396 Y103.736 E.00401 -G1 X125.581 Y103.815 E.00681 -G1 X126.108 Y103.829 E.01784 -G1 X126.295 Y103.364 F18000 +G1 F1240 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 P32 R4 +M73 Q32 S4 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.098 Y104.026 E.00697 +G1 X121.003 Y103.854 E.00665 +G1 X120.632 Y103.672 E.01399 +G1 X120.632 Y100.632 E.1029 +G1 X122.587 Y100.632 E.06617 +G1 X122.733 Y100.968 E.0124 +G1 X122.955 Y101.1 E.00874 +G1 X123.149 Y101.132 E.00666 +G1 X124.191 Y101.132 E.03527 +G1 X124.301 Y101.122 E.00374 +G1 X124.559 Y101.009 E.00953 +G1 X124.739 Y100.792 E.00954 +G1 X124.776 Y100.632 E.00556 +G1 X125.216 Y100.632 E.01489 +G1 X125.253 Y100.792 E.00556 +G1 X125.432 Y101.009 E.00952 +G1 X125.543 Y101.075 E.00437 +G1 X125.8 Y101.132 E.00891 +G1 X126.891 Y101.132 E.03693 +G1 X126.954 Y101.129 E.00213 +G1 X127.31 Y100.965 E.01327 +G1 X127.454 Y100.632 E.01228 +G1 X129.368 Y100.632 E.06479 +G1 X129.368 Y103.949 E.11228 +G1 X128.988 Y104.14 E.0144 +G1 X128.87 Y104.456 E.01142 +G1 X128.878 Y105.519 E.03598 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -;WIDTH:0.473996 -G1 F1200 -G1 X126.353 Y103.237 E.005 -G1 X126.763 Y103.237 E.0147 -G1 X126.763 Y103.575 E.01212 -G1 X126.632 Y103.631 E.00511 -;WIDTH:0.47007 -G1 X126.574 Y103.787 E.00591 -;WIDTH:0.452967 -G1 X126.591 Y104.327 E.01842 -;WIDTH:0.449999 -G1 X126.775 Y104.45 E.00749 -G1 X126.775 Y105.568 E.03784 -G1 X126.59 Y105.693 E.00756 -G1 X126.575 Y106.213 E.01761 -G1 X126.674 Y106.388 E.00681 -G1 X126.775 Y106.416 E.00355 -G1 X126.775 Y106.775 E.01215 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y103.225 E.12016 -G1 X123.703 Y103.225 E.01618 -M73 P78 R0 -M73 Q78 S0 -G1 X123.741 Y103.342 E.00416 -G1 X123.906 Y103.425 E.00625 -G1 X124.424 Y103.414 E.01754 -G1 X124.56 Y103.225 E.00788 -G1 X125.524 Y103.225 E.03263 -G1 X125.636 Y103.405 E.00718 -G1 X126.142 Y103.425 E.01714 -;WIDTH:0.470069 -G1 X126.245 Y103.391 E.00385 -G1 X126.614 Y103.237 F18000 -G1 E-.7 F2100 -G1 X123.978 Y106.022 Z3.467 F18000 +G1 F1240 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.701 Y104.133 I-33.322 J-.339 E.05546 +G1 X120.521 Y104.025 E.00711 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.946 Y100.225 E.0921 +G1 X122.946 Y100.521 E.01002 +G1 X123.01 Y100.67 E.00549 +G1 X123.149 Y100.725 E.00506 +G1 X124.191 Y100.725 E.03527 +G1 X124.234 Y100.72 E.00147 +G1 X124.374 Y100.612 E.00599 +G1 X124.395 Y100.225 E.01312 +G1 X125.597 Y100.225 E.04069 +G1 X125.597 Y100.521 E.01002 +G1 X125.678 Y100.684 E.00616 +G1 X125.8 Y100.725 E.00436 +G1 X126.891 Y100.725 E.03693 +G1 X127.031 Y100.669 E.0051 +G1 X127.095 Y100.521 E.00546 +G1 X127.095 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09071 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y103.626 E.09227 +G1 X129.775 Y104.301 E.02285 +G1 X129.479 Y104.301 E.01002 +G1 X129.293 Y104.421 E.00749 +G2 X129.289 Y105.482 I10.707 J.565 E.03593 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z3.506 F18000 G1 Z3.4 F720 G1 E.7 F1500 -;TYPE:Solid infill -;WIDTH:0.449999 -G1 F1200 -G1 X123.978 Y104.178 E.06242 -G2 X124.651 Y104.132 I.154 J-2.662 E.02289 -G1 X124.913 Y103.978 E.01029 -G1 X125.17 Y103.978 E.0087 -G1 X125.219 Y104.031 E.00244 -G1 X125.466 Y104.14 E.00914 -G1 X125.833 Y104.169 E.01246 -G2 X125.901 Y104.614 I1.087 J.06 E.01535 -M73 Q79 S0 -G1 X126.016 Y104.782 E.00689 -G1 X126.022 Y105.223 E.01493 -G1 X125.941 Y105.31 E.00402 -G1 X125.828 Y105.726 E.01459 -G1 X125.822 Y106.022 E.01002 -M73 P79 R0 -G1 X124.182 Y106.022 E.05551 -G1 X124.385 Y105.615 E.01539 -G1 X124.385 Y104.583 E.03493 -G2 X124.987 Y104.425 I-.119 J-1.683 E.02119 -G1 X125.454 Y104.553 E.01639 -G1 X125.464 Y104.608 E.00189 -G1 X125.615 Y104.937 E.01225 -G3 X125.436 Y105.615 I-3.91 J-.668 E.02377 -G1 X124.589 Y105.615 E.02867 -G1 X124.772 Y105.228 E.01449 -;WIDTH:0.40871 -G1 X124.772 Y104.916 E.00949 -G1 X125.013 Y104.84 E.00769 -G1 X125.147 Y104.867 E.00416 -G1 X125.196 Y104.985 E.00389 -G1 X125.135 Y105.228 E.00762 -G1 X124.975 Y105.228 E.00487 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1240 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P33 R4 +M73 Q33 S4 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.76 E.08171 +G2 X128.525 Y104.419 I.201 J.669 E.02989 +G1 X128.522 Y105.431 E.03426 +G2 X129.022 Y106.14 I.703 J.035 E.03164 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:3.6 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;3.6 -M201 X3999.9 Y3999.9 +M201 X3999.68 Y3999.68 G1 E-.7 F2100 -G1 X124.975 Y105.228 Z3.4 F18000 -G1 X126.368 Y103.632 Z3.6 F8041.528 +G1 X120.978 Y103.346 Z3.4 F18000 +G1 X129.368 Y109.368 Z3.6 ;AFTER_LAYER_CHANGE ;3.6 -M74 W0.0966924 +M74 W0.297332 -G1 X126.368 Y103.632 F18000 +G1 X129.368 Y109.368 G1 Z3.6 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y106.368 E.09261 -G1 X123.632 Y106.368 E.09261 -M73 P80 R0 -M73 Q80 S0 -G1 X123.632 Y103.632 E.09261 -G1 X126.308 Y103.632 E.09058 -G1 X126.775 Y103.225 F18000 +G1 F1237 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G2 X121.107 Y104.057 I-37.678 J-.315 E.05803 +G1 X121.005 Y103.856 E.00763 +G1 X120.632 Y103.672 E.01408 +G1 X120.632 Y100.632 E.1029 +G1 X122.737 Y100.632 E.07125 +G1 X122.891 Y100.977 E.01279 +G1 X123.076 Y101.09 E.00734 +G1 X123.298 Y101.132 E.00765 +G1 X124.426 Y101.125 E.03818 +G1 X124.698 Y101.012 E.00997 +G1 X124.93 Y100.632 E.01507 +G1 X125.064 Y100.632 E.00454 +G1 X125.296 Y101.012 E.01507 +G1 X125.426 Y101.086 E.00506 +G1 X125.659 Y101.132 E.00804 +G1 X126.748 Y101.132 E.03686 +G1 X126.824 Y101.127 E.00258 +G1 X127.158 Y100.974 E.01244 +G1 X127.309 Y100.632 E.01265 +G1 X129.368 Y100.632 E.06969 +G1 X129.368 Y103.949 E.11228 +G1 X128.987 Y104.141 E.01444 +G1 X128.941 Y104.215 E.00295 +G1 X128.868 Y104.479 E.00927 +G1 X128.878 Y105.519 E.0352 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X126.775 Y106.775 E.12016 -G1 X123.225 Y106.775 E.12016 -M73 Q81 S0 -G1 X123.225 Y103.225 E.12016 -M73 P81 R0 -G1 X126.715 Y103.225 E.11813 -G1 X126.774 Y103.621 F18000 -G1 E-.7 F2100 -G1 X123.978 Y103.978 Z3.649 F18000 +G1 F1237 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 P34 R4 +G1 X120.521 Y105.975 E.01002 +M73 Q34 S4 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.704 Y104.139 I-38.949 J-.325 E.05525 +G1 X120.521 Y104.025 E.0073 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.094 Y100.225 E.09711 +G1 X123.094 Y100.521 E.01002 +G1 X123.162 Y100.673 E.00564 +G1 X123.298 Y100.725 E.00493 +G1 X124.391 Y100.717 E.037 +G1 X124.531 Y100.577 E.0067 +G1 X124.539 Y100.225 E.01192 +G1 X125.456 Y100.225 E.03104 +G1 X125.456 Y100.521 E.01002 +G1 X125.538 Y100.685 E.00621 +G1 X125.659 Y100.725 E.00431 +G1 X126.748 Y100.725 E.03686 +G1 X126.884 Y100.672 E.00494 +G1 X126.951 Y100.521 E.00559 +G1 X126.951 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09559 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.289 Y104.431 E.00779 +G2 X129.289 Y105.482 I12.061 J.52 E.03559 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z3.706 F18000 G1 Z3.6 F720 -M73 Q82 S0 G1 E.7 F1500 -;TYPE:Solid infill -G1 F1200 -G1 X126.022 Y103.978 E.06919 -G1 X126.022 Y106.022 E.06919 -M73 P82 R0 -G1 X123.978 Y106.022 E.06919 -G1 X123.978 Y104.182 E.06228 -G1 X124.385 Y104.385 E.01539 -G1 X125.615 Y104.385 E.04163 -G1 X125.615 Y105.615 E.04163 -G1 X124.385 Y105.615 E.04163 -G1 X124.385 Y104.589 E.03473 -M73 Q83 S0 -G1 X124.794 Y104.794 E.01549 -;WIDTH:0.454205 -G1 X125.206 Y104.794 E.01409 -M73 P83 R0 -G1 X125.206 Y105.206 E.01409 -G1 X124.794 Y105.206 E.01409 -G1 X124.794 Y104.998 E.00711 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1237 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.761 E.08174 +G2 X128.583 Y104.162 I.153 J.607 E.02097 +G2 X128.588 Y105.758 I3.658 J.788 E.05444 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:3.8 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;3.8 -M201 X3999.89 Y3999.89 +M201 X3999.67 Y3999.67 G1 E-.7 F2100 -G1 X124.794 Y104.998 Z3.6 F18000 -G1 X126.368 Y103.632 Z3.8 F7921.031 +G1 X120.978 Y103.346 Z3.6 F18000 +G1 X129.368 Y109.368 Z3.8 ;AFTER_LAYER_CHANGE ;3.8 -M74 W0.100739 +M74 W0.309075 -G1 X126.368 Y103.632 F18000 +G1 X129.368 Y109.368 G1 Z3.8 F720 G1 E.7 F1500 ;TYPE:Perimeter ;WIDTH:0.449999 -G1 F1200 -G1 X126.368 Y106.368 E.09261 -G1 X123.632 Y106.368 E.09261 -G1 X123.632 Y103.632 E.09261 -G1 X126.308 Y103.632 E.09058 -M73 Q84 S0 -G1 X126.775 Y103.225 F18000 -M73 P84 R0 +G1 F1234 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 P35 R4 +M73 Q35 S4 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.115 Y104.086 E.05704 +G1 X121.007 Y103.858 E.00854 +G1 X120.632 Y103.673 E.01415 +G1 X120.632 Y100.632 E.10293 +G1 X122.886 Y100.632 E.0763 +G1 X123.049 Y100.985 E.01316 +G1 X123.199 Y101.08 E.00601 +G1 X123.446 Y101.132 E.00854 +G1 X124.55 Y101.128 E.03737 +G1 X124.825 Y101.025 E.00994 +G1 X124.999 Y100.727 E.01168 +G1 X125.173 Y101.025 E.01168 +G1 X125.318 Y101.098 E.00549 +G1 X125.515 Y101.132 E.00677 +G1 X126.605 Y101.132 E.0369 +G1 X126.692 Y101.126 E.00295 +G1 X127.005 Y100.982 E.01166 +G1 X127.165 Y100.632 E.01303 +G1 X129.368 Y100.632 E.07457 +G1 X129.368 Y103.95 E.11231 +G1 X128.986 Y104.143 E.01449 +G1 X128.899 Y104.312 E.00643 +G2 X128.878 Y105.519 I11.126 J.795 E.04088 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -G1 F1200 -G1 X126.775 Y106.775 E.12016 -G1 X123.225 Y106.775 E.12016 -G1 X123.225 Y103.225 E.12016 -G1 X126.715 Y103.225 E.11813 -M73 Q85 S0 -G1 X126.774 Y103.621 F18000 -M73 P85 R0 -G1 X126.207 Y104.573 -M204 P2000 -;TYPE:Top solid infill -;WIDTH:0.428043 -G1 F1200 -G1 X125.427 Y103.793 E.03532 -M204 P4000 -G1 X125.427 Y103.793 F18000 -G1 X124.883 Y103.793 -M204 P2000 -G1 F1200 -G1 X126.207 Y105.117 E.05996 -M204 P4000 -G1 X126.207 Y105.117 F18000 -G1 X126.207 Y105.662 -M204 P2000 -G1 F1200 -G1 X124.338 Y103.793 E.08464 -M204 P4000 -G1 X124.338 Y103.793 F18000 -G1 X123.793 Y103.793 -M204 P2000 -G1 F1200 -G1 X126.207 Y106.207 E.10932 -M204 P4000 -G1 X126.207 Y106.207 F18000 -M73 P86 R0 -M73 Q86 S0 -G1 X125.662 Y106.207 -M204 P2000 -G1 F1200 -G1 X123.793 Y104.338 E.08464 -M204 P4000 -G1 X123.793 Y104.338 F18000 -G1 X123.793 Y104.883 -M204 P2000 -G1 F1200 -G1 X125.117 Y106.207 E.05996 -M204 P4000 -G1 X125.117 Y106.207 F18000 -G1 X124.573 Y106.207 -M204 P2000 -G1 F1200 -G1 X123.793 Y105.427 E.03532 -M204 P4000 +G1 F1234 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.707 Y104.145 I-45.686 J-.313 E.05504 +G1 X120.521 Y104.025 E.00749 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.243 Y100.225 E.10216 +G1 X123.243 Y100.521 E.01002 +G1 X123.314 Y100.676 E.00577 +G1 X123.446 Y100.725 E.00477 +G1 X124.525 Y100.72 E.03652 +;WIDTH:0.488106 +G1 X124.597 Y100.661 E.00345 +;WIDTH:0.526213 +G1 X124.668 Y100.603 E.00368 +;WIDTH:0.564319 +G1 X124.739 Y100.544 E.004 +G1 X124.739 Y100.281 E.0114 +;WIDTH:0.562712 +G1 X125.258 Y100.281 E.02243 +G1 X125.258 Y100.521 E.01037 +G1 X125.307 Y100.577 E.00322 +;WIDTH:0.525141 +G1 X125.355 Y100.633 E.00296 +;WIDTH:0.48757 +G1 X125.403 Y100.689 E.00273 +;WIDTH:0.449999 +G1 X125.517 Y100.725 E.00405 +G1 X126.605 Y100.725 E.03683 +G1 X126.738 Y100.675 E.00481 +G1 X126.808 Y100.521 E.00573 +G1 X126.808 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10043 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.314 Y104.384 E.00625 +G1 X129.285 Y104.44 E.00213 +G2 X129.289 Y105.482 I13.416 J.473 E.03528 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z3.906 F18000 +G1 Z3.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1234 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P36 R4 +M73 Q36 S4 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.762 E.08178 +G2 X128.522 Y104.493 I.214 J.682 E.03232 +G1 X128.522 Y105.431 E.03175 +G2 X129.022 Y106.14 I.703 J.035 E.03164 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 ;LAYER_CHANGE ;Z:4 ;HEIGHT:0.2 ;BEFORE_LAYER_CHANGE G92 E0.0 ;4 -M201 X3999.89 Y3999.89 +M201 X3999.66 Y3999.66 -G1 X123.793 Y105.427 Z3.8 F18000 -G1 X123.992 Y106.438 Z4 F4277.994 +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z3.8 F18000 +G1 X129.368 Y109.368 Z4 ;AFTER_LAYER_CHANGE ;4 -M74 W0.104665 +M74 W0.320831 -G1 X123.992 Y106.438 F18000 +G1 X129.368 Y109.368 G1 Z4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1234 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.121 Y104.115 E.05605 +G1 X121.009 Y103.86 E.00943 +G1 X120.632 Y103.673 E.01424 +G1 X120.632 Y100.632 E.10293 +G1 X123.036 Y100.632 E.08137 +G1 X123.206 Y100.992 E.01348 +G1 X123.323 Y101.068 E.00472 +G1 X123.595 Y101.132 E.00946 +G1 X124.623 Y101.132 E.0348 +G1 X124.899 Y101.066 E.00961 +G1 X125 Y100.917 E.00609 +G1 X125.101 Y101.066 E.00609 +G1 X125.227 Y101.113 E.00455 +G1 X125.496 Y101.132 E.00913 +G1 X126.559 Y101.124 E.03598 +G1 X126.853 Y100.99 E.01094 +G1 X127.02 Y100.632 E.01337 +G1 X129.368 Y100.632 E.07948 +G1 X129.368 Y103.95 E.11231 +G1 X128.985 Y104.145 E.01455 +G1 X128.891 Y104.339 E.0073 +G2 X128.878 Y105.519 I13.161 J.729 E.03996 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 ;TYPE:External perimeter -;WIDTH:0.500812 -G1 F1200 -G1 X123.941 Y106.415 E.00213 -;WIDTH:0.543929 -G1 X123.887 Y106.392 E.00245 -;WIDTH:0.587046 -G1 X123.834 Y106.368 E.00263 -G1 X123.832 Y105.972 E.01792 -;WIDTH:0.582954 -G1 X123.9 Y105.907 E.00422 -;WIDTH:0.538636 -G1 X123.968 Y105.843 E.00385 -;WIDTH:0.494318 -G1 X124.036 Y105.779 E.0035 -;WIDTH:0.449999 -G1 X125.083 Y105.768 E.03544 -G1 X124.319 Y104.813 E.0414 -;WIDTH:0.472785 -G1 X123.783 Y104.161 E.03017 -M73 Q87 S0 -;WIDTH:0.507579 -G1 X123.718 Y104.027 E.00575 -M73 P87 R0 -G1 X123.718 Y103.679 E.01345 -;WIDTH:0.524205 -G1 X123.727 Y103.591 E.00354 -G1 X123.783 Y103.572 E.00237 -;WIDTH:0.486019 -G1 X123.839 Y103.552 E.00219 -;WIDTH:0.447833 -G1 X123.895 Y103.533 E.00199 -;WIDTH:0.409647 -G1 X123.951 Y103.514 E.0018 -;WIDTH:0.37146 -G1 X126.049 Y103.514 E.05731 -;WIDTH:0.409647 -G1 X126.105 Y103.533 E.0018 -;WIDTH:0.447833 -G1 X126.161 Y103.552 E.00199 -;WIDTH:0.48602 -G1 X126.217 Y103.572 E.00219 -;WIDTH:0.524206 -G1 X126.273 Y103.591 E.00237 -G1 X126.28 Y104.069 E.01913 -;WIDTH:0.511099 -G1 X126.165 Y104.15 E.00548 -;WIDTH:0.480549 -G1 X126.049 Y104.232 E.00517 -;WIDTH:0.449999 -G1 X124.841 Y104.232 E.04089 -G1 X125.666 Y105.245 E.04422 -;WIDTH:0.459646 -G1 X126.168 Y105.854 E.02735 -;WIDTH:0.494254 -G1 X126.209 Y105.92 E.00292 -;WIDTH:0.528861 -G1 X126.25 Y105.986 E.00314 -;WIDTH:0.541746 -G1 X126.243 Y106.401 E.01722 -G1 X126.184 Y106.422 E.0026 -;WIDTH:0.499175 -G1 X126.125 Y106.443 E.00238 -;WIDTH:0.456603 -G1 X126.066 Y106.464 E.00215 -;WIDTH:0.414032 -G1 X126.007 Y106.486 E.00194 -;WIDTH:0.37146 -G1 X124.102 Y106.486 E.05204 -;WIDTH:0.414578 -G1 X124.048 Y106.462 E.00183 -G1 X123.825 Y106.13 F18000 -G1 E-.7 F2100 -G1 X126.035 Y105.042 Z4.043 F18000 +G1 F1234 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 P37 R4 +M73 Q37 S4 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.71 Y104.151 E.05484 +G1 X120.521 Y104.025 E.00769 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.391 Y100.225 E.10717 +G1 X123.391 Y100.521 E.01002 +G1 X123.465 Y100.678 E.00587 +G1 X123.595 Y100.725 E.00468 +G1 X124.673 Y100.719 E.03649 +G1 X124.812 Y100.538 E.00772 +;WIDTH:0.421205 +G1 X124.812 Y100.21 E.01032 +;WIDTH:0.419765 +G1 X125.189 Y100.21 E.01181 +G1 X125.189 Y100.521 E.00975 +;WIDTH:0.449999 +G1 X125.285 Y100.703 E.00696 +G1 X125.377 Y100.725 E.0032 +G1 X126.461 Y100.725 E.03669 +G1 X126.592 Y100.677 E.00472 +G1 X126.665 Y100.521 E.00583 +G1 X126.665 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10527 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.294 Y104.418 E.00741 +G1 X129.275 Y104.623 E.00697 +G1 X129.289 Y105.482 E.02908 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z4.106 F18000 G1 Z4 F720 -M73 Q88 S0 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1234 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.762 E.08178 +G2 X128.556 Y104.249 I.161 J.621 E.02402 +G2 X128.588 Y105.758 I3.971 J.67 E.05139 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:4.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.2 +M201 X3999.64 Y3999.64 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z4 F18000 +G1 X125 Y100.259 Z4.2 +;AFTER_LAYER_CHANGE +;4.2 +M74 W0.33254 + +G1 X125 Y100.259 +G1 Z4.2 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.519004 +G1 F1252 +G1 X125 Y100.542 E.0112 +G1 E-.7 F2100 +G1 X125 Y100.542 F18000 +G1 X129.368 Y109.368 Z4.372 +G1 Z4.2 F720 +M73 P38 R4 +M73 Q38 S4 G1 E.7 F1500 ;TYPE:Perimeter -;WIDTH:0.464061 -G1 F1200 -G1 X125.712 Y104.646 E.0179 -G1 X126.125 Y104.641 E.01446 -M73 P88 R0 -G1 X126.361 Y104.417 E.01139 -G1 X126.361 Y105.442 E.03589 -G1 X126.073 Y105.089 E.01595 +;WIDTH:0.449999 +G1 F1252 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.126 Y104.143 E.05511 +G1 X121.011 Y103.863 E.01025 +G1 X120.632 Y103.673 E.01435 +G1 X120.632 Y100.632 E.10293 +G1 X123.186 Y100.632 E.08645 +G1 X123.362 Y100.999 E.01378 +G1 X123.611 Y101.118 E.00934 +G1 X124.229 Y101.132 E.02092 +G1 X124.948 Y101.103 E.02436 +G1 X125 Y101.038 E.00282 +G1 X125.052 Y101.103 E.00282 +G1 X125.496 Y101.132 E.01506 +G1 X126.426 Y101.122 E.03148 +G1 X126.702 Y100.996 E.01027 +G1 X126.876 Y100.632 E.01366 +G1 X129.368 Y100.632 E.08435 +G1 X129.368 Y103.95 E.11231 +G1 X128.984 Y104.147 E.01461 +G1 X128.884 Y104.365 E.00812 +G2 X128.878 Y105.519 I15.586 J.653 E.03907 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1252 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.684 Y104.107 E.00436 +G1 X120.521 Y104.025 E.00618 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.54 Y100.225 E.11221 +G1 X123.54 Y100.521 E.01002 +G1 X123.616 Y100.681 E.006 +G1 X123.699 Y100.72 E.0031 +G1 X124.229 Y100.725 E.01794 +G1 X124.775 Y100.725 E.01848 +;WIDTH:0.484502 +G1 X124.888 Y100.633 E.00535 +;WIDTH:0.519004 +G1 X125 Y100.542 E.00571 +G1 X125.087 Y100.629 E.00487 +;WIDTH:0.484502 +G1 X125.175 Y100.715 E.00452 +;WIDTH:0.449999 +G1 X125.496 Y100.725 E.01087 +G1 X126.397 Y100.709 E.0305 +G1 X126.522 Y100.521 E.00764 +G1 X126.522 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11011 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.292 Y104.424 E.00758 +G2 X129.289 Y105.482 I10.324 J.553 E.03583 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 G1 E-.7 F2100 -G1 X126.073 Y105.089 F18000 -G1 X123.634 Y105.77 Z4.044 -G1 Z4 F720 +G1 X123.346 Y109.022 Z4.306 F18000 +G1 Z4.2 F720 +M73 P39 R4 G1 E.7 F1500 -;WIDTH:0.453747 -G1 F1200 -G1 X123.634 Y104.611 E.03959 -G1 X124.232 Y105.359 E.03271 -G1 X123.905 Y105.392 E.01123 -G1 X123.669 Y105.721 E.01383 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1252 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +M73 Q39 S4 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.763 E.08181 +G2 X128.546 Y104.288 I.169 J.633 E.02535 +G2 X128.588 Y105.758 I4.088 J.619 E.05004 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:4.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.4 +M201 X3999.63 Y3999.63 + G1 E-.7 F2100 -G1 X126.738 Y103.262 Z4.069 F18000 -G1 Z4 F720 +G1 X120.978 Y103.346 Z4.2 F18000 +G1 X124.997 Y100.128 Z4.4 +;AFTER_LAYER_CHANGE +;4.4 +M74 W0.344214 + +G1 X124.997 Y100.128 +G1 Z4.4 F720 G1 E.7 F1500 ;TYPE:External perimeter -;WIDTH:0.524206 -G1 F1200 -M73 Q89 S0 -G1 X126.738 Y103.503 E.00964 -G1 X126.744 Y104.109 E.02425 -;WIDTH:0.511099 -G1 X126.76 Y104.48 E.01446 -;WIDTH:0.480549 -G1 X126.775 Y104.85 E.01347 -;WIDTH:0.48943 -G1 X126.755 Y105.418 E.0211 -;WIDTH:0.528861 -G1 X126.736 Y105.986 E.02296 -M73 P89 R0 -;WIDTH:0.541746 -G1 X126.729 Y106.729 E.03082 -G1 X126.549 Y106.75 E.00752 -;WIDTH:0.499175 -G1 X126.368 Y106.772 E.00692 -;WIDTH:0.456603 -G1 X126.187 Y106.793 E.00627 -;WIDTH:0.414032 -G1 X126.007 Y106.814 E.00559 -;WIDTH:0.37146 -G1 X124.102 Y106.814 E.05204 -;WIDTH:0.414906 -G1 X123.995 Y106.793 E.00337 -;WIDTH:0.458352 -G1 X123.888 Y106.771 E.00377 -;WIDTH:0.501798 -G1 X123.781 Y106.749 E.00417 -;WIDTH:0.545244 -G1 X123.674 Y106.727 E.00456 -;WIDTH:0.588689 -G1 X123.567 Y106.706 E.00495 -G1 X123.294 Y106.706 E.01239 -G1 X123.291 Y105.972 E.03331 -;WIDTH:0.582954 -G1 X123.269 Y105.713 E.01167 -;WIDTH:0.538636 -G1 X123.247 Y105.455 E.01067 -;WIDTH:0.494318 -G1 X123.225 Y105.197 E.00972 -;WIDTH:0.478789 -G1 X123.239 Y104.612 E.02121 -;WIDTH:0.507579 -G1 X123.254 Y104.027 E.02261 -;WIDTH:0.524205 -G1 X123.262 Y103.262 E.03062 -G1 X123.434 Y103.243 E.00693 -;WIDTH:0.486019 -G1 X123.606 Y103.224 E.00638 -;WIDTH:0.447833 -G1 X123.779 Y103.205 E.00586 -;WIDTH:0.409647 -G1 X123.951 Y103.186 E.00528 -;WIDTH:0.37146 -G1 X126.049 Y103.186 E.05731 -;WIDTH:0.409647 -G1 X126.161 Y103.205 E.00346 -M73 Q90 S0 -;WIDTH:0.447833 -G1 X126.273 Y103.224 E.00382 -;WIDTH:0.48602 -G1 X126.385 Y103.243 E.00419 -;WIDTH:0.524206 -G1 X126.497 Y103.262 E.00455 -G1 X126.678 Y103.262 E.00724 -G1 X126.739 Y103.657 F18000 -M486 S-1 +;WIDTH:0.38292 +G1 F1254 +G1 X124.997 Y100.531 E.01139 G1 E-.7 F2100 -M107 -;TYPE:Custom -; Filament-specific end gcode -G1 Z5 F720 ; Move print head up -M104 S0 ; turn off temperature -M140 S0 ; turn off heatbed -M107 ; turn off fan -G1 X241 Y170 F3600 ; park -M73 P90 R0 -G1 Z27 F300 ; Move print head up -M73 P93 R0 -M73 Q93 S0 +G1 X124.997 Y100.531 F18000 +G1 X129.368 Y109.368 Z4.572 +G1 Z4.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1254 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.129 Y104.171 E.05416 +G1 X121.013 Y103.866 E.01105 +G1 X120.632 Y103.674 E.01444 +G1 X120.632 Y100.632 E.10297 +G1 X123.335 Y100.632 E.09149 +G1 X123.519 Y101.005 E.01408 +G1 X123.751 Y101.116 E.00871 +G1 X124.229 Y101.132 E.01619 +G1 X125.496 Y101.132 E.04289 +G1 X126.292 Y101.121 E.02695 +G1 X126.551 Y101.002 E.00965 +G1 X126.732 Y100.632 E.01394 +G1 X129.368 Y100.632 E.08923 +G1 X129.368 Y103.95 E.11231 +G1 X128.982 Y104.149 E.0147 +G1 X128.878 Y104.391 E.00892 +G2 X128.878 Y105.519 I18.455 J.566 E.03819 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 +M73 P40 R4 +;TYPE:External perimeter +G1 F1254 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 Q40 S4 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.685 Y104.108 E.00431 +G1 X120.521 Y104.025 E.00622 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.688 Y100.225 E.11722 +G1 X123.688 Y100.521 E.01002 +G1 X123.767 Y100.683 E.0061 +G1 X123.891 Y100.725 E.00443 +G1 X124.891 Y100.725 E.03385 +G1 X124.944 Y100.628 E.00374 +;WIDTH:0.41646 +G1 X124.997 Y100.531 E.00343 +G1 X125.036 Y100.627 E.00322 +;WIDTH:0.449999 +G1 X125.075 Y100.723 E.00351 +G1 X125.496 Y100.725 E.01425 +G1 X126.261 Y100.706 E.0259 +G1 X126.378 Y100.521 E.00741 +G1 X126.378 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11498 +G1 X129.775 Y104.301 E.13797 +G1 X129.479 Y104.301 E.01002 +G1 X129.289 Y104.429 E.00775 +G2 X129.289 Y105.482 I11.732 J.53 E.03565 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +M73 P40 R3 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z4.506 F18000 +G1 Z4.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1254 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.764 E.08185 +G2 X128.538 Y104.327 I.178 J.643 E.02665 +G2 X128.588 Y105.758 I4.162 J.571 E.0487 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:4.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.6 +M201 X3999.62 Y3999.62 + +G1 E-.7 F2100 +M73 P41 R3 +G1 X120.978 Y103.346 Z4.4 F18000 +G1 X129.368 Y109.368 Z4.6 +;AFTER_LAYER_CHANGE +;4.6 +M74 W0.355884 + +G1 X129.368 Y109.368 +M73 Q41 S4 +G1 Z4.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1226 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.131 Y104.197 E.05328 +G1 X121.015 Y103.868 E.01181 +G1 X120.632 Y103.674 E.01453 +G1 X120.632 Y100.632 E.10297 +G1 X123.485 Y100.632 E.09657 +G1 X123.706 Y101.033 E.0155 +G1 X124.04 Y101.132 E.01179 +G1 X125.496 Y101.132 E.04928 +G1 X126.157 Y101.119 E.02238 +G1 X126.401 Y101.008 E.00907 +G1 X126.587 Y100.632 E.0142 +G1 X129.368 Y100.632 E.09413 +G1 X129.368 Y103.951 E.11234 +G1 X128.981 Y104.151 E.01475 +G1 X128.874 Y104.417 E.0097 +G2 X128.878 Y105.519 I21.775 J.471 E.03731 +G1 X128.972 Y105.748 E.00838 +G1 X129.368 Y105.959 E.01519 +G1 X129.368 Y109.308 E.11336 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1226 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.229 E.05219 +G1 X120.686 Y104.108 E.0043 +G1 X120.521 Y104.025 E.00625 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.836 Y100.225 E.12223 +G1 X123.836 Y100.521 E.01002 +M73 Q41 S3 +G1 X123.918 Y100.685 E.00621 +G1 X124.04 Y100.725 E.00435 +G1 X125.496 Y100.725 E.04928 +G1 X126.124 Y100.703 E.02127 +G1 X126.235 Y100.521 E.00722 +G1 X126.235 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11982 +G1 X129.775 Y100.9 E.02285 +G1 X129.775 Y104.301 E.11512 +G1 X129.479 Y104.301 E.01002 +G1 X129.313 Y104.386 E.00631 +G1 X129.275 Y104.504 E.0042 +G1 X129.289 Y105.482 E.03311 +G1 X129.479 Y105.611 E.00777 +G1 X129.775 Y105.611 E.01002 +G1 X129.775 Y109.715 E.13892 +G1 X129.379 Y109.774 F18000 +M73 P42 R3 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z4.706 F18000 +G1 Z4.6 F720 +M73 Q42 S3 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1226 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.766 E.08191 +G2 X128.532 Y104.365 I.186 J.652 E.02788 +G2 X128.588 Y105.758 I4.183 J.529 E.04741 +G2 X129.022 Y106.14 I.585 J-.228 E.02034 +G1 X129.022 Y108.654 E.0851 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:4.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;4.8 +M201 X3999.61 Y3999.61 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z4.6 F18000 +G1 X129.368 Y109.368 Z4.8 +;AFTER_LAYER_CHANGE +;4.8 +M74 W0.367497 + +G1 X129.368 Y109.368 +G1 Z4.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1223 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.127 Y104.152 E.0548 +G1 X121.017 Y103.871 E.01021 +G1 X120.632 Y103.675 E.01462 +G1 X120.632 Y100.632 E.103 +G1 X123.634 Y100.632 E.10161 +G1 X123.83 Y101.016 E.01459 +G1 X123.938 Y101.078 E.00422 +G1 X124.188 Y101.132 E.00866 +G1 X125.888 Y101.132 E.05754 +G1 X126.208 Y101.042 E.01125 +G1 X126.443 Y100.632 E.016 +G1 X129.368 Y100.632 E.09901 +G1 X129.368 Y103.819 E.10788 +G1 X128.982 Y104.018 E.0147 +G1 X128.896 Y104.19 E.00651 +G1 X128.868 Y104.373 E.00627 +G1 X128.877 Y105.647 E.04312 +G1 X128.976 Y105.89 E.00888 +G1 X129.368 Y106.096 E.01499 +G1 X129.368 Y109.308 E.10872 +G1 X129.775 Y109.775 F18000 +M73 P43 R3 +;TYPE:External perimeter +G1 F1223 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 Q43 S3 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.706 Y104.143 I-44.979 J-.292 E.05511 +G1 X120.521 Y104.025 E.00743 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.985 Y100.225 E.12727 +G1 X123.985 Y100.521 E.01002 +G1 X124.069 Y100.686 E.00627 +G1 X124.188 Y100.725 E.00424 +G1 X125.627 Y100.725 E.04871 +G1 X125.995 Y100.695 E.0125 +G1 X126.092 Y100.521 E.00674 +G1 X126.092 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12466 +G1 X129.775 Y104.169 E.1335 +G1 X129.479 Y104.169 E.01002 +G1 X129.313 Y104.254 E.00631 +G1 X129.275 Y104.373 E.00423 +G1 X129.289 Y105.617 E.04211 +G1 X129.479 Y105.747 E.00779 +G1 X129.775 Y105.747 E.01002 +G1 X129.775 Y109.715 E.13431 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z4.906 F18000 +G1 Z4.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1223 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.633 E.07741 +G2 X128.564 Y104.085 I.163 J.623 E.02281 +G2 X128.536 Y105.708 I8.393 J.961 E.05503 +G2 X129.022 Y106.279 I.662 J-.072 E.02696 +G1 X129.022 Y108.654 E.08039 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +M73 P44 R3 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:5 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5 +M201 X3999.59 Y3999.59 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z4.8 F18000 +M73 Q44 S3 +G1 X129.368 Y109.368 Z5 +;AFTER_LAYER_CHANGE +;5 +M74 W0.37911 + +G1 X129.368 Y109.368 +G1 Z5 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1227 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.16 E.05453 +G1 X121.029 Y103.889 E.00977 +G1 X120.632 Y103.675 E.01527 +G1 X120.632 Y100.632 E.103 +G1 X123.783 Y100.632 E.10666 +G1 X123.983 Y101.019 E.01475 +G1 X124.079 Y101.075 E.00376 +G1 X124.337 Y101.132 E.00894 +G1 X125 Y101.132 E.02244 +G1 X125.818 Y101.128 E.02769 +G1 X126.101 Y101.018 E.01028 +G1 X126.299 Y100.632 E.01468 +G1 X129.368 Y100.632 E.10388 +G1 X129.368 Y103.685 E.10334 +G1 X128.983 Y103.883 E.01465 +G1 X128.922 Y103.987 E.00408 +G1 X128.868 Y104.239 E.00872 +G1 X128.877 Y105.788 E.05243 +G1 X128.98 Y106.034 E.00903 +G1 X129.368 Y106.235 E.01479 +G1 X129.368 Y109.308 E.10402 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1227 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.708 Y104.148 E.05494 +G1 X120.521 Y104.025 E.00758 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.133 Y100.225 E.13228 +G1 X124.133 Y100.521 E.01002 +G1 X124.219 Y100.687 E.00633 +G1 X124.337 Y100.725 E.0042 +G1 X125 Y100.725 E.02244 +G1 X125.83 Y100.707 E.0281 +G1 X125.949 Y100.521 E.00747 +G1 X125.949 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12951 +G1 X129.775 Y104.035 E.12896 +G1 X129.479 Y104.035 E.01002 +G1 X129.313 Y104.12 E.00631 +G1 X129.275 Y104.239 E.00423 +G1 X129.289 Y105.757 E.05138 +G1 X129.479 Y105.885 E.00775 +G1 X129.775 Y105.885 E.01002 +G1 X129.775 Y109.715 E.12964 +G1 X129.379 Y109.774 F18000 +M73 P45 R3 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z5.106 F18000 +G1 Z5 F720 +M73 Q45 S3 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1227 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.499 E.07288 +G2 X128.552 Y103.999 I.163 J.624 E.02449 +G2 X128.536 Y105.849 I13.558 J1.04 E.06267 +G2 X129.022 Y106.42 I.664 J-.073 E.02695 +G1 X129.022 Y108.654 E.07562 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +G1 X121.346 Y100.978 E.01762 +G1 X123.346 Y100.978 E.0677 +;LAYER_CHANGE +;Z:5.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.2 +M201 X3999.58 Y3999.58 + +G1 E-.7 F2100 +G1 X123.346 Y100.978 Z5 F18000 +G1 X129.368 Y109.368 Z5.2 +;AFTER_LAYER_CHANGE +;5.2 +M74 W0.390723 + +G1 X129.368 Y109.368 +G1 Z5.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1222 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.229 E.05219 +G1 X121.044 Y103.913 E.0111 +G1 X120.632 Y103.675 E.01611 +G1 X120.632 Y100.632 E.103 +G1 X123.932 Y100.632 E.1117 +G1 X124.135 Y101.022 E.01488 +G1 X124.306 Y101.105 E.00643 +G1 X124.485 Y101.132 E.00613 +G1 X125 Y101.132 E.01743 +G1 X125.65 Y101.13 E.022 +G1 X125.956 Y101.019 E.01102 +G1 X126.156 Y100.632 E.01475 +G1 X129.368 Y100.632 E.10872 +G1 X129.368 Y103.55 E.09877 +G1 X128.987 Y103.743 E.01446 +G1 X128.955 Y103.791 E.00195 +G1 X128.868 Y104.105 E.01103 +G1 X128.878 Y105.928 E.06171 +G1 X128.984 Y106.179 E.00922 +M73 P46 R3 +G1 X129.368 Y106.374 E.01458 +G1 X129.368 Y109.308 E.09931 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1222 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +M73 Q46 S3 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.485 E.04353 +G1 X120.696 Y104.123 E.01229 +G1 X120.521 Y104.025 E.00679 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.458 Y100.225 E.10943 +G1 X124.282 Y100.225 E.02789 +G1 X124.282 Y100.521 E.01002 +G1 X124.369 Y100.688 E.00637 +G1 X124.485 Y100.725 E.00412 +G1 X125.602 Y100.725 E.03781 +G1 X125.72 Y100.687 E.0042 +G1 X125.806 Y100.521 E.00633 +G1 X125.806 Y100.225 E.01002 +G1 X129.775 Y100.225 E.13435 +G1 X129.775 Y103.902 E.12446 +G1 X129.479 Y103.902 E.01002 +G1 X129.315 Y103.984 E.00621 +G1 X129.275 Y104.105 E.00431 +G1 X129.29 Y105.897 E.06066 +G1 X129.479 Y106.024 E.00771 +G1 X129.775 Y106.024 E.01002 +G1 X129.775 Y109.715 E.12494 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z5.306 F18000 +G1 Z5.2 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1222 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.362 E.06824 +G2 X128.522 Y104.097 I.208 J.679 E.03251 +G1 X128.522 Y105.827 E.05856 +G2 X128.716 Y106.406 I1.064 J-.035 E.02096 +G1 X129.022 Y106.562 E.01163 +G1 X129.022 Y108.654 E.07081 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +M73 P47 R3 +G1 X121.346 Y100.978 E.01762 +G1 X123.346 Y100.978 E.0677 +;LAYER_CHANGE +;Z:5.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.4 +M201 X3999.57 Y3999.57 + +G1 E-.7 F2100 +M73 Q47 S3 +G1 X123.346 Y100.978 Z5.2 F18000 +G1 X129.368 Y109.368 Z5.4 +;AFTER_LAYER_CHANGE +;5.4 +M74 W0.402336 + +G1 X129.368 Y109.368 +G1 Z5.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1220 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.162 E.05446 +G1 X121.036 Y103.899 E.00943 +G1 X120.632 Y103.675 E.01564 +G1 X120.632 Y100.632 E.103 +G1 X123.844 Y100.632 E.10872 +G1 X124.045 Y101.02 E.01479 +G1 X124.137 Y101.074 E.00361 +G1 X124.397 Y101.132 E.00902 +G1 X125.692 Y101.128 E.04383 +G1 X125.982 Y101.017 E.01051 +G1 X126.179 Y100.632 E.01464 +G1 X129.368 Y100.632 E.10794 +G1 X129.368 Y103.416 E.09423 +G1 X128.992 Y103.603 E.01421 +G1 X128.883 Y103.837 E.00874 +G2 X128.878 Y106.069 I44.583 J1.216 E.07556 +G1 X128.989 Y106.324 E.00941 +G1 X129.368 Y106.514 E.01435 +G1 X129.368 Y109.308 E.09457 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1220 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.709 Y104.15 E.05487 +G1 X120.521 Y104.025 E.00764 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.194 Y100.225 E.13435 +G1 X124.194 Y100.521 E.01002 +G1 X124.28 Y100.688 E.00636 +G1 X124.397 Y100.725 E.00415 +G1 X125.625 Y100.725 E.04157 +G1 X125.744 Y100.687 E.00423 +G1 X125.829 Y100.521 E.00631 +G1 X125.829 Y100.225 E.01002 +G1 X129.775 Y100.225 E.13357 +G1 X129.775 Y103.768 E.11993 +G1 X129.479 Y103.768 E.01002 +G1 X129.316 Y103.849 E.00616 +G1 X129.275 Y103.971 E.00436 +G1 X129.275 Y104.968 E.03375 +M73 P48 R3 +G3 X129.275 Y105.041 I-.015 J.036 E.00315 +G1 X129.291 Y106.037 E.03372 +G1 X129.479 Y106.162 E.00764 +G1 X129.775 Y106.162 E.01002 +G1 X129.775 Y109.715 E.12026 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z5.506 F18000 +M73 Q48 S3 +G1 Z5.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1220 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.225 E.0636 +G2 X128.522 Y103.964 I.215 J.685 E.03259 +G1 X128.522 Y105.965 E.06773 +G2 X128.724 Y106.554 I1.062 J-.035 E.02139 +G1 X129.022 Y106.703 E.01128 +G1 X129.022 Y108.654 E.06604 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +G1 X121.346 Y100.978 E.01762 +G1 X123.346 Y100.978 E.0677 +;LAYER_CHANGE +;Z:5.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.6 +M201 X3999.56 Y3999.56 + +G1 E-.7 F2100 +G1 X123.346 Y100.978 Z5.4 F18000 +G1 X129.465 Y105.012 Z5.6 +;AFTER_LAYER_CHANGE +;5.6 +M74 W0.413952 + +G1 X129.465 Y105.012 +G1 Z5.6 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1228 +G1 X129.82 Y105.012 E.01004 +G1 E-.7 F2100 +G1 X129.82 Y105.012 F18000 +G1 X129.368 Y109.368 Z5.676 +G1 Z5.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1228 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.128 Y104.158 E.0546 +G1 X121.02 Y103.876 E.01022 +G1 X120.632 Y103.675 E.01479 +G1 X120.632 Y100.632 E.103 +G1 X123.701 Y100.632 E.10388 +G1 X123.899 Y101.018 E.01468 +G1 X124 Y101.076 E.00394 +G1 X124.25 Y101.132 E.00867 +G1 X125.841 Y101.128 E.05385 +G1 X126.138 Y101.012 E.01079 +G1 X126.329 Y100.632 E.0144 +G1 X129.368 Y100.632 E.10287 +G1 X129.368 Y103.281 E.08967 +M73 P49 R3 +G1 X128.997 Y103.462 E.01397 +G1 X128.884 Y103.7 E.00892 +G2 X128.868 Y104.875 I23.583 J.895 E.03978 +G1 X128.917 Y105.012 E.00492 +G1 X128.881 Y105.044 E.00163 +G1 X128.868 Y105.5 E.01544 +G1 X128.893 Y106.271 E.02611 +G1 X128.995 Y106.469 E.00754 +M73 Q49 S3 +G1 X129.368 Y106.653 E.01408 +G1 X129.368 Y109.308 E.08987 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1228 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.707 Y104.145 I-47.105 J-.292 E.05504 +G1 X120.521 Y104.025 E.00749 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X124.051 Y100.225 E.12951 +G1 X124.051 Y100.521 E.01002 +G1 X124.136 Y100.687 E.00631 +G1 X124.253 Y100.725 E.00416 +G1 X125.774 Y100.725 E.05148 +G1 X125.895 Y100.685 E.00431 +G1 X125.978 Y100.521 E.00622 +G1 X125.978 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12852 +G1 X129.775 Y103.634 E.11539 +G1 X129.479 Y103.634 E.01002 +G1 X129.303 Y103.735 E.00687 +G2 X129.275 Y104.861 I12.17 J.862 E.03814 +G1 X129.37 Y104.936 E.0041 +;WIDTH:0.41646 +G1 X129.465 Y105.012 E.00378 +G1 X129.372 Y105.07 E.0034 +;WIDTH:0.449999 +G1 X129.279 Y105.128 E.00371 +G1 X129.275 Y105.17 E.00143 +G1 X129.275 Y106.097 E.03138 +G1 X129.317 Y106.221 E.00443 +G1 X129.479 Y106.301 E.00612 +G1 X129.775 Y106.301 E.01002 +G1 X129.775 Y109.715 E.11556 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X128.85 Y106.761 Z5.653 F18000 +G1 Z5.6 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1228 +G1 X129.022 Y106.845 E.00648 +G1 X129.022 Y108.654 E.06123 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +G1 E-.7 F2100 +G1 X126.654 Y100.978 Z5.707 F18000 +G1 Z5.6 F720 +G1 E.7 F1500 +G1 F1228 +G1 X128.654 Y100.978 E.0677 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y100.978 E.01246 +G1 X120.978 Y109.022 E.38506 +G1 X120.978 Y108.654 E.01246 +M73 P50 R3 +M73 Q50 S3 +G1 X121.346 Y109.022 E.01762 +G1 X123.346 Y109.022 E.0677 +;LAYER_CHANGE +;Z:5.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;5.8 +M201 X3999.54 Y3999.54 + +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z5.6 F18000 +G1 X129.701 Y105.014 Z5.8 +;AFTER_LAYER_CHANGE +;5.8 +M74 W0.425237 + +G1 X129.701 Y105.014 +G1 Z5.8 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.600836 +G1 F1376 +G1 X129.454 Y105.014 E.01146 +G1 E-.7 F2100 +G1 X129.454 Y105.014 F18000 +G1 X129.368 Y109.368 Z5.876 +G1 Z5.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1376 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.126 Y104.14 E.05521 +G1 X121.016 Y103.87 E.00987 +G1 X120.632 Y103.674 E.01459 +G1 X120.632 Y100.632 E.10297 +G1 X123.558 Y100.632 E.09904 +G1 X123.75 Y101.013 E.01444 +G1 X123.868 Y101.081 E.00461 +G1 X124.112 Y101.132 E.00844 +G1 X125.991 Y101.128 E.0636 +G1 X126.294 Y101.006 E.01106 +G1 X126.479 Y100.632 E.01412 +G1 X129.368 Y100.632 E.09779 +G1 X129.368 Y103.146 E.0851 +G1 X129.003 Y103.321 E.0137 +G1 X128.956 Y103.389 E.0028 +G1 X128.868 Y103.704 E.01107 +G1 X128.868 Y104.736 E.03493 +G1 X128.906 Y104.949 E.00732 +G1 X128.991 Y105.014 E.00362 +G1 X128.906 Y105.078 E.0036 +G1 X128.868 Y105.5 E.01434 +G1 X128.894 Y106.413 E.03092 +G1 X129 Y106.615 E.00772 +G1 X129.368 Y106.793 E.01384 +G1 X129.368 Y109.308 E.08513 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1376 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G2 X120.705 Y104.141 I-43.397 J-.29 E.05518 +G1 X120.521 Y104.025 E.00736 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X123.909 Y100.225 E.1247 +G1 X123.909 Y100.521 E.01002 +G1 X123.992 Y100.685 E.00622 +G1 X124.112 Y100.725 E.00428 +G1 X125.923 Y100.725 E.0613 +G1 X126.047 Y100.683 E.00443 +G1 X126.127 Y100.521 E.00612 +G1 X126.127 Y100.225 E.01002 +G1 X129.775 Y100.225 E.12348 +G1 X129.775 Y103.5 E.11085 +G1 X129.479 Y103.5 E.01002 +M73 P51 R3 +G1 X129.304 Y103.599 E.00681 +G2 X129.275 Y104.748 I11.508 J.864 E.03892 +;WIDTH:0.487709 +G1 X129.32 Y104.814 E.00295 +;WIDTH:0.525418 +G1 X129.365 Y104.881 E.00324 +;WIDTH:0.563127 +G1 X129.41 Y104.947 E.00346 +;WIDTH:0.600836 +G1 X129.454 Y105.014 E.00372 +G1 X129.413 Y105.065 E.00304 +;WIDTH:0.563127 +G1 X129.371 Y105.117 E.00289 +;WIDTH:0.525418 +G1 X129.329 Y105.169 E.00268 +;WIDTH:0.487709 +G1 X129.288 Y105.221 E.00245 +;WIDTH:0.449999 +G1 X129.275 Y105.292 E.00244 +G1 X129.275 Y106.236 E.03195 +G1 X129.319 Y106.362 E.00452 +G1 X129.479 Y106.439 E.00601 +G1 X129.775 Y106.439 E.01002 +G1 X129.775 Y109.715 E.11089 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +M73 Q51 S3 +G1 X128.729 Y106.835 Z5.853 F18000 +G1 Z5.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1376 +G1 X129.022 Y106.987 E.01117 +G1 X129.022 Y108.654 E.05643 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z5.907 F18000 +G1 Z5.8 F720 +G1 E.7 F1500 +G1 F1376 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X129.022 Y101.346 E.01246 +G1 X128.654 Y100.978 E.01762 +G1 X126.663 Y100.993 E.06739 +G1 E-.7 F2100 +G1 X126.663 Y100.993 F18000 +G1 X120.45 Y104.045 Z5.921 +G1 Z5.8 F720 +G1 E.7 F1500 +;TYPE:Ironing +;WIDTH:0.40161 +;HEIGHT:0.0075 +G1 F900 +G1 X120.45 Y102.7 E.00168 +G1 X120.35 Y102.7 E.00012 +G1 X120.35 Y104.045 E.00168 +G1 E-.7 F2100 +G1 X120.45 Y107.345 Z5.858 F18000 +G1 Z5.8 F720 +G1 E.7 F1500 +G1 F900 +G1 X120.45 Y106 E.00168 +G1 X120.35 Y106 E.00012 +G1 X120.35 Y107.345 E.00168 +;LAYER_CHANGE +;Z:6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6 +M201 X3999.53 Y3999.53 + +G1 E-.7 F2100 +G1 X120.35 Y107.345 Z5.8 F18000 +G1 X125.046 Y100.526 Z6 +;AFTER_LAYER_CHANGE +;6 +M74 W0.43656 + +G1 X125.046 Y100.526 +G1 Z6 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.38292 +G1 F1275 +G1 X125.046 Y100.5 E.00074 +G1 F900 +G1 X125.046 Y100.3 E.00565 +M204 P1500 +;TYPE:Overhang perimeter +M106 S255 +G1 X125.046 Y100.072 E.00645 +M106 S252.45 +M204 P4000 +G1 E-.7 F2100 +G1 X125.046 Y100.072 F18000 +G1 X129.368 Y109.368 Z6.179 +G1 Z6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1275 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.94 E.04834 +G1 X120.928 Y107.828 E.01071 +G1 X121.092 Y107.588 E.00984 +G1 X121.132 Y107.371 E.00747 +G1 X121.132 Y103.299 E.13783 +G1 X121.129 Y102.563 E.02491 +M73 P52 R3 +G1 X120.928 Y102.172 E.01488 +G1 X120.632 Y102.06 E.01071 +G1 X120.632 Y100.632 E.04834 +G1 X123.411 Y100.632 E.09407 +G1 X123.577 Y100.989 E.01333 +M73 Q52 S3 +G1 X123.691 Y101.065 E.00464 +G1 X123.97 Y101.132 E.00971 +G1 X124.996 Y101.132 E.03473 +G1 X125.386 Y101.132 E.0132 +G1 X126.14 Y101.128 E.02552 +G1 X126.451 Y101 E.01138 +G1 X126.629 Y100.632 E.01384 +G1 X129.368 Y100.632 E.09271 +G1 X129.368 Y103.012 E.08056 +G1 X129.009 Y103.18 E.01342 +G1 X128.961 Y103.247 E.00279 +G1 X128.868 Y103.57 E.01138 +G1 X128.868 Y104.614 E.03534 +G1 X128.941 Y104.904 E.01012 +G1 X129.11 Y105.015 E.00684 +G1 X128.941 Y105.127 E.00686 +G1 X128.889 Y105.257 E.00474 +G1 X128.868 Y105.5 E.00826 +G1 X128.895 Y106.555 E.03572 +G1 X129.006 Y106.761 E.00792 +G1 X129.368 Y106.932 E.01355 +G1 X129.368 Y109.308 E.08042 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1275 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y107.575 E.07447 +G1 X120.521 Y107.575 E.01002 +G1 X120.657 Y107.523 E.00493 +G1 X120.725 Y107.371 E.00564 +G1 X120.725 Y102.629 E.16051 +G1 X120.657 Y102.477 E.00564 +G1 X120.521 Y102.425 E.00493 +G1 X120.225 Y102.425 E.01002 +G1 X120.225 Y100.225 E.07447 +G1 X123.766 Y100.225 E.11986 +G1 X123.766 Y100.521 E.01002 +G1 X123.839 Y100.677 E.00583 +G1 X123.97 Y100.725 E.00472 +G1 X124.996 Y100.725 E.03473 +G1 X125.021 Y100.625 E.00349 +;WIDTH:0.41646 +G1 X125.046 Y100.526 E.00317 +G1 X125.07 Y100.625 E.00316 +;WIDTH:0.449999 +G1 X125.093 Y100.725 E.00347 +G1 X126.072 Y100.725 E.03314 +G1 X126.198 Y100.681 E.00452 +G1 X126.276 Y100.521 E.00603 +G1 X126.276 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11844 +G1 X129.775 Y103.367 E.10635 +G1 X129.479 Y103.367 E.01002 +G1 X129.322 Y103.44 E.00586 +G1 X129.281 Y103.522 E.0031 +G1 X129.275 Y103.928 E.01374 +G1 X129.282 Y104.667 E.02502 +G1 X129.461 Y104.815 E.00786 +;WIDTH:0.445393 +G1 X129.778 Y104.815 E.01061 +;WIDTH:0.443861 +G1 X129.778 Y105.216 E.01337 +G1 X129.479 Y105.216 E.00997 +;WIDTH:0.449999 +G1 X129.299 Y105.32 E.00704 +G1 X129.275 Y105.416 E.00335 +G1 X129.275 Y106.374 E.03243 +G1 X129.321 Y106.503 E.00464 +G1 X129.479 Y106.578 E.00592 +G1 X129.775 Y106.578 E.01002 +G1 X129.775 Y109.715 E.10618 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z6.106 F18000 +G1 Z6 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1275 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.813 E.04966 +G2 X128.522 Y103.562 I.231 J.695 E.03283 +M73 P53 R3 +G2 X128.554 Y104.864 I9.23 J.422 E.04412 +G3 X128.555 Y105.165 I-.207 J.151 E.01089 +G2 X128.564 Y106.661 I5.706 J.713 E.05078 +G2 X129.022 Y107.13 I.637 J-.165 E.02323 +G1 X129.022 Y108.654 E.05159 +G1 X128.654 Y109.022 E.01762 +M73 Q53 S3 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y101.836 E.01659 +G1 X121.16 Y101.905 E.00659 +G3 X121.473 Y102.53 I-1.369 J1.078 E.02382 +G1 X121.475 Y103.139 E.02061 +;LAYER_CHANGE +;Z:6.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.2 +M201 X3999.52 Y3999.52 + +G1 E-.7 F2100 +G1 X121.475 Y103.139 Z6 F18000 +G1 X125.05 Y100.199 Z6.2 F16718.138 +;AFTER_LAYER_CHANGE +;6.2 +M74 W0.448345 + +G1 X125.05 Y100.199 F18000 +G1 Z6.2 F720 +G1 E.7 F1500 +;TYPE:External perimeter +;WIDTH:0.39964 +G1 F1271 +G1 X125.05 Y100.537 E.01003 +G1 E-.7 F2100 +G1 X125.05 Y100.537 F18000 +G1 X129.368 Y109.368 Z6.372 +G1 Z6.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1271 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.737 E.05521 +G1 X120.945 Y107.611 E.01142 +G1 X121.096 Y107.379 E.00937 +G1 X121.132 Y107.171 E.00715 +G1 X121.132 Y102.829 E.14697 +G1 X121.094 Y102.616 E.00732 +G1 X120.945 Y102.389 E.00919 +G1 X120.632 Y102.263 E.01142 +G1 X120.632 Y100.632 E.05521 +G1 X123.268 Y100.632 E.08923 +G1 X123.43 Y100.985 E.01315 +G1 X123.527 Y101.053 E.00401 +G1 X123.827 Y101.132 E.0105 +G1 X124.873 Y101.132 E.03541 +G1 X124.921 Y101.13 E.00163 +G1 X125.05 Y101.074 E.00476 +G1 X125.086 Y101.115 E.00185 +G1 X125.51 Y101.132 E.01436 +G1 X126.289 Y101.128 E.02637 +G1 X126.608 Y100.994 E.01171 +G1 X126.779 Y100.632 E.01355 +G1 X129.368 Y100.632 E.08763 +G1 X129.368 Y102.877 E.07599 +G1 X129.016 Y103.038 E.0131 +G1 X128.886 Y103.288 E.00954 +G1 X128.868 Y103.779 E.01663 +G1 X128.869 Y104.532 E.02549 +G1 X128.975 Y104.836 E.0109 +G1 X129.285 Y105.016 E.01213 +G1 X128.975 Y105.197 E.01215 +G1 X128.902 Y105.341 E.00546 +G1 X128.868 Y105.543 E.00693 +G1 X128.897 Y106.698 E.03911 +G1 X129.013 Y106.908 E.00812 +G1 X129.368 Y107.072 E.01324 +G1 X129.368 Y109.308 E.07569 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1271 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y107.375 E.08124 +G1 X120.521 Y107.375 E.01002 +G1 X120.663 Y107.318 E.00518 +G1 X120.725 Y107.171 E.0054 +G1 X120.725 Y106.496 E.02285 +G1 X120.725 Y103.504 E.10128 +G1 X120.725 Y102.829 E.02285 +G1 X120.663 Y102.682 E.0054 +M73 P54 R3 +G1 X120.521 Y102.625 E.00518 +G1 X120.225 Y102.625 E.01002 +G1 X120.225 Y100.225 E.08124 +G1 X123.624 Y100.225 E.11505 +G1 X123.624 Y100.521 E.01002 +G1 X123.695 Y100.676 E.00577 +M73 Q54 S3 +G1 X123.827 Y100.725 E.00477 +G1 X124.882 Y100.725 E.03571 +G1 X124.966 Y100.631 E.00427 +;WIDTH:0.42482 +G1 X125.05 Y100.537 E.004 +G1 X125.115 Y100.628 E.00355 +;WIDTH:0.449999 +G1 X125.181 Y100.719 E.00381 +G1 X125.228 Y100.725 E.0016 +G1 X126.221 Y100.725 E.03361 +G1 X126.35 Y100.679 E.00464 +G1 X126.425 Y100.521 E.00592 +G1 X126.425 Y100.225 E.01002 +G1 X129.775 Y100.225 E.11339 +G1 X129.775 Y103.233 E.10182 +G1 X129.479 Y103.233 E.01002 +G1 X129.324 Y103.304 E.00577 +G1 X129.281 Y103.387 E.00316 +G1 X129.275 Y103.779 E.01327 +G1 X129.278 Y104.525 E.02525 +;WIDTH:0.490464 +G1 X129.337 Y104.601 E.00358 +;WIDTH:0.530929 +G1 X129.396 Y104.677 E.0039 +;WIDTH:0.571394 +G1 X129.456 Y104.753 E.00425 +G1 X129.715 Y104.753 E.01138 +;WIDTH:0.570084 +G1 X129.715 Y105.279 E.02306 +G1 X129.479 Y105.279 E.01034 +G1 X129.423 Y105.329 E.00329 +;WIDTH:0.530056 +G1 X129.367 Y105.378 E.00301 +;WIDTH:0.490028 +G1 X129.311 Y105.428 E.00279 +;WIDTH:0.449999 +G1 X129.275 Y105.541 E.00401 +G1 X129.275 Y106.513 E.0329 +G1 X129.323 Y106.644 E.00472 +G1 X129.479 Y106.716 E.00582 +G1 X129.775 Y106.716 E.01002 +G1 X129.775 Y109.715 E.10151 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z6.306 F18000 +G1 Z6.2 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1271 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.675 E.04499 +G2 X128.522 Y103.428 I.237 J.7 E.03292 +G1 X128.522 Y104.496 E.03615 +G2 X128.681 Y105.016 I1.001 J-.021 E.01864 +G2 X128.522 Y105.528 I.844 J.543 E.01837 +G2 X128.566 Y106.806 I6.748 J.409 E.04335 +G2 X129.022 Y107.272 I.64 J-.17 E.02307 +G1 X129.022 Y108.654 E.04678 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +G1 X121.346 Y100.978 E.01762 +G1 X123.067 Y100.978 E.05825 +G1 X123.183 Y101.232 E.00945 +;LAYER_CHANGE +;Z:6.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.4 +M201 X3999.51 Y3999.51 + +G1 E-.7 F2100 +G1 X123.183 Y101.232 Z6.2 F18000 +G1 X129.368 Y109.368 Z6.4 +;AFTER_LAYER_CHANGE +;6.4 +M74 W0.460161 + +G1 X129.368 Y109.368 +G1 Z6.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1262 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.535 E.06204 +G1 X120.96 Y107.396 E.01206 +G1 X121.098 Y107.172 E.00891 +G1 X121.132 Y106.971 E.0069 +G1 X121.132 Y103.685 E.11123 +G1 X121.118 Y102.899 E.02661 +M73 P55 R3 +G1 X120.96 Y102.604 E.01133 +G1 X120.632 Y102.465 E.01206 +G1 X120.632 Y100.632 E.06204 +G1 X123.125 Y100.632 E.08438 +G1 X123.286 Y100.983 E.01307 +M73 Q55 S3 +G1 X123.423 Y101.073 E.00555 +G1 X123.685 Y101.132 E.00909 +G1 X124.74 Y101.132 E.03571 +G1 X124.978 Y101.084 E.00822 +G1 X125.055 Y100.978 E.00443 +G1 X125.131 Y101.084 E.00441 +G1 X125.37 Y101.132 E.00825 +G1 X126.37 Y101.132 E.03385 +G1 X126.588 Y101.092 E.0075 +G1 X126.765 Y100.987 E.00697 +G1 X126.93 Y100.632 E.01325 +G1 X129.368 Y100.632 E.08252 +G1 X129.368 Y102.742 E.07142 +G1 X129.023 Y102.896 E.01279 +G1 X128.887 Y103.15 E.00975 +G1 X128.868 Y103.63 E.01626 +G1 X128.874 Y104.447 E.02766 +G1 X128.986 Y104.725 E.01014 +G1 X129.368 Y104.959 E.01516 +G1 X129.368 Y105.076 E.00396 +G1 X128.986 Y105.31 E.01516 +G1 X128.913 Y105.441 E.00508 +G1 X128.868 Y105.672 E.00797 +G1 X128.868 Y106.651 E.03314 +G1 X128.898 Y106.84 E.00648 +G1 X129.02 Y107.055 E.00837 +G1 X129.368 Y107.212 E.01292 +G1 X129.368 Y109.308 E.07095 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1262 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y107.175 E.08801 +G1 X120.521 Y107.175 E.01002 +M73 P55 R2 +G1 X120.668 Y107.113 E.0054 +G1 X120.725 Y106.971 E.00518 +G1 X120.725 Y103.685 E.11123 +G1 X120.702 Y102.934 E.02543 +G1 X120.521 Y102.825 E.00715 +G1 X120.225 Y102.825 E.01002 +G1 X120.225 Y100.225 E.08801 +G1 X123.481 Y100.225 E.11021 +G1 X123.481 Y100.521 E.01002 +G1 X123.552 Y100.675 E.00574 +G1 X123.685 Y100.725 E.00481 +G1 X124.74 Y100.725 E.03571 +G1 X124.819 Y100.709 E.00273 +G1 X124.858 Y100.622 E.00323 +;WIDTH:0.404589 +G1 X124.897 Y100.535 E.00287 +;WIDTH:0.359179 +G1 X124.897 Y100.179 E.00936 +;WIDTH:0.357976 +G1 X125.212 Y100.179 E.00825 +G1 X125.212 Y100.521 E.00896 +;WIDTH:0.403988 +G1 X125.251 Y100.615 E.00306 +;WIDTH:0.449999 +G1 X125.29 Y100.709 E.00344 +G1 X125.37 Y100.725 E.00276 +G1 X126.37 Y100.725 E.03385 +G1 X126.502 Y100.677 E.00475 +G1 X126.574 Y100.521 E.00582 +G1 X126.574 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10835 +G1 X129.775 Y103.099 E.09728 +G1 X129.479 Y103.099 E.01002 +G1 X129.327 Y103.167 E.00564 +G1 X129.281 Y103.252 E.00327 +G1 X129.275 Y103.63 E.0128 +G1 X129.283 Y104.419 E.02671 +G1 X129.425 Y104.56 E.00677 +G1 X129.775 Y104.567 E.01185 +G1 X129.775 Y105.468 E.0305 +G1 X129.479 Y105.468 E.01002 +G1 X129.314 Y105.551 E.00625 +G1 X129.275 Y105.672 E.0043 +G1 X129.275 Y106.651 E.03314 +G1 X129.326 Y106.786 E.00488 +G1 X129.479 Y106.855 E.00568 +G1 X129.775 Y106.855 E.01002 +G1 X129.775 Y109.715 E.09681 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z6.506 F18000 +G1 Z6.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1262 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P56 R2 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.537 E.04031 +G2 X128.522 Y103.294 I.244 J.705 E.03301 +G1 X128.522 Y104.371 E.03646 +G2 X128.822 Y105.017 I.785 J.029 E.02502 +G2 X128.523 Y105.629 I.477 J.613 E.02387 +G2 X128.568 Y106.951 I6.85 J.427 E.04484 +M73 Q56 S3 +G2 X129.022 Y107.415 I.643 J-.176 E.02295 +G1 X129.022 Y108.654 E.04194 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +G1 X121.346 Y100.978 E.01762 +G1 X122.923 Y100.978 E.05338 +G2 X123.158 Y101.311 I.483 J-.091 E.01422 +;LAYER_CHANGE +;Z:6.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.6 +M201 X3999.49 Y3999.49 + +G1 E-.7 F2100 +G1 X123.158 Y101.311 Z6.4 F18000 +G1 X129.368 Y109.368 Z6.6 +;AFTER_LAYER_CHANGE +;6.6 +M74 W0.472005 + +G1 X129.368 Y109.368 +G1 Z6.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1263 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.333 E.06888 +M73 Q56 S2 +G1 X120.973 Y107.183 E.01261 +G1 X121.101 Y106.965 E.00856 +G1 X121.132 Y106.771 E.00665 +G1 X121.132 Y103.543 E.10926 +G1 X121.112 Y103.073 E.01592 +G1 X120.973 Y102.817 E.00986 +G1 X120.632 Y102.667 E.01261 +G1 X120.632 Y100.632 E.06888 +G1 X122.982 Y100.632 E.07954 +G1 X123.142 Y100.983 E.01306 +G1 X123.323 Y101.091 E.00713 +G1 X123.543 Y101.132 E.00757 +G1 X124.601 Y101.132 E.03581 +G1 X124.66 Y101.129 E.002 +G1 X124.922 Y101.041 E.00936 +G1 X125.06 Y100.817 E.00891 +G1 X125.198 Y101.041 E.00891 +G1 X125.338 Y101.105 E.00521 +G1 X125.518 Y101.132 E.00616 +G1 X126.519 Y101.132 E.03388 +G1 X126.741 Y101.09 E.00765 +G1 X126.923 Y100.979 E.00722 +G1 X127.08 Y100.632 E.01289 +G1 X129.368 Y100.632 E.07745 +G1 X129.368 Y102.607 E.06685 +G1 X129.031 Y102.754 E.01245 +G1 X128.889 Y103.012 E.00997 +G1 X128.868 Y103.481 E.01589 +G1 X128.876 Y104.334 E.02887 +G1 X128.99 Y104.599 E.00976 +G1 X129.22 Y104.786 E.01003 +G1 X129.368 Y104.819 E.00513 +G1 X129.368 Y105.217 E.01347 +G1 X129.22 Y105.25 E.00513 +G1 X128.99 Y105.436 E.01001 +G1 X128.923 Y105.55 E.00448 +G1 X128.868 Y105.803 E.00876 +G1 X128.868 Y106.789 E.03337 +G1 X128.899 Y106.983 E.00665 +G1 X129.028 Y107.202 E.0086 +G1 X129.368 Y107.351 E.01257 +G1 X129.368 Y109.308 E.06624 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1263 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.975 E.09478 +G1 X120.521 Y106.975 E.01002 +G1 X120.672 Y106.909 E.00558 +G1 X120.725 Y106.771 E.005 +G1 X120.725 Y103.229 E.11989 +G1 X120.689 Y103.113 E.00411 +G1 X120.521 Y103.025 E.00642 +G1 X120.225 Y103.025 E.01002 +M73 P57 R2 +G1 X120.225 Y100.225 E.09478 +G1 X123.339 Y100.225 E.10541 +G1 X123.339 Y100.521 E.01002 +G1 X123.409 Y100.675 E.00573 +G1 X123.543 Y100.725 E.00484 +G1 X124.601 Y100.725 E.03581 +G1 X124.661 Y100.716 E.00205 +M73 Q57 S2 +;WIDTH:0.476675 +G1 X124.746 Y100.629 E.00439 +;WIDTH:0.50335 +G1 X124.83 Y100.542 E.00463 +G1 X124.83 Y100.251 E.01114 +;WIDTH:0.501597 +G1 X125.289 Y100.251 E.01751 +G1 X125.289 Y100.521 E.0103 +G1 X125.35 Y100.608 E.00405 +;WIDTH:0.475798 +G1 X125.411 Y100.695 E.00382 +;WIDTH:0.449999 +G1 X125.518 Y100.725 E.00376 +G1 X126.519 Y100.725 E.03388 +G1 X126.654 Y100.674 E.00488 +G1 X126.723 Y100.521 E.00568 +G1 X126.723 Y100.225 E.01002 +G1 X129.775 Y100.225 E.10331 +G1 X129.775 Y102.966 E.09278 +G1 X129.479 Y102.966 E.01002 +G1 X129.329 Y103.031 E.00553 +G1 X129.282 Y103.117 E.00332 +G1 X129.275 Y103.481 E.01232 +G1 X129.279 Y104.274 E.02684 +G1 X129.392 Y104.417 E.00617 +G1 X129.775 Y104.436 E.01298 +G1 X129.775 Y105.599 E.03937 +G1 X129.479 Y105.599 E.01002 +G1 X129.316 Y105.681 E.00618 +G1 X129.275 Y105.803 E.00436 +G1 X129.275 Y106.789 E.03337 +G1 X129.328 Y106.927 E.005 +G1 X129.479 Y106.993 E.00558 +G1 X129.775 Y106.993 E.01002 +G1 X129.775 Y109.715 E.09214 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z6.706 F18000 +G1 Z6.6 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1263 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.399 E.03564 +G2 X128.522 Y103.16 I.253 J.711 E.03309 +G1 X128.522 Y104.241 E.03659 +G2 X128.967 Y105.018 I.903 J-.001 E.03171 +G2 X128.522 Y105.797 I.455 J.776 E.03179 +G2 X128.57 Y107.097 I6.426 J.413 E.04411 +G2 X129.022 Y107.558 I.649 J-.183 E.02279 +G1 X129.022 Y108.654 E.0371 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X120.978 Y101.346 E.01246 +G1 X121.346 Y100.978 E.01762 +G1 X122.78 Y100.978 E.04854 +G2 X123.14 Y101.381 I.551 J-.129 E.01907 +;LAYER_CHANGE +;Z:6.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;6.8 +M201 X3999.48 Y3999.48 + +G1 E-.7 F2100 +G1 X123.14 Y101.381 Z6.6 F18000 +G1 X129.368 Y109.368 Z6.8 +;AFTER_LAYER_CHANGE +;6.8 +M74 W0.483904 + +G1 X129.368 Y109.368 +G1 Z6.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1264 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y107.131 E.07572 +G1 X120.984 Y106.97 E.0131 +G1 X121.103 Y106.758 E.00823 +G1 X121.132 Y106.571 E.00641 +G1 X121.132 Y103.429 E.10635 +G1 X121.105 Y103.248 E.00619 +M73 P58 R2 +G1 X120.987 Y103.034 E.00827 +G1 X120.632 Y102.868 E.01327 +G1 X120.632 Y100.632 E.07569 +G1 X122.84 Y100.632 E.07474 +G1 X123 Y100.982 E.01303 +M73 Q58 S2 +G1 X123.225 Y101.107 E.00871 +G1 X123.4 Y101.132 E.00598 +G1 X124.46 Y101.132 E.03588 +G1 X124.539 Y101.127 E.00268 +G1 X124.817 Y101.017 E.01012 +G1 X125.059 Y100.636 E.01528 +G1 X125.313 Y101.017 E.0155 +G1 X125.449 Y101.091 E.00524 +G1 X125.67 Y101.132 E.00761 +G1 X126.668 Y101.132 E.03378 +G1 X126.895 Y101.088 E.00783 +G1 X127.082 Y100.971 E.00747 +G1 X127.23 Y100.632 E.01252 +G1 X129.368 Y100.632 E.07237 +G1 X129.368 Y102.472 E.06228 +G1 X129.04 Y102.611 E.01206 +G1 X128.89 Y102.873 E.01022 +G1 X128.868 Y103.035 E.00553 +G1 X128.868 Y104.102 E.03612 +G1 X128.93 Y104.37 E.00931 +G1 X129.148 Y104.615 E.0111 +G1 X129.368 Y104.68 E.00776 +G1 X129.368 Y105.357 E.02292 +G1 X129.148 Y105.421 E.00776 +G1 X128.93 Y105.666 E.0111 +G1 X128.868 Y105.935 E.00934 +G1 X128.868 Y106.928 E.03361 +G1 X128.901 Y107.126 E.00679 +G1 X129.037 Y107.35 E.00887 +G1 X129.368 Y107.491 E.01218 +G1 X129.368 Y109.308 E.0615 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1264 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.775 E.10155 +G1 X120.521 Y106.775 E.01002 +G1 X120.676 Y106.704 E.00577 +G1 X120.725 Y106.571 E.0048 +G1 X120.725 Y103.429 E.10635 +G1 X120.677 Y103.297 E.00475 +G1 X120.521 Y103.225 E.00582 +G1 X120.225 Y103.225 E.01002 +G1 X120.225 Y100.225 E.10155 +G1 X123.197 Y100.225 E.1006 +G1 X123.197 Y100.521 E.01002 +G1 X123.267 Y100.675 E.00573 +G1 X123.4 Y100.725 E.00481 +G1 X124.46 Y100.725 E.03588 +G1 X124.533 Y100.711 E.00252 +G1 X124.66 Y100.56 E.00668 +G1 X124.663 Y100.225 E.01134 +G1 X125.466 Y100.225 E.02718 +G1 X125.466 Y100.521 E.01002 +G1 X125.551 Y100.687 E.00631 +G1 X125.67 Y100.725 E.00423 +G1 X126.668 Y100.725 E.03378 +G1 X126.806 Y100.671 E.00502 +G1 X126.872 Y100.521 E.00555 +G1 X126.872 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09826 +G1 X129.775 Y102.832 E.08824 +G1 X129.479 Y102.832 E.01002 +G1 X129.332 Y102.894 E.0054 +G1 X129.282 Y102.981 E.0034 +G1 X129.275 Y103.332 E.01188 +G1 X129.277 Y104.129 E.02698 +G1 X129.368 Y104.273 E.00577 +G1 X129.775 Y104.305 E.01382 +G1 X129.775 Y105.731 E.04827 +G1 X129.479 Y105.731 E.01002 +G1 X129.315 Y105.813 E.00621 +G1 X129.275 Y105.935 E.00435 +G1 X129.275 Y106.928 E.03361 +G1 X129.331 Y107.069 E.00514 +G1 X129.479 Y107.131 E.00543 +G1 X129.775 Y107.131 E.01002 +G1 X129.775 Y109.715 E.08747 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z6.906 F18000 +G1 Z6.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1264 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P59 R2 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.26 E.03094 +G2 X128.522 Y103.026 I.263 J.718 E.03319 +G1 X128.522 Y104.107 E.03659 +G2 X129.022 Y104.93 I.931 J-.002 E.03426 +G1 X129.022 Y105.106 E.00596 +G2 X128.522 Y105.927 I.438 J.83 E.03416 +M73 Q59 S2 +G2 X128.573 Y107.243 I6.286 J.417 E.04466 +G2 X129.022 Y107.702 I.656 J-.193 E.02262 +G1 X129.022 Y108.654 E.03222 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y102.668 E.04475 +G3 X121.429 Y103.129 I-.182 J.629 E.02282 +;LAYER_CHANGE +;Z:7 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7 +M201 X3999.47 Y3999.47 + +G1 E-.7 F2100 +G1 X121.429 Y103.129 Z6.8 F18000 +G1 X129.368 Y109.368 Z7 +;AFTER_LAYER_CHANGE +;7 +M74 W0.495817 + +G1 X129.368 Y109.368 +G1 Z7 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1264 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.93 E.08252 +G1 X120.994 Y106.758 E.01357 +G1 X121.105 Y106.552 E.00792 +G1 X121.132 Y106.371 E.00619 +G1 X121.132 Y104.318 E.06949 +G1 X121.127 Y103.551 E.02596 +G1 X120.986 Y103.232 E.01181 +G1 X120.632 Y103.069 E.01319 +G1 X120.632 Y100.632 E.08249 +G1 X122.696 Y100.632 E.06986 +G1 X122.848 Y100.974 E.01267 +G1 X123.107 Y101.113 E.00995 +G1 X123.629 Y101.132 E.01768 +G1 X124.418 Y101.124 E.02671 +G1 X124.684 Y101.011 E.00978 +G1 X124.881 Y100.757 E.01088 +G1 X124.907 Y100.632 E.00432 +G1 X125.233 Y100.632 E.01103 +G1 X125.258 Y100.757 E.00431 +G1 X125.456 Y101.011 E.0109 +G1 X125.574 Y101.08 E.00463 +G1 X125.821 Y101.132 E.00854 +G1 X126.817 Y101.132 E.03371 +G1 X127.049 Y101.086 E.00801 +G1 X127.235 Y100.967 E.00747 +G1 X127.381 Y100.632 E.01237 +G1 X129.368 Y100.632 E.06726 +G1 X129.368 Y102.336 E.05768 +G1 X129.049 Y102.467 E.01167 +G1 X128.891 Y102.734 E.0105 +G1 X128.868 Y102.902 E.00574 +G1 X128.868 Y103.971 E.03618 +G1 X128.911 Y104.197 E.00779 +G1 X129.094 Y104.446 E.01046 +G1 X129.368 Y104.542 E.00983 +G1 X129.368 Y105.495 E.03226 +G1 X129.094 Y105.592 E.00984 +G1 X128.911 Y105.841 E.01046 +G1 X128.868 Y106.066 E.00775 +G1 X128.868 Y107.066 E.03385 +G1 X128.903 Y107.27 E.00701 +G1 X129.047 Y107.498 E.00913 +G1 X129.368 Y107.631 E.01176 +G1 X129.368 Y109.308 E.05676 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1264 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y106.575 E.10832 +G1 X120.521 Y106.575 E.01002 +G1 X120.679 Y106.5 E.00592 +G1 X120.725 Y106.371 E.00464 +G1 X120.725 Y103.629 E.09281 +G1 X120.676 Y103.496 E.0048 +M73 P60 R2 +G1 X120.521 Y103.425 E.00577 +G1 X120.225 Y103.425 E.01002 +G1 X120.225 Y100.225 E.10832 +G1 X123.054 Y100.225 E.09576 +G1 X123.054 Y100.521 E.01002 +G1 X123.121 Y100.672 E.00559 +G1 X123.208 Y100.719 E.00335 +G1 X123.629 Y100.725 E.01425 +G1 X124.367 Y100.719 E.02498 +M73 Q60 S2 +G1 X124.506 Y100.6 E.00619 +G1 X124.522 Y100.225 E.0127 +G1 X125.618 Y100.225 E.0371 +G1 X125.618 Y100.521 E.01002 +G1 X125.7 Y100.685 E.00621 +G1 X125.821 Y100.725 E.00431 +G1 X126.817 Y100.725 E.03371 +G1 X126.957 Y100.67 E.00509 +G1 X127.021 Y100.521 E.00549 +G1 X127.021 Y100.225 E.01002 +G1 X129.775 Y100.225 E.09322 +G1 X129.775 Y102.698 E.08371 +G1 X129.479 Y102.698 E.01002 +G1 X129.335 Y102.757 E.00527 +G1 X129.275 Y102.902 E.00531 +G1 X129.275 Y103.971 E.03618 +G1 X129.304 Y104.076 E.00369 +G1 X129.479 Y104.175 E.00681 +G1 X129.775 Y104.175 E.01002 +G1 X129.775 Y105.863 E.05714 +G1 X129.479 Y105.863 E.01002 +G1 X129.351 Y105.908 E.00459 +G1 X129.275 Y106.066 E.00593 +G1 X129.275 Y107.066 E.03385 +G1 X129.335 Y107.21 E.00528 +G1 X129.479 Y107.27 E.00528 +G1 X129.775 Y107.27 E.01002 +G1 X129.775 Y109.715 E.08276 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z7.106 F18000 +G1 Z7 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1264 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.121 E.02623 +G2 X128.558 Y102.635 I.213 J.659 E.02454 +G2 X128.562 Y104.248 I5.794 J.791 E.05477 +G2 X129.022 Y104.775 I.758 J-.197 E.02455 +G1 X129.022 Y105.263 E.01652 +G2 X128.53 Y105.941 I.334 J.76 E.02972 +G2 X128.575 Y107.39 I6.379 J.525 E.04918 +G2 X129.022 Y107.846 I.666 J-.206 E.02245 +G1 X129.022 Y108.654 E.02735 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y102.868 E.05152 +G3 X121.338 Y103.151 I-.092 J.488 E.01611 +;LAYER_CHANGE +;Z:7.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.2 +M201 X3999.46 Y3999.46 + +G1 E-.7 F2100 +G1 X121.338 Y103.151 Z7 F18000 +G1 X129.368 Y109.368 Z7.2 +;AFTER_LAYER_CHANGE +;7.2 +M74 W0.507736 + +G1 X129.368 Y109.368 +G1 Z7.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1263 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.728 E.08936 +G1 X121.002 Y106.548 E.01393 +M73 P61 R2 +G1 X121.106 Y106.347 E.00766 +G1 X121.132 Y106.171 E.00602 +G1 X121.132 Y104.984 E.04018 +G1 X121.114 Y103.683 E.04404 +G1 X120.99 Y103.437 E.00932 +G1 X120.632 Y103.27 E.01337 +G1 X120.632 Y100.632 E.08929 +G1 X122.552 Y100.632 E.06499 +G1 X122.696 Y100.966 E.01231 +M73 Q61 S2 +G1 X122.846 Y101.07 E.00618 +G1 X123.115 Y101.132 E.00934 +G1 X124.177 Y101.132 E.03595 +G1 X124.443 Y101.071 E.00924 +G1 X124.696 Y100.843 E.01153 +G1 X124.756 Y100.632 E.00743 +G1 X125.394 Y100.632 E.0216 +G1 X125.454 Y100.843 E.00743 +G1 X125.707 Y101.071 E.01153 +G1 X125.973 Y101.132 E.00924 +G1 X126.967 Y101.132 E.03365 +G1 X127.204 Y101.084 E.00819 +G1 X127.385 Y100.967 E.0073 +G1 X127.532 Y100.632 E.01238 +G1 X129.368 Y100.632 E.06215 +G1 X129.368 Y102.201 E.05311 +G1 X129.06 Y102.323 E.01121 +G1 X128.893 Y102.594 E.01077 +G1 X128.868 Y102.768 E.00595 +G1 X128.868 Y103.84 E.03629 +G1 X128.898 Y104.031 E.00654 +G1 X129.054 Y104.279 E.00992 +G1 X129.368 Y104.406 E.01146 +G1 X129.368 Y105.632 E.0415 +G1 X129.054 Y105.759 E.01146 +G1 X128.917 Y105.959 E.00821 +G1 X128.868 Y106.198 E.00826 +G1 X128.868 Y107.205 E.03409 +G1 X128.905 Y107.414 E.00718 +G1 X129.058 Y107.647 E.00944 +G1 X129.368 Y107.771 E.0113 +G1 X129.368 Y109.308 E.05203 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1263 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y106.375 E.11509 +G1 X120.521 Y106.375 E.01002 +G1 X120.682 Y106.297 E.00606 +G1 X120.725 Y106.171 E.00451 +G1 X120.725 Y104.984 E.04018 +G1 X120.705 Y103.741 E.04208 +G1 X120.521 Y103.625 E.00736 +G1 X120.225 Y103.625 E.01002 +G1 X120.225 Y100.225 E.11509 +G1 X122.912 Y100.225 E.09095 +G1 X122.912 Y100.521 E.01002 +G1 X122.976 Y100.67 E.00549 +G1 X123.115 Y100.725 E.00506 +G1 X124.217 Y100.721 E.0373 +G1 X124.35 Y100.629 E.00547 +G1 X124.38 Y100.225 E.01371 +G1 X125.77 Y100.225 E.04705 +G1 X125.77 Y100.521 E.01002 +G1 X125.852 Y100.685 E.00621 +G1 X125.973 Y100.725 E.00431 +G1 X126.967 Y100.725 E.03365 +G1 X127.106 Y100.67 E.00506 +G1 X127.17 Y100.521 E.00549 +G1 X127.17 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08818 +G1 X129.775 Y102.564 E.07917 +G1 X129.479 Y102.564 E.01002 +G1 X129.339 Y102.62 E.0051 +G1 X129.275 Y102.768 E.00546 +G1 X129.275 Y103.84 E.03629 +G1 X129.304 Y103.945 E.00369 +G1 X129.479 Y104.044 E.00681 +G1 X129.775 Y104.044 E.01002 +G1 X129.775 Y105.995 E.06604 +G1 X129.479 Y105.995 E.01002 +G1 X129.337 Y106.052 E.00518 +G1 X129.275 Y106.198 E.00537 +G1 X129.275 Y107.205 E.03409 +G1 X129.338 Y107.352 E.00541 +G1 X129.479 Y107.408 E.00514 +G1 X129.775 Y107.408 E.01002 +G1 X129.775 Y109.715 E.07809 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z7.306 F18000 +G1 Z7.2 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1263 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P62 R2 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y101.982 E.02153 +G2 X128.56 Y102.493 I.226 J.668 E.02436 +G2 X128.553 Y104.084 I6.706 J.827 E.05398 +G2 X129.022 Y104.623 I.706 J-.14 E.02531 +M73 Q62 S2 +G1 X129.022 Y105.415 E.02681 +G2 X128.553 Y105.955 I.237 J.68 E.02534 +G2 X128.578 Y107.537 I5.444 J.703 E.05374 +G1 X128.823 Y107.91 E.01511 +G1 X129.022 Y107.99 E.00726 +G1 X129.022 Y108.654 E.02248 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.071 E.05839 +G1 X121.228 Y103.187 E.00933 +;LAYER_CHANGE +;Z:7.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.4 +M201 X3999.44 Y3999.44 + +G1 E-.7 F2100 +G1 X121.228 Y103.187 Z7.2 F18000 +G1 X129.368 Y109.368 Z7.4 +;AFTER_LAYER_CHANGE +;7.4 +M74 W0.519659 + +G1 X129.368 Y109.368 +G1 Z7.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1264 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.527 E.09616 +G1 X121.01 Y106.337 E.01432 +G1 X121.085 Y106.206 E.00511 +G1 X121.132 Y105.971 E.00811 +G1 X121.132 Y104.035 E.06553 +G1 X121.095 Y103.819 E.00742 +G1 X120.995 Y103.643 E.00685 +G1 X120.632 Y103.47 E.01361 +G1 X120.632 Y100.632 E.09606 +G1 X122.408 Y100.632 E.06012 +G1 X122.544 Y100.956 E.01189 +G1 X122.773 Y101.099 E.00914 +G1 X122.973 Y101.132 E.00686 +G1 X124.029 Y101.132 E.03574 +G1 X124.26 Y101.089 E.00795 +G1 X124.511 Y100.904 E.01055 +G1 X124.607 Y100.632 E.00976 +G1 X125.553 Y100.632 E.03202 +G1 X125.649 Y100.904 E.00976 +G1 X125.9 Y101.089 E.01055 +G1 X126.125 Y101.132 E.00775 +G1 X127.116 Y101.132 E.03354 +G1 X127.359 Y101.081 E.0084 +G1 X127.534 Y100.967 E.00707 +G1 X127.682 Y100.632 E.0124 +G1 X129.368 Y100.632 E.05707 +G1 X129.368 Y102.066 E.04854 +G1 X129.072 Y102.179 E.01072 +G1 X128.895 Y102.454 E.01107 +G1 X128.868 Y102.884 E.01458 +G1 X128.892 Y103.878 E.03366 +G1 X129.023 Y104.116 E.0092 +G1 X129.368 Y104.271 E.0128 +G1 X129.368 Y105.769 E.05071 +G1 X129.023 Y105.923 E.01279 +G1 X128.921 Y106.08 E.00634 +G1 X128.868 Y106.33 E.00865 +G1 X128.868 Y107.343 E.03429 +G1 X128.907 Y107.558 E.0074 +G1 X129.07 Y107.797 E.00979 +G1 X129.368 Y107.911 E.0108 +G1 X129.368 Y109.308 E.04729 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1264 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y106.175 E.12186 +G1 X120.521 Y106.175 E.01002 +M73 P63 R2 +G1 X120.684 Y106.093 E.00618 +G1 X120.725 Y105.971 E.00436 +G1 X120.725 Y104.959 E.03425 +G1 X120.7 Y103.93 E.03484 +G1 X120.521 Y103.825 E.00702 +G1 X120.225 Y103.825 E.01002 +G1 X120.225 Y100.225 E.12186 +G1 X122.769 Y100.225 E.08611 +G1 X122.769 Y100.521 E.01002 +M73 Q63 S2 +G1 X122.83 Y100.666 E.00532 +G1 X122.973 Y100.725 E.00524 +G1 X124.029 Y100.725 E.03574 +G1 X124.14 Y100.696 E.00388 +G1 X124.239 Y100.521 E.00681 +G1 X124.239 Y100.225 E.01002 +G1 X125.921 Y100.225 E.05693 +G1 X125.921 Y100.521 E.01002 +G1 X125.966 Y100.649 E.00459 +G1 X126.125 Y100.725 E.00597 +G1 X127.116 Y100.725 E.03354 +G1 X127.255 Y100.67 E.00506 +G1 X127.319 Y100.521 E.00549 +G1 X127.319 Y100.225 E.01002 +G1 X129.775 Y100.225 E.08313 +G1 X129.775 Y102.431 E.07467 +G1 X129.479 Y102.431 E.01002 +G1 X129.343 Y102.482 E.00492 +G1 X129.275 Y102.634 E.00564 +G1 X129.275 Y103.709 E.03639 +G1 X129.304 Y103.814 E.00369 +G1 X129.479 Y103.913 E.00681 +G1 X129.775 Y103.913 E.01002 +G1 X129.775 Y106.127 E.07494 +G1 X129.479 Y106.127 E.01002 +G1 X129.327 Y106.194 E.00562 +G1 X129.275 Y106.33 E.00493 +G1 X129.275 Y107.343 E.03429 +G1 X129.342 Y107.495 E.00562 +G1 X129.479 Y107.547 E.00496 +G1 X129.775 Y107.547 E.01002 +G1 X129.775 Y109.715 E.07338 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z7.506 F18000 +G1 Z7.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1264 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y101.842 E.01679 +G2 X128.563 Y102.35 I.243 J.681 E.02414 +G2 X128.558 Y103.973 I6.333 J.833 E.05509 +G2 X129.022 Y104.475 I.651 J-.136 E.0243 +G1 X129.022 Y105.564 E.03686 +G2 X128.539 Y106.147 I.21 J.666 E.02708 +G2 X128.582 Y107.686 I5.809 J.608 E.05227 +G1 X128.841 Y108.065 E.01554 +G1 X129.022 Y108.135 E.00657 +G1 X129.022 Y108.654 E.01757 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.274 E.06526 +G1 X121.043 Y103.305 E.00244 +;LAYER_CHANGE +;Z:7.6 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.6 +M201 X3999.43 Y3999.43 + +G1 E-.7 F2100 +G1 X121.043 Y103.305 Z7.4 F18000 +G1 X129.368 Y109.368 Z7.6 +;AFTER_LAYER_CHANGE +;7.6 +M74 W0.531582 + +G1 X129.368 Y109.368 +G1 Z7.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1263 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.325 E.103 +M73 P64 R2 +G1 X121.017 Y106.128 E.01464 +G1 X121.088 Y106 E.00495 +G1 X121.132 Y105.771 E.00789 +G1 X121.132 Y104.927 E.02857 +G1 X121.129 Y104.171 E.02559 +G1 X121 Y103.85 E.01171 +G1 X120.632 Y103.671 E.01385 +G1 X120.632 Y100.632 E.10287 +G1 X122.264 Y100.632 E.05524 +M73 Q64 S2 +G1 X122.391 Y100.945 E.01143 +G1 X122.702 Y101.118 E.01205 +G1 X122.83 Y101.132 E.00436 +G1 X123.894 Y101.132 E.03602 +G1 X124.053 Y101.111 E.00543 +G1 X124.33 Y100.949 E.01086 +G1 X124.459 Y100.632 E.01158 +G1 X125.711 Y100.632 E.04238 +G1 X125.841 Y100.949 E.0116 +G1 X126.036 Y101.083 E.00801 +G1 X126.276 Y101.132 E.00829 +G1 X127.265 Y101.132 E.03348 +G1 X127.515 Y101.078 E.00866 +G1 X127.683 Y100.967 E.00682 +G1 X127.833 Y100.632 E.01242 +G1 X129.368 Y100.632 E.05196 +G1 X129.368 Y101.93 E.04394 +G1 X129.085 Y102.033 E.01019 +G1 X128.897 Y102.314 E.01144 +G1 X128.868 Y102.735 E.01428 +G1 X128.88 Y103.699 E.03263 +G1 X128.999 Y103.957 E.00962 +G1 X129.368 Y104.136 E.01388 +G1 X129.368 Y105.905 E.05988 +G1 X128.999 Y106.084 E.01388 +G1 X128.925 Y106.204 E.00477 +G1 X128.868 Y106.462 E.00894 +G1 X128.868 Y107.482 E.03453 +G1 X128.91 Y107.703 E.00761 +G1 X129.083 Y107.947 E.01012 +G1 X129.368 Y108.052 E.01028 +G1 X129.368 Y109.308 E.04251 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1263 +G1 X120.9 Y109.775 E.30041 +G1 X120.225 Y109.775 E.02285 +G1 X120.225 Y105.975 E.12863 +G1 X120.521 Y105.975 E.01002 +G1 X120.687 Y105.89 E.00631 +G1 X120.725 Y105.771 E.00423 +G1 X120.725 Y104.927 E.02857 +G1 X120.704 Y104.139 E.02668 +G1 X120.521 Y104.025 E.0073 +G1 X120.225 Y104.025 E.01002 +G1 X120.225 Y100.225 E.12863 +G1 X122.627 Y100.225 E.0813 +G1 X122.627 Y100.521 E.01002 +G1 X122.684 Y100.663 E.00518 +G1 X122.83 Y100.725 E.00537 +G1 X123.894 Y100.725 E.03602 +G1 X123.999 Y100.696 E.00369 +G1 X124.097 Y100.521 E.00679 +G1 X124.097 Y100.225 E.01002 +G1 X126.073 Y100.225 E.06689 +G1 X126.073 Y100.521 E.01002 +G1 X126.131 Y100.664 E.00522 +G1 X126.276 Y100.725 E.00532 +G1 X127.265 Y100.725 E.03348 +G1 X127.404 Y100.67 E.00506 +G1 X127.468 Y100.521 E.00549 +G1 X127.468 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07809 +G1 X129.775 Y102.297 E.07013 +G1 X129.479 Y102.297 E.01002 +G1 X129.347 Y102.345 E.00475 +G1 X129.285 Y102.438 E.00378 +G1 X129.275 Y102.735 E.01006 +G1 X129.294 Y103.665 E.03149 +G1 X129.479 Y103.782 E.00741 +G1 X129.775 Y103.782 E.01002 +G1 X129.775 Y106.258 E.08381 +G1 X129.479 Y106.258 E.01002 +G1 X129.319 Y106.336 E.00603 +G1 X129.275 Y106.462 E.00452 +G1 X129.275 Y107.482 E.03453 +G1 X129.347 Y107.637 E.00578 +G1 X129.479 Y107.685 E.00475 +G1 X129.775 Y107.685 E.01002 +G1 X129.775 Y109.715 E.06871 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z7.706 F18000 +G1 Z7.6 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1263 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P65 R2 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y101.701 E.01202 +G2 X128.522 Y102.489 I.33 J.762 E.03354 +G2 X128.565 Y103.865 I7.706 J.45 E.04666 +M73 Q65 S2 +G2 X129.022 Y104.33 I.616 J-.149 E.02318 +G1 X129.022 Y105.711 E.04675 +G2 X128.545 Y106.251 I.177 J.637 E.02579 +G2 X128.586 Y107.835 I5.499 J.651 E.05382 +G1 X128.861 Y108.221 E.01604 +G1 X129.022 Y108.28 E.0058 +G1 X129.022 Y108.654 E.01266 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +;LAYER_CHANGE +;Z:7.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;7.8 +M201 X3999.42 Y3999.42 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z7.6 F18000 +G1 X129.368 Y109.368 Z7.8 +;AFTER_LAYER_CHANGE +;7.8 +M74 W0.543504 + +G1 X129.368 Y109.368 +G1 Z7.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1260 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y106.124 E.10981 +G1 X121.023 Y105.919 E.01494 +G1 X121.09 Y105.794 E.0048 +G1 X121.132 Y105.571 E.00768 +G1 X121.132 Y104.429 E.03866 +G1 X121.039 Y104.105 E.01141 +G1 X120.632 Y103.873 E.01586 +G1 X120.632 Y100.632 E.1097 +G1 X122.12 Y100.632 E.05037 +G1 X122.237 Y100.933 E.01093 +G1 X122.439 Y101.079 E.00844 +G1 X122.688 Y101.132 E.00862 +G1 X123.752 Y101.132 E.03602 +G1 X123.931 Y101.105 E.00613 +G1 X124.153 Y100.982 E.00859 +G1 X124.312 Y100.632 E.01301 +G1 X125.868 Y100.632 E.05267 +G1 X126.028 Y100.982 E.01303 +G1 X126.176 Y101.078 E.00597 +G1 X126.428 Y101.132 E.00872 +G1 X127.414 Y101.132 E.03337 +G1 X127.672 Y101.075 E.00894 +G1 X127.832 Y100.966 E.00655 +G1 X127.984 Y100.632 E.01242 +G1 X129.368 Y100.632 E.04685 +G1 X129.368 Y101.795 E.03937 +G1 X129.1 Y101.887 E.00959 +G1 X129.038 Y101.944 E.00285 +G1 X128.9 Y102.172 E.00902 +G1 X128.868 Y102.586 E.01406 +G1 X128.882 Y103.578 E.03358 +G1 X128.98 Y103.801 E.00825 +G1 X129.368 Y104.001 E.01478 +G1 X129.368 Y106.04 E.06902 +G1 X128.98 Y106.241 E.01479 +G1 X128.948 Y106.291 E.00201 +G1 X128.868 Y106.594 E.01061 +G1 X128.868 Y107.62 E.03473 +G1 X128.912 Y107.849 E.00789 +G1 X129.099 Y108.098 E.01054 +G1 X129.368 Y108.192 E.00965 +G1 X129.368 Y109.308 E.03778 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1260 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +G1 X120.225 Y106.65 E.08293 +M73 P66 R2 +G1 X120.225 Y105.775 E.02962 +G1 X120.521 Y105.775 E.01002 +G1 X120.689 Y105.687 E.00642 +G1 X120.725 Y105.571 E.00411 +G1 X120.725 Y104.429 E.03866 +G1 X120.694 Y104.321 E.0038 +G1 X120.521 Y104.225 E.0067 +G1 X120.225 Y104.225 E.01002 +G1 X120.225 Y103.35 E.02962 +G1 X120.225 Y100.9 E.08293 +G1 X120.225 Y100.225 E.02285 +G1 X122.484 Y100.225 E.07646 +G1 X122.484 Y100.521 E.01002 +M73 Q66 S2 +G1 X122.537 Y100.659 E.005 +G1 X122.688 Y100.725 E.00558 +G1 X123.752 Y100.725 E.03602 +G1 X123.836 Y100.707 E.00291 +G1 X123.956 Y100.521 E.00749 +G1 X123.956 Y100.225 E.01002 +G1 X126.225 Y100.225 E.0768 +G1 X126.225 Y100.521 E.01002 +G1 X126.295 Y100.675 E.00573 +G1 X126.428 Y100.725 E.00481 +G1 X127.414 Y100.725 E.03337 +G1 X127.553 Y100.67 E.00506 +G1 X127.617 Y100.521 E.00549 +G1 X127.617 Y100.225 E.01002 +G1 X129.775 Y100.225 E.07305 +G1 X129.775 Y102.163 E.0656 +G1 X129.479 Y102.163 E.01002 +G1 X129.352 Y102.207 E.00455 +G1 X129.286 Y102.302 E.00392 +G1 X129.275 Y102.586 E.00962 +G1 X129.29 Y103.526 E.03182 +G1 X129.479 Y103.651 E.00767 +G1 X129.775 Y103.651 E.01002 +G1 X129.775 Y106.39 E.09271 +G1 X129.479 Y106.39 E.01002 +G1 X129.312 Y106.476 E.00636 +G1 X129.275 Y106.594 E.00419 +G1 X129.275 Y107.62 E.03473 +G1 X129.352 Y107.78 E.00601 +G1 X129.479 Y107.824 E.00455 +G1 X129.775 Y107.824 E.01002 +G1 X129.775 Y109.715 E.06401 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z7.906 F18000 +G1 Z7.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1260 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y101.56 E.00724 +G2 X128.522 Y102.355 I.355 J.779 E.03365 +G1 X128.523 Y103.491 E.03845 +G2 X129.022 Y104.186 I.706 J.019 E.03111 +G1 X129.022 Y105.855 E.05649 +G2 X128.522 Y106.584 I.209 J.68 E.03229 +G2 X128.59 Y107.985 I5.477 J.435 E.04761 +G1 X128.884 Y108.378 E.01661 +G1 X129.022 Y108.426 E.00495 +G1 X129.022 Y108.654 E.00772 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +M106 S255 +;LAYER_CHANGE +;Z:8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8 +M201 X3999.4 Y3999.4 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z7.8 F18000 +G1 X129.368 Y109.368 Z8 +;AFTER_LAYER_CHANGE +;8 +M74 W0.555426 + +G1 X129.368 Y109.368 +G1 Z8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y105.923 E.11661 +M73 P67 R2 +G1 X121.029 Y105.711 E.01523 +G1 X121.091 Y105.59 E.0046 +G1 X121.132 Y105.371 E.00754 +G1 X121.128 Y104.555 E.02762 +G1 X121.014 Y104.267 E.01048 +G1 X120.632 Y104.074 E.01449 +G1 X120.632 Y100.632 E.11651 +M73 Q67 S2 +G1 X121.976 Y100.632 E.04549 +G1 X122.082 Y100.919 E.01036 +G1 X122.325 Y101.091 E.01008 +G1 X122.545 Y101.132 E.00757 +G1 X123.611 Y101.132 E.03608 +G1 X123.708 Y101.124 E.00329 +G1 X123.98 Y101.008 E.01001 +G1 X124.167 Y100.632 E.01421 +G1 X126.024 Y100.632 E.06286 +G1 X126.21 Y101.008 E.0142 +G1 X126.319 Y101.073 E.0043 +G1 X126.58 Y101.132 E.00906 +G1 X127.563 Y101.132 E.03327 +G1 X127.829 Y101.071 E.00924 +G1 X128.046 Y100.895 E.00946 +G1 X128.136 Y100.632 E.00941 +G1 X129.368 Y100.632 E.0417 +G1 X129.368 Y101.659 E.03476 +G1 X129.053 Y101.795 E.01161 +G1 X128.903 Y102.03 E.00944 +G1 X128.868 Y102.233 E.00697 +G1 X128.868 Y103.317 E.03669 +G1 X128.905 Y103.528 E.00725 +G1 X129.076 Y103.777 E.01022 +G1 X129.368 Y103.886 E.01055 +G1 X129.368 Y106.156 E.07684 +G1 X129.076 Y106.266 E.01056 +G1 X128.93 Y106.457 E.00814 +G1 X128.868 Y106.725 E.00931 +G1 X128.868 Y107.759 E.035 +G1 X128.916 Y107.995 E.00815 +G1 X129.116 Y108.25 E.01097 +G1 X129.368 Y108.333 E.00898 +G1 X129.368 Y109.308 E.033 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1200 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +G1 X120.225 Y106.45 E.0897 +G1 X120.225 Y105.575 E.02962 +G1 X120.521 Y105.575 E.01002 +G1 X120.691 Y105.485 E.00651 +G1 X120.725 Y105.373 E.00396 +G1 X120.725 Y104.629 E.02518 +G1 X120.686 Y104.508 E.0043 +G1 X120.521 Y104.425 E.00625 +G1 X120.225 Y104.425 E.01002 +G1 X120.225 Y103.55 E.02962 +G1 X120.225 Y100.9 E.0897 +G1 X120.225 Y100.225 E.02285 +G1 X122.342 Y100.225 E.07166 +G1 X122.342 Y100.521 E.01002 +G1 X122.391 Y100.654 E.0048 +G1 X122.545 Y100.725 E.00574 +G1 X123.611 Y100.725 E.03608 +G1 X123.686 Y100.711 E.00258 +G1 X123.814 Y100.521 E.00775 +G1 X123.814 Y100.225 E.01002 +G1 X126.376 Y100.225 E.08672 +G1 X126.376 Y100.521 E.01002 +G1 X126.457 Y100.684 E.00616 +G1 X126.58 Y100.725 E.00439 +G1 X127.563 Y100.725 E.03327 +G1 X127.702 Y100.67 E.00506 +G1 X127.766 Y100.521 E.00549 +G1 X127.766 Y100.225 E.01002 +G1 X129.775 Y100.225 E.068 +G1 X129.775 Y102.029 E.06106 +G1 X129.479 Y102.029 E.01002 +G1 X129.337 Y102.087 E.00519 +G1 X129.275 Y102.233 E.00537 +G1 X129.275 Y103.317 E.03669 +G1 X129.307 Y103.427 E.00388 +G1 X129.479 Y103.521 E.00663 +G1 X129.775 Y103.521 E.01002 +G1 X129.775 Y106.522 E.10158 +G1 X129.479 Y106.522 E.01002 +G1 X129.344 Y106.572 E.00487 +G1 X129.275 Y106.725 E.00568 +G1 X129.275 Y107.759 E.035 +G1 X129.358 Y107.923 E.00622 +G1 X129.479 Y107.962 E.0043 +G1 X129.775 Y107.962 E.01002 +G1 X129.775 Y109.715 E.05934 +G1 X129.379 Y109.774 F18000 +G1 X129.022 Y108.818 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G1 X129.022 Y108.654 E.00555 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +M73 P68 R2 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +G1 E-.7 F2100 +M73 Q68 S2 +G1 X123.346 Y109.022 Z8.107 F18000 +G1 Z8 F720 +G1 E.7 F1500 +G1 F1200 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y101.182 E.00555 +M106 S252.45 +;LAYER_CHANGE +;Z:8.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.2 +M201 X3999.39 Y3999.39 + +G1 E-.7 F2100 +G1 X129.022 Y101.182 Z8 F18000 +G1 X129.368 Y109.368 Z8.2 +;AFTER_LAYER_CHANGE +;8.2 +M74 W0.566558 + +G1 X129.368 Y109.368 +G1 Z8.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1229 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y105.722 E.12341 +G1 X121.034 Y105.503 E.0155 +G1 X121.108 Y105.342 E.006 +G1 X121.125 Y104.735 E.02055 +G1 X121.02 Y104.476 E.00946 +G1 X120.632 Y104.275 E.01479 +G1 X120.632 Y100.632 E.12331 +G1 X121.831 Y100.632 E.04058 +G1 X121.927 Y100.904 E.00976 +G1 X122.175 Y101.088 E.01045 +G1 X122.403 Y101.132 E.00786 +G1 X122.753 Y101.132 E.01185 +G1 X123.6 Y101.118 E.02867 +G1 X123.812 Y101.027 E.00781 +G1 X124.021 Y100.632 E.01513 +G1 X126.18 Y100.632 E.07308 +G1 X126.389 Y101.027 E.01513 +G1 X126.464 Y101.07 E.00293 +G1 X126.731 Y101.132 E.00928 +G1 X127.712 Y101.132 E.03321 +G1 X127.988 Y101.066 E.00961 +G1 X128.201 Y100.887 E.00942 +G1 X128.287 Y100.632 E.00911 +G1 X129.368 Y100.632 E.03659 +G1 X129.368 Y101.523 E.03016 +G1 X129.07 Y101.645 E.0109 +G1 X128.906 Y101.887 E.0099 +G1 X128.868 Y102.099 E.00729 +G1 X128.868 Y103.186 E.03679 +G1 X128.899 Y103.378 E.00658 +G1 X129.057 Y103.628 E.01001 +G1 X129.368 Y103.753 E.01135 +G1 X129.368 Y106.291 E.08591 +G1 X129.057 Y106.415 E.01133 +G1 X128.931 Y106.586 E.00719 +G1 X128.868 Y106.857 E.00942 +G1 X128.868 Y107.897 E.0352 +G1 X128.919 Y108.142 E.00847 +G1 X129.136 Y108.403 E.01149 +G1 X129.368 Y108.474 E.00821 +G1 X129.368 Y109.308 E.02823 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1229 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y109.1 E.02285 +M73 P69 R2 +G1 X120.225 Y106.25 E.09647 +G1 X120.225 Y105.375 E.02962 +G1 X120.521 Y105.375 E.01002 +G1 X120.692 Y105.282 E.00659 +G1 X120.717 Y105.228 E.00201 +G1 X120.711 Y104.755 E.01601 +G1 X120.521 Y104.625 E.00779 +G1 X120.225 Y104.625 E.01002 +G1 X120.225 Y103.75 E.02962 +G1 X120.225 Y100.9 E.09647 +G1 X120.225 Y100.225 E.02285 +G1 X122.199 Y100.225 E.06682 +M73 Q69 S2 +G1 X122.199 Y100.521 E.01002 +G1 X122.244 Y100.649 E.00459 +G1 X122.327 Y100.71 E.00349 +G1 X122.753 Y100.725 E.01443 +G1 X123.542 Y100.712 E.02671 +G1 X123.673 Y100.521 E.00784 +G1 X123.673 Y100.225 E.01002 +G1 X126.528 Y100.225 E.09664 +G1 X126.528 Y100.521 E.01002 +G1 X126.617 Y100.69 E.00647 +G1 X126.731 Y100.725 E.00404 +G1 X127.712 Y100.725 E.03321 +G1 X127.851 Y100.67 E.00506 +G1 X127.915 Y100.521 E.00549 +G1 X127.915 Y100.225 E.01002 +G1 X129.775 Y100.225 E.06296 +G1 X129.775 Y101.896 E.05656 +G1 X129.479 Y101.896 E.01002 +G1 X129.343 Y101.948 E.00493 +G1 X129.275 Y102.099 E.00561 +G1 X129.275 Y103.186 E.03679 +G1 X129.303 Y103.29 E.00365 +G1 X129.479 Y103.39 E.00685 +G1 X129.775 Y103.39 E.01002 +G1 X129.775 Y106.654 E.11048 +G1 X129.479 Y106.654 E.01002 +G1 X129.338 Y106.71 E.00514 +G1 X129.275 Y106.857 E.00541 +G1 X129.275 Y107.897 E.0352 +G1 X129.364 Y108.066 E.00647 +G1 X129.479 Y108.101 E.00407 +G1 X129.775 Y108.101 E.01002 +G1 X129.775 Y109.715 E.05463 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X128.818 Y100.978 Z8.354 F18000 +G1 Z8.2 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1229 +G1 X128.654 Y100.978 E.00555 +G1 X128.968 Y101.292 E.01503 +G1 X129.022 Y101.276 E.00191 +G1 X129.022 Y100.978 E.01009 +G1 X120.978 Y109.022 E.38506 +G1 X120.978 Y108.654 E.01246 +G1 X121.346 Y109.022 E.01762 +G1 X128.654 Y109.022 E.24737 +G1 X128.971 Y108.705 E.01517 +G1 X129.022 Y108.72 E.0018 +G1 X129.022 Y109.022 E.01022 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +M106 S255 +;LAYER_CHANGE +;Z:8.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.4 +M201 X3999.38 Y3999.38 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z8.2 F18000 +G1 X129.368 Y109.368 Z8.4 +;AFTER_LAYER_CHANGE +;8.4 +M74 W0.57819 + +G1 X129.368 Y109.368 +G1 Z8.4 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1200 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +M73 P70 R2 +G1 X129.368 Y100.632 E.2957 +M73 Q70 S2 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +M73 P70 R1 +;TYPE:External perimeter +G1 F1200 +G1 X129.1 Y109.775 E.02285 +G1 X120.225 Y109.775 E.30041 +G1 X120.225 Y100.9 E.30041 +G1 X120.225 Y100.225 E.02285 +G1 X122.424 Y100.225 E.07443 +G1 F1033.332 +G1 X122.624 Y100.225 E.00677 +M204 P1500 +;TYPE:Overhang perimeter +G1 X123.248 Y100.225 E.02112 +M204 P4000 +;TYPE:External perimeter +G1 X126.953 Y100.225 E.12541 +M204 P1500 +;TYPE:Overhang perimeter +G1 X127.49 Y100.225 E.01818 +M204 P4000 +;TYPE:External perimeter +G1 X127.69 Y100.225 E.00677 +G1 F1200 +G1 X129.775 Y100.225 E.07057 +G1 X129.775 Y102.121 E.06418 +G1 F1033.332 +G1 X129.775 Y102.321 E.00677 +M204 P1500 +;TYPE:Overhang perimeter +G1 X129.775 Y102.965 E.0218 +M204 P4000 +;TYPE:External perimeter +G1 X129.775 Y103.165 E.00677 +G1 F1200 +G1 X129.775 Y105 E.06211 +G1 X129.775 Y106.879 E.0636 +G1 F1033.332 +G1 X129.775 Y107.079 E.00677 +M204 P1500 +;TYPE:Overhang perimeter +G1 X129.775 Y107.676 E.02021 +M204 P4000 +;TYPE:External perimeter +G1 X129.775 Y107.876 E.00677 +G1 F1200 +G1 X129.775 Y109.715 E.06225 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.346 Y109.022 Z8.506 F18000 +G1 Z8.4 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1200 +G1 X121.346 Y109.022 E.0677 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X129.022 Y100.978 E.38506 +G1 X128.654 Y100.978 E.01246 +M73 P71 R1 +M73 Q70 S1 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y108.654 E.24737 +G1 X128.654 Y109.022 E.01762 +M73 Q71 S1 +G1 X129.022 Y109.022 E.01246 +G1 X120.978 Y100.978 E.38506 +G1 X121.346 Y100.978 E.01246 +G1 X120.978 Y101.346 E.01762 +G1 X120.978 Y103.346 E.0677 +M106 S252.45 +;LAYER_CHANGE +;Z:8.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.6 +M201 X3999.37 Y3999.37 + +G1 E-.7 F2100 +G1 X120.978 Y103.346 Z8.4 F18000 +G1 X129.368 Y109.368 Z8.6 +;AFTER_LAYER_CHANGE +;8.6 +M74 W0.589359 + +G1 X129.368 Y109.368 +G1 Z8.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1332 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1332 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +M73 P72 R1 +G1 X129.775 Y109.715 E.32122 +M73 Q72 S1 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X129.022 Y105.062 Z8.682 F18000 +G1 Z8.6 F720 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.45003 +G1 F1332 +G1 X129.022 Y103.092 E.06669 +G1 X128.615 Y102.363 F18000 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1332 +G1 X128.615 Y102.109 E.0086 +G1 X129.022 Y102.516 E.01948 +G1 X129.022 Y101.346 E.0396 +G1 X128.654 Y100.978 E.01762 +G1 X129.022 Y100.978 E.01246 +G1 X120.978 Y109.022 E.38506 +G1 X120.978 Y108.654 E.01246 +G1 X121.346 Y109.022 E.01762 +G1 X123.179 Y109.022 E.06204 +G1 X122.771 Y108.615 E.01951 +G1 X128.615 Y108.615 E.19781 +G1 X121.385 Y101.385 E.3461 +G1 X121.385 Y103.092 E.05778 +G1 X121.178 Y102.885 E.00991 +G1 X121.178 Y102.885 F18000 +G1 X120.978 Y102.109 +;TYPE:Solid infill +;WIDTH:0.450032 +G1 F1332 +G1 X120.978 Y101.182 E.03138 +;WIDTH:0.484952 +G1 X120.996 Y101.097 E.00319 +;WIDTH:0.519876 +G1 X121.013 Y101.013 E.0034 +G1 X121.097 Y100.996 E.0034 +;WIDTH:0.484952 +G1 X121.182 Y100.978 E.00319 +;WIDTH:0.45003 +G1 X126.908 Y100.978 E.19383 +G1 E-.7 F2100 +G1 X123.754 Y109.022 Z8.751 F18000 +G1 Z8.6 F720 +G1 E.7 F1500 +;WIDTH:0.450028 +G1 F1332 +G1 X128.818 Y109.022 E.17142 +;WIDTH:0.484952 +G1 X128.903 Y109.004 E.00319 +;WIDTH:0.519876 +G1 X128.987 Y108.987 E.0034 +G1 X129.004 Y108.903 E.0034 +;WIDTH:0.484952 +G1 X129.022 Y108.818 E.00319 +;WIDTH:0.45003 +G1 X129.022 Y108.183 E.0215 +;LAYER_CHANGE +;Z:8.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;8.8 +M201 X3999.36 Y3999.36 + +G1 X129.022 Y108.183 Z8.6 F18000 +G1 X129.368 Y109.368 Z8.8 F4985.791 +;AFTER_LAYER_CHANGE +;8.8 +M74 W0.601853 + +G1 X129.368 Y109.368 F18000 +G1 Z8.8 F720 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F1402 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +M73 P73 R1 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +M73 Q73 S1 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F1402 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X123.06 Y108.904 Z8.911 F18000 +G1 Z8.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F1402 +G1 X123.179 Y109.022 E.00567 +G1 X121.346 Y109.022 E.06204 +G1 X120.978 Y108.654 E.01762 +G1 X120.978 Y109.022 E.01246 +G1 X122.078 Y107.922 E.05266 +G1 X121.783 Y107.626 E.01415 +G1 X120.978 Y106.468 E.04774 +G1 X120.978 Y106.639 E.00579 +G1 X120.978 Y106.639 F18000 +G1 X121.285 Y106.513 +M204 P1500 +;TYPE:Bridge infill +;WIDTH:0.412501 +;HEIGHT:0.4 +M106 S255 +G1 F3000 +G1 X123.772 Y109 E.19542 +G1 X124.426 Y109 E.03634 +G1 X121.417 Y105.991 E.23643 +G1 X121.576 Y105.858 E.01152 +G1 X122.735 Y107.018 E.09111 +G1 X122.916 Y106.836 E.01426 +G1 X125.08 Y109 E.17004 +G1 X125.734 Y109 E.03634 +G1 X123.243 Y106.509 E.19573 +G1 X123.57 Y106.182 E.02569 +M73 P74 R1 +G1 X126.388 Y109 E.22143 +G1 X127.042 Y109 E.03634 +G1 X123.897 Y105.855 E.24712 +G1 X124.224 Y105.528 E.02569 +G1 X127.696 Y109 E.27282 +G1 X128.351 Y109 E.03639 +G1 X121 Y101.649 E.57761 +G1 X121 Y101 E.03606 +M73 Q74 S1 +G1 X129 Y108.996 E.62845 +G1 X129 Y108.342 E.03634 +G1 X121.658 Y101 E.5769 +G1 X122.312 Y101 E.03634 +G1 X125.78 Y104.468 E.2725 +G1 X126.107 Y104.141 E.02569 +G1 X122.966 Y101 E.24681 +G1 X123.62 Y101 E.03634 +G1 X126.434 Y103.814 E.22111 +G1 X126.761 Y103.486 E.02573 +G1 X124.274 Y101 E.19538 +G1 X124.928 Y101 E.03634 +G1 X127.088 Y103.159 E.16968 +G1 X127.119 Y103.129 E.0024 +G1 X129 Y105.01 E.1478 +G1 X129 Y104.418 E.03289 +G1 X125.582 Y101 E.26857 +G1 X126.236 Y101 E.03634 +G1 X129 Y103.764 E.21718 +G1 X129 Y103.11 E.03634 +G1 X126.688 Y100.797 E.18171 +M106 S252.45 +M204 P4000 +G1 E-.7 F2100 +G1 X128.396 Y100.978 Z8.83 F18000 +G1 Z8.8 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +;HEIGHT:0.2 +G1 F1402 +G1 X127.484 Y100.978 E.03087 +G1 X128.253 Y101.747 E.03681 +G1 X129.022 Y100.978 E.03681 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y102.516 E.0396 +G1 X128.435 Y101.93 E.02808 +;LAYER_CHANGE +;Z:9 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9 +M201 X3999.33 Y3999.33 + +G1 E-.7 F2100 +G1 X128.435 Y101.93 Z8.8 F18000 +G1 X129.368 Y109.368 Z9 +;AFTER_LAYER_CHANGE +;9 +M74 W0.628068 + +G1 X129.368 Y109.368 +G1 Z9 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2354 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F2354 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +M73 P75 R1 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +M73 Q75 S1 +G1 E-.7 F2100 +G1 X129.022 Y106.654 Z9.055 F18000 +G1 Z9 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2354 +G1 X129.022 Y108.654 E.0677 +G1 X128.654 Y109.022 E.01762 +G1 X129.022 Y109.022 E.01246 +G1 X128.499 Y108.499 E.02504 +G2 X128.514 Y107.401 I-5.04 J-.618 E.03724 +G1 X128.296 Y106.821 E.02097 +G1 X126.007 Y103.993 E.12315 +G1 X126.423 Y103.577 E.01991 +G1 X127.705 Y103.573 E.04339 +G2 X128.633 Y102.539 I-.14 J-1.059 E.0512 +G2 X128.577 Y101.423 I-3.715 J-.375 E.03797 +G1 X129.022 Y100.978 E.0213 +G1 X128.654 Y100.978 E.01246 +G1 X129.022 Y101.346 E.01762 +G1 X129.022 Y103.346 E.0677 +G1 E-.7 F2100 +G1 X122.151 Y107.509 Z9.14 F18000 +G1 Z9 F720 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.449999 +G1 F2354 +G1 X122.293 Y107.094 E.01485 +G1 X122.614 Y106.858 E.01349 +G1 X122.81 Y106.826 E.00672 +G1 X124.34 Y106.826 E.05179 +G1 X124.527 Y106.713 E.0074 +G1 X126.841 Y109.022 E.11065 +G1 X122.586 Y109.022 E.14403 +G1 X122.523 Y108.851 E.00617 +G1 X122.343 Y108.704 E.00787 +G1 X122.163 Y108.372 E.01278 +G1 X122.154 Y107.713 E.02231 +G1 X122.56 Y107.644 E.01394 +G1 X122.637 Y107.346 E.01042 +G1 X122.843 Y107.234 E.00794 +G1 X124.34 Y107.234 E.05067 +G1 X124.424 Y107.181 E.00336 +G1 X125.859 Y108.615 E.06867 +G1 X122.846 Y108.615 E.10199 +G3 X122.569 Y108.266 I2.55 J-2.308 E.01509 +G1 X122.563 Y107.848 E.01415 +G1 X123.022 Y107.884 E.01558 +;WIDTH:0.530032 +G1 X123.04 Y108.149 E.01076 +G1 X124.779 Y108.168 E.07044 +G1 X124.292 Y107.681 E.0279 +G1 X123.008 Y107.681 E.05201 +G1 E-.7 F2100 +G1 X123.008 Y107.681 F18000 +G1 X122.328 Y103.717 Z9.07 +G1 Z9 F720 +G1 E.7 F1500 +;WIDTH:0.407218 +G1 F2354 +G1 X123.915 Y105.49 E.07208 +;WIDTH:0.449999 +G1 X124.173 Y105.777 E.01306 +G1 X127.417 Y109.022 E.15531 +G1 X127.863 Y109.109 E.01538 +;WIDTH:0.38292 +G1 X127.874 Y109.15 E.0012 +G1 X127.803 Y109.169 E.00208 +G1 X127.822 Y109.098 E.00208 +;WIDTH:0.41646 +G1 X127.813 Y108.965 E.00414 +;WIDTH:0.449999 +G1 X127.804 Y108.833 E.00448 +G1 X121.916 Y102.945 E.28185 +G1 X121.804 Y102.791 E.00645 +;WIDTH:0.41646 +G1 X121.693 Y102.637 E.0059 +;WIDTH:0.38292 +G1 X121.704 Y102.678 E.0012 +G1 X121.633 Y102.697 E.00208 +G1 X121.652 Y102.626 E.00208 +;WIDTH:0.449999 +G1 X121.774 Y102.227 E.01412 +G1 X128.056 Y108.509 E.30072 +G1 X128.12 Y107.997 E.01747 +G1 X121.79 Y101.667 E.30301 +G1 X122.027 Y101.329 E.01397 +G1 X128.073 Y107.375 E.28942 +G1 X128.067 Y107.173 E.00684 +;WIDTH:0.41646 +G1 X128.061 Y106.971 E.00628 +;WIDTH:0.409322 +G1 X126.661 Y105.416 E.06375 +;WIDTH:0.449999 +G1 X126.402 Y105.128 E.01311 +G1 X122.332 Y101.058 E.19483 +G1 X122.828 Y100.978 E.01701 +M73 P76 R1 +G1 X125.256 Y103.407 E.11625 +G1 X125.321 Y103.55 E.00532 +;WIDTH:0.41646 +G1 X125.386 Y103.694 E.00491 +;WIDTH:0.38292 +G1 X125.911 Y104.278 E.0222 +G1 X125.911 Y104.278 F18000 +G1 X125.598 Y103.172 +;WIDTH:0.449999 +G1 F2354 +G1 X123.403 Y100.978 E.10505 +G1 X127.663 Y100.978 E.1442 +G1 X127.663 Y101.023 E.00152 +G1 X127.763 Y101.221 E.00751 +G1 X128.154 Y101.504 E.01634 +G1 X128.224 Y101.745 E.00849 +G1 X128.225 Y102.533 E.02667 +G1 X128.111 Y102.885 E.01252 +G1 X127.879 Y103.095 E.01059 +G1 X127.655 Y103.166 E.00795 +G1 X126.03 Y103.171 E.055 +G1 X126.029 Y103.028 E.00484 +G1 X124.123 Y101.122 E.09124 +G1 X124.962 Y101.385 F18000 +M73 Q76 S1 +G1 F2354 +G1 X127.402 Y101.385 E.08259 +G1 X127.508 Y101.538 E.0063 +G1 X127.772 Y101.73 E.01105 +G1 X127.817 Y101.803 E.0029 +G1 X127.818 Y102.474 E.02271 +G1 X127.771 Y102.644 E.00597 +G1 X127.592 Y102.759 E.0072 +G3 X126.317 Y102.741 I-.245 J-26.841 E.04317 +G1 X125.106 Y101.529 E.05799 +G1 X125.106 Y101.529 F18000 +G1 X126.039 Y101.831 +;WIDTH:0.527863 +G1 F2354 +G1 X127.191 Y101.831 E.04645 +;WIDTH:0.52718 +G1 X127.372 Y101.99 E.0097 +G1 X127.372 Y102.314 E.01305 +G1 X127.158 Y102.315 E.00862 +;WIDTH:0.527863 +G1 X126.524 Y102.316 E.02556 +G1 X126.182 Y101.975 E.01947 +G1 E-.7 F2100 +G1 X126.182 Y101.975 F18000 +G1 X120.978 Y103.346 Z9.094 +G1 Z9 F720 +G1 E.7 F1500 +;TYPE:Internal infill +;WIDTH:0.45 +G1 F2354 +G1 X120.978 Y101.346 E.0677 +G1 X121.346 Y100.978 E.01762 +G1 X120.978 Y100.978 E.01246 +G1 X121.423 Y101.423 E.0213 +G2 X121.399 Y102.788 I3.686 J.747 E.04647 +G1 X121.594 Y103.205 E.01558 +G1 X123.905 Y106.095 E.12525 +G1 X123.581 Y106.419 E.01551 +G2 X122.477 Y106.469 I-.368 J4.163 E.03752 +G2 X121.94 Y106.869 I.537 J1.279 E.02289 +G1 X121.744 Y107.459 E.02104 +G1 X121.744 Y108.256 E.02698 +G1 X120.978 Y109.022 E.03667 +G1 X120.978 Y108.654 E.01246 +G1 X121.346 Y109.022 E.01762 +G1 X122.077 Y109.022 E.02474 +G1 X121.759 Y108.459 E.02189 +;LAYER_CHANGE +;Z:9.2 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.2 +M201 X3999.3 Y3999.3 + +G1 E-.7 F2100 +G1 X121.759 Y108.459 Z9 F18000 +G1 X129.368 Y109.368 Z9.2 +;AFTER_LAYER_CHANGE +;9.2 +M74 W0.649737 + +G1 X129.368 Y109.368 +G1 Z9.2 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2645 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F2645 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X121.089 Y101.011 Z9.411 F18000 +G1 Z9.2 F720 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F2645 +G2 X121.072 Y101.072 I-.059 J.017 E.01322 +;WIDTH:0.449999 +G1 X120.978 Y101.722 E.02223 +G1 X121.722 Y100.978 E.03561 +G1 X122.298 Y100.978 E.0195 +G1 X120.978 Y102.298 E.06319 +G1 X120.978 Y102.874 E.0195 +G1 X122.874 Y100.978 E.09076 +G1 X123.45 Y100.978 E.0195 +G1 X120.978 Y103.45 E.11833 +G1 X120.978 Y104.025 E.01946 +M73 P77 R1 +G1 X124.025 Y100.978 E.14586 +G1 X124.601 Y100.978 E.0195 +G1 X120.978 Y104.601 E.17343 +G1 X120.978 Y105.177 E.0195 +G1 X125.177 Y100.978 E.201 +G1 X125.752 Y100.978 E.01946 +M73 Q77 S1 +G1 X120.978 Y105.752 E.22853 +G1 X120.978 Y106.328 E.0195 +G1 X126.328 Y100.978 E.2561 +G1 X126.904 Y100.978 E.0195 +G1 X120.978 Y106.904 E.28367 +G1 X120.978 Y107.479 E.01946 +G1 X127.479 Y100.978 E.3112 +G1 X128.055 Y100.978 E.0195 +G1 X120.978 Y108.055 E.33877 +G1 X120.978 Y108.631 E.0195 +G1 X128.631 Y100.978 E.36634 +G1 X129.161 Y100.869 E.01832 +;WIDTH:0.38292 +G1 X129.09 Y100.85 E.00208 +G1 X129.109 Y100.921 E.00208 +G1 X129.15 Y100.91 E.0012 +;WIDTH:0.41646 +G1 X129.086 Y101.036 E.00439 +;WIDTH:0.449999 +G1 X129.022 Y101.163 E.00481 +G1 X121.163 Y109.022 E.37621 +G1 X121.036 Y109.086 E.00481 +;WIDTH:0.41646 +G1 X120.91 Y109.15 E.00439 +;WIDTH:0.38292 +G1 X120.869 Y109.161 E.0012 +G1 X120.85 Y109.09 E.00208 +G1 X120.921 Y109.109 E.00208 +G1 X120.921 Y109.109 F18000 +G1 X121.738 Y109.022 +;WIDTH:0.449999 +G1 F2645 +G1 X129.022 Y101.738 E.34868 +G1 X129.022 Y102.314 E.0195 +G1 X122.314 Y109.022 E.32111 +G1 X122.89 Y109.022 E.0195 +G1 X129.022 Y102.89 E.29354 +G1 X129.022 Y103.465 E.01946 +G1 X123.465 Y109.022 E.26601 +G1 X124.041 Y109.022 E.0195 +G1 X129.022 Y104.041 E.23844 +G1 X129.022 Y104.617 E.0195 +G1 X124.617 Y109.022 E.21086 +G1 X125.193 Y109.022 E.0195 +G1 X129.022 Y105.193 E.18329 +G1 X129.022 Y105.768 E.01946 +G1 X125.768 Y109.022 E.15577 +G1 X126.344 Y109.022 E.0195 +G1 X129.022 Y106.344 E.12819 +G1 X129.237 Y106.752 E.01561 +;WIDTH:0.38292 +G1 X129.166 Y106.733 E.00208 +G1 X129.185 Y106.804 E.00208 +G1 X129.226 Y106.793 E.0012 +;WIDTH:0.41646 +G1 X129.124 Y106.857 E.00374 +;WIDTH:0.449999 +G1 X129.022 Y106.92 E.00406 +G1 X126.92 Y109.022 E.10062 +G1 X126.857 Y109.124 E.00406 +;WIDTH:0.41646 +G1 X126.793 Y109.226 E.00374 +;WIDTH:0.38292 +G1 X126.752 Y109.237 E.0012 +G1 X126.733 Y109.166 E.00208 +G1 X126.804 Y109.185 E.00208 +G1 E-.7 F2100 +G1 X126.804 Y109.185 F18000 +G1 X128.878 Y107.639 Z9.245 +G1 Z9.2 F720 +M73 P78 R1 +G1 E.7 F1500 +;WIDTH:0.449999 +G1 F2645 +G1 X127.495 Y109.022 E.0662 +G1 X129.022 Y109.022 E.05169 +G1 X129.022 Y107.495 E.05169 +G1 X129.022 Y107.495 F18000 +G1 X128.634 Y108.559 +;WIDTH:0.530084 +G1 F2645 +G2 X128.618 Y108.618 I-.056 J.017 E.01225 +;LAYER_CHANGE +;Z:9.4 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.4 +M201 X3999.28 Y3999.28 + +G1 X128.618 Y108.618 Z9.2 F18000 +G1 X129.368 Y109.368 Z9.4 F4386.028 +;AFTER_LAYER_CHANGE +;9.4 +M74 W0.674802 + +G1 X129.368 Y109.368 F18000 +M73 Q78 S1 +G1 Z9.4 F720 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2547 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F2547 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X121.011 Y108.911 Z9.547 F18000 +G1 Z9.4 F720 +G1 E.7 F1500 +;TYPE:Solid infill +;WIDTH:0.548766 +G1 F2547 +G2 X121.072 Y108.928 I.017 J.059 E.01322 +;WIDTH:0.449999 +G1 X120.978 Y108.278 E.02223 +G1 X121.722 Y109.022 E.03561 +G1 X122.298 Y109.022 E.0195 +G1 X120.978 Y107.702 E.06319 +G1 X120.978 Y107.126 E.0195 +G1 X122.874 Y109.022 E.09076 +G1 X123.45 Y109.022 E.0195 +G1 X120.978 Y106.551 E.11831 +G1 X120.978 Y105.975 E.0195 +G1 X124.025 Y109.022 E.14586 +G1 X124.601 Y109.022 E.0195 +G1 X120.978 Y105.399 E.17343 +G1 X120.978 Y104.823 E.0195 +G1 X125.177 Y109.022 E.201 +G1 X125.752 Y109.022 E.01946 +G1 X120.978 Y104.248 E.22853 +G1 X120.978 Y103.672 E.0195 +G1 X126.328 Y109.022 E.2561 +G1 X126.904 Y109.022 E.0195 +G1 X120.978 Y103.096 E.28367 +G1 X120.978 Y102.521 E.01946 +G1 X127.479 Y109.022 E.3112 +G1 X128.055 Y109.022 E.0195 +G1 X120.978 Y101.945 E.33877 +G1 X120.978 Y101.369 E.0195 +M73 P79 R1 +G1 X128.631 Y109.022 E.36634 +G1 X129.109 Y109.079 E.01629 +M73 Q79 S1 +;WIDTH:0.38292 +G1 X129.09 Y109.15 E.00208 +G1 X129.161 Y109.131 E.00208 +G1 X129.15 Y109.09 E.0012 +;WIDTH:0.41646 +G1 X129.086 Y108.964 E.00439 +;WIDTH:0.449999 +G1 X129.022 Y108.837 E.00481 +G1 X121.163 Y100.978 E.37621 +G1 X121.036 Y100.914 E.00481 +;WIDTH:0.41646 +G1 X120.91 Y100.85 E.00439 +;WIDTH:0.38292 +G1 X120.921 Y100.891 E.0012 +G1 X120.85 Y100.91 E.00208 +G1 X120.869 Y100.839 E.00208 +G1 X120.869 Y100.839 F18000 +G1 X121.738 Y100.978 +;WIDTH:0.449999 +G1 F2547 +G1 X129.022 Y108.262 E.34868 +G1 X129.022 Y107.686 E.0195 +G1 X122.314 Y100.978 E.32111 +G1 X122.89 Y100.978 E.0195 +G1 X129.022 Y107.11 E.29354 +G1 X129.022 Y106.535 E.01946 +G1 X123.465 Y100.978 E.26601 +G1 X124.041 Y100.978 E.0195 +G1 X129.022 Y105.959 E.23844 +G1 X129.022 Y105.383 E.0195 +G1 X124.617 Y100.978 E.21086 +G1 X125.193 Y100.978 E.0195 +G1 X129.022 Y104.807 E.18329 +G1 X129.022 Y104.232 E.01946 +G1 X125.768 Y100.978 E.15577 +G1 X126.344 Y100.978 E.0195 +G1 X129.022 Y103.656 E.12819 +G1 X129.185 Y103.196 E.01652 +;WIDTH:0.38292 +G1 X129.166 Y103.267 E.00208 +G1 X129.237 Y103.248 E.00208 +G1 X129.226 Y103.207 E.0012 +;WIDTH:0.41646 +G1 X129.124 Y103.143 E.00374 +;WIDTH:0.449999 +G1 X129.022 Y103.08 E.00406 +G1 X126.92 Y100.978 E.10062 +G1 X126.857 Y100.876 E.00406 +;WIDTH:0.41646 +G1 X126.793 Y100.774 E.00374 +;WIDTH:0.38292 +G1 X126.804 Y100.815 E.0012 +G1 X126.733 Y100.834 E.00208 +G1 X126.752 Y100.763 E.00208 +;WIDTH:0.449999 +G1 X127.495 Y100.978 E.02618 +G1 X129.022 Y100.978 E.05169 +G1 X129.022 Y102.505 E.05169 +G1 X127.639 Y101.122 E.0662 +G1 X127.639 Y101.122 F18000 +G1 X128.559 Y101.366 +;WIDTH:0.530084 +G1 F2547 +G2 X128.618 Y101.382 I.017 J.056 E.01225 +M106 S219.3 +;LAYER_CHANGE +;Z:9.6 +;HEIGHT:0.200001 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.6 +M201 X3999.25 Y3999.25 + +G1 E-.7 F2100 +G1 X128.618 Y101.382 Z9.4 F18000 +G1 X129.368 Y109.368 Z9.6 +;AFTER_LAYER_CHANGE +;9.6 +M74 W0.699941 + +G1 X129.368 Y109.368 +G1 Z9.6 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F10200 +G1 X120.632 Y109.368 E.2957 +G1 X120.632 Y100.632 E.2957 +G1 X129.368 Y100.632 E.2957 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F10199.989 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 E-.7 F2100 +G1 X129.02 Y102.549 Z9.726 F18000 +G1 Z9.6 F720 +G1 E.7 F1500 +M204 P2000 +;TYPE:Top solid infill +;WIDTH:0.425208 +G1 F6000 +M73 Q80 S1 +G1 X127.601 Y103.968 E.06379 +M204 P4000 +G1 X127.601 Y103.968 F18000 +M73 P80 R1 +G1 X127.06 Y103.968 +M204 P2000 +G1 F6000 +G1 X129.02 Y102.008 E.08811 +M204 P4000 +G1 X129.02 Y102.008 F18000 +G1 X128.99 Y101.497 +M204 P2000 +G1 F6000 +G1 X126.52 Y103.968 E.11106 +M204 P4000 +G1 E-.7 F2100 +G1 X126.52 Y103.968 F18000 +G1 X128.914 Y108.061 Z9.683 +G1 Z9.6 F720 +G1 E.7 F1500 +M204 P2000 +G1 F6000 +G1 X127.768 Y109.207 E.05152 +M204 P4000 +G1 X127.768 Y109.207 F18000 +G1 X127.228 Y109.207 +M204 P2000 +G1 F6000 +G1 X128.914 Y107.521 E.07579 +M204 P4000 +G1 X128.914 Y107.521 F18000 +G1 X128.846 Y107.047 +M204 P2000 +G1 F6000 +G1 X126.687 Y109.207 E.09708 +M204 P4000 +G1 X126.687 Y109.207 F18000 +G1 X126.146 Y109.207 +M204 P2000 +G1 F6000 +G1 X128.671 Y106.682 E.11351 +M204 P4000 +G1 X128.671 Y106.682 F18000 +G1 X128.435 Y106.378 +M204 P2000 +G1 F6000 +G1 X125.606 Y109.207 E.12718 +M204 P4000 +G1 X125.606 Y109.207 F18000 +G1 X125.065 Y109.207 +M204 P2000 +G1 F6000 +G1 X128.193 Y106.079 E.14062 +M204 P4000 +G1 X128.193 Y106.079 F18000 +G1 X127.951 Y105.78 +M204 P2000 +G1 F6000 +G1 X124.525 Y109.207 E.15404 +M204 P4000 +G1 X124.525 Y109.207 F18000 +G1 X123.984 Y109.207 +M204 P2000 +G1 F6000 +G1 X127.709 Y105.481 E.16748 +M204 P4000 +G1 X127.709 Y105.481 F18000 +G1 X127.468 Y105.182 +M204 P2000 +G1 F6000 +G1 X123.443 Y109.207 E.18094 +M204 P4000 +G1 X123.443 Y109.207 F18000 +G1 X122.903 Y109.207 +M204 P2000 +G1 F6000 +G1 X127.226 Y104.884 E.19434 +M204 P4000 +G1 X127.226 Y104.884 F18000 +G1 X126.984 Y104.585 +M204 P2000 +G1 F6000 +G1 X122.362 Y109.207 E.20778 +M204 P4000 +G1 X122.362 Y109.207 F18000 +G1 X121.821 Y109.207 +M204 P2000 +G1 F6000 +G1 X126.742 Y104.286 E.22122 +M204 P4000 +G1 X126.5 Y103.987 F18000 +M204 P2000 +G1 F6000 +G1 X121.545 Y108.942 E.22275 +M204 P4000 +G1 X121.545 Y108.942 F18000 +G1 X121.396 Y108.55 +M204 P2000 +G1 F6000 +G1 X128.85 Y101.097 E.33507 +M204 P4000 +G1 X128.85 Y101.097 F18000 +G1 X128.613 Y100.793 +M204 P2000 +G1 F6000 +G1 X121.357 Y108.049 E.32619 +M204 P4000 +G1 X121.357 Y108.049 F18000 +G1 X121.357 Y107.509 +M204 P2000 +G1 F6000 +G1 X122.833 Y106.032 E.06637 +M204 P4000 +G1 X122.833 Y106.032 F18000 +G1 X123.126 Y105.74 +M204 P2000 +G1 F6000 +G1 X128.072 Y100.793 E.22237 +M204 P4000 +G1 X128.072 Y100.793 F18000 +G1 X127.531 Y100.793 +M204 P2000 +G1 F6000 +G1 X122.885 Y105.439 E.20886 +M204 P4000 +G1 X122.885 Y105.439 F18000 +G1 X122.645 Y105.139 +M204 P2000 +G1 F6000 +G1 X126.991 Y100.793 E.19537 +M204 P4000 +G1 X126.991 Y100.793 F18000 +G1 X126.45 Y100.793 +M204 P2000 +G1 F6000 +G1 X122.405 Y104.839 E.18186 +M204 P4000 +G1 X122.405 Y104.839 F18000 +G1 X122.165 Y104.538 +M204 P2000 +G1 F6000 +G1 X125.91 Y100.793 E.16835 +M204 P4000 +G1 X125.91 Y100.793 F18000 +G1 X125.369 Y100.793 +M204 P2000 +G1 F6000 +G1 X121.924 Y104.238 E.15487 +M204 P4000 +G1 X121.924 Y104.238 F18000 +G1 X121.684 Y103.937 +M204 P2000 +G1 F6000 +G1 X124.828 Y100.793 E.14134 +M204 P4000 +G1 X124.828 Y100.793 F18000 +G1 X124.288 Y100.793 +M204 P2000 +G1 F6000 +G1 X121.444 Y103.637 E.12785 +M204 P4000 +G1 X121.211 Y103.33 F18000 +M204 P2000 +G1 F6000 +G1 X123.747 Y100.793 E.11403 +M204 P4000 +G1 X123.747 Y100.793 F18000 +G1 X123.206 Y100.793 +M204 P2000 +G1 F6000 +G1 X121.04 Y102.96 E.09739 +M204 P4000 +G1 X121.04 Y102.96 F18000 +G1 X120.98 Y102.479 +M204 P2000 +G1 F6000 +G1 X122.666 Y100.793 E.07579 +M204 P4000 +G1 X122.666 Y100.793 F18000 +G1 X122.125 Y100.793 +M204 P2000 +G1 F6000 +G1 X120.98 Y101.939 E.05149 +M204 P4000 +G1 X120.98 Y101.939 F18000 +G1 X120.973 Y100.973 +;TYPE:Solid infill +;WIDTH:0.439344 +G1 F15000 +G1 X120.925 Y101.259 E.00956 +;WIDTH:0.411132 +G1 X120.877 Y101.545 E.00888 +;WIDTH:0.38292 +G1 X120.868 Y102.555 E.02855 +G1 X120.912 Y103.066 E.0145 +;WIDTH:0.413726 +G1 X120.942 Y103.315 E.00773 +;WIDTH:0.449999 +G1 X120.978 Y103.636 E.01093 +G1 X120.978 Y106.979 E.11316 +;WIDTH:0.490354 +G1 F14750.786 +G1 X120.998 Y107.086 E.00405 +;WIDTH:0.530709 +G1 F13530.454 +G1 X121.018 Y107.194 E.00445 +;WIDTH:0.571064 +G1 F12496.61 +G1 X121.039 Y107.301 E.00479 +;WIDTH:0.611418 +G1 F11609.54 +G1 X121.059 Y107.408 E.00515 +G1 X121.161 Y107.18 E.01181 +;WIDTH:0.571064 +G1 F12496.61 +G1 X121.262 Y106.952 E.01095 +;WIDTH:0.530709 +G1 F13530.454 +G1 X121.364 Y106.724 E.01013 +;WIDTH:0.490354 +G1 F14750.786 +G1 X121.466 Y106.496 E.00929 +;WIDTH:0.449999 +G1 F15000 +G1 X121.808 Y106.15 E.01647 +G1 X122.259 Y105.919 E.01715 +G1 X122.73 Y105.813 E.01634 +G1 X122.898 Y105.812 E.00569 +G1 X121.119 Y103.586 E.09645 +G1 X121.019 Y103.35 E.00868 +;WIDTH:0.42042 +G1 X120.92 Y103.113 E.00806 +G1 E-.7 F2100 +G1 X120.92 Y103.113 F18000 +G1 X121.432 Y104.706 Z9.629 +G1 Z9.6 F720 +G1 E.7 F1500 +;WIDTH:0.544582 +G1 F13156.268 +G1 X122.052 Y105.514 E.04248 +G1 X121.432 Y105.869 E.0298 +G1 X121.432 Y104.91 E.04 +G1 E-.7 F2100 +G1 X121.432 Y104.91 F18000 +G1 X121.059 Y107.408 Z9.644 +G1 Z9.6 F720 +M73 Q81 S1 +G1 E.7 F1500 +;WIDTH:0.611418 +G1 F11609.54 +G1 X121.058 Y108.327 E.04344 +;WIDTH:0.613282 +G1 F11571.599 +G3 X121.038 Y108.481 I-.42 J.024 E.00741 +;WIDTH:0.56609 +G1 F12615.399 +G1 X121.015 Y108.586 E.00468 +;WIDTH:0.518899 +G1 F13866.18 +G1 X120.993 Y108.684 E.00398 +;WIDTH:0.47611 +G1 F15000 +G1 X120.971 Y108.781 E.00358 +;WIDTH:0.433322 +G1 X120.948 Y108.878 E.00324 +;WIDTH:0.390533 +G1 X120.948 Y109.052 E.00503 +G1 X121.407 Y109.052 E.01327 +;WIDTH:0.433684 +G1 X121.337 Y108.919 E.00488 +;WIDTH:0.476835 +G1 X121.268 Y108.786 E.00541 +;WIDTH:0.519985 +G1 F13834.597 +G1 X121.199 Y108.654 E.00591 +;WIDTH:0.563136 +G1 F12687.053 +G1 X121.13 Y108.521 E.00648 +;WIDTH:0.606286 +G1 F11715.297 +G1 X121.061 Y108.388 E.00702 +G1 E-.7 F2100 +M73 P81 R1 +G1 X129.021 Y100.979 Z9.79 F18000 +G1 Z9.6 F720 +G1 E.7 F1500 +;WIDTH:0.452232 +G1 F15000 +G1 X129.075 Y101.316 E.01162 +;WIDTH:0.417576 +G1 X129.13 Y101.653 E.01064 +;WIDTH:0.38292 +G1 X129.131 Y102.556 E.02553 +;WIDTH:0.449999 +G1 X129.022 Y103.284 E.02492 +G1 X129.022 Y106.413 E.10591 +G1 X129.037 Y106.968 E.01879 +;WIDTH:0.424817 +G1 X129.019 Y106.909 E.00196 +;WIDTH:0.449999 +G1 X128.773 Y106.442 E.01787 +G1 X126.95 Y104.189 E.0981 +G1 X127.646 Y104.187 E.02356 +G1 X128.118 Y104.081 E.01637 +G1 X128.569 Y103.85 E.01715 +G1 X128.874 Y103.555 E.01436 +G1 X128.925 Y103.463 E.00356 +G1 X128.925 Y103.463 F18000 +G1 X128.568 Y104.361 +;WIDTH:0.543755 +G1 F13177.993 +G1 X128.568 Y105.467 E.04606 +G1 X127.872 Y104.607 E.04607 +G2 X128.383 Y104.444 I-.221 J-1.574 E.02244 +G1 E-.7 F2100 +G1 X128.383 Y104.444 F18000 +G1 X129.037 Y106.968 Z9.646 +G1 Z9.6 F720 +G1 E.7 F1500 +;WIDTH:0.419986 +G1 F15000 +G1 X129.079 Y107.474 E.01592 +;WIDTH:0.38292 +G1 X129.072 Y108.425 E.02689 +;WIDTH:0.427548 +G1 X129.044 Y108.61 E.00598 +;WIDTH:0.472175 +G1 X129.016 Y108.794 E.00664 +;WIDTH:0.516802 +G1 F13927.518 +G1 X128.989 Y108.979 E.00737 +;WIDTH:0.519606 +G1 F13845.593 +G1 X128.987 Y108.987 E.00033 +G1 E-.7 F2100 +G1 X128.987 Y108.987 F18000 +G1 X127.458 Y108.15 Z9.63 +G1 Z9.6 F720 +G1 E.7 F1500 +;TYPE:Ironing +;WIDTH:0.40161 +;HEIGHT:0.0075 +G1 F900 +G1 X122.857 Y108.15 E.00574 +G1 X122.857 Y108.05 E.00012 +G1 X127.413 Y108.05 E.00568 +G1 X127.413 Y107.95 E.00012 +G1 X122.857 Y107.95 E.00568 +G1 X122.857 Y107.85 E.00012 +G1 X127.413 Y107.85 E.00568 +G1 X127.413 Y107.75 E.00012 +G1 X122.857 Y107.75 E.00568 +G1 X122.857 Y107.65 E.00012 +G1 X127.413 Y107.65 E.00568 +G1 X127.413 Y107.55 E.00012 +G1 X122.812 Y107.55 E.00574 +G1 E-.7 F2100 +G1 X122.812 Y107.55 F18000 +G1 X126.41 Y107.45 Z9.663 +G1 Z9.6 F720 +G1 E.7 F1500 +G1 F900 +G1 X127.372 Y107.45 E.0012 +G1 X127.291 Y107.35 E.00016 +G1 X126.379 Y107.35 E.00114 +G1 X126.285 Y107.25 E.00017 +G1 X127.21 Y107.25 E.00115 +G1 X127.129 Y107.15 E.00016 +G1 X126.195 Y107.15 E.00116 +G1 X126.107 Y107.05 E.00017 +G1 X127.048 Y107.05 E.00117 +G1 X126.967 Y106.95 E.00016 +G1 X126.022 Y106.95 E.00118 +G1 X125.938 Y106.85 E.00016 +G1 X126.886 Y106.85 E.00118 +G1 X126.805 Y106.75 E.00016 +G1 X125.856 Y106.75 E.00118 +G1 X125.775 Y106.65 E.00016 +M73 Q82 S1 +G1 X126.725 Y106.65 E.00118 +G1 X126.644 Y106.55 E.00016 +G1 X125.695 Y106.55 E.00118 +G1 X125.615 Y106.45 E.00016 +G1 X126.563 Y106.45 E.00118 +G1 X126.482 Y106.35 E.00016 +G1 X125.535 Y106.35 E.00118 +G1 X125.455 Y106.25 E.00016 +M73 P82 R1 +G1 X126.401 Y106.25 E.00118 +G1 X126.32 Y106.15 E.00016 +G1 X125.375 Y106.15 E.00118 +G1 X125.295 Y106.05 E.00016 +G1 X126.239 Y106.05 E.00118 +G1 X126.158 Y105.95 E.00016 +G1 X125.215 Y105.95 E.00118 +G1 X125.135 Y105.85 E.00016 +G1 X126.077 Y105.85 E.00117 +G1 X125.996 Y105.75 E.00016 +G1 X125.055 Y105.75 E.00117 +G1 X124.975 Y105.65 E.00016 +G1 X125.915 Y105.65 E.00117 +G1 X125.834 Y105.55 E.00016 +G1 X124.895 Y105.55 E.00117 +G1 X124.816 Y105.45 E.00016 +G1 X125.753 Y105.45 E.00117 +G1 X125.673 Y105.35 E.00016 +G1 X124.736 Y105.35 E.00117 +G1 X124.656 Y105.25 E.00016 +G1 X125.592 Y105.25 E.00117 +G1 X125.511 Y105.15 E.00016 +G1 X124.576 Y105.15 E.00117 +G1 X124.496 Y105.05 E.00016 +G1 X125.43 Y105.05 E.00116 +G1 X125.349 Y104.95 E.00016 +G1 X124.416 Y104.95 E.00116 +G1 X124.336 Y104.85 E.00016 +G1 X125.268 Y104.85 E.00116 +G1 X125.187 Y104.75 E.00016 +G1 X124.256 Y104.75 E.00116 +G1 X124.176 Y104.65 E.00016 +G1 X125.106 Y104.65 E.00116 +G1 X125.025 Y104.55 E.00016 +G1 X124.096 Y104.55 E.00116 +G1 X124.016 Y104.45 E.00016 +G1 X124.944 Y104.45 E.00116 +G1 X124.863 Y104.35 E.00016 +G1 X123.936 Y104.35 E.00116 +G1 X123.856 Y104.25 E.00016 +G1 X124.782 Y104.25 E.00115 +G1 X124.701 Y104.15 E.00016 +G1 X123.776 Y104.15 E.00115 +G1 X123.696 Y104.05 E.00016 +G1 X124.621 Y104.05 E.00115 +G1 X124.54 Y103.95 E.00016 +G1 X123.616 Y103.95 E.00115 +G1 X123.536 Y103.85 E.00016 +G1 X124.459 Y103.85 E.00115 +G1 X124.378 Y103.75 E.00016 +G1 X123.456 Y103.75 E.00115 +G1 X123.376 Y103.65 E.00016 +G1 X124.297 Y103.65 E.00115 +G1 X124.216 Y103.55 E.00016 +G1 X123.296 Y103.55 E.00115 +G1 X123.216 Y103.45 E.00016 +G1 X124.135 Y103.45 E.00115 +G1 X124.054 Y103.35 E.00016 +G1 X123.136 Y103.35 E.00114 +G1 X123.056 Y103.25 E.00016 +G1 X123.973 Y103.25 E.00114 +G1 X123.892 Y103.15 E.00016 +G1 X122.976 Y103.15 E.00114 +G1 X122.896 Y103.05 E.00016 +G1 X123.811 Y103.05 E.00114 +G1 X123.727 Y102.95 E.00016 +G1 X122.816 Y102.95 E.00114 +G1 X122.736 Y102.85 E.00016 +G1 X123.641 Y102.85 E.00113 +G1 X123.554 Y102.75 E.00017 +G1 X122.656 Y102.75 E.00112 +G1 X122.576 Y102.65 E.00016 +G1 X123.468 Y102.65 E.00111 +G1 X123.382 Y102.55 E.00016 +G1 X122.496 Y102.55 E.00111 +G3 X122.481 Y102.45 I.102 J-.067 E.00013 +G1 X127.519 Y102.45 E.00628 +G1 X127.519 Y102.35 E.00012 +G1 X122.481 Y102.35 E.00628 +G1 X122.481 Y102.25 E.00012 +G1 X127.519 Y102.25 E.00628 +G1 X127.519 Y102.15 E.00012 +M73 Q83 S1 +G1 X122.481 Y102.15 E.00628 +G1 X122.481 Y102.05 E.00012 +M73 P83 R1 +G1 X127.519 Y102.05 E.00628 +G1 X127.519 Y101.95 E.00012 +G1 X122.481 Y101.95 E.00628 +G1 X122.481 Y101.85 E.00012 +G1 X127.564 Y101.85 E.00634 +M106 S252.45 +;LAYER_CHANGE +;Z:9.8 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;9.8 +M201 X3999.22 Y3999.22 + +G1 E-.7 F2100 +G1 X127.564 Y101.85 Z9.6 F18000 +G1 X122.353 Y108.692 Z9.8 +;AFTER_LAYER_CHANGE +;9.8 +M74 W0.725367 + +G1 X122.353 Y108.692 +G1 Z9.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F2403 +G1 X122.185 Y108.377 E.01208 +G1 X122.175 Y107.461 E.03101 +G1 X122.312 Y107.076 E.01383 +G1 X122.585 Y106.885 E.01128 +G1 X122.786 Y106.851 E.0069 +G1 X125.063 Y106.851 E.07707 +G1 X121.932 Y102.937 E.16966 +G1 X121.808 Y102.661 E.01024 +G1 X121.799 Y101.729 E.03155 +G1 X121.844 Y101.498 E.00797 +;WIDTH:0.495848 +G1 X122.3 Y101.105 E.02267 +G1 X122.409 Y101.098 E.00411 +;WIDTH:0.489381 +G1 X127.591 Y101.098 E.19237 +G1 X128.156 Y101.498 E.0257 +;WIDTH:0.449999 +G1 X128.201 Y101.729 E.00797 +G1 X128.201 Y102.539 E.02742 +G1 X128.106 Y102.866 E.01153 +G1 X127.893 Y103.069 E.00996 +G1 X127.591 Y103.149 E.01057 +G1 X124.769 Y103.149 E.09552 +G1 X127.959 Y107.091 E.17165 +G1 X128.095 Y107.475 E.01379 +G1 X128.095 Y108.271 E.02694 +G1 X128.061 Y108.473 E.00693 +G1 X127.869 Y108.746 E.0113 +;WIDTH:0.495848 +G1 X127.594 Y108.895 E.01178 +G1 X127.484 Y108.902 E.00415 +;WIDTH:0.489381 +G1 X122.786 Y108.902 E.17441 +G1 X122.397 Y108.728 E.01582 +G1 X122.397 Y108.728 F18000 +G1 X122.641 Y108.408 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F2403 +G1 X122.582 Y108.271 E.00505 +G1 X122.582 Y107.461 E.02742 +G1 X122.628 Y107.333 E.0046 +G1 X122.786 Y107.258 E.00592 +G1 X125.912 Y107.258 E.10581 +G1 X125.587 Y106.855 E.01752 +G1 X125.585 Y106.852 E.00012 +G1 X122.25 Y102.683 E.18071 +G1 X122.206 Y102.556 E.00455 +G1 X122.206 Y101.729 E.02799 +G1 X122.221 Y101.652 E.00266 +G1 X122.409 Y101.525 E.00768 +G1 X127.591 Y101.525 E.1754 +G1 X127.779 Y101.652 E.00768 +G1 X127.794 Y102.539 E.03003 +G1 X127.763 Y102.648 E.00384 +G1 X127.619 Y102.74 E.00578 +G1 X127.591 Y102.742 E.00095 +G1 X123.911 Y102.742 E.12456 +G1 X123.989 Y102.832 E.00403 +G1 X127.643 Y107.347 E.19661 +G1 X127.688 Y107.475 E.00459 +G1 X127.688 Y108.271 E.02694 +G1 X127.613 Y108.43 E.00595 +G1 X127.484 Y108.475 E.00462 +G1 X122.786 Y108.475 E.15902 +G1 X122.691 Y108.437 E.00346 +G1 X122.59 Y108.05 F18000 +G1 E-.7 F2100 +G1 X129.368 Y109.368 Z9.921 F18000 +G1 Z9.8 F720 +G1 E.7 F1500 +;TYPE:Perimeter +G1 F2403 +G1 X128.09 Y109.368 E.04326 +;WIDTH:0.489381 +G1 X127.484 Y109.348 E.02251 +G1 X122.786 Y109.348 E.17441 +G1 X122.103 Y109.368 E.02537 +;WIDTH:0.449999 +G1 X120.632 Y109.368 E.04979 +G1 X120.632 Y100.632 E.2957 +G1 X121.261 Y100.632 E.02129 +M73 Q84 S1 +;WIDTH:0.489381 +G1 X122.409 Y100.652 E.04262 +G1 X127.591 Y100.652 E.19237 +G1 X128.739 Y100.632 E.04262 +;WIDTH:0.449999 +G1 X129.368 Y100.632 E.02129 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +M73 P84 R1 +;TYPE:External perimeter +G1 F2403 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 X128.189 Y108.97 +;TYPE:Solid infill +;WIDTH:0.554633 +G1 F2403 +G1 X128.432 Y108.614 E.01834 +G1 X128.493 Y108.178 E.01873 +G1 X128.841 Y108.525 E.02091 +G1 X128.97 Y108.545 E.00555 +G1 X128.97 Y108.97 E.01808 +G1 X128.393 Y108.97 E.02455 +G1 X128.393 Y108.97 F18000 +G1 X129.022 Y108.056 +;WIDTH:0.449999 +G1 F2403 +G1 X128.441 Y107.476 E.02779 +G1 X128.353 Y107.332 E.00571 +;WIDTH:0.41646 +G1 X128.264 Y107.188 E.00526 +;WIDTH:0.38292 +G1 X127.927 Y106.745 E.01574 +G1 X127.436 Y105.895 F18000 +;WIDTH:0.449999 +G1 F2403 +G1 X129.022 Y107.481 E.07592 +G1 X129.022 Y106.905 E.0195 +G1 X125.611 Y103.494 E.16328 +G1 X126.018 Y103.278 E.0156 +;WIDTH:0.38292 +G1 X125.998 Y103.349 E.00209 +G1 X126.07 Y103.33 E.00211 +G1 X126.059 Y103.289 E.0012 +;WIDTH:0.41646 +G1 X126.122 Y103.39 E.0037 +;WIDTH:0.449999 +G1 X126.185 Y103.492 E.00406 +G1 X129.022 Y106.329 E.13581 +G1 E-.7 F2100 +G1 X129.022 Y106.329 F18000 +G1 X126.903 Y103.634 Z9.86 +G1 Z9.8 F720 +G1 E.7 F1500 +G1 F2403 +G1 X129.022 Y105.754 E.10146 +G1 X129.022 Y103.127 E.08892 +;WIDTH:0.483687 +G1 X129.005 Y101.74 E.05084 +;WIDTH:0.530947 +G1 X128.981 Y101.019 E.02927 +G1 X128.13 Y101.04 E.03454 +G1 X128.433 Y101.259 E.01517 +G1 X128.521 Y101.411 E.00713 +;WIDTH:0.515248 +G1 X128.564 Y101.737 E.01291 +;WIDTH:0.489317 +G1 X128.556 Y102.668 E.03456 +G1 X128.367 Y103.087 E.01706 +;WIDTH:0.449999 +G1 X128.027 Y103.385 E.0153 +G1 X127.692 Y103.488 E.01186 +G1 X126.759 Y103.49 E.03158 +G1 X126.759 Y103.49 F18000 +G1 X127.836 Y103.921 +;WIDTH:0.550261 +G1 F2403 +G1 X128.214 Y103.803 E.01671 +G1 X128.565 Y103.53 E.01876 +G1 X128.565 Y104.65 E.04725 +G1 X127.98 Y104.065 E.0349 +G1 E-.7 F2100 +G1 X127.98 Y104.065 F18000 +G1 X120.978 Y104.451 Z9.922 +G1 Z9.8 F720 +G1 E.7 F1500 +;WIDTH:0.449999 +G1 F2403 +G1 X120.978 Y109.022 E.15472 +G1 X122.217 Y109.015 E.04194 +G1 X122.091 Y108.915 E.00544 +G1 X121.849 Y108.464 E.01732 +G1 X121.829 Y107.472 E.03358 +G1 X122.013 Y106.919 E.01973 +G3 X122.507 Y106.55 I1.362 J1.309 E.02096 +G3 X123.235 Y106.505 I.543 J2.848 E.02475 +G1 X120.978 Y104.248 E.10804 +G1 X120.978 Y103.672 E.0195 +G1 X123.811 Y106.505 E.13561 +G1 X124.416 Y106.504 E.02048 +;WIDTH:0.38292 +G1 X124.397 Y106.575 E.00208 +G1 X124.468 Y106.556 E.00208 +G1 X124.457 Y106.515 E.0012 +;WIDTH:0.41646 +G1 X124.312 Y106.4 E.00575 +;WIDTH:0.449999 +G1 X124.167 Y106.286 E.00624 +G1 X121.036 Y103.154 E.1499 +G1 X120.935 Y103.079 E.00426 +;WIDTH:0.41646 +G1 X120.834 Y103.004 E.00391 +;WIDTH:0.38292 +G1 X120.825 Y103.157 E.00433 +G1 X120.825 Y103.157 F18000 +G1 X121.653 Y103.17 +;WIDTH:0.485656 +G1 F2403 +G1 X122.339 Y103.937 E.03788 +;WIDTH:0.451411 +G1 X123.026 Y104.704 E.03497 +;WIDTH:0.417166 +G1 X123.712 Y105.471 E.03202 +G1 E-.7 F2100 +G1 X123.712 Y105.471 F18000 +G1 X121.385 Y106.741 Z9.846 +G1 Z9.8 F720 +G1 E.7 F1500 +;WIDTH:0.449999 +G1 F2403 +G1 X121.385 Y105.231 E.05111 +G1 X122.343 Y106.188 E.04583 +G1 X122.19 Y106.251 E.0056 +M73 Q85 S1 +G1 X121.854 Y106.487 E.0139 +G1 X121.63 Y106.783 E.01256 +;WIDTH:0.487236 +G1 X121.526 Y107.019 E.00953 +;WIDTH:0.524472 +G1 X121.422 Y107.255 E.01033 +G1 X121.4 Y106.944 E.01248 +G3 X121.404 Y107.472 I-2.061 J.279 E.0212 +;WIDTH:0.521628 +G1 X121.421 Y108.579 E.04407 +G1 E-.7 F2100 +G1 X120.995 Y102.667 Z9.903 F18000 +G1 Z9.8 F720 +G1 E.7 F1500 +;WIDTH:0.48367 +G1 F2403 +G1 X120.995 Y102.689 E.00081 +G1 X121.167 Y102.689 E.0063 +;WIDTH:0.485375 +G1 X121.613 Y103.131 E.0231 +;WIDTH:0.485656 +G1 X121.653 Y103.17 E.00206 +G1 X121.448 Y102.7 E.01888 +;WIDTH:0.48367 +G1 X121.436 Y101.734 E.03541 +;WIDTH:0.528181 +G1 X121.485 Y101.364 E.01506 +G1 X121.8 Y101.049 E.01797 +G1 X121.017 Y101.025 E.03161 +G1 X120.997 Y101.649 E.02519 +;WIDTH:0.487815 +G1 X120.995 Y102.464 E.03015 +M73 P85 R1 +M106 S178.5 +;LAYER_CHANGE +;Z:10 +;HEIGHT:0.2 +;BEFORE_LAYER_CHANGE +G92 E0.0 +;10 +M201 X3999.2 Y3999.2 + +G1 E-.7 F2100 +G1 X120.995 Y102.464 Z9.8 F18000 +G1 X122.353 Y108.692 Z10 +;AFTER_LAYER_CHANGE +;10 +M74 W0.747282 + +G1 X122.353 Y108.692 +G1 Z10 F720 +G1 E.7 F1500 +;TYPE:Perimeter +;WIDTH:0.449999 +G1 F10200 +G1 X122.185 Y108.377 E.01208 +G1 X122.175 Y107.461 E.03101 +G1 X122.312 Y107.076 E.01383 +G1 X122.585 Y106.885 E.01128 +G1 X122.786 Y106.851 E.0069 +G1 X125.063 Y106.851 E.07707 +G1 X121.932 Y102.937 E.16966 +G1 X121.808 Y102.661 E.01024 +G1 X121.799 Y101.729 E.03155 +G1 X121.844 Y101.498 E.00797 +;WIDTH:0.495848 +G1 X122.3 Y101.105 E.02267 +G1 X122.409 Y101.098 E.00411 +;WIDTH:0.489381 +G1 X127.591 Y101.098 E.19237 +G1 X128.156 Y101.498 E.0257 +;WIDTH:0.449999 +G1 X128.201 Y101.729 E.00797 +G1 X128.201 Y102.539 E.02742 +G1 X128.106 Y102.866 E.01153 +G1 X127.893 Y103.069 E.00996 +G1 X127.591 Y103.149 E.01057 +G1 X124.769 Y103.149 E.09552 +G1 X127.959 Y107.091 E.17165 +G1 X128.095 Y107.475 E.01379 +G1 X128.095 Y108.271 E.02694 +G1 X128.061 Y108.473 E.00693 +G1 X127.869 Y108.746 E.0113 +;WIDTH:0.495848 +G1 X127.594 Y108.895 E.01178 +G1 X127.484 Y108.902 E.00415 +;WIDTH:0.489381 +G1 X122.786 Y108.902 E.17441 +G1 X122.397 Y108.728 E.01582 +G1 X122.397 Y108.728 F18000 +G1 X122.641 Y108.408 +;TYPE:External perimeter +;WIDTH:0.449999 +G1 F10200 +G1 X122.582 Y108.271 E.00505 +G1 X122.582 Y107.461 E.02742 +G1 X122.628 Y107.333 E.0046 +G1 X122.786 Y107.258 E.00592 +G1 X125.912 Y107.258 E.10581 +G1 X122.25 Y102.683 E.19836 +M73 P85 R0 +G1 X122.206 Y102.556 E.00455 +G1 X122.206 Y101.729 E.02799 +G1 X122.221 Y101.652 E.00266 +G1 X122.409 Y101.525 E.00768 +G1 F10199.989 +G1 X127.591 Y101.525 E.1754 +G1 F10200 +G1 X127.779 Y101.652 E.00768 +G1 X127.794 Y102.539 E.03003 +G1 X127.763 Y102.648 E.00384 +G1 X127.619 Y102.74 E.00578 +G1 X127.591 Y102.742 E.00095 +G1 X123.911 Y102.742 E.12456 +G1 X127.643 Y107.347 E.20063 +G1 X127.688 Y107.475 E.00459 +G1 X127.688 Y108.271 E.02694 +G1 X127.613 Y108.43 E.00595 +G1 X127.484 Y108.475 E.00462 +G1 F10199.989 +G1 X122.786 Y108.475 E.15902 +G1 F10200 +G1 X122.691 Y108.437 E.00346 +G1 X122.59 Y108.05 F18000 +G1 E-.7 F2100 +G1 X129.368 Y109.368 Z10.121 F18000 +G1 Z10 F720 +M73 Q85 S0 +G1 E.7 F1500 +;TYPE:Perimeter +G1 F10200 +G1 X128.09 Y109.368 E.04326 +;WIDTH:0.489381 +G1 X127.484 Y109.348 E.02251 +G1 X122.786 Y109.348 E.17441 +G1 X122.103 Y109.368 E.02537 +;WIDTH:0.449999 +G1 X120.632 Y109.368 E.04979 +G1 X120.632 Y100.632 E.2957 +G1 X121.261 Y100.632 E.02129 +;WIDTH:0.489381 +G1 X122.409 Y100.652 E.04262 +G1 X127.591 Y100.652 E.19237 +G1 X128.739 Y100.632 E.04262 +;WIDTH:0.449999 +G1 X129.368 Y100.632 E.02129 +G1 X129.368 Y109.308 E.29367 +G1 X129.775 Y109.775 F18000 +;TYPE:External perimeter +G1 F10199.989 +G1 X120.225 Y109.775 E.32326 +G1 X120.225 Y100.225 E.32326 +G1 X129.775 Y100.225 E.32326 +G1 X129.775 Y109.715 E.32122 +G1 X129.379 Y109.774 F18000 +G1 X129.207 Y108.403 +M204 P2000 +;TYPE:Top solid infill +;WIDTH:0.439333 +G1 F6000 +G1 X128.403 Y109.207 E.03748 +M204 P4000 +G1 X128.403 Y109.207 F18000 +G1 X127.878 Y109.171 +M204 P2000 +G1 F6000 +G1 X129.207 Y107.843 E.06193 +M204 P4000 +G1 X129.207 Y107.843 F18000 +G1 X129.207 Y107.282 +M204 P2000 +G1 F6000 +G1 X128.256 Y108.232 E.04431 +M204 P4000 +G1 X128.256 Y108.232 F18000 +G1 X128.256 Y107.672 +M204 P2000 +G1 F6000 +G1 X129.207 Y106.721 E.04433 +M204 P4000 +G1 X129.207 Y106.721 F18000 +G1 X129.207 Y106.161 +M204 P2000 +G1 F6000 +G1 X128.159 Y107.208 E.04883 +M204 P4000 +G1 X128.159 Y107.208 F18000 +G1 X127.965 Y106.842 +M204 P2000 +G1 F6000 +G1 X129.207 Y105.6 E.0579 +M204 P4000 +G1 X129.207 Y105.6 F18000 +G1 X129.207 Y105.04 +M204 P2000 +G1 F6000 +G1 X127.714 Y106.532 E.06957 +M204 P4000 +G1 X127.714 Y106.532 F18000 +G1 X127.463 Y106.222 +M204 P2000 +G1 F6000 +G1 X129.207 Y104.479 E.08127 +M204 P4000 +G1 X129.207 Y104.479 F18000 +G1 X129.207 Y103.918 +M204 P2000 +G1 F6000 +G1 X127.213 Y105.912 E.09295 +M204 P4000 +G1 X127.213 Y105.912 F18000 +G1 X126.962 Y105.603 +M204 P2000 +G1 F6000 +G1 X129.207 Y103.358 E.10465 +M204 P4000 +G1 X129.207 Y103.358 F18000 +G1 X129.207 Y102.797 +M204 P2000 +G1 F6000 +G1 X126.711 Y105.293 E.11635 +M204 P4000 +G1 X126.711 Y105.293 F18000 +G1 X126.46 Y104.983 +M204 P2000 +G1 F6000 +M73 Q86 S0 +G1 X129.207 Y102.237 E.12803 +M204 P4000 +G1 X129.207 Y102.237 F18000 +G1 X129.207 Y101.676 +M204 P2000 +G1 F6000 +G1 X128.363 Y102.52 E.03934 +M204 P4000 +G1 X128.363 Y102.52 F18000 +G1 X128.363 Y101.959 +M204 P2000 +G1 F6000 +G1 X129.207 Y101.115 E.03934 +M204 P4000 +G1 X129.207 Y101.115 F18000 +G1 X128.968 Y100.793 +M204 P2000 +G1 F6000 +G1 X128.308 Y101.453 E.03077 +M204 P4000 +G1 X128.308 Y101.453 F18000 +G1 X127.987 Y101.214 +M204 P2000 +G1 F6000 +G1 X128.396 Y100.805 E.01907 +M204 P4000 +G1 E-.7 F2100 +G1 X128.396 Y100.805 F18000 +G1 X127.579 Y103.303 Z10.046 +G1 Z10 F720 +G1 E.7 F1500 +M204 P2000 +G1 F6000 +G1 X126.21 Y104.673 E.06384 +M204 P4000 +G1 X126.21 Y104.673 F18000 +G1 X125.959 Y104.363 +M204 P2000 +G1 F6000 +G1 X127.017 Y103.305 E.04932 +M204 P4000 +G1 X126.455 Y103.307 F18000 +M204 P2000 +G1 F6000 +G1 X125.708 Y104.053 E.0348 +M204 P4000 +G1 X125.708 Y104.053 F18000 +G1 X125.457 Y103.743 +M204 P2000 +G1 F6000 +G1 X125.892 Y103.308 E.02028 +M204 P4000 +G1 E-.7 F2100 +G1 X125.892 Y103.308 F18000 +G1 X121.608 Y109.207 Z10.127 +G1 Z10 F720 +G1 E.7 F1500 +M204 P2000 +;WIDTH:0.439988 +G1 F6000 +G1 X122.157 Y108.657 E.02566 +M204 P4000 +G1 X122.157 Y108.657 F18000 +M73 P86 R0 +G1 X122.014 Y108.239 +M204 P2000 +G1 F6000 +G1 X121.046 Y109.207 E.0452 +M204 P4000 +G1 X121.046 Y109.207 F18000 +G1 X120.793 Y108.898 +M204 P2000 +G1 F6000 +G1 X122.014 Y107.677 E.05701 +M204 P4000 +G1 E-.7 F2100 +G1 X122.014 Y107.677 F18000 +G1 X124.125 Y106.69 Z10.041 +G1 Z10 F720 +G1 E.7 F1500 +M204 P2000 +G1 F6000 +G1 X124.459 Y106.355 E.01562 +M204 P4000 +G1 X124.459 Y106.355 F18000 +G1 X124.21 Y106.043 +M204 P2000 +G1 F6000 +G1 X123.563 Y106.69 E.03021 +M204 P4000 +G1 X123.563 Y106.69 F18000 +G1 X123.002 Y106.69 +M204 P2000 +G1 F6000 +G1 X123.96 Y105.731 E.04475 +M204 P4000 +G1 X123.96 Y105.731 F18000 +G1 X123.711 Y105.419 +M204 P2000 +G1 F6000 +G1 X120.793 Y108.336 E.13622 +M204 P4000 +G1 X120.793 Y108.336 F18000 +G1 X120.793 Y107.775 +M204 P2000 +G1 F6000 +G1 X123.461 Y105.107 E.12457 +M204 P4000 +G1 X123.461 Y105.107 F18000 +G1 X123.212 Y104.795 +M204 P2000 +G1 F6000 +G1 X120.793 Y107.213 E.11292 +M204 P4000 +G1 X120.793 Y107.213 F18000 +G1 X120.793 Y106.652 +M204 P2000 +G1 F6000 +G1 X122.962 Y104.483 E.10128 +M204 P4000 +G1 X122.962 Y104.483 F18000 +G1 X122.713 Y104.171 +M204 P2000 +G1 F6000 +G1 X120.793 Y106.09 E.08963 +M204 P4000 +G1 X120.793 Y106.09 F18000 +G1 X120.793 Y105.529 +M204 P2000 +G1 F6000 +G1 X122.463 Y103.859 E.07798 +M204 P4000 +G1 X122.463 Y103.859 F18000 +G1 X122.214 Y103.547 +M204 P2000 +G1 F6000 +G1 X120.793 Y104.967 E.06633 +M204 P4000 +G1 X120.793 Y104.967 F18000 +G1 X120.793 Y104.406 +M204 P2000 +G1 F6000 +G1 X121.964 Y103.235 E.05468 +M204 P4000 +G1 X121.743 Y102.894 F18000 +M204 P2000 +G1 F6000 +G1 X120.793 Y103.844 E.04436 +M204 P4000 +G1 X120.793 Y103.844 F18000 +G1 X120.793 Y103.283 +M204 P2000 +G1 F6000 +G1 X121.637 Y102.439 E.03941 +M204 P4000 +G1 X121.637 Y102.439 F18000 +G1 X121.637 Y101.877 +M204 P2000 +G1 F6000 +G1 X120.793 Y102.721 E.03941 +M204 P4000 +G1 X120.793 Y102.721 F18000 +G1 X120.793 Y102.159 +M204 P2000 +G1 F6000 +G1 X122.12 Y100.833 E.06194 +M204 P4000 +G1 X122.12 Y100.833 F18000 +G1 X121.583 Y100.808 +M204 P2000 +G1 F6000 +G1 X120.793 Y101.598 E.03689 +M204 P4000 +G1 E-.7 F2100 +G1 X120.793 Y101.598 F18000 +G1 X120.205 Y109.75 Z10.143 +G1 Z10 F720 +G1 E.7 F1500 +;TYPE:Ironing +;WIDTH:0.40161 +;HEIGHT:0.0075 +G1 F900 +G1 X129.75 Y109.75 E.01191 +G1 X129.75 Y109.65 E.00012 +G1 X120.25 Y109.65 E.01185 +G1 X120.25 Y109.55 E.00012 +G1 X129.75 Y109.55 E.01185 +G1 X129.75 Y109.45 E.00012 +M73 Q87 S0 +G1 X120.25 Y109.45 E.01185 +G1 X120.25 Y109.35 E.00012 +M73 P87 R0 +G1 X129.75 Y109.35 E.01185 +G1 X129.75 Y109.25 E.00012 +G1 X120.25 Y109.25 E.01185 +G1 X120.25 Y109.15 E.00012 +G1 X129.75 Y109.15 E.01185 +G1 X129.75 Y109.05 E.00012 +G1 X120.25 Y109.05 E.01185 +G1 X120.25 Y108.95 E.00012 +G1 X129.75 Y108.95 E.01185 +G1 X129.75 Y108.85 E.00012 +M73 Q88 S0 +G1 X120.25 Y108.85 E.01185 +G1 X120.25 Y108.75 E.00012 +G1 X129.75 Y108.75 E.01185 +G1 X129.75 Y108.65 E.00012 +M73 P88 R0 +G1 X120.25 Y108.65 E.01185 +G1 X120.25 Y108.55 E.00012 +G1 X129.75 Y108.55 E.01185 +G1 X129.75 Y108.45 E.00012 +G1 X127.713 Y108.45 E.00254 +G1 X127.713 Y108.35 E.00012 +G1 X129.75 Y108.35 E.00254 +G1 X129.75 Y108.25 E.00012 +G1 X127.713 Y108.25 E.00254 +G1 X127.713 Y108.15 E.00012 +G1 X129.75 Y108.15 E.00254 +G1 X129.75 Y108.05 E.00012 +G1 X127.713 Y108.05 E.00254 +G1 X127.713 Y107.95 E.00012 +G1 X129.75 Y107.95 E.00254 +G1 X129.75 Y107.85 E.00012 +G1 X127.713 Y107.85 E.00254 +G1 X127.713 Y107.75 E.00012 +G1 X129.75 Y107.75 E.00254 +G1 X129.75 Y107.65 E.00012 +G1 X127.713 Y107.65 E.00254 +G1 X127.713 Y107.55 E.00012 +G1 X129.75 Y107.55 E.00254 +G1 X129.75 Y107.45 E.00012 +M73 Q89 S0 +G1 X127.713 Y107.45 E.00254 +G2 X127.677 Y107.35 I-.097 J-.022 E.00014 +G1 X129.75 Y107.35 E.00259 +G1 X129.75 Y107.25 E.00012 +G1 X127.596 Y107.25 E.00269 +G1 X127.515 Y107.15 E.00016 +G1 X129.75 Y107.15 E.00279 +G1 X129.75 Y107.05 E.00012 +G1 X127.434 Y107.05 E.00289 +G1 X127.353 Y106.95 E.00016 +M73 P89 R0 +G1 X129.75 Y106.95 E.00299 +G1 X129.75 Y106.85 E.00012 +G1 X127.272 Y106.85 E.00309 +G1 X127.191 Y106.75 E.00016 +G1 X129.75 Y106.75 E.00319 +G1 X129.75 Y106.65 E.00012 +G1 X127.111 Y106.65 E.00329 +G1 X127.03 Y106.55 E.00016 +G1 X129.75 Y106.55 E.00339 +G1 X129.75 Y106.45 E.00012 +G1 X126.949 Y106.45 E.00349 +G1 X126.868 Y106.35 E.00016 +G1 X129.75 Y106.35 E.00359 +G1 X129.75 Y106.25 E.00012 +G1 X126.787 Y106.25 E.0037 +G1 X126.706 Y106.15 E.00016 +G1 X129.75 Y106.15 E.0038 +G1 X129.75 Y106.05 E.00012 +G1 X126.625 Y106.05 E.0039 +G1 X126.544 Y105.95 E.00016 +G1 X129.75 Y105.95 E.004 +G1 X129.75 Y105.85 E.00012 +G1 X126.463 Y105.85 E.0041 +G1 X126.382 Y105.75 E.00016 +G1 X129.75 Y105.75 E.0042 +G1 X129.75 Y105.65 E.00012 +G1 X126.301 Y105.65 E.0043 +G1 X126.22 Y105.55 E.00016 +G1 X129.75 Y105.55 E.0044 +G1 X129.75 Y105.45 E.00012 +G1 X126.139 Y105.45 E.0045 +G1 X126.058 Y105.35 E.00016 +M73 Q90 S0 +G1 X129.75 Y105.35 E.0046 +G1 X129.75 Y105.25 E.00012 +G1 X125.978 Y105.25 E.0047 +G1 X125.897 Y105.15 E.00016 +G1 X129.75 Y105.15 E.00481 +G1 X129.75 Y105.05 E.00012 +M73 P90 R0 +G1 X125.816 Y105.05 E.00491 +G1 X125.735 Y104.95 E.00016 +G1 X129.75 Y104.95 E.00501 +G1 X129.75 Y104.85 E.00012 +G1 X125.654 Y104.85 E.00511 +G1 X125.573 Y104.75 E.00016 +G1 X129.75 Y104.75 E.00521 +G1 X129.75 Y104.65 E.00012 +G1 X125.492 Y104.65 E.00531 +G1 X125.411 Y104.55 E.00016 +G1 X129.75 Y104.55 E.00541 +G1 X129.75 Y104.45 E.00012 +G1 X125.33 Y104.45 E.00551 +G1 X125.249 Y104.35 E.00016 +G1 X129.75 Y104.35 E.00561 +G1 X129.75 Y104.25 E.00012 +G1 X125.168 Y104.25 E.00571 +G1 X125.087 Y104.15 E.00016 +G1 X129.75 Y104.15 E.00582 +G1 X129.75 Y104.05 E.00012 +G1 X125.006 Y104.05 E.00592 +G1 X124.926 Y103.95 E.00016 +M73 Q91 S0 +G1 X129.75 Y103.95 E.00602 +G1 X129.75 Y103.85 E.00012 +G1 X124.845 Y103.85 E.00612 +G1 X124.764 Y103.75 E.00016 +M73 P91 R0 +G1 X129.75 Y103.75 E.00622 +G1 X129.75 Y103.65 E.00012 +G1 X124.683 Y103.65 E.00632 +G1 X124.602 Y103.55 E.00016 +G1 X129.75 Y103.55 E.00642 +G1 X129.75 Y103.45 E.00012 +G1 X124.521 Y103.45 E.00652 +G1 X124.44 Y103.35 E.00016 +G1 X129.75 Y103.35 E.00662 +G1 X129.75 Y103.25 E.00012 +G1 X124.359 Y103.25 E.00672 +G1 X124.278 Y103.15 E.00016 +G1 X129.75 Y103.15 E.00682 +G1 X129.75 Y103.05 E.00012 +G1 X124.197 Y103.05 E.00693 +G1 X124.116 Y102.95 E.00016 +G1 X129.75 Y102.95 E.00703 +G1 X129.75 Y102.85 E.00012 +G1 X123.977 Y102.85 E.0072 +G1 X123.977 Y102.85 F18000 +M73 Q92 S0 +G1 X123.892 Y102.75 +G1 F900 +G1 X129.75 Y102.75 E.00731 +G1 X129.75 Y102.65 E.00012 +M73 P92 R0 +G1 X127.819 Y102.65 E.00241 +G1 X127.819 Y102.55 E.00012 +G1 X129.75 Y102.55 E.00241 +G1 X129.75 Y102.45 E.00012 +G1 X127.819 Y102.45 E.00241 +G1 X127.819 Y102.35 E.00012 +G1 X129.75 Y102.35 E.00241 +G1 X129.75 Y102.25 E.00012 +G1 X127.819 Y102.25 E.00241 +G1 X127.819 Y102.15 E.00012 +G1 X129.75 Y102.15 E.00241 +G1 X129.75 Y102.05 E.00012 +G1 X127.819 Y102.05 E.00241 +G1 X127.819 Y101.95 E.00012 +G1 X129.75 Y101.95 E.00241 +G1 X129.75 Y101.85 E.00012 +G1 X127.819 Y101.85 E.00241 +G1 X127.819 Y101.75 E.00012 +G1 X129.75 Y101.75 E.00241 +G1 X129.75 Y101.65 E.00012 +G1 X127.819 Y101.65 E.00241 +G1 X127.819 Y101.55 E.00012 +G1 X129.795 Y101.55 E.00246 +G1 E-.7 F2100 +G1 X122.602 Y108.45 Z10.174 F18000 +G1 Z10 F720 +G1 E.7 F1500 +G1 F900 +G1 X120.25 Y108.45 E.00293 +G1 X120.25 Y108.35 E.00012 +G1 X122.557 Y108.35 E.00288 +G1 X122.557 Y108.25 E.00012 +G1 X120.25 Y108.25 E.00288 +G1 X120.25 Y108.15 E.00012 +G1 X122.557 Y108.15 E.00288 +G1 X122.557 Y108.05 E.00012 +G1 X120.25 Y108.05 E.00288 +G1 X120.25 Y107.95 E.00012 +G1 X122.557 Y107.95 E.00288 +G1 X122.557 Y107.85 E.00012 +G1 X120.25 Y107.85 E.00288 +G1 X120.25 Y107.75 E.00012 +G1 X122.557 Y107.75 E.00288 +G1 X122.557 Y107.65 E.00012 +G1 X120.25 Y107.65 E.00288 +G1 X120.25 Y107.55 E.00012 +G1 X122.557 Y107.55 E.00288 +G1 X122.557 Y107.45 E.00012 +M73 Q93 S0 +G1 X120.25 Y107.45 E.00288 +G1 X120.25 Y107.35 E.00012 +G1 X122.557 Y107.35 E.00288 +G1 X122.557 Y107.25 E.00012 +G1 X120.25 Y107.25 E.00288 +G1 X120.25 Y107.15 E.00012 +M73 P93 R0 +G1 X125.798 Y107.15 E.00692 +G1 X125.714 Y107.05 E.00016 +G1 X120.25 Y107.05 E.00682 +G1 X120.25 Y106.95 E.00012 +G1 X125.632 Y106.95 E.00671 +G1 X125.551 Y106.85 E.00016 +G1 X120.25 Y106.85 E.00661 +G1 X120.25 Y106.75 E.00012 +G1 X125.471 Y106.75 E.00651 +G1 X125.391 Y106.65 E.00016 +G1 X120.25 Y106.65 E.00641 +G1 X120.25 Y106.55 E.00012 +G1 X125.311 Y106.55 E.00631 +G1 X125.231 Y106.45 E.00016 +G1 X120.25 Y106.45 E.00621 +G1 X120.25 Y106.35 E.00012 +G1 X125.151 Y106.35 E.00611 +G1 X125.071 Y106.25 E.00016 +G1 X120.25 Y106.25 E.00601 +G1 X120.25 Y106.15 E.00012 +M73 Q94 S0 +G1 X124.991 Y106.15 E.00591 +G1 X124.911 Y106.05 E.00016 +G1 X120.25 Y106.05 E.00581 +G1 X120.25 Y105.95 E.00012 +M73 P94 R0 +G1 X124.831 Y105.95 E.00571 +G1 X124.751 Y105.85 E.00016 +G1 X120.25 Y105.85 E.00561 +G1 X120.25 Y105.75 E.00012 +G1 X124.671 Y105.75 E.00551 +G1 X124.591 Y105.65 E.00016 +G1 X120.25 Y105.65 E.00541 +G1 X120.25 Y105.55 E.00012 +G1 X124.511 Y105.55 E.00531 +G1 X124.431 Y105.45 E.00016 +G1 X120.25 Y105.45 E.00521 +G1 X120.25 Y105.35 E.00012 +G1 X124.351 Y105.35 E.00511 +G1 X124.271 Y105.25 E.00016 +G1 X120.25 Y105.25 E.00502 +G1 X120.25 Y105.15 E.00012 +G1 X124.191 Y105.15 E.00492 +G1 X124.111 Y105.05 E.00016 +G1 X120.25 Y105.05 E.00482 +G1 X120.25 Y104.95 E.00012 +G1 X124.031 Y104.95 E.00472 +G1 X123.951 Y104.85 E.00016 +G1 X120.25 Y104.85 E.00462 +G1 X120.25 Y104.75 E.00012 +G1 X123.871 Y104.75 E.00452 +G1 X123.792 Y104.65 E.00016 +M73 Q95 S0 +G1 X120.25 Y104.65 E.00442 +G1 X120.25 Y104.55 E.00012 +M73 P95 R0 +G1 X123.712 Y104.55 E.00432 +G1 X123.632 Y104.45 E.00016 +G1 X120.25 Y104.45 E.00422 +G1 X120.25 Y104.35 E.00012 +G1 X123.552 Y104.35 E.00412 +G1 X123.472 Y104.25 E.00016 +G1 X120.25 Y104.25 E.00402 +G1 X120.25 Y104.15 E.00012 +G1 X123.392 Y104.15 E.00392 +G1 X123.312 Y104.05 E.00016 +G1 X120.25 Y104.05 E.00382 +G1 X120.25 Y103.95 E.00012 +G1 X123.232 Y103.95 E.00372 +G1 X123.152 Y103.85 E.00016 +G1 X120.25 Y103.85 E.00362 +G1 X120.25 Y103.75 E.00012 +G1 X123.072 Y103.75 E.00352 +G1 X122.992 Y103.65 E.00016 +G1 X120.25 Y103.65 E.00342 +G1 X120.25 Y103.55 E.00012 +G1 X122.912 Y103.55 E.00332 +G1 X122.832 Y103.45 E.00016 +G1 X120.25 Y103.45 E.00322 +G1 X120.25 Y103.35 E.00012 +G1 X122.752 Y103.35 E.00312 +G1 X122.672 Y103.25 E.00016 +G1 X120.25 Y103.25 E.00302 +G1 X120.25 Y103.15 E.00012 +G1 X122.592 Y103.15 E.00292 +G1 X122.512 Y103.05 E.00016 +G1 X120.25 Y103.05 E.00282 +G1 X120.25 Y102.95 E.00012 +G1 X122.432 Y102.95 E.00272 +G1 X122.352 Y102.85 E.00016 +G1 X120.25 Y102.85 E.00262 +G1 X120.25 Y102.75 E.00012 +G1 X122.272 Y102.75 E.00252 +G1 X122.192 Y102.65 E.00016 +M73 Q96 S0 +G1 X120.25 Y102.65 E.00242 +G1 X120.25 Y102.55 E.00012 +G1 X122.181 Y102.55 E.00241 +G1 X122.181 Y102.45 E.00012 +M73 P96 R0 +G1 X120.25 Y102.45 E.00241 +G1 X120.25 Y102.35 E.00012 +G1 X122.181 Y102.35 E.00241 +G1 X122.181 Y102.25 E.00012 +G1 X120.25 Y102.25 E.00241 +G1 X120.25 Y102.15 E.00012 +G1 X122.181 Y102.15 E.00241 +G1 X122.181 Y102.05 E.00012 +G1 X120.25 Y102.05 E.00241 +G1 X120.25 Y101.95 E.00012 +G1 X122.181 Y101.95 E.00241 +G1 X122.181 Y101.85 E.00012 +G1 X120.25 Y101.85 E.00241 +G1 X120.25 Y101.75 E.00012 +G1 X122.181 Y101.75 E.00241 +G1 X122.181 Y101.65 E.00012 +G1 X120.25 Y101.65 E.00241 +G1 X120.25 Y101.55 E.00012 +G1 X122.226 Y101.55 E.00246 +G1 E-.7 F2100 +G1 X122.226 Y101.55 F18000 +G1 X120.205 Y101.45 Z10.035 +G1 Z10 F720 +G1 E.7 F1500 +G1 F900 +G1 X129.75 Y101.45 E.01191 +G1 X129.75 Y101.35 E.00012 +G1 X120.25 Y101.35 E.01185 +G1 X120.25 Y101.25 E.00012 +G1 X129.75 Y101.25 E.01185 +G1 X129.75 Y101.15 E.00012 +G1 X120.25 Y101.15 E.01185 +G1 X120.25 Y101.05 E.00012 +M73 P97 R0 +M73 Q97 S0 +G1 X129.75 Y101.05 E.01185 +G1 X129.75 Y100.95 E.00012 +G1 X120.25 Y100.95 E.01185 +G1 X120.25 Y100.85 E.00012 +G1 X129.75 Y100.85 E.01185 +G1 X129.75 Y100.75 E.00012 +G1 X120.25 Y100.75 E.01185 +G1 X120.25 Y100.65 E.00012 +G1 X129.75 Y100.65 E.01185 +G1 X129.75 Y100.55 E.00012 +G1 X120.25 Y100.55 E.01185 +G1 X120.25 Y100.45 E.00012 +M73 Q98 S0 +G1 X129.75 Y100.45 E.01185 +G1 X129.75 Y100.35 E.00012 +M73 P98 R0 +G1 X120.205 Y100.35 E.01191 +M486 S-1 +G1 E-.7 F2100 +M107 +;TYPE:Custom +; Filament-specific end gcode +G1 Z11 F720 ; Move print head up +M104 S0 ; turn off temperature +M140 S0 ; turn off heatbed +M107 ; turn off fan +G1 X241 Y170 F3600 ; park +G1 Z33 F300 ; Move print head up G4 ; wait M572 S0 ; reset PA M593 X T2 F0 ; disable IS M593 Y T2 F0 ; disable IS M84 X Y E ; disable motors -; max_layer_z = 4 +; max_layer_z = 10 M73 P100 R0 M73 Q100 S0 -; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl","polygon":[[127.000,107.000],[123.000,107.000],[123.000,103.000],[127.000,103.000]]}]} -; filament used [mm] = 55.25 -; filament used [cm3] = 0.13 -; filament used [g] = 0.16 -; filament cost = 0.00 -; total filament used [g] = 0.16 -; total filament cost = 0.00 +; objects_info = {"objects":[{"name":"xyz-cali-cube-by-r3d_v1.stl","polygon":[[130.000,110.000],[120.000,110.000],[120.000,100.000],[130.000,100.000]]}]} +; filament used [mm] = 277.70 +; filament used [cm3] = 0.67 +; filament used [g] = 0.83 +; filament cost = 0.02 +; total filament used [g] = 0.83 +; total filament cost = 0.02 ; total filament used for wipe tower [g] = 0.00 -; estimated printing time (normal mode) = 1m 7s -; estimated printing time (silent mode) = 1m 8s -; estimated first layer printing time (normal mode) = 19s -; estimated first layer printing time (silent mode) = 19s +; estimated printing time (normal mode) = 6m 42s +; estimated printing time (silent mode) = 6m 51s +; estimated first layer printing time (normal mode) = 34s +; estimated first layer printing time (silent mode) = 35s ; prusaslicer_config = begin ; arc_fitting = emit_center @@ -3655,7 +10747,7 @@ M73 Q100 S0 ; infill_speed = 250 ; inherits_cummulative = ;;"Original Prusa MK4S HF0.4 nozzle" ; interface_shells = 0 -; ironing = 0 +; ironing = 1 ; ironing_flowrate = 15% ; ironing_spacing = 0.1 ; ironing_speed = 15 From 044fba837f557cbb683c5edc2f5f16c68f97d53a Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 10 Nov 2024 21:59:00 -0500 Subject: [PATCH 059/194] fix: added os[Port] back in, so the repr methods could reference them. --- Tests/parallel_test_runner.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index e68b8560..3caa394d 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -1,3 +1,4 @@ +import os import re import subprocess import threading @@ -34,7 +35,9 @@ showAnything = False verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" def run_tests_for_port(comm_port): - subprocess.Popen(["pytest", "test_runner.py", verbosityCommand, f"--myVerbose={verbosity}", f"--port={comm_port}"]).wait() + env = os.environ.copy() + env["PORT"] = comm_port + subprocess.Popen(["pytest", ".", verbosityCommand, f"--myVerbose={verbosity}", f"--port={comm_port}"], env=env).wait() if __name__ == "__main__": # Create and start a thread for each port From e6aa20927a2db44c510c93b80a3902394289503d Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Sun, 10 Nov 2024 23:00:05 -0500 Subject: [PATCH 060/194] fix: removed lots of extra spaces --- server/controllers/jobs.py | 361 ++++++++++++++++++------------------- 1 file changed, 180 insertions(+), 181 deletions(-) diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 555db30b..0d5a0b20 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -57,7 +57,7 @@ def getJobs(): @jobs_bp.route('/addjobtoqueue', methods=["POST"]) def add_job_to_queue(): try: - # retrieve job data + # retrieve job data file = request.files['file'] # Access file directly from request.files file_name_original = file.filename name = request.form['name'] # Access other form fields from request.form @@ -67,29 +67,29 @@ def add_job_to_queue(): # quantity = request.form['quantity'] td_id = int(request.form['td_id']) filament = request.form['filament'] - favoriteOne = False + favoriteOne = False # for i in range(int(quantity)): if(favorite == 'true' and not favoriteOne): favorite = 1 favoriteOne = True - else: + else: favorite = 0 - - status = 'inqueue' # set status - res = Job.jobHistoryInsert(name, printer_id, status, file, file_name_original, favorite, td_id) # insert into DB - + + status = 'inqueue' # set status + res = Job.jobHistoryInsert(name, printer_id, status, file, file_name_original, favorite, td_id) # insert into DB + # retrieve job from DB id = res['id'] - + job = Job.query.get(id) - + base_name, extension = os.path.splitext(file_name_original) # Append the ID to the base name file_name_pk = f"{base_name}_{id}{extension}" - - job.setFileName(file_name_pk) # set unique in-memory file name + + job.setFileName(file_name_pk) # set unique in-memory file name job.setFilament(filament) # set filament type @@ -99,16 +99,16 @@ def add_job_to_queue(): findPrinterObject(printer_id).getQueue().addToFront(job, printer_id) else: findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) - + return jsonify({"success": True, "message": "Job added to printer queue."}), 200 - + except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/autoqueue', methods=["POST"]) -def auto_queue(): - try: +def auto_queue(): + try: file = request.files['file'] # Access file directly from request.files file_name_original = file.filename name = request.form['name'] # Access other form fields from request.form @@ -118,37 +118,37 @@ def auto_queue(): td_id = request.form['td_id'] filament = request.form['filament'] - favoriteOne = False + favoriteOne = False # for i in range(int(quantity)): - status = 'inqueue' # set status + status = 'inqueue' # set status printer_id = getSmallestQueue() - + if(favorite == 'true' and not favoriteOne): favorite = 1 favoriteOne = True - else: + else: favorite = 0 # favorite = 1 if _favorite == 'true' else 0 - - res = Job.jobHistoryInsert(name, printer_id, status, file, file_name_original, favorite, td_id) # insert into DB - + + res = Job.jobHistoryInsert(name, printer_id, status, file, file_name_original, favorite, td_id) # insert into DB + id = res['id'] - + job = Job.query.get(id) - + base_name, extension = os.path.splitext(file_name_original) # Append the ID to the base name file_name_pk = f"{base_name}_{id}{extension}" - - job.setFileName(file_name_pk) # set unique in-memory file name + + job.setFileName(file_name_pk) # set unique in-memory file name job.setFilament(filament) # set filament type - findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) - + findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) + return jsonify({"success": True, "message": "Job added to printer queue."}), 200 - + except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 @@ -157,21 +157,21 @@ def auto_queue(): def rerun_job(): try: data = request.get_json() - printerpk = data['printerpk'] # printer to rerun job on + printerpk = data['printerpk'] # printer to rerun job on jobpk = data['jobpk'] - + rerunjob(printerpk, jobpk, "back") return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + # route to insert job into database @jobs_bp.route('/jobdbinsert', methods=["POST"]) def job_db_insert(): try: jobdata = request.form.get('jobdata') - + jobdata = json.loads(jobdata) # Convert jobdata from JSON to a Python dictionary # Get the individual fields from jobdata @@ -188,18 +188,18 @@ def job_db_insert(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - - # cancel queued job -@jobs_bp.route('/canceljob', methods=["POST"]) + + # cancel queued job +@jobs_bp.route('/canceljob', methods=["POST"]) def remove_job(): try: # job has: printer id. job info. # 0 = cancel job, 1 = clear job, 2 = fail job, 3 = clear job (but also rerun) data = request.get_json() jobpk = data['jobpk'] - # Retrieve job to delete & printer id - job = Job.findJob(jobpk) - printerid = job.getPrinterId() + # Retrieve job to delete & printer id + job = Job.findJob(jobpk) + printerid = job.getPrinterId() jobstatus = job.getStatus() # retrieve printer object & corresponding queue @@ -207,11 +207,11 @@ def remove_job(): # printerobject.setStatus("complete") queue = printerobject.getQueue() inmemjob = queue.getJob(job) - if jobstatus == 'printing': # only change statuses, dont remove from queue + if jobstatus == 'printing': # only change statuses, dont remove from queue printerobject.setStatus("complete") - else: - queue.deleteJob(jobpk, printerid) - + else: + queue.deleteJob(jobpk, printerid) + inmemjob.setStatus("cancelled") Job.update_job_status(jobpk, "cancelled") @@ -219,21 +219,21 @@ def remove_job(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - - # cancel queued job -@jobs_bp.route('/cancelfromqueue', methods=["POST"]) + + # cancel queued job +@jobs_bp.route('/cancelfromqueue', methods=["POST"]) def remove_job_from_queue(): try: # job has: printer id. job info. # 0 = cancel job, 1 = clear job, 2 = fail job, 3 = clear job (but also rerun) data = request.get_json() jobarr = data['jobarr'] - + for jobpk in jobarr: - # Retrieve job to delete & printer id - job = Job.findJob(jobpk) - printerid = job.getPrinterId() + # Retrieve job to delete & printer id + job = Job.findJob(jobpk) + printerid = job.getPrinterId() jobstatus = job.getStatus() # retrieve printer object & corresponding queue @@ -241,11 +241,11 @@ def remove_job_from_queue(): # printerobject.setStatus("complete") queue = printerobject.getQueue() inmemjob = queue.getJob(job) - if jobstatus == 'printing': # only change statuses, dont remove from queue + if jobstatus == 'printing': # only change statuses, dont remove from queue printerobject.setStatus("complete") - else: - queue.deleteJob(jobpk, printerid) - + else: + queue.deleteJob(jobpk, printerid) + inmemjob.setStatus("cancelled") Job.update_job_status(jobpk, "cancelled") @@ -253,47 +253,47 @@ def remove_job_from_queue(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - - + + @jobs_bp.route('/releasejob', methods=["POST"]) -def releasejob(): - try: +def releasejob(): + try: data = request.get_json() jobpk = data['jobpk'] key = data['key'] - job = Job.findJob(jobpk) - printerid = job.getPrinterId() + job = Job.findJob(jobpk) + printerid = job.getPrinterId() printerobject = findPrinterObject(printerid) printerobject.error = "" queue = printerobject.getQueue() queue.deleteJob(jobpk, printerid) # remove job from queue - + printerid = data['printerid'] - + currentStatus = printerobject.getStatus() - if key == 3: + if key == 3: Job.update_job_status(jobpk, "error") printerobject.setError(job.comments) - printerobject.setStatus("error") # printer ready to accept new prints - - elif key == 2: + printerobject.setStatus("error") # printer ready to accept new prints + + elif key == 2: rerunjob(printerid, jobpk, "front") - + if currentStatus!="offline": - printerobject.setStatus("ready") # printer ready to accept new prints - - elif key == 1: + printerobject.setStatus("ready") # printer ready to accept new prints + + elif key == 1: if currentStatus!="offline": - printerobject.setStatus("ready") # printer ready to accept new prints - + printerobject.setStatus("ready") # printer ready to accept new prints + return jsonify({"success": True, "message": "Job released successfully."}), 200 - - except Exception as e: + + except Exception as e: print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + return jsonify({"error": "Unexpected error occurred"}), 500 + @jobs_bp.route('/bumpjob', methods=["POST"]) def bumpjob(): try: @@ -301,99 +301,99 @@ def bumpjob(): printer_id = data['printerid'] job_id = data['jobid'] choice = data['choice'] - + printerobject = findPrinterObject(printer_id) - - if choice == 1: + + if choice == 1: printerobject.queue.bump(True, job_id) - elif choice == 2: + elif choice == 2: printerobject.queue.bump(False, job_id) - elif choice == 3: + elif choice == 3: printerobject.queue.bumpExtreme(True, job_id, printer_id) - elif choice == 4: + elif choice == 4: printerobject.queue.bumpExtreme(False, job_id, printer_id) - else: + else: return jsonify({"error": "Unexpected error occurred"}), 500 - + return jsonify({"success": True, "message": "Job bumped up in printer queue."}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/movejob', methods=["POST"]) def moveJob(): try: data = request.get_json() printer_id = data['printerid'] arr = data['arr'] - + printerobject = findPrinterObject(printer_id) printerobject.queue.reorder(arr) return jsonify({"success": True, "message": "Queue updated successfully."}), 200 except Exception as e: print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + return jsonify({"error": "Unexpected error occurred"}), 500 + @jobs_bp.route('/updatejobstatus', methods=["POST"]) def updateJobStatus(): try: data = request.get_json() job_id = data['jobid'] newstatus = data['status'] - + res = Job.update_job_status(job_id, newstatus) - - job = Job.findJob(job_id) - printerid = job.getPrinterId() + + job = Job.findJob(job_id) + printerid = job.getPrinterId() printerobject = findPrinterObject(printerid) queue = printerobject.getQueue() - + # queue.deleteJob(job_id, printerid) - + return jsonify(res), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/assigntoerror', methods=["POST"]) def assignToError(): try: data = request.get_json() job_id = data['jobid'] newstatus = data['status'] - + res = Job.update_job_status(job_id, newstatus) - - job = Job.findJob(job_id) - printerid = job.getPrinterId() + + job = Job.findJob(job_id) + printerid = job.getPrinterId() printerobject = findPrinterObject(printerid) queue = printerobject.getQueue() - + queue.deleteJob(job_id, printerid) - + return jsonify(res), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/deletejob', methods=["POST"]) def delete_job(): try: data = request.get_json() job_id = data['jobid'] - - # Retrieve job to delete & printer id - job = Job.findJob(job_id) - printer_id = job.getPrinterId() + + # Retrieve job to delete & printer id + job = Job.findJob(job_id) + printer_id = job.getPrinterId() print("ID: ", printer_id) - + if printer_id != 0: # Retrieve printer object & corresponding queue printer_object = findPrinterObject(printer_id) queue = printer_object.getQueue() - + # Delete job from the queue - queue.deleteJob(job_id, printer_id) + queue.deleteJob(job_id, printer_id) # Delete job from the database Job.delete_job(job_id) @@ -407,14 +407,14 @@ def delete_job(): @jobs_bp.route("/setstatus", methods=["POST"]) def setStatus(): try: - data = request.get_json() # get json data + data = request.get_json() # get json data printer_id = data['printerid'] newstatus = data['status'] - + printerobject = findPrinterObject(printer_id) - + printerobject.setStatus(newstatus) - + return jsonify({"success": True, "message": "Status updated successfully."}), 200 except Exception as e: @@ -424,36 +424,36 @@ def setStatus(): @jobs_bp.route('/getfile', methods=["GET"]) def getFile(): try: - job_id = request.args.get('jobid', default=-1, type=int) - job = Job.findJob(job_id) + job_id = request.args.get('jobid', default=-1, type=int) + job = Job.findJob(job_id) file_blob = job.getFile() # Assuming this returns the file blob decompressed_file = gzip.decompress(file_blob).decode('utf-8') - + return jsonify({"file": decompressed_file, "file_name": job.getFileNameOriginal()}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/nullifyjobs', methods=["POST"]) def nullifyJobs(): - try: + try: data = request.get_json() printerid = data['printerid'] res = Job.nullifyPrinterId(printerid) - return res + return res except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/clearspace', methods=["GET"]) -def clearSpace(): - try: +def clearSpace(): + try: res = Job.clearSpace() - return res + return res except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/getfavoritejobs', methods=["GET"]) def getFavoriteJobs(): try: @@ -462,10 +462,10 @@ def getFavoriteJobs(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/favoritejob', methods=["POST"]) def favoriteJob(): - try: + try: data = request.get_json() jobid = data['jobid'] favorite = data['favorite'] @@ -475,10 +475,10 @@ def favoriteJob(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/assignissue', methods=["POST"]) def assignIssue(): - try: + try: data = request.get_json() jobid = data['jobid'] issueid = data['issueid'] @@ -489,10 +489,10 @@ def assignIssue(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/removeissue', methods=["POST"]) def removeIssue(): - try: + try: data = request.get_json() jobid = data['jobid'] job = Job.findJob(jobid) @@ -502,10 +502,10 @@ def removeIssue(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/startprint', methods=["POST"]) -def startPrint(): - try: +def startPrint(): + try: data = request.get_json() printerid = data['printerid'] jobid = data['jobid'] @@ -514,27 +514,27 @@ def startPrint(): inmemjob = queue.getJobById(jobid) print(inmemjob) inmemjob.setReleased(1) - + return jsonify({"success": True, "message": "Job started successfully."}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected ersetupPortRepairSocketror occurred"}), 500 - + @jobs_bp.route('/savecomment', methods=["POST"]) -def saveComment(): - try: +def saveComment(): + try: data = request.get_json() jobid = data['jobid'] comments = data['comments'] - + # job = Job.findJob(jobid) res = Job.setComment(jobid, comments) - return res - + return res + except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/downloadcsv', methods=["GET", "POST"]) def downloadCSV(): try: @@ -550,17 +550,17 @@ def downloadCSV(): res = Job.downloadCSV(0, jobids) print(res) - return res + return res except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - + @jobs_bp.route('/removeCSV', methods=["GET", "POST"]) def removeCSV(): try: - # Create in-memory uploads folder + # Create in-memory uploads folder csv_folder = os.path.join('../tempcsv') if os.path.exists(csv_folder): shutil.rmtree(csv_folder) @@ -569,8 +569,8 @@ def removeCSV(): else: # Create the uploads folder if it doesn't exist os.makedirs(csv_folder) - print("TempCSV folder created successfully.") - + print("TempCSV folder created successfully.") + return jsonify({"success": True, "message": "CSV file removed successfully."}), 200 except Exception as e: @@ -578,14 +578,14 @@ def removeCSV(): return jsonify({"error": "Unexpected error occurred"}), 500 @jobs_bp.route("/repairports", methods=["POST", "GET"]) -def repair_ports(): +def repair_ports(): try: - ports = serial.tools.list_ports.comports() - for port in ports: - hwid = port.hwid # get hwid + ports = serial.tools.list_ports.comports() + for port in ports: + hwid = port.hwid # get hwid hwid_without_location = hwid.split(' LOCATION=')[0] printer = Printer.getPrinterByHwid(hwid_without_location) - if printer is not None: + if printer is not None: if(printer.getDevice()!=port.device): printer.editPort(printer.getId(), port.device) printerthread = findPrinterObject(printer.getId()) @@ -594,10 +594,10 @@ def repair_ports(): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - -@jobs_bp.route("/refetchtimedata", methods=['POST', 'GET']) -def refetch_time(): - try: + +@jobs_bp.route("/refetchtimedata", methods=['POST', 'GET']) +def refetch_time(): + try: data = request.get_json() jobid = data['jobid'] printerid = data['printerid'] @@ -605,54 +605,53 @@ def refetch_time(): printer = findPrinterObject(printerid) job = printer.getQueue().getNext() - timearray = job.job_time + timearray = job.job_time timejson = { - 'total': timearray[0], - 'eta': timearray[1].isoformat(), - 'timestart': timearray[2].isoformat(), + 'total': timearray[0], + 'eta': timearray[1].isoformat(), + 'timestart': timearray[2].isoformat(), 'pause': timearray[3].isoformat() } - return jsonify(timejson) + return jsonify(timejson) except Exception as e: print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - -def findPrinterObject(printer_id): + return jsonify({"error": "Unexpected error occurred"}), 500 + +def findPrinterObject(printer_id): threads = printer_status_service.getThreadArray() - return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer + return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer def getSmallestQueue(): threads = printer_status_service.getThreadArray() smallest_queue_thread = min(threads, key=lambda thread: thread.printer.queue.getSize()) return smallest_queue_thread.printer.id - + def rerunjob(printerpk, jobpk, position): - job = Job.findJob(jobpk) # retrieve Job to rerun - - status = 'inqueue' # set status + job = Job.findJob(jobpk) # retrieve Job to rerun + + status = 'inqueue' # set status file_name_original = job.getFileNameOriginal() # get original file name favorite = job.getFileFavorite() # get favorite status td_id = job.getTdId() - # Insert new job into DB and return new PK - res = Job.jobHistoryInsert(name=job.getName(), printer_id=printerpk, status=status, file=job.getFile(), file_name_original=file_name_original, favorite=favorite, td_id=td_id) # insert into DB - + # Insert new job into DB and return new PK + res = Job.jobHistoryInsert(name=job.getName(), printer_id=printerpk, status=status, file=job.getFile(), file_name_original=file_name_original, favorite=favorite, td_id=td_id) # insert into DB + id = res['id'] file_name_pk = file_name_original + f"_{id}" # append id to file name to make it unique - + rjob = Job.query.get(id) - + base_name, extension = os.path.splitext(file_name_original) # Append the ID to the base name file_name_pk = f"{base_name}_{id}{extension}" - - rjob.setFileName(file_name_pk) # set unique file name - + + rjob.setFileName(file_name_pk) # set unique file name + if position == "back": - findPrinterObject(printerpk).getQueue().addToBack(rjob, rjob.printer_id) - else: - findPrinterObject(printerpk).getQueue().addToFront(rjob, rjob.printer_id) - \ No newline at end of file + findPrinterObject(printerpk).getQueue().addToBack(rjob, rjob.fabricator_id) + else: + findPrinterObject(printerpk).getQueue().addToFront(rjob, rjob.fabricator_id) From 7977f2c0ce7e30fe6ee005007dc773c13b50dba5 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:49:28 -0500 Subject: [PATCH 061/194] feat: added support for multiple chars in the line seperator. --- Tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 7c8db321..d105e486 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -23,7 +23,7 @@ def pytest_addoption(parser): def line_separator(interrupter: str, symbol: str = "-", length: int = 104) -> str: if not interrupter: - return symbol * length + return symbol * (length//len(symbol)) interrupterNoColor = re.sub(r'\033\[[0-9;]*m', '', interrupter) side = (length - 2 - len(interrupterNoColor)) / 2 return symbol * math.ceil(side) + " " + interrupter + " " + symbol * math.floor(side) From d5b1ca26056a96d0f49b585b9a2e6796d69dd7b4 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:49:50 -0500 Subject: [PATCH 062/194] fix: print the whole stack trace on error --- Tests/conftest.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index d105e486..5b5f7106 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -111,18 +111,20 @@ def pytest_sessionfinish(session, exitstatus) -> None: for failTest in session.config.failNames: logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) #todo: break this out into something that can be called anywhere for the Logger class - things = list(session.config.fails[failTest].reprtraceback.reprentries[0].reprfuncargs.args) - for args in things: - logger.logMessageOnly(f"{args[0]} = {args[1]}") - logger.logMessageOnly("") - for line in list(session.config.fails[failTest].reprtraceback.reprentries[0].lines): - if line.startswith("E") or line.startswith(">"): - logger.logMessageOnly(line.__str__(), logLevel=logger.ERROR) - else: - logger.logMessageOnly(line.__str__()) - loc = session.config.fails[failTest].reprtraceback.reprentries[0].reprfileloc - logger.logMessageOnly("\n" + loc.path + ":" + loc.lineno.__str__() + ": " + loc.message, logLevel=logger.ERROR) - + for reprentry in session.config.fails[failTest].reprtraceback.reprentries: + if reprentry.reprfuncargs is not None: + things = list(reprentry.reprfuncargs.args) + for args in things: + logger.logMessageOnly(f"{args[0]} = {args[1]}") + logger.logMessageOnly("") + for line in list(reprentry.lines): + if line.startswith("E") or line.startswith(">"): + logger.logMessageOnly(line.__str__(), logLevel=logger.ERROR) + else: + logger.logMessageOnly(line.__str__()) + loc = reprentry.reprfileloc + logger.logMessageOnly("\n" + loc.path + ":" + loc.lineno.__str__() + ": " + loc.message, logLevel=logger.ERROR) + logger.logMessageOnly(line_separator("", symbol="- "), logLevel=logger.info) logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) visited_modules = set() From 93dfbe6628f9cb7bf170d0d28786c8e825afa676 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:51:14 -0500 Subject: [PATCH 063/194] feat: added checking to ensure that the gcode is able to print on the fabricator it is assigned to --- server/Classes/Fabricators/Fabricator.py | 50 +++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index b0cb8b46..dda07089 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -7,7 +7,6 @@ from Classes.Fabricators.Device import Device from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence -#from Mixins.hasStartupSequence import hasStartupSequence from models.db import db from datetime import datetime, timezone @@ -130,13 +129,15 @@ def begin(self): assert len(self.queue) > 0 self.job = self.queue.getNext() + if self.job is None: + return # TODO: return error message + + #TODO: check filament type + self.checkValidJob() # if isinstance(self.device, hasStartupSequence): # self.device.startupSequence() - if self.job is None: - return # TODO: return error message - assert self.setStatus("printing"), "Failed to set status to printing" assert self.device.parseGcode(self.job.file_name_original), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. self.handleVerdict() @@ -251,4 +252,43 @@ def getHwid(self): return self.hwid def getDescription(self): - return self.description \ No newline at end of file + return self.description + + + def checkValidJob(self): + """checks if the job is valid for the fabricator""" + settingsDict = getFileConfig(self.job.file_name_original) + from Classes.Fabricators.Printers.Printer import Printer + from Classes.Fabricators.CNCMachines.CNCMachine import CNCMachine + from Classes.Fabricators.LaserCutters.LaserCutter import LaserCutter + if isinstance(self.device, Printer): + assert self.device.filamentType is not None, "Filament type not set" + assert self.device.filamentDiameter is not None, "Filament diameter not set" + assert self.device.nozzleDiameter is not None, "Nozzle diameter not set" + assert self.device.filamentType == settingsDict["filament_type"], f"Filament type mismatch: {self.device.filamentType} != {settingsDict['filament_type']}" + assert self.device.filamentDiameter == float(settingsDict["filament_diameter"]), f"Filament diameter mismatch: {self.device.filamentDiameter} != {float(settingsDict['filament_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0" + assert self.device.nozzleDiameter == float(settingsDict["nozzle_diameter"]), f"Nozzle diameter mismatch: {self.device.nozzleDiameter} != {float(settingsDict['nozzle_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0.0" + + elif isinstance(self.device, CNCMachine): + # if self.device.bitDiameter is not None and self.device.bitDiameter != float(settingsDict["bit_diameter"]): + # return False + pass + elif isinstance(self.device, LaserCutter): + # if self.device.laserPower is not None and self.device.laserPower != int(settingsDict["laser_power"]): + # return False + pass + + +def getFileConfig(file): + """Get the config lines from the job file.""" + with open(file, 'r') as f: + lines = f.readlines() + comment_lines = [line.lstrip('; ').strip() for line in lines if line.startswith(';')] + if "prusaslicer" in comment_lines[0].lower(): + return {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines[-358:] if '=' in line} + elif "cura" in comment_lines[11].lower(): + settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} + settingsDict["filament_type"] = settingsDict["material_type"] + settingsDict["filament_diameter"] = settingsDict["material_diameter"] + settingsDict["nozzle_diameter"] = settingsDict["machine_nozzle_size"] + return settingsDict From 63ba79e930d05d8fb1af6d9974daa71e840edfba Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:51:53 -0500 Subject: [PATCH 064/194] feat: added FabricatorStatusService.py to be removed once integrated into FabricatorList.py --- server/Classes/FabricatorStatusService.py | 231 ++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 server/Classes/FabricatorStatusService.py diff --git a/server/Classes/FabricatorStatusService.py b/server/Classes/FabricatorStatusService.py new file mode 100644 index 00000000..6f44fcd6 --- /dev/null +++ b/server/Classes/FabricatorStatusService.py @@ -0,0 +1,231 @@ +from threading import Thread +import time +from flask import jsonify + +from Classes.Fabricators.Fabricator import Fabricator + + +class FabricatorThread(Thread): + def __init__(self, fabricator, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fabricator = fabricator + + +class FabricatorStatusService: + # in order to access the app context, we need to pass the app to the FabricatorStatusService, mainly for the websockets + def __init__(self, app): + self.ping_thread = None + self.app = app + self.fabricator_threads = [] # array of fabricator threads + + # TODO: make this make sense? + def start_fabricator_thread(self, fabricator): + # also pass the app to the fabricator thread + thread = FabricatorThread(fabricator, target=self.update_thread, args=(fabricator, self.app)) + thread.daemon = True # lets you kill the thread when the main program exits, allows for the server to be shut down + thread.start() + return thread + + #TODO: make this make sense? + def create_fabricator_threads(self, fabricators_data): + # all fabricator statuses initialized to be 'online.' Instantly changes to 'ready' on initialization -- test with 'reset fabricator' command. + for fabricator_info in fabricators_data: + fabricator = Fabricator( + id=fabricator_info["id"], + device=fabricator_info["device"], + description=fabricator_info["description"], + hwid=fabricator_info["hwid"], + name=fabricator_info["name"], + status='configuring', + ) + fabricator_thread = self.start_fabricator_thread( + fabricator + ) # creating a thread for each fabricator object + self.fabricator_threads.append(fabricator_thread) + + # creating separate thread to loop through all of the fabricator threads to ping them for print status + self.ping_thread = Thread(target=self.pingForStatus) + + # TODO: make this make sense? + def queue_restore(self, fabricators_data, status, queue): + # all fabricator statuses initialized to be 'online.' Instantly changes to 'ready' on initialization -- test with 'reset fabricator' command. + for fabricator_info in fabricators_data: + fabricator = Fabricator( + id=fabricator_info["id"], + device=fabricator_info["device"], + description=fabricator_info["description"], + hwid=fabricator_info["hwid"], + name=fabricator_info["name"], + ) + for job in queue: + if (job.status != 'inqueue'): + job.setStatus('inqueue') + job.setDBstatus(job.id, 'inqueue') + fabricator.setQueue(queue) + fabricator.setStatus(status) + fabricator_thread = self.start_fabricator_thread( + fabricator + ) # creating a thread for each fabricator object + self.fabricator_threads.append(fabricator_thread) + + # TODO: make this make sense? + # passing app here to access the app context + def update_thread(self, fabricator, app): + with app.app_context(): + while True: + time.sleep(2) + status = fabricator.getStatus() # get fabricator status + + queueSize = fabricator.getQueue().getSize() # get size of queue + fabricator.responseCount = 0 + if (status == "ready" and queueSize > 0): + time.sleep(2) # wait for 2 seconds to allow the fabricator to process the queue + if status != "offline": + fabricator.printNextInQueue() + + def resetThread(self, fabricator_id): + try: + for thread in self.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.terminated = 1 + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + } + self.fabricator_threads.remove(thread) + self.create_fabricator_threads([thread_data]) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + def queueRestore(self, fabricator_id, status): + try: + for thread in self.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.terminated = 1 + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + } + self.fabricator_threads.remove(thread) + self.queue_restore([thread_data], status, fabricator.getQueue()) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + def deleteThread(self, fabricator_id): + try: + for thread in self.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name + } + self.fabricator_threads.remove(thread) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + def editName(self, fabricator_id, name): + try: + for thread in self.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.name = name + break + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + # this method will be called by the UI to get the fabricators that have a threads information + def retrieve_fabricator_info(self): + fabricator_info_list = [] + for thread in self.fabricator_threads: + fabricator = ( + thread.fabricator + ) # get the fabricator object associated with the thread + fabricator_info = { + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + "status": fabricator.status, + "id": fabricator.id, + "error": fabricator.error, + "canPause": fabricator.canPause, + "queue": [], # empty queue to store job objects + "colorChangeBuffer": fabricator.colorbuff + # "colorChangeBuffer": fabricator.colorChangeBuffer + } + queue = fabricator.getQueue() + for job in queue: + job_info = { + "id": job.id, + "name": job.name, + "status": job.status, + "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), + "fabricatorid": job.fabricator_id, + "errorid": job.error_id, + "file_name_original": job.file_name_original, + "progress": job.progress, + "sent_lines": job.sent_lines, + "favorite": job.favorite, + "released": job.released, + "file_pause": job.filePause, + "comments": job.comments, + "extruded": job.extruded, + "td_id": job.td_id, + "time_started": job.time_started, + "fabricator_name": job.fabricator_name, + "max_layer_height": job.max_layer_height, + "current_layer_height": job.current_layer_height, + "filament": job.filament, + } + fabricator_info['queue'].append(job_info) + + fabricator_info_list.append(fabricator_info) + + return fabricator_info_list + + def getThreadArray(self): + return self.fabricator_threads + + def pingForStatus(self): + """_summary_ pseudo code + for fabricator in threads: + status = fabricator.getStatus() + if status == printing: + GCODE for print status + """ + pass + + def moveFabricatorList(self, fabricator_ids): + # fabricator_ids is a list of fabricator ids in the order they should be displayed + new_thread_list = [] + for id in fabricator_ids: + for thread in self.fabricator_threads: + if thread.fabricator.id == id: + new_thread_list.append(thread) + break + self.fabricator_threads = new_thread_list + return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) + From bbc0a653d09b01ab87a199fe5a842b8f1947047b Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:52:24 -0500 Subject: [PATCH 065/194] fix: removed LocationResponse.py because it was only used once. --- server/Classes/LocationResponse.py | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 server/Classes/LocationResponse.py diff --git a/server/Classes/LocationResponse.py b/server/Classes/LocationResponse.py deleted file mode 100644 index d89f4bc6..00000000 --- a/server/Classes/LocationResponse.py +++ /dev/null @@ -1,11 +0,0 @@ -import re -class LocationResponse: - def __init__(self, response: str): - loc = [item for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item and item.strip()] - self.x = float(loc[0]) - self.y = float(loc[1]) - self.z = float(loc[2]) - self.e = float(loc[3]) - self.count_x = int(loc[4]) - self.count_y = int(loc[5]) - self.count_z = int(loc[6]) \ No newline at end of file From d3e332c9abd79a19d250bc88985621e7f5bb8df2 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:53:27 -0500 Subject: [PATCH 066/194] fix: minor cleanup in the logger creation. --- server/Classes/Logger.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index b565e3a7..04677de3 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -13,7 +13,7 @@ class Logger(logging.Logger): CRITICAL = logging.CRITICAL def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): - super().__init__(f"Logger_{port}_{deviceName}") + super().__init__(f"_".join(["Logger", port, deviceName])) self.setLevel(loggingLevel) info = [] if showDate: @@ -24,15 +24,14 @@ def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggin info.append("%(module)s.%(funcName)s:%(lineno)d") formatString = " - ".join(info + ["%(message)s"]) if consoleLogger is not None: - console_handler = logging.StreamHandler(consoleLogger) - console_handler.setFormatter(CustomFormatter(formatString)) - console_handler.setLevel(loggingLevel) - self.addHandler(console_handler) + consoleLogger = logging.StreamHandler(consoleLogger) + consoleLogger.setLevel(loggingLevel) else: - console_handler = logging.StreamHandler(sys.stdout) - console_handler.setFormatter(CustomFormatter(formatString)) - console_handler.setLevel(self.ERROR) - self.addHandler(console_handler) + consoleLogger = logging.StreamHandler(sys.stdout) + consoleLogger.setLevel(self.ERROR) + consoleLogger.setFormatter(CustomFormatter(formatString)) + self.consoleLogger = consoleLogger + self.addHandler(consoleLogger) if fileLogger is None: log_folder = "./server/logs" os.makedirs(log_folder, exist_ok=True) @@ -41,9 +40,13 @@ def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggin from datetime import datetime fileLogger = logging.FileHandler(os.path.join(subfolder, f"{datetime.now().strftime('%m-%d-%Y__%H-%M-%S')}.log")) else: - fileLogger = logging.FileHandler(fileLogger) + if not os.path.exists(fileLogger): + fileLogger = logging.FileHandler(fileLogger, mode='w') + else: + fileLogger = logging.FileHandler(fileLogger) fileLogger.setFormatter(logging.Formatter(formatString)) fileLogger.setLevel(loggingLevel) + self.fileLogger = fileLogger self.addHandler(fileLogger) def formatLog(self, msg): From 24e1122bb10209ae24a70484cc62586215513273 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:54:44 -0500 Subject: [PATCH 067/194] feat: added the changeFilament and changeNozzle methods --- .../Classes/Fabricators/Printers/Printer.py | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 7ac1faa1..0fadbea7 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -1,4 +1,5 @@ from abc import ABCMeta +from app import app from Classes.Fabricators.Device import Device class Printer(Device, metaclass=ABCMeta): @@ -8,5 +9,27 @@ class Printer(Device, metaclass=ABCMeta): bedTemperature: int | None = None nozzleTemperature: int | None = None - def changeFilament(self): - pass \ No newline at end of file + def changeFilament(self, filamentType: str, filamentDiameter: float): + if not isinstance(filamentDiameter, float): + filamentDiameter = float(filamentDiameter) + try: + if self.status is "idle": + self.filamentType = filamentType + self.filamentDiameter = filamentDiameter + else: + raise Exception("Printer is not idle") + except Exception as e: + if self.logger is None: + with app.app_context(): + app.logger.error("Error changing filament:") + app.logger.error(e) + else: + self.logger.error("Error changing filament:") + self.logger.error(e) + self.filamentType = filamentType + self.filamentDiameter = filamentDiameter + + def changeNozzle(self, nozzleDiameter: float): + if not isinstance(nozzleDiameter, float): + nozzleDiameter = float(nozzleDiameter) + self.nozzleDiameter = nozzleDiameter \ No newline at end of file From 1dcdb621d7f2180f249e46b2f6d848c059938d7b Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:56:10 -0500 Subject: [PATCH 068/194] feat: added tests for the app config --- Tests/test_app.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Tests/test_app.py diff --git a/Tests/test_app.py b/Tests/test_app.py new file mode 100644 index 00000000..d6811302 --- /dev/null +++ b/Tests/test_app.py @@ -0,0 +1,33 @@ +import os +import pytest + +from Classes.Logger import Logger +from parallel_test_runner import testLevel +from app import app +def __desc__(): return "App Tests" +with app.app_context(): + @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") + def test_db(): + db_file_no_path = app.config["SQLALCHEMY_DATABASE_URI"].split("/")[-1].split("\\")[-1] + assert db_file_no_path, "database_uri doesn't exist?" + assert db_file_no_path == "hvamc.db", f"database_uri is {db_file_no_path}" + assert os.path.exists(app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]), f"Database file {app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]} does not exist" + + @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") + def test_base_url(): + assert app.config["base_url"], "base_url doesnt exist?" + assert app.config["base_url"] == "http://127.0.0.1:8000", f"base_url is {app.config['base_url']}" + + @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") + def test_environment(): + assert app.config["environment"], "environment doesnt exist?" + assert app.config["environment"] == "development", f"environment is {app.config['environment']}" + + @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") + def test_logger(): + assert app.logger, "myLogger doesnt exist?" + assert app.logger.name, "name doesnt exist?" + assert str(app.logger.name) == "Logger__App", f"myLogger is {str(app.logger.name)}" + assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" + assert app.logger.fileLogger, "fileLogger doesnt exist?" + assert app.logger.consoleLogger, "consoleLogger doesn't exists?" From badd6c81701b411a975942ddc8617679af55e7de Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:58:12 -0500 Subject: [PATCH 069/194] fix: added new catches to make sure that the device has the right filament and nozzle type for the test. --- Tests/test_fabricator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 6d0345c0..4e68bb96 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -119,6 +119,10 @@ def test_gcode_print_time(): expectedTime = 10 * 60 if shortTest else 1800 # expectedTime = 2040 # for my personal home test, 1072 expectedMinutes, expectedSeconds = divmod(expectedTime, 60) + from Classes.Fabricators.Fabricator import getFileConfig + config = getFileConfig(file) + fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) + fabricator.device.changeNozzle(float(config["nozzle_diameter"])) time = datetime.now() with open(file, "r") as f: fabricator.queue.addToFront \ From 5cb7a001a0f50149447cbd4d38e2d8d4f55a257f Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Mon, 11 Nov 2024 17:59:04 -0500 Subject: [PATCH 070/194] fix: moved locationResponse to here, because it was the only place it was used. --- server/Mixins/gcode/usesMarlinGcode.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/Mixins/gcode/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py index b36ab353..ef438007 100644 --- a/server/Mixins/gcode/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -3,13 +3,24 @@ from typing_extensions import Buffer from Classes.Fabricators.Device import Device -from Classes.LocationResponse import LocationResponse from Classes.Vector3 import Vector3 from Mixins.canPause import canPause from Mixins.gcode.usesVanillaGcode import usesVanillaGcode from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue, checkBedTemp, checkExtruderTemp, hasResponsecodes +import re +class LocationResponse: + def __init__(self, response: str): + loc = [item for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item and item.strip()] + self.x = float(loc[0]) + self.y = float(loc[1]) + self.z = float(loc[2]) + self.e = float(loc[3]) + self.count_x = int(loc[4]) + self.count_y = int(loc[5]) + self.count_z = int(loc[6]) + class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=ABCMeta): cancelCMD: Buffer = b"M112\n" From f536a92953d1225be1a5373132074dbcebb048fb Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 12:23:35 -0500 Subject: [PATCH 071/194] feat: better error handling and start to remove error handling in gcode commands --- printeremu/cmd/test_printer.go | 14 ++- printeremu/src/emulator.go | 162 ++++++++++++++++--------------- printeremu/src/extruder.go | 19 ++-- printeremu/src/gcode_commands.go | 94 +++++++++++++----- printeremu/src/printer.go | 148 +++++++++++++++++++++++----- 5 files changed, 301 insertions(+), 136 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index cd4c5380..947f3b90 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -19,8 +19,14 @@ func main() { return } + handleCommand(extruder, printer) + //handleConnection(extruder, printer) + +} + +func handleConnection(extruder *src.Extruder, printer *src.Printer) { ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + defer cancel() signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) @@ -36,4 +42,8 @@ func main() { <-ctx.Done() log.Println("Shutting down...") -} \ No newline at end of file +} + +func handleCommand(extruder *src.Extruder, printer *src.Printer) { + src.RunCommand(extruder, printer) +} diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index f592ab85..b7c9578a 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -40,7 +40,11 @@ func Init(id int, device string, description string, hwid string, name string, s TargetTemp: 100.0, // Target temperature for the heatbed } - printer := NewPrinter(id, device, description, hwid, name, status, time.Now().String(), extruder, heatbed) + printer, err := NewPrinter(id, device, description, hwid, name, status, time.Now().String(), extruder, heatbed) + + if err != nil { + log.Fatal("Failed to create printer:", err) + } fmt.Println(printer.String()) @@ -48,91 +52,91 @@ func Init(id int, device string, description string, hwid string, name string, s } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { - serverURL := "ws://127.0.0.1:8000" - - conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) - - if err != nil { - log.Fatal("Failed to connect to server:", err) - } - - defer conn.Close() - - fmt.Println("Connected to WebSocket server") - - RegisterPrinter(conn, printer) - - pingTicker := time.NewTicker(5 * time.Second) - defer pingTicker.Stop() - - for { - select { - case <-ctx.Done(): - fmt.Println("Context canceled, closing connection.") - return - case <-pingTicker.C: - pingMessage := map[string]interface{}{ - "event": "ping", - "data": "alive", - } - - jsonPingMessage, err := json.Marshal(pingMessage) + serverURL := "ws://127.0.0.1:8000" - if err != nil { - log.Println("Failed to marshal ping message:", err) - return - } + conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) - if err := conn.WriteMessage(websocket.TextMessage, jsonPingMessage); err != nil { - log.Println("Error sending ping message:", err) - return - } - - default: - messageType, message, err := conn.ReadMessage() - - if err != nil { - log.Println("Error reading message:", err) - return - } - - if messageType == websocket.TextMessage && string(message) == "close" { - fmt.Println("Received 'close' signal from server, ending connection.") - return - } - - var parsedMessage map[string]interface{} - - if err := json.Unmarshal(message, &parsedMessage); err != nil { - log.Println("Error parsing received message:", err) - continue - } - - event, ok := parsedMessage["event"].(string) + if err != nil { + log.Fatal("Failed to connect to server:", err) + } - if !ok { - log.Println("Missing or invalid 'event' field:", parsedMessage) - continue - } + defer conn.Close() - data := parsedMessage["data"] + fmt.Println("Connected to WebSocket server") - switch event { - case "info": - if message, exists := parsedMessage["message"].(string); exists { - log.Println("Info:", message) - } else { - fmt.Println("Missing or invalid 'message' field in 'info' event data") - } + RegisterPrinter(conn, printer) - case "error": - log.Println("Error:", data) + pingTicker := time.NewTicker(5 * time.Second) + defer pingTicker.Stop() - default: - log.Println("Received from server:", string(message)) - } - } - } + for { + select { + case <-ctx.Done(): + fmt.Println("Context canceled, closing connection.") + return + case <-pingTicker.C: + pingMessage := map[string]interface{}{ + "event": "ping", + "data": "alive", + } + + jsonPingMessage, err := json.Marshal(pingMessage) + + if err != nil { + log.Println("Failed to marshal ping message:", err) + return + } + + if err := conn.WriteMessage(websocket.TextMessage, jsonPingMessage); err != nil { + log.Println("Error sending ping message:", err) + return + } + + default: + messageType, message, err := conn.ReadMessage() + + if err != nil { + log.Println("Error reading message:", err) + return + } + + if messageType == websocket.TextMessage && string(message) == "close" { + fmt.Println("Received 'close' signal from server, ending connection.") + return + } + + var parsedMessage map[string]interface{} + + if err := json.Unmarshal(message, &parsedMessage); err != nil { + log.Println("Error parsing received message:", err) + continue + } + + event, ok := parsedMessage["event"].(string) + + if !ok { + log.Println("Missing or invalid 'event' field:", parsedMessage) + continue + } + + data := parsedMessage["data"] + + switch event { + case "info": + if message, exists := parsedMessage["message"].(string); exists { + log.Println("Info:", message) + } else { + fmt.Println("Missing or invalid 'message' field in 'info' event data") + } + + case "error": + log.Println("Error:", data) + + default: + log.Println("Received from server:", string(message)) + } + } + } } func RegisterPrinter(conn *websocket.Conn, printer *Printer) { diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go index 9796758b..8a7ceade 100644 --- a/printeremu/src/extruder.go +++ b/printeremu/src/extruder.go @@ -13,11 +13,12 @@ type Vector3 struct { // Extruder struct to handle extruder settings, including temperature and fan speed. type Extruder struct { - Position Vector3 - FanSpeed float64 - ExtruderTemp float64 - TargetTemp float64 // Desired temperature for the extruder. + Position Vector3 + FanSpeed float64 + ExtruderTemp float64 + TargetTemp float64 // Desired temperature for the extruder. AbsolutePositioning bool + MaxZHeight float64 // Maximum height of the extruder } // Heatbed struct to handle heatbed temperature. @@ -25,15 +26,17 @@ type Heatbed struct { Temp float64 TargetTemp float64 // Desired temperature for the heatbed. Heating bool // Indicates if the bed is currently heating. + Width float64 + Length float64 } // NewExtruder creates a new Extruder with specified initial values. func NewExtruder(position Vector3, extruderTemp, targetTemp, fanSpeed float64) *Extruder { return &Extruder{ - Position: position, - ExtruderTemp: extruderTemp, - TargetTemp: targetTemp, - FanSpeed: fanSpeed, + Position: position, + ExtruderTemp: extruderTemp, + TargetTemp: targetTemp, + FanSpeed: fanSpeed, AbsolutePositioning: true, // Default positioning mode } } diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index ac6098c8..045ccc3f 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -47,7 +47,10 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { - printer.Extruder.Position = Vector3{X: 0, Y: 0, Z: 0} + if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + return "Homing completed\n" } @@ -58,14 +61,16 @@ type G0G1Command struct { func NewG0G1Command(command string, printer *Printer) *G0G1Command { target, feedRate := parseMoveCommand(command, printer.Extruder.Position) + return &G0G1Command{target: target, feedRate: feedRate} } func (cmd *G0G1Command) Execute(printer *Printer) string { - if printer.Paused { - return "Printer is paused\n" + // TODO: Implement feed rate + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) } - printer.Extruder.Position = cmd.target + return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } @@ -77,6 +82,7 @@ type G2G3Command struct { func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Command { target, radius := parseArcCommand(command, printer.Extruder.Position) + return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } @@ -84,6 +90,7 @@ func (cmd *G2G3Command) Execute(printer *Printer) string { if printer.Paused { return "Printer is paused\n" } + currentPos := printer.Extruder.Position arcAngle := 180.0 angleRad := arcAngle * math.Pi / 180.0 @@ -95,7 +102,10 @@ func (cmd *G2G3Command) Execute(printer *Printer) string { cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) } - printer.Extruder.Position = cmd.target + + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } @@ -117,6 +127,7 @@ type G90Command struct{} func (cmd *G90Command) Execute(printer *Printer) string { printer.Extruder.AbsolutePositioning = true + return "Set to Absolute Positioning\n" } @@ -124,6 +135,7 @@ type G91Command struct{} func (cmd *G91Command) Execute(printer *Printer) string { printer.Extruder.AbsolutePositioning = false + return "Set to Relative Positioning\n" } @@ -133,11 +145,15 @@ type G92Command struct { func NewG92Command(command string) *G92Command { position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) + return &G92Command{position: position} } func (cmd *G92Command) Execute(printer *Printer) string { - printer.Extruder.Position = cmd.position + if err := printer.MoveExtruder(cmd.position); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) } @@ -165,12 +181,16 @@ type M104Command struct { func NewM104Command(command string) *M104Command { temperature := parseTemperature(command) + return &M104Command{temperature: temperature} } func (cmd *M104Command) Execute(printer *Printer) string { - printer.Extruder.TargetTemp = cmd.temperature - return fmt.Sprintf("Extruder temperature set to %.2f\n", cmd.temperature) + if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + + return fmt.Sprintf("Extruder temperature set to %.2f°C\n", cmd.temperature) } type M106Command struct { @@ -179,18 +199,23 @@ type M106Command struct { func NewM106Command(command string) *M106Command { fanSpeed := parseFanSpeed(command) + return &M106Command{fanSpeed: fanSpeed} } func (cmd *M106Command) Execute(printer *Printer) string { - printer.Extruder.FanSpeed = cmd.fanSpeed + if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) } type M107Command struct{} func (cmd *M107Command) Execute(printer *Printer) string { - printer.Extruder.FanSpeed = 0.0 + printer.SetFanSpeed(0) + return "Fan turned off\n" } @@ -200,13 +225,18 @@ type M140Command struct { func NewM140Command(command string) *M140Command { temperature := parseTemperature(command) + return &M140Command{temperature: temperature} } func (cmd *M140Command) Execute(printer *Printer) string { // Set the target temperature of the bed, but do not block - printer.Heatbed.TargetTemp = cmd.temperature + if err := printer.SetBedTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + printer.Heatbed.Heating = true // Start heating + return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) } @@ -217,11 +247,13 @@ type M190Command struct { func NewM190Command(command string) *M190Command { temperature := parseTemperature(command) + return &M190Command{temperature: temperature} } func (cmd *M190Command) Execute(printer *Printer) string { // Set the target temperature and check if the bed is heated + // TODO: use error handling! printer.Heatbed.TargetTemp = cmd.temperature // Check if the current temperature is below the target @@ -265,11 +297,13 @@ type M73Command struct { func NewM73Command(command string) *M73Command { progress := parseProgress(command) + return &M73Command{progress: progress} } func (cmd *M73Command) Execute(printer *Printer) string { - printer.Progress = cmd.progress + printer.UpdateProgress(cmd.progress) + return fmt.Sprintf("Progress set to %d%%\n", cmd.progress) } @@ -278,7 +312,8 @@ func (cmd *M73Command) Execute(printer *Printer) string { type CancelCommand struct{} func (cmd *CancelCommand) Execute(printer *Printer) string { - printer.Paused = true + printer.Pause() + return "Emergency stop activated, printer paused\n" } @@ -298,21 +333,17 @@ func (cmd *M997Command) Execute(printer *Printer) string { type M601Command struct{} func (cmd *M601Command) Execute(printer *Printer) string { - if printer.Paused { - return "Printer is already paused\n" - } - printer.Paused = true - return "Machine is paused\n" + printer.Pause() + + return "Printer paused\n" } type M602Command struct{} func (cmd *M602Command) Execute(printer *Printer) string { - if !printer.Paused { - return "Printer is not paused\n" - } - printer.Paused = false - return "Machine is no longer paused\n" + printer.Resume() + + return "Printer not paused\n" } type M900Command struct { @@ -321,11 +352,13 @@ type M900Command struct { func NewM900Command(command string) *M900Command { kFactor := parseKFactor(command) // Extracts the K value from the command + return &M900Command{kFactor: kFactor} } func (cmd *M900Command) Execute(printer *Printer) string { printer.LinearAdvanceFactor = cmd.kFactor + return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) } @@ -335,11 +368,13 @@ type M142Command struct { func NewM142Command(command string) *M142Command { temperature := parseTemperature(command) // Uses parseTemperature to get S value + return &M142Command{temperature: temperature} } func (cmd *M142Command) Execute(printer *Printer) string { printer.HeatbreakTemp = cmd.temperature + return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) } @@ -349,6 +384,7 @@ type M84Command struct { func NewM84Command(command string) *M84Command { axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command + return &M84Command{axes: axes} } @@ -356,6 +392,7 @@ func (cmd *M84Command) Execute(printer *Printer) string { for _, axis := range cmd.axes { printer.DisableMotor(axis) } + return fmt.Sprintf("Motors %v disabled\n", cmd.axes) } @@ -373,12 +410,15 @@ func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { if xMatch := reX.FindStringSubmatch(command); xMatch != nil { target.X, _ = strconv.ParseFloat(xMatch[1], 64) } + if yMatch := reY.FindStringSubmatch(command); yMatch != nil { target.Y, _ = strconv.ParseFloat(yMatch[1], 64) } + if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { target.Z, _ = strconv.ParseFloat(zMatch[1], 64) } + if fMatch := reF.FindStringSubmatch(command); fMatch != nil { feedRate, _ = strconv.ParseFloat(fMatch[1], 64) } @@ -394,6 +434,7 @@ func parseArcCommand(command string, currentPos Vector3) (Vector3, float64) { if rMatch := reR.FindStringSubmatch(command); rMatch != nil { radius, _ = strconv.ParseFloat(rMatch[1], 64) } + return target, radius } @@ -414,6 +455,7 @@ func parseTemperature(command string) float64 { if sMatch := reS.FindStringSubmatch(command); sMatch != nil { temperature, _ = strconv.ParseFloat(sMatch[1], 64) } + return temperature } @@ -424,6 +466,7 @@ func parseFanSpeed(command string) float64 { if sMatch := reS.FindStringSubmatch(command); sMatch != nil { fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) } + return fanSpeed } @@ -434,6 +477,7 @@ func parseAcceleration(command string) float64 { if sMatch := reS.FindStringSubmatch(command); sMatch != nil { acceleration, _ = strconv.ParseFloat(sMatch[1], 64) } + return acceleration } @@ -444,20 +488,24 @@ func parseProgress(command string) int { if sMatch := reS.FindStringSubmatch(command); sMatch != nil { progress, _ = strconv.Atoi(sMatch[1]) } + return progress } func parseKFactor(command string) float64 { reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) kFactor := 0.0 + if kMatch := reK.FindStringSubmatch(command); kMatch != nil { kFactor, _ = strconv.ParseFloat(kMatch[1], 64) } + return kFactor } func parseAxes(command string) []string { reAxes := regexp.MustCompile(`[XYZE]`) + return reAxes.FindAllString(command, -1) } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 52952f69..f526566b 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -26,36 +26,90 @@ type Printer struct { EnabledMotors map[string]bool } -func (printer *Printer) DisableMotor(axis string) { - if printer.EnabledMotors == nil { - printer.EnabledMotors = make(map[string]bool) +// DisableMotor disables a motor for a specific axis +func (printer *Printer) DisableMotor(axis string) error { + if axis != "x" && axis != "y" && axis != "z" { + return fmt.Errorf("invalid axis: %s. Valid axes are 'x', 'y', or 'z'", axis) } + printer.EnabledMotors[axis] = false + + return nil +} + +// EnableMotor enables a motor for a specific axis +func (printer *Printer) EnableMotor(axis string) error { + if axis != "x" && axis != "y" && axis != "z" { + return fmt.Errorf("invalid axis: %s. Valid axes are 'x', 'y', or 'z'", axis) + } + + printer.EnabledMotors[axis] = true + + return nil +} + +// AllMotorsEnabled checks if all motors are enabled +func (printer *Printer) AllMotorsEnabled() bool { + for _, enabled := range printer.EnabledMotors { + if !enabled { + return false + } + } + return true } // NewPrinter initializes a Printer with given specifications -func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) *Printer { - return &Printer{ - Id: id, - Device: device, - Description: description, - Hwid: hwid, - Name: name, - Status: status, - Date: date, - Extruder: extruder, - Heatbed: heatbed, - Paused: false, - Units: "mm", // Default units - KeepAliveTime: time.Now(), // Initialize with current time - Acceleration: 0.0, // Default acceleration - Progress: 0, // Default progress percentage +func NewPrinter(id int, device, description, hwid, name, status, date string, extruder *Extruder, heatbed *Heatbed) (*Printer, error) { + if extruder == nil || heatbed == nil { + return nil, fmt.Errorf("extruder and heatbed must not be nil") } + if name == "" || device == "" { + return nil, fmt.Errorf("name and device must be provided") + } + + printer := &Printer{ + Id: id, + Device: device, + Description: description, + Hwid: hwid, + Name: name, + Status: status, + Date: date, + Extruder: extruder, + Heatbed: heatbed, + Paused: false, + Units: "mm", // Default units + KeepAliveTime: time.Now(), // Initialize with current time + Acceleration: 0.0, // Default acceleration + Progress: 0, // Default progress percentage + LinearAdvanceFactor: 0.0, // Default linear advance factor + HeatbreakTemp: 0.0, // Default heatbreak temp + EnabledMotors: make(map[string]bool), + } + + return printer, nil +} + +// SetUnits allows the printer to switch between mm or inches +func (printer *Printer) SetUnits(units string) error { + if units != "mm" && units != "inches" { + return fmt.Errorf("invalid units: %s. Supported values are 'mm' or 'inches'", units) + } + + printer.Units = units + + return nil } // SetExtruderTemp sets the extruder temperature -func (printer *Printer) SetExtruderTemp(temp float64) { +func (printer *Printer) SetExtruderTemperature(temp float64) error { + if temp < 0 || temp > 300 { + return fmt.Errorf("invalid extruder temperature: %.2f°C. Valid range: 0°C to 300°C", temp) + } + printer.Extruder.SetExtruderTemp(temp) + + return nil } // GetExtruderTemp gets the extruder temperature @@ -64,8 +118,14 @@ func (printer *Printer) GetExtruderTemp() float64 { } // SetBedTemp sets the target bed temperature and begins heating -func (printer *Printer) SetBedTemp(temp float64) { +func (printer *Printer) SetBedTemperature(temp float64) error { + if temp < 0 || temp > 120 { + return fmt.Errorf("invalid bed temperature: %.2f°C. Valid range: 0°C to 120°C", temp) + } + printer.Heatbed.SetBedTemp(temp) + + return nil } // GetBedTemp gets the current bed temperature @@ -82,8 +142,14 @@ func (printer *Printer) UpdateBedTemperature(currentTemp float64) { } // SetFanSpeed sets the fan speed on the extruder -func (printer *Printer) SetFanSpeed(speed float64) { +func (printer *Printer) SetFanSpeed(speed float64) error { + if speed < 0 || speed > 100 { + return fmt.Errorf("invalid fan speed: %.2f. Valid range: 0 to 100", speed) + } + printer.Extruder.SetFanSpeed(speed) + + return nil } // GetFanSpeed gets the current fan speed on the extruder @@ -101,8 +167,42 @@ func (printer *Printer) Resume() { printer.Paused = false } +// UpdateProgress updates the progress of the print job +func (printer *Printer) UpdateProgress(progress int) error { + if progress >= 0 && progress <= 100 { + printer.Progress = progress + } else { + return fmt.Errorf("invalid progress: %d. Valid range: 0 to 100", progress) + } + + return nil +} + +// MoveExtruder moves the extruder to the specified position. +// It validates the position and checks if the printer is in a valid state (not paused). +func (printer *Printer) MoveExtruder(targetPos Vector3) error { + // Ensure the printer is not paused + if printer.Paused { + return fmt.Errorf("cannot move extruder: printer is paused") + } + + // Optionally, check if the position is within the allowed print area + if targetPos.X < 0 || targetPos.Y < 0 || targetPos.Z < 0 { + return fmt.Errorf("invalid position: coordinates cannot be negative") + } + + // Check for the printable area bounds (example) + if targetPos.X > printer.Heatbed.Width || targetPos.Y > printer.Heatbed.Length || targetPos.Z > printer.Extruder.MaxZHeight { + return fmt.Errorf("position out of bounds: target exceeds printable area") + } + + // If all checks pass, update the extruder position + printer.Extruder.Position = targetPos + return nil +} + // String provides a formatted string representation of the Printer state func (printer *Printer) String() string { - return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, Progress: %d%%, KeepAliveTime: %v}", - printer.Id, printer.Device, printer.Description, printer.Hwid, printer.Name, printer.Status, printer.Date, printer.Extruder.String(), printer.Heatbed.String(), printer.Acceleration, printer.Progress, printer.KeepAliveTime) + return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, LinearAdvanceFactor: %.2f, HeatbreakTemp: %.2f, Units: %s, Progress: %d%%, KeepAliveTime: %v, EnabledMotors: %v}", + printer.Id, printer.Device, printer.Description, printer.Hwid, printer.Name, printer.Status, printer.Date, printer.Extruder.String(), printer.Heatbed.String(), printer.Acceleration, printer.LinearAdvanceFactor, printer.HeatbreakTemp, printer.Units, printer.Progress, printer.KeepAliveTime, printer.EnabledMotors) } From e4bd944010d9d4d3f2348de08a55526ae780f4d8 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 12:27:37 -0500 Subject: [PATCH 072/194] feat: allow user to choose between connection or command in cli --- printeremu/cmd/test_printer.go | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 947f3b90..4944456c 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -1,11 +1,13 @@ package main import ( + "bufio" "context" "fmt" "log" "os" "os/signal" + "strings" "syscall" "printeremu/src" @@ -19,9 +21,40 @@ func main() { return } - handleCommand(extruder, printer) - //handleConnection(extruder, printer) + scanner := bufio.NewScanner(os.Stdin) + fmt.Println("Choose command or connection for emulator (type 'exit' or 'quit' to quit):") + + for { + fmt.Print("$ ") + scanner.Scan() + command := scanner.Text() + + if strings.ToLower(command) == "exit" || strings.ToLower(command) == "quit" { + fmt.Println("Exiting printer emulator...") + break + } + + if strings.ToLower(command) == "command" { + fmt.Println("Running command emulator...") + + handleCommand(extruder, printer) + + break + } + + if strings.ToLower(command) == "connection" { + fmt.Println("Running command emulator...") + + handleConnection(extruder, printer) + + break + } + } + + if err := scanner.Err(); err != nil { + log.Println("Error reading input:", err) + } } func handleConnection(extruder *src.Extruder, printer *src.Printer) { From 981b1c228a670ea233044cdbd97f0c8be7784a7b Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 12:29:15 -0500 Subject: [PATCH 073/194] feat: more command alts --- printeremu/cmd/test_printer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 4944456c..7884d17f 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -35,7 +35,7 @@ func main() { break } - if strings.ToLower(command) == "command" { + if strings.ToLower(command) == "command" || strings.ToLower(command) == "comm" { fmt.Println("Running command emulator...") handleCommand(extruder, printer) @@ -43,7 +43,7 @@ func main() { break } - if strings.ToLower(command) == "connection" { + if strings.ToLower(command) == "connection" || strings.ToLower(command) == "conn" { fmt.Println("Running command emulator...") handleConnection(extruder, printer) From 5bb7edaa725280f3cb82ec9cec2eb0049193de9c Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 12:54:58 -0500 Subject: [PATCH 074/194] feat: basic printer registry --- printeremu/cmd/test_printer.go | 16 ++++++++- printeremu/data/options.json | 6 ++++ printeremu/data/printerdata.json | 3 ++ printeremu/data/printers.json | 19 +++++++++++ printeremu/src/printerregistry.go | 54 +++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 printeremu/data/options.json create mode 100644 printeremu/data/printerdata.json create mode 100644 printeremu/data/printers.json create mode 100644 printeremu/src/printerregistry.go diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 7884d17f..3f64141f 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -14,6 +14,14 @@ import ( ) func main() { + printers, err := src.LoadPrinters("data/printers.json") + + if err != nil { + log.Fatalf("Error loading printers: %v", err) + } + + src.PrintPrinters(printers) + extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "EMU032uhb3293n2", "Testing Printer 1", "Init") if err != nil { @@ -44,12 +52,18 @@ func main() { } if strings.ToLower(command) == "connection" || strings.ToLower(command) == "conn" { - fmt.Println("Running command emulator...") + fmt.Println("Running connection emulator...") handleConnection(extruder, printer) break } + + if strings.ToLower(command) == "registry" { + fmt.Println("Printer registry...") + + src.PrintPrinters(printers) + } } if err := scanner.Err(); err != nil { diff --git a/printeremu/data/options.json b/printeremu/data/options.json new file mode 100644 index 00000000..64061958 --- /dev/null +++ b/printeremu/data/options.json @@ -0,0 +1,6 @@ +{ + "enabledPrinters": [], + "defaultAddress": "127.0.0.1", + "defaultPort": 8000, + "startup": "default" +} \ No newline at end of file diff --git a/printeremu/data/printerdata.json b/printeremu/data/printerdata.json new file mode 100644 index 00000000..8033e798 --- /dev/null +++ b/printeremu/data/printerdata.json @@ -0,0 +1,3 @@ +{ + "usesMarlin": [ "Ender 3", "Prusa MK4" ] +} \ No newline at end of file diff --git a/printeremu/data/printers.json b/printeremu/data/printers.json new file mode 100644 index 00000000..28194214 --- /dev/null +++ b/printeremu/data/printers.json @@ -0,0 +1,19 @@ +[ + { + "id": 1, + "name": "Prusa MK4", + "brand": { + "printername": "Prusa", + "printermodel": "MK4" + } + }, + { + "id": 2, + "name": "Ender 3", + "brand": { + "printername": "Creality", + "printermodel": "Ender 3" + } + } + ] + \ No newline at end of file diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go new file mode 100644 index 00000000..557128cd --- /dev/null +++ b/printeremu/src/printerregistry.go @@ -0,0 +1,54 @@ +package src + +import ( + "encoding/json" + "fmt" + "io" + "os" +) + +type PrinterConfig struct { + Id int `json:"id"` + Name string `json:"name"` + Brand PrinterBrand `json:"brand"` +} + +type PrinterBrand struct { + PrinterName string `json:"printername"` + PrinterModel string `json:"printermodel"` +} + +// LoadPrinters loads a list of printers from the specified JSON file +func LoadPrinters(filePath string) ([]PrinterConfig, error) { + file, err := os.Open(filePath) + + if err != nil { + return nil, fmt.Errorf("failed to open file %s: %v", filePath, err) + } + + defer file.Close() + + fileBytes, err := io.ReadAll(file) + + if err != nil { + return nil, fmt.Errorf("failed to read file: %v", err) + } + + var printers []PrinterConfig + err = json.Unmarshal(fileBytes, &printers) + + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON data: %v", err) + } + + return printers, nil +} + +func PrintPrinters(printers []PrinterConfig) { + fmt.Println("Loaded printers:") + + for _, printer := range printers { + fmt.Printf("Printer ID: %d, Name: %s, Brand: %s, Model: %s\n", + printer.Id, printer.Name, printer.Brand.PrinterName, printer.Brand.PrinterModel) + } +} From 15e0cfe0f6676894d7324dc45b9536288eef1e17 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 13:21:57 -0500 Subject: [PATCH 075/194] feat: finish load from config, allow user to specify which printer to use --- printeremu/cmd/test_printer.go | 45 +++++++++++++++++++--- printeremu/data/printerdata.json | 2 +- printeremu/data/printers.json | 14 ++++--- printeremu/src/emulator.go | 4 +- printeremu/src/printerregistry.go | 63 +++++++++++++++++++++++++------ 5 files changed, 103 insertions(+), 25 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 3f64141f..359ae227 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -7,6 +7,7 @@ import ( "log" "os" "os/signal" + "strconv" "strings" "syscall" @@ -20,15 +21,16 @@ func main() { log.Fatalf("Error loading printers: %v", err) } - src.PrintPrinters(printers) + fmt.Println("Loaded printers...") - extruder, printer, err := src.Init(1, "Generic", "Marlin GCode", "EMU032uhb3293n2", "Testing Printer 1", "Init") + var printer *src.Printer - if err != nil { - fmt.Println("Error initializing printer:", err) - return + for printer == nil { + printer = askForPrinterID(printers) } + extruder := printer.Extruder + scanner := bufio.NewScanner(os.Stdin) fmt.Println("Choose command or connection for emulator (type 'exit' or 'quit' to quit):") @@ -94,3 +96,36 @@ func handleConnection(extruder *src.Extruder, printer *src.Printer) { func handleCommand(extruder *src.Extruder, printer *src.Printer) { src.RunCommand(extruder, printer) } + +func askForPrinterID(printers []src.Printer) *src.Printer { + scanner := bufio.NewScanner(os.Stdin) + + for { + fmt.Println("Available printers:") + + for _, p := range printers { + fmt.Printf("ID: %d, Name: %s\n", p.Id, p.Name) + } + + fmt.Print("Enter the printer ID: ") + + scanner.Scan() + idInput := scanner.Text() + + id, err := strconv.Atoi(idInput) + + if err != nil { + fmt.Println("Invalid input, please enter a valid number.") + continue + } + + for _, p := range printers { + if p.Id == id { + fmt.Printf("Selected printer: %s (ID: %d)\n", p.Name, p.Id) + return &p + } + } + + fmt.Println("Printer with that ID doesn't exist. Please try again.") + } +} diff --git a/printeremu/data/printerdata.json b/printeremu/data/printerdata.json index 8033e798..544b7b4d 100644 --- a/printeremu/data/printerdata.json +++ b/printeremu/data/printerdata.json @@ -1,3 +1,3 @@ { - "usesMarlin": [ "Ender 3", "Prusa MK4" ] + } \ No newline at end of file diff --git a/printeremu/data/printers.json b/printeremu/data/printers.json index 28194214..e70e24e8 100644 --- a/printeremu/data/printers.json +++ b/printeremu/data/printers.json @@ -3,17 +3,19 @@ "id": 1, "name": "Prusa MK4", "brand": { - "printername": "Prusa", - "printermodel": "MK4" - } + "printerName": "Prusa", + "printerModel": "MK4" + }, + "usesMarlin": true }, { "id": 2, "name": "Ender 3", "brand": { - "printername": "Creality", - "printermodel": "Ender 3" - } + "printerName": "Creality", + "printerModel": "Ender 3" + }, + "usesMarlin": true } ] \ No newline at end of file diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index b7c9578a..2e6d504e 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -29,7 +29,7 @@ func main() { // Init function initializes the Extruder and Printer func Init(id int, device string, description string, hwid string, name string, status string) (*Extruder, *Printer, error) { - fmt.Println("Initializing printer...") + //fmt.Println("Initializing printer...") position := Vector3{X: 100, Y: 27, Z: 76} extruder := NewExtruder(position, 250, 100, 125) @@ -46,7 +46,7 @@ func Init(id int, device string, description string, hwid string, name string, s log.Fatal("Failed to create printer:", err) } - fmt.Println(printer.String()) + //fmt.Println(printer.String()) return extruder, printer, nil } diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index 557128cd..1f500d89 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -5,21 +5,25 @@ import ( "fmt" "io" "os" + "time" + + "golang.org/x/exp/rand" ) type PrinterConfig struct { - Id int `json:"id"` - Name string `json:"name"` - Brand PrinterBrand `json:"brand"` + Id int `json:"id"` + Name string `json:"name"` + Brand PrinterBrand `json:"brand"` + UsesMarlin bool `json:"usesMarlin"` } type PrinterBrand struct { - PrinterName string `json:"printername"` - PrinterModel string `json:"printermodel"` + PrinterName string `json:"printerName"` + PrinterModel string `json:"printerModel"` } // LoadPrinters loads a list of printers from the specified JSON file -func LoadPrinters(filePath string) ([]PrinterConfig, error) { +func LoadPrinters(filePath string) ([]Printer, error) { file, err := os.Open(filePath) if err != nil { @@ -34,21 +38,58 @@ func LoadPrinters(filePath string) ([]PrinterConfig, error) { return nil, fmt.Errorf("failed to read file: %v", err) } - var printers []PrinterConfig - err = json.Unmarshal(fileBytes, &printers) + var printersConfig []PrinterConfig + err = json.Unmarshal(fileBytes, &printersConfig) if err != nil { return nil, fmt.Errorf("failed to unmarshal JSON data: %v", err) } + var printers []Printer + + for _, printerConfig := range printersConfig { + _, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", "EMU-"+RandomString(8), printerConfig.Name, "Init") + + // TODO: expand upon marlin gcode and non marlin printers + // if printerConfig.UsesMarlin {} + + if err != nil { + return nil, fmt.Errorf("failed to initialize printer %d: %v", printerConfig.Id, err) + } + + printers = append(printers, *printer) + } + return printers, nil } -func PrintPrinters(printers []PrinterConfig) { +func PrintPrinters(printers []Printer) { fmt.Println("Loaded printers:") for _, printer := range printers { - fmt.Printf("Printer ID: %d, Name: %s, Brand: %s, Model: %s\n", - printer.Id, printer.Name, printer.Brand.PrinterName, printer.Brand.PrinterModel) + fmt.Println(printer.String()) + } +} + +func GetPrinter(id int, printers []Printer) *Printer { + for _, printer := range printers { + if printer.Id == id { + return &printer + } } + + return nil +} + +func RandomString(length int) string { + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + rand.Seed(uint64(time.Now().UnixNano())) + + var result []byte + + for i := 0; i < length; i++ { + result = append(result, charset[rand.Intn(len(charset))]) + } + + return string(result) } From 29aec0b8a5b211f1b49d59d90352114eb5ff54f0 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 14:03:35 -0500 Subject: [PATCH 076/194] feat: attributes, marlin and brands --- printeremu/src/printer.go | 14 ++++++++++++-- printeremu/src/printerregistry.go | 7 +++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index f526566b..0a703065 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -24,6 +24,7 @@ type Printer struct { LinearAdvanceFactor float64 HeatbreakTemp float64 EnabledMotors map[string]bool + Attributes map[string]string } // DisableMotor disables a motor for a specific axis @@ -85,6 +86,7 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex LinearAdvanceFactor: 0.0, // Default linear advance factor HeatbreakTemp: 0.0, // Default heatbreak temp EnabledMotors: make(map[string]bool), + Attributes: make(map[string]string), } return printer, nil @@ -201,8 +203,16 @@ func (printer *Printer) MoveExtruder(targetPos Vector3) error { return nil } +func (printer *Printer) AddAttribute(key string, value string) { + printer.Attributes[key] = value +} + +func (printer *Printer) GetAttribute(key string) string { + return printer.Attributes[key] +} + // String provides a formatted string representation of the Printer state func (printer *Printer) String() string { - return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, LinearAdvanceFactor: %.2f, HeatbreakTemp: %.2f, Units: %s, Progress: %d%%, KeepAliveTime: %v, EnabledMotors: %v}", - printer.Id, printer.Device, printer.Description, printer.Hwid, printer.Name, printer.Status, printer.Date, printer.Extruder.String(), printer.Heatbed.String(), printer.Acceleration, printer.LinearAdvanceFactor, printer.HeatbreakTemp, printer.Units, printer.Progress, printer.KeepAliveTime, printer.EnabledMotors) + return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, LinearAdvanceFactor: %.2f, HeatbreakTemp: %.2f, Units: %s, Progress: %d%%, KeepAliveTime: %v, EnabledMotors: %v, Attributes: %v}", + printer.Id, printer.Device, printer.Description, printer.Hwid, printer.Name, printer.Status, printer.Date, printer.Extruder.String(), printer.Heatbed.String(), printer.Acceleration, printer.LinearAdvanceFactor, printer.HeatbreakTemp, printer.Units, printer.Progress, printer.KeepAliveTime, printer.EnabledMotors, printer.Attributes) } diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index 1f500d89..519db238 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -50,8 +50,11 @@ func LoadPrinters(filePath string) ([]Printer, error) { for _, printerConfig := range printersConfig { _, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", "EMU-"+RandomString(8), printerConfig.Name, "Init") - // TODO: expand upon marlin gcode and non marlin printers - // if printerConfig.UsesMarlin {} + printer.AddAttribute("model", printerConfig.Brand.PrinterName) + + if printerConfig.UsesMarlin { + printer.AddAttribute("usesMarlin", "true") + } if err != nil { return nil, fmt.Errorf("failed to initialize printer %d: %v", printerConfig.Id, err) From e5ff1a556f63c87f4e6933ecd404181a82f04361 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 14:32:17 -0500 Subject: [PATCH 077/194] feat: begin basic serial emulation --- printeremu/src/gcode_commands.go | 1 + printeremu/src/printer.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 045ccc3f..7aeaaf2c 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -29,6 +29,7 @@ func CommandHandler(command string, printer *Printer) string { if cmd == nil { return "Unknown command\n" } + return cmd.Execute(printer) } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 0a703065..baa6713e 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -3,6 +3,8 @@ package src import ( "fmt" "time" + + "github.com/gorilla/websocket" ) // Printer struct with associated components as pointers for easy modification @@ -25,6 +27,7 @@ type Printer struct { HeatbreakTemp float64 EnabledMotors map[string]bool Attributes map[string]string + WSConnection *websocket.Conn } // DisableMotor disables a motor for a specific axis @@ -87,6 +90,7 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex HeatbreakTemp: 0.0, // Default heatbreak temp EnabledMotors: make(map[string]bool), Attributes: make(map[string]string), + WSConnection: nil, // Default will be offline emulator } return printer, nil @@ -211,6 +215,33 @@ func (printer *Printer) GetAttribute(key string) string { return printer.Attributes[key] } +func (printer *Printer) WriteSerial(command string) error { + if printer.WSConnection == nil { + return fmt.Errorf("WebSocket connection not established") + } + + err := printer.WSConnection.WriteMessage(websocket.TextMessage, []byte(command)) + if err != nil { + return fmt.Errorf("failed to send command over WebSocket: %v", err) + } + + return nil +} + +func (printer *Printer) ReadSerial() (string, error) { + if printer.WSConnection == nil { + return "", fmt.Errorf("WebSocket connection not established") + } + + _, message, err := printer.WSConnection.ReadMessage() + + if err != nil { + return "", fmt.Errorf("failed to read message from WebSocket: %v", err) + } + + return string(message), nil +} + // String provides a formatted string representation of the Printer state func (printer *Printer) String() string { return fmt.Sprintf("Printer{Id: %d, Device: %s, Description: %s, Hwid: %s, Name: %s, Status: %s, Date: %s, Extruder: %v, Heatbed: %v, Acceleration: %.2f, LinearAdvanceFactor: %.2f, HeatbreakTemp: %.2f, Units: %s, Progress: %d%%, KeepAliveTime: %v, EnabledMotors: %v, Attributes: %v}", From 966bf7f30bdcb84011c59f639dbad438666996c4 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 14:45:59 -0500 Subject: [PATCH 078/194] fix: make writeserial function properly send data over websockets --- printeremu/src/printer.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index baa6713e..960ffa41 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -1,6 +1,7 @@ package src import ( + "encoding/json" "fmt" "time" @@ -215,12 +216,24 @@ func (printer *Printer) GetAttribute(key string) string { return printer.Attributes[key] } -func (printer *Printer) WriteSerial(command string) error { +func (printer *Printer) WriteSerial(event string, data interface{}) error { if printer.WSConnection == nil { return fmt.Errorf("WebSocket connection not established") } - err := printer.WSConnection.WriteMessage(websocket.TextMessage, []byte(command)) + message := map[string]interface{}{ + "event": event, + "data": data, + } + + jsonMessage, err := json.Marshal(message) + + if err != nil { + return fmt.Errorf("failed to marshal printer object: %v", err) + } + + err = printer.WSConnection.WriteMessage(websocket.TextMessage, []byte(jsonMessage)) + if err != nil { return fmt.Errorf("failed to send command over WebSocket: %v", err) } From fa447c9de46351bd47158a5f177166468ff90a48 Mon Sep 17 00:00:00 2001 From: CJ Date: Tue, 12 Nov 2024 16:49:05 -0500 Subject: [PATCH 079/194] feat: basic setting control for printer emulator --- printeremu/cmd/test_printer.go | 12 +++-- .../data/{options.json => settings.json} | 0 printeremu/src/emulator.go | 46 +++++++++++++++++-- printeremu/src/emulatorsettings.go | 40 ++++++++++++++++ printeremu/testserver/src/index.ts | 45 +++++++++++++++++- 5 files changed, 135 insertions(+), 8 deletions(-) rename printeremu/data/{options.json => settings.json} (100%) create mode 100644 printeremu/src/emulatorsettings.go diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 359ae227..2f88c726 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -15,6 +15,12 @@ import ( ) func main() { + settings, err := src.LoadSettings("data/settings.json") + + if err != nil { + log.Fatalf("Error loading settings: %v", err) + } + printers, err := src.LoadPrinters("data/printers.json") if err != nil { @@ -56,7 +62,7 @@ func main() { if strings.ToLower(command) == "connection" || strings.ToLower(command) == "conn" { fmt.Println("Running connection emulator...") - handleConnection(extruder, printer) + handleConnection(extruder, printer, &settings) break } @@ -73,7 +79,7 @@ func main() { } } -func handleConnection(extruder *src.Extruder, printer *src.Printer) { +func handleConnection(extruder *src.Extruder, printer *src.Printer, settings *src.EmulatorSettings) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -86,7 +92,7 @@ func handleConnection(extruder *src.Extruder, printer *src.Printer) { }() go func() { - src.RunConnection(ctx, extruder, printer) + src.RunConnection(ctx, extruder, printer, settings) }() <-ctx.Done() diff --git a/printeremu/data/options.json b/printeremu/data/settings.json similarity index 100% rename from printeremu/data/options.json rename to printeremu/data/settings.json diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 2e6d504e..97cb4c8d 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -51,8 +51,15 @@ func Init(id int, device string, description string, hwid string, name string, s return extruder, printer, nil } -func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { - serverURL := "ws://127.0.0.1:8000" +func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, settings *EmulatorSettings) { + loadedAddress := func() string { + if settings.DefaultAddress != "" && settings.DefaultPort != 0 { + return fmt.Sprintf("%s:%d", settings.DefaultAddress, settings.DefaultPort) + } + return "127.0.0.1:8000" // default address + }() + + serverURL := "ws://" + loadedAddress conn, _, err := websocket.DefaultDialer.Dial(serverURL, nil) @@ -130,7 +137,40 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer) { } case "error": - log.Println("Error:", data) + if data != nil { + log.Println("Error:", data) + } + + case "gcode": + if data != nil { + gcodeCommand, ok := data.(string) + + if ok { + response := CommandHandler(gcodeCommand, printer) + fmt.Println("G-code executed:", response) + + gcodeResponse := map[string]interface{}{ + "event": "gcode_response", + "data": fmt.Sprintf("Response: %s", response), + } + + responseMessage, err := json.Marshal(gcodeResponse) + + if err != nil { + log.Println("Failed to marshal gcode_response:", err) + continue + } + + if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { + log.Println("Error sending gcode_response:", err) + continue + } + } else { + log.Println("Received G-code command, but data is not a string:", data) + } + } else { + log.Println("Received G-code command, but no data") + } default: log.Println("Received from server:", string(message)) diff --git a/printeremu/src/emulatorsettings.go b/printeremu/src/emulatorsettings.go new file mode 100644 index 00000000..a0068066 --- /dev/null +++ b/printeremu/src/emulatorsettings.go @@ -0,0 +1,40 @@ +package src + +import ( + "encoding/json" + "fmt" + "io" + "os" +) + +type EmulatorSettings struct { + EnabledPrinters []string `json:"enabledPrinters"` + DefaultAddress string `json:"defaultAddress"` + DefaultPort int `json:"defaultPort"` + Startup string `json:"startup"` +} + +func LoadSettings(filePath string) (EmulatorSettings, error) { + file, err := os.Open(filePath) + + if err != nil { + return EmulatorSettings{}, fmt.Errorf("failed to open file %s: %v", filePath, err) + } + + defer file.Close() + + fileBytes, err := io.ReadAll(file) + + if err != nil { + return EmulatorSettings{}, fmt.Errorf("failed to read file: %v", err) + } + + var settings EmulatorSettings + err = json.Unmarshal(fileBytes, &settings) + + if err != nil { + return EmulatorSettings{}, fmt.Errorf("failed to unmarshal JSON data: %v", err) + } + + return settings, nil +} diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts index 9d8a3651..a93924a3 100644 --- a/printeremu/testserver/src/index.ts +++ b/printeremu/testserver/src/index.ts @@ -28,15 +28,56 @@ const eventHandlers = { if (data.data !== 'alive'){ ws.send(JSON.stringify({ event: 'error', message: 'Invalid ping data' })); } + }, + gcode: (ws, data) => { + // Handle G-code command from the client here + console.log(`Sent G-code: ${data}`); + }, + gcode_response: (ws, data) => { + console.log(`Received G-code response: ${data}`); } - // TODO: more events }; +function sendGCodeCommands(ws) { + const gcodeCommands = [ + "G28", // Home all axes + "G1 X10 Y10 Z10 F1500", // Move to X:10 Y:10 Z:10 + "G1 X20 Y20 Z20 F1500", // Move to X:20 Y:20 Z:20 + "G1 Z0", // Move to Z:0 + "M104 S200", // Set extruder temperature + "M140 S60", // Set bed temperature + "M107", // Turn off fan + ]; + + gcodeCommands.forEach((gcode, index) => { + setTimeout(() => { + console.log(`Sending G-code command: ${gcode}`); + ws.send(JSON.stringify({ + event: 'gcode', + data: gcode + })); + }, index * 2000); // Send every 2 seconds + }); +} + const printersMap = new Map(); wss.on('connection', (ws) => { console.log('WebSocket client connected'); + // Send a "ping" or "gcode" after a connection is established + setTimeout(() => { + ws.send(JSON.stringify({ + event: 'ping', + data: 'alive' + })); + }, 1000); // Send ping after 1 second + + // Send a series of G-code commands after the initial ping + setTimeout(() => { + sendGCodeCommands(ws); // Call sendGCodeCommands function explicitly here + }, 3000); // Start sending G-code after 3 seconds + ws.on('message', (message) => { let parsedMessage; @@ -74,4 +115,4 @@ wss.on('connection', (ws) => { httpServer.listen(8000, () => { console.log("WebSocket server is running on http://127.0.0.1:8000"); -}); +}); \ No newline at end of file From af7afb8f92bf596ffe9b582379f8f34d297e80c6 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:32:47 -0500 Subject: [PATCH 080/194] feat: added prusaGcode and moved everything that was prusa specific to there. --- server/Mixins/gcode/usesMarlinGcode.py | 29 +++++--------- server/Mixins/gcode/usesPrusaGcode.py | 51 +++++++++++++++++++++++++ server/Mixins/gcode/usesVanillaGcode.py | 6 --- 3 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 server/Mixins/gcode/usesPrusaGcode.py diff --git a/server/Mixins/gcode/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py index ef438007..0e1ef762 100644 --- a/server/Mixins/gcode/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -33,9 +33,6 @@ class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=AB getMachineNameCMD: Buffer = b"M997\n" callablesHashtable = { - "G29 P1": [alwaysTrue, checkOK], # Auto bed leveling - "G29 P9": [checkOK, checkOK], # Auto bed leveling - "M73": [alwaysTrue, checkOK], # Set build percentage "M104": [alwaysTrue, alwaysTrue], # Set hotend temp "M109": [alwaysTrue, checkExtruderTemp], # Wait for hotend to reach target temp "M114": [alwaysTrue, checkXYZ], # Get current position @@ -102,18 +99,14 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): assert isinstance(gcode, bytes) self.serialConnection.write(gcode) hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] - if hashIndex == "G29": - g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] - if g29addon == "P9": - hashIndex += " " + g29addon - else: - hashIndex += " P1" - elif hashIndex == "M109" or hashIndex == "M190": + if hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) if callables[0] != alwaysTrue: - while not callables[0](self.serialConnection.readline()): - pass + line = "" + while not callables[0](line): + line = self.serialConnection.readline() + if isVerbose: self.logger.debug(line) if callables[1] != alwaysTrue: while True: try: @@ -198,16 +191,14 @@ def resume(self): def connect(self: Device): Device.connect(self) try: - if self.serialConnection: + if self.serialConnection and self.serialConnection.is_open: self.serialConnection.write(b"M155 S1\n") return True except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error connecting:") - self.logger.error(e) - return False + from app import app + with app.app_context(): + return app.handle_error_and_logging(e, self) + def disconnect(self: Device): diff --git a/server/Mixins/gcode/usesPrusaGcode.py b/server/Mixins/gcode/usesPrusaGcode.py new file mode 100644 index 00000000..539a87af --- /dev/null +++ b/server/Mixins/gcode/usesPrusaGcode.py @@ -0,0 +1,51 @@ +from abc import ABCMeta +from typing_extensions import Buffer +from Classes.Fabricators.Device import Device +from Mixins.gcode.usesMarlinGcode import usesMarlinGcode +from Mixins.hasResponseCodes import alwaysTrue, checkOK + +class usesPrusaGcode(usesMarlinGcode, metaclass=ABCMeta): + callablesHashtable = { + "G29 P1": [alwaysTrue, checkOK], # Auto bed leveling + "G29 P9": [checkOK, checkOK], # Auto bed leveling + "M73": [alwaysTrue, checkOK], # Set build percentage + } + + callablesHashtable = {**usesMarlinGcode.callablesHashtable, **callablesHashtable} + + def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): + assert self.serialConnection.is_open + assert isinstance(gcode, bytes) + self.serialConnection.write(gcode) + hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] + if hashIndex == "G29": + g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] + if g29addon == "P9": + hashIndex += " " + g29addon + else: + hashIndex += " P1" + elif hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") + assert isinstance(self, usesPrusaGcode) + callables = self.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) + assert isinstance(self, Device) + if callables[0] != alwaysTrue: + line = "" + while not callables[0](line): + line = self.serialConnection.readline() + if isVerbose: self.logger.debug(line) + if callables[1] != alwaysTrue: + while True: + try: + line = self.serialConnection.readline() + if "processing" in line.decode("utf-8"): + continue + if isVerbose: self.logger.debug(line) + if callables[1](line): + self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) + return True + except UnicodeDecodeError as e: + continue + except Exception as e: + self.logger.error(e) + return False \ No newline at end of file diff --git a/server/Mixins/gcode/usesVanillaGcode.py b/server/Mixins/gcode/usesVanillaGcode.py index 3587f666..d53f1d9e 100644 --- a/server/Mixins/gcode/usesVanillaGcode.py +++ b/server/Mixins/gcode/usesVanillaGcode.py @@ -11,11 +11,6 @@ class usesVanillaGcode: homeCMD: Buffer = b"G28\n" callablesHashtable = { - "G0": [alwaysTrue, checkOK], # rapid positioning - "G1": [alwaysTrue, checkOK], # linear interpolation - "G2": [alwaysTrue, checkOK], # clockwise arc - "G3": [alwaysTrue, checkOK], # counter-clockwise arc - "G4": [alwaysTrue, checkOK], # dwell "G28": [alwaysTrue, checkXYZ], # Home } @@ -24,7 +19,6 @@ def goTo(self: Device, loc: Vector3, isVerbose: bool = False): assert isinstance(isVerbose, bool) assert isinstance(self, Device) self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) - return loc == self.getToolHeadLocation() def home(self, isVerbose: bool = False): try: From a66d6036ec25c5c5ba98c428a91c260248b4d921 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:34:01 -0500 Subject: [PATCH 081/194] fix: broke out cali-cube setup method --- Tests/test_fabricator.py | 43 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 4e68bb96..4c7defa7 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -19,6 +19,22 @@ def __desc__(): return "Fabricator Tests" def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" + +def cali_cube_setup(): + file = "../server/xyz-cali-cube" + from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 + from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 + from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S + if shortTest: + file = file + "-mini" + if isinstance(fabricator.device, Ender3): + file = file + "_ENDER3.gcode" + elif isinstance(fabricator.device, PrusaMK4S): + file = file + "_MK4S.gcode" + elif isinstance(fabricator.device, PrusaMK4): + file = file + "_MK4.gcode" + return file + def fabricator_setup(port): if not port: return None return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) @@ -44,15 +60,7 @@ def test_status(): @pytest.mark.dependency(depends=["test_device.py::test_connection"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") def test_add_job(): - file = "../server/xyz-cali-cube" - if shortTest: - file = file + "-mini" - if isinstance(fabricator.device, Ender3): - file = file + "_ENDER3.gcode" - elif isinstance(fabricator.device, PrusaMK4S): - file = file + "_MK4S.gcode" - elif isinstance(fabricator.device, PrusaMK4): - file = file + "_MK4.gcode" + file = cali_cube_setup() with open(file, "r") as f: assert fabricator.queue.addToFront( Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name), @@ -104,19 +112,10 @@ def pause_and_resume_fabricator(): @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") def test_gcode_print_time(): - expectedTime = 3 * 60 - file = "../server/xyz-cali-cube" - if shortTest: - file = file + "-mini" - if isinstance(fabricator.device, Ender3): - file = file + "_ENDER3.gcode" - expectedTime = 14 * 60 + 15 if shortTest else 28 * 60 - elif isinstance(fabricator.device, PrusaMK4S): - file = file + "_MK4S.gcode" - expectedTime = 8 * 60 + 45 if shortTest else 1800 - elif isinstance(fabricator.device, PrusaMK4): - file = file + "_MK4.gcode" - expectedTime = 10 * 60 if shortTest else 1800 + from Classes.Fabricators.Printers.Printer import Printer + if not isinstance(fabricator.device, Printer): + pytest.skip(f"{fabricator.getDescription()} doesn't support printing gcode") + file = cali_cube_setup() # expectedTime = 2040 # for my personal home test, 1072 expectedMinutes, expectedSeconds = divmod(expectedTime, 60) from Classes.Fabricators.Fabricator import getFileConfig From d32864b57667cc14eac318368c10dd70beb58fd4 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:34:40 -0500 Subject: [PATCH 082/194] fix: optimized imports --- Tests/test_fabricator.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 4c7defa7..f66b0096 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -3,14 +3,8 @@ from datetime import datetime import re import pytest - -from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 -from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 -from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S -from Classes.Ports import Ports from Classes.Jobs import Job from Classes.Fabricators.Fabricator import Fabricator -from Mixins.canPause import canPause from parallel_test_runner import testLevel testLevelToRun = testLevel @@ -37,6 +31,7 @@ def cali_cube_setup(): def fabricator_setup(port): if not port: return None + from Classes.Ports import Ports return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) @pytest.fixture(scope="module", autouse=True) @@ -73,6 +68,7 @@ def test_add_job(): @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") def test_pause_and_resume(): + from Mixins.canPause import canPause if not isinstance(fabricator.device, canPause): pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") From 23ab30420efdbbdf5863e4e59c7d57d389e14626 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:35:44 -0500 Subject: [PATCH 083/194] fix: rewrote the pause test so it would fail if something failed, like it is supposed to. --- Tests/test_fabricator.py | 53 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index f66b0096..918b81d8 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -66,44 +66,59 @@ def test_add_job(): assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") -@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +@pytest.mark.skipif(condition=testLevelToRun < 7, reason="Not doing lvl 7 tests") def test_pause_and_resume(): from Mixins.canPause import canPause if not isinstance(fabricator.device, canPause): pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") - from time import sleep - import threading - def parse_gcode(): - fabricator.queue.addToFront \ - (Job("../server/pauseAndResumeTest.gcode", "pauseAndResumeTest", fabricator.dbID, "ready", - "../server/pauseAndResumeTest.gcode", False, 1, fabricator.name), fabricator.dbID) - fabricator.begin() + file = "../server/pauseAndResumeTest.gcode" + from Classes.Fabricators.Fabricator import getFileConfig + config = getFileConfig(file) + from Classes.Fabricators.Printers.Printer import Printer + if isinstance(fabricator.device, Printer): + assert config["filament_type"] is not None, "Failed to get filament_type from {file}" + assert config["filament_diameter"] is not None, f"Failed to get filament_diameter from {file}" + assert config["nozzle_diameter"] is not None, f"Failed to get nozzle_diameter from {file}" + fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) + fabricator.device.changeNozzle(float(config["nozzle_diameter"])) + with open(file, "r") as f: + job = Job(f.read(), "pauseAndResumeTest", fabricator.dbID, "ready", file, False, 1, fabricator.name) + fabricator.queue.addToFront(job) + result = fabricator.begin() + import traceback + assert not isinstance(result, Exception), f"Failed to begin on {fabricator.getDescription()}: {result}:\n{''.join(traceback.format_exception(None, result, result.__traceback__))}" assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" def pause_and_resume_fabricator(): - sleep(2) + from time import sleep + while fabricator.getStatus() != "printing": + assert fabricator.getStatus() != "error", f"Failed to print on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(1) assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(20) + sleep(30) assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(1) assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(20) + sleep(10) fabricator.resetToIdle() assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - parse_thread = threading.Thread(target=parse_gcode) - pause_thread = threading.Thread(target=pause_and_resume_fabricator) - - parse_thread.start() - pause_thread.start() - - parse_thread.join() - pause_thread.join() + assert fabricator.device.home(), f"Failed to home on {fabricator.getDescription()}" + from concurrent.futures import ThreadPoolExecutor, as_completed + with ThreadPoolExecutor(max_workers=2) as executor: + parse_future = executor.submit(parse_gcode) + pause_future = executor.submit(pause_and_resume_fabricator) + + for future in as_completed([parse_future, pause_future]): + try: + future.result() + except Exception as e: + raise e @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") From 2670b607ac4ad373340b1c100a26cdef283d664a Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:36:27 -0500 Subject: [PATCH 084/194] fix: made logging for expected and measured time more accurate --- Tests/test_fabricator.py | 86 +++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 918b81d8..e040a7db 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -128,26 +128,92 @@ def test_gcode_print_time(): pytest.skip(f"{fabricator.getDescription()} doesn't support printing gcode") file = cali_cube_setup() # expectedTime = 2040 # for my personal home test, 1072 - expectedMinutes, expectedSeconds = divmod(expectedTime, 60) from Classes.Fabricators.Fabricator import getFileConfig config = getFileConfig(file) + expectedTime = int(config["estimated_time"]) fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) fabricator.device.changeNozzle(float(config["nozzle_diameter"])) + expectedDays, expectedHours, expectedMinutes, expectedSeconds = 0, 0, 0, 0 + if expectedTime >= 86400: + expectedDays, expectedTime = divmod(expectedTime, 86400) + if expectedTime >= 3600: + expectedHours, expectedTime = divmod(expectedTime, 3600) + if expectedTime >= 60: + expectedMinutes, expectedTime = divmod(expectedTime, 60) time = datetime.now() with open(file, "r") as f: - fabricator.queue.addToFront \ - (Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name) - , fabricator.dbID) + fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)) + time = datetime.now() fabricator.begin() time = datetime.now() - time - minutes, seconds = divmod(time.seconds, 60) fabricator.device.serialConnection.write(b"M31\n") line = "" while not re.search(r"\d+m \d+s", line): line = fabricator.device.serialConnection.readline().decode("utf-8") - printMinutes, printSeconds = map(int, re.findall(r"\d+", line)) - printTime = printMinutes * 60 + printSeconds + minutes, seconds = divmod(time.seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + measuredTimeList = [] + if days > 0: + measuredTimeList.append(f"{days:02}") + measuredTimeList.append(f"{hours:02}") + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + elif hours > 0: + measuredTimeList.append(f"{hours:02}") + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + elif minutes > 0: + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + else: + measuredTimeList.append(f"{seconds:02}") + measuredTimeString = ":".join(measuredTimeList) + + actualTimeList = re.findall(r"\d+", line) + printDays, printHours, printMinutes, printSeconds = 0, 0, 0, 0 + if len(actualTimeList) == 1: + printSeconds = int(actualTimeList[0]) + elif len(actualTimeList) == 2: + printMinutes, printSeconds = map(int, actualTimeList) + elif len(actualTimeList) == 3: + printHours, printMinutes, printSeconds = map(int, actualTimeList) + elif len(actualTimeList) == 4: + printDays, printHours, printMinutes, printSeconds = map(int, actualTimeList) + printTime = printDays * 86400 + printHours * 3600 + printMinutes * 60 + printSeconds + printList = [] + if printDays > 0: + printList.append(f"{printDays:02}") + printList.append(f"{printHours:02}") + printList.append(f"{printMinutes:02}") + printList.append(f"{printSeconds:02}") + elif printHours > 0: + printList.append(f"{printHours:02}") + printList.append(f"{printMinutes:02}") + printList.append(f"{printSeconds:02}") + elif printMinutes > 0: + printList.append(f"{printMinutes:02}") + printList.append(f"{printSeconds:02}") + else: + printList.append(f"{printSeconds:02}") + printString = ":".join(map(str, printList)) + expectedList = [] + if expectedDays > 0: + expectedList.append(f"{expectedDays:02}") + expectedList.append(f"{expectedHours:02}") + expectedList.append(f"{expectedMinutes:02}") + expectedList.append(f"{expectedSeconds:02}") + elif expectedHours > 0: + expectedList.append(f"{expectedHours:02}") + expectedList.append(f"{expectedMinutes:02}") + expectedList.append(f"{expectedSeconds:02}") + elif expectedMinutes > 0: + expectedList.append(f"{expectedMinutes:02}") + expectedList.append(f"{expectedSeconds:02}") + else: + expectedList.append(f"{expectedSeconds:02}") + expectedString = ":".join(map(str, expectedList)) - timeBoundary = max(120, expectedTime // 5) - assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(printMinutes):02}:{int(printSeconds):02}" - assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {int(expectedMinutes):02}:{int(expectedSeconds):02}, Actual: {int(minutes):02}:{int(seconds):02}" \ No newline at end of file + timeBoundary = 120 + assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {printString}" + assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of measured time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {measuredTimeString}" \ No newline at end of file From 4cb3843e4b82095b001ec58168c32a34e39eb1c6 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:37:39 -0500 Subject: [PATCH 085/194] fix: updated checks for base_url to be a valid input, not just 127.0.0.1 --- Tests/test_app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_app.py b/Tests/test_app.py index d6811302..4d98f7f6 100644 --- a/Tests/test_app.py +++ b/Tests/test_app.py @@ -1,4 +1,5 @@ import os +import re import pytest from Classes.Logger import Logger @@ -16,7 +17,7 @@ def test_db(): @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_base_url(): assert app.config["base_url"], "base_url doesnt exist?" - assert app.config["base_url"] == "http://127.0.0.1:8000", f"base_url is {app.config['base_url']}" + assert re.match(r"http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$", app.config["base_url"]) or re.match(r"http://localhost:\d{1,5}$", app.config["base_url"]), f"base_url is {app.config['base_url']}" @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_environment(): @@ -27,7 +28,7 @@ def test_environment(): def test_logger(): assert app.logger, "myLogger doesnt exist?" assert app.logger.name, "name doesnt exist?" - assert str(app.logger.name) == "Logger__App", f"myLogger is {str(app.logger.name)}" + assert str(app.logger.name) == "Logger_App", f"myLogger is {str(app.logger.name)}" assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" assert app.logger.fileLogger, "fileLogger doesnt exist?" assert app.logger.consoleLogger, "consoleLogger doesn't exists?" From 2ded9e67311990a43213c32cd08093b44d4dc539 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 13:38:47 -0500 Subject: [PATCH 086/194] fix: updated to use Prusa Gcode instead of Marlin --- .../Printers/Prusa/PrusaPrinter.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index ccc4e69f..c8a9062f 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -2,31 +2,30 @@ from typing_extensions import Buffer from Classes.Vector3 import Vector3 from Classes.Fabricators.Printers.Printer import Printer +from Mixins.gcode.usesPrusaGcode import usesPrusaGcode from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.gcode.usesMarlinGcode import usesMarlinGcode -class PrusaPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): +class PrusaPrinter(Printer, hasEndingSequence, usesPrusaGcode, metaclass=ABCMeta): VENDORID = 0x2C99 - def sendGcode(self, gcode: Buffer, isVerbose: bool = False): - return usesMarlinGcode.sendGcode(self, gcode, isVerbose) + return usesPrusaGcode.sendGcode(self, gcode, isVerbose) def parseGcode(self, file, isVerbose=False): - return usesMarlinGcode.parseGcode(self, file, isVerbose) + return usesPrusaGcode.parseGcode(self, file, isVerbose) def connect(self): - return usesMarlinGcode.connect(self) + return usesPrusaGcode.connect(self) def disconnect(self): - usesMarlinGcode.disconnect(self) + usesPrusaGcode.disconnect(self) def home(self, isVerbose: bool = False): - return usesMarlinGcode.home(self, isVerbose) + return usesPrusaGcode.home(self, isVerbose) def goTo(self, loc: Vector3, isVerbose: bool = False): - return usesMarlinGcode.goTo(self, loc, isVerbose) + return usesPrusaGcode.goTo(self, loc, isVerbose) @abstractmethod def endSequence(self): @@ -37,4 +36,4 @@ def getPrintTime(self): pass def getToolHeadLocation(self) -> Vector3: - return usesMarlinGcode.getToolHeadLocation(self) \ No newline at end of file + return usesPrusaGcode.getToolHeadLocation(self) \ No newline at end of file From 743b956f9cbc103bc66b9995e2ca2355fd3e7528 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 14:17:18 -0500 Subject: [PATCH 087/194] fix: updated to have proper logging and env setup. --- server/app.py | 99 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/server/app.py b/server/app.py index 89a9ad2f..f04d45dc 100644 --- a/server/app.py +++ b/server/app.py @@ -1,34 +1,47 @@ -from flask import Flask, jsonify, request, Response, url_for, send_from_directory -from threading import Thread -from flask_cors import CORS -import os +from flask import Flask, request, Response, send_from_directory +from flask_cors import CORS +import os from models.db import db -from models.printers import Printer from models.PrinterStatusService import PrinterStatusService from flask_migrate import Migrate -from dotenv import load_dotenv, set_key -from controllers.ports import getRegisteredPrinters +from dotenv import load_dotenv +from controllers.ports import getRegisteredFabricators import shutil from flask_socketio import SocketIO -from datetime import datetime, timedelta -from sqlalchemy import text -import json from models.config import Config - # moved this up here so we can pass the app to the PrinterStatusService # Basic app setup app = Flask(__name__, static_folder='../client/dist') -app.config.from_object(__name__) # update application instantly +app.config.from_object(__name__) # update application instantly +logs = os.path.join('./logs') +from Classes.Logger import Logger +app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.join(logs, "app.log")) +# start database connection +app.config["environment"] = Config.get('environment') +app.config["ip"] = Config.get('ip') +app.config["port"] = Config.get('port') +app.config["base_url"] = Config.get('base_url') + +load_dotenv() +basedir = os.path.abspath(os.path.dirname(__file__)) +database_file = os.path.join(basedir, Config.get('database_uri')) +if isinstance(database_file, bytes): + database_file = database_file.decode('utf-8') +databaseuri = 'sqlite:///' + database_file +app.config['SQLALCHEMY_DATABASE_URI'] = databaseuri +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +db.init_app(app) +migrate = Migrate(app, db) # moved this before importing the blueprints so that it can be accessed by the PrinterStatusService printer_status_service = PrinterStatusService(app) # Initialize SocketIO, which will be used to send printer status updates to the frontend -# and this specific socketit will be used throughout the backend +# and this specific socket it will be used throughout the backend -if Config.get('environment') == 'production': +if app.config["environment"] == 'production': async_mode = 'eventlet' # Use 'eventlet' for production else: async_mode = 'threading' # Use 'threading' for development @@ -36,11 +49,23 @@ socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object -# IMPORTING BLUEPRINTS -from controllers.ports import ports_bp -from controllers.jobs import jobs_bp -from controllers.statusService import status_bp, getStatus -from controllers.issues import issue_bp +def handle_errors_and_logging(e: Exception, fabricator = None): + from Classes.Fabricators.Fabricator import Fabricator + device = fabricator + if isinstance(fabricator, Fabricator): + device = fabricator.device + if device.logger is None: + if app.logger is None: + print(e) + else: + app.logger.error("Error:") + app.logger.error(e) + else: + device.logger.error("Error:") + device.logger.error(e) + return True + +app.handle_error_and_logging = handle_errors_and_logging CORS(app) @@ -63,16 +88,11 @@ def serve_static(path='index.html'): def serve_assets(filename): return send_from_directory(os.path.join(app.static_folder, 'assets'), filename) -# start database connection -load_dotenv() -basedir = os.path.abspath(os.path.dirname(__file__)) -database_file = os.path.join(basedir, Config.get('database_uri')) -databaseuri = 'sqlite:///' + database_file -app.config['SQLALCHEMY_DATABASE_URI'] = databaseuri -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -db.init_app(app) - -migrate = Migrate(app, db) +# IMPORTING BLUEPRINTS +from controllers.ports import ports_bp +from controllers.jobs import jobs_bp +from controllers.statusService import status_bp +from controllers.issues import issue_bp # # Register the display_bp Blueprint app.register_blueprint(ports_bp) @@ -87,15 +107,15 @@ def handle_ping(): # own thread with app.app_context(): try: + # Create in-memory uploads folder + uploads_folder = os.path.join('../uploads') + tempcsv = os.path.join('../tempcsv') + # Creating printer threads from registered printers on server start - res = getRegisteredPrinters() # gets registered printers from DB + res = getRegisteredFabricators() # gets registered fabricators from DB data = res[0].get_json() # converts to JSON printers_data = data.get("printers", []) # gets the values w/ printer data printer_status_service.create_printer_threads(printers_data) - - # Create in-memory uploads folder - uploads_folder = os.path.join('../uploads') - tempcsv = os.path.join('../tempcsv') if os.path.exists(uploads_folder): # Remove the uploads folder and all its contents @@ -106,14 +126,14 @@ def handle_ping(): os.makedirs(uploads_folder) os.makedirs(tempcsv) - print("Uploads folder recreated as an empty directory.") + app.logger.info("Uploads folder recreated as an empty directory.") else: # Create the uploads folder if it doesn't exist os.makedirs(uploads_folder) os.makedirs(tempcsv) - print("Uploads folder created successfully.") + app.logger.info("Uploads folder created successfully.") except Exception as e: - print(f"Unexpected error: {e}") + app.logger.error(f"Unexpected error: {e}") if __name__ == "__main__": @@ -122,7 +142,4 @@ def handle_ping(): # Before sending to printer, query for status. If error, throw error. # since we are using socketio, we need to use socketio.run instead of app.run # which passes the app anyways - socketio.run(app, debug=True) # Replace app.run with socketio.run - -def create_app(): - return app \ No newline at end of file + socketio.run(app, debug=True) # Replace app.run with socketio.run \ No newline at end of file From 524c750b0ef6110fade9c3715d45c3677f360fd9 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 14:49:22 -0500 Subject: [PATCH 088/194] fix: updated to have proper logging. --- Tests/conftest.py | 54 ++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 5b5f7106..2d037b36 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -7,6 +7,14 @@ import pluggy import pytest +from Classes.Logger import Logger + +intLogger = Logger("Internal Errors", consoleLogger=sys.stdout, fileLogger="internal_errors.log", loggingLevel=Logger.INFO) + +def pytest_internalerror(excrepr, excinfo): + # This hook is called when pytest encounters an internal error + intLogger.error(f"Internal pytest error:\n{excrepr}") + def pytest_addoption(parser): parser.addoption( "--myVerbose", @@ -37,8 +45,6 @@ def setup_logger(port): subfolder = os.path.join(log_folder, timestamp) os.makedirs(subfolder, exist_ok=True) log_file_path = os.path.join(subfolder, f"test_{port}.log") - - from Classes.Logger import Logger return Logger(port, "Test Printer", consoleLogger=sys.stdout, fileLogger=log_file_path, showFile=False, showLevel=False) @pytest.hookimpl(tryfirst=True) @@ -89,6 +95,18 @@ def pytest_sessionfinish(session, exitstatus) -> None: xpasses = session.config.xpassed_count logger = session.config.logger + if hasattr(session.config, "_capturemanager"): + capture_manager = session.config._capturemanager + # Suspend capturing to retrieve the output + captured = capture_manager.read_global_and_disable() + + # Print the captured stdout and stderr + print("\nCaptured output during tests:\n") + print(captured) + + # Re-enable capture if needed for further use + capture_manager.resume_global_capture() + stats = [] if passes > 0: stats.append(f"\033[32m\033[1m{passes} passed") if fails > 0: stats.append(f"\033[31m\033[1m{fails} failed") @@ -110,21 +128,7 @@ def pytest_sessionfinish(session, exitstatus) -> None: logger.logMessageOnly(headerText, logLevel=logger.ERROR) for failTest in session.config.failNames: logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) - #todo: break this out into something that can be called anywhere for the Logger class - for reprentry in session.config.fails[failTest].reprtraceback.reprentries: - if reprentry.reprfuncargs is not None: - things = list(reprentry.reprfuncargs.args) - for args in things: - logger.logMessageOnly(f"{args[0]} = {args[1]}") - logger.logMessageOnly("") - for line in list(reprentry.lines): - if line.startswith("E") or line.startswith(">"): - logger.logMessageOnly(line.__str__(), logLevel=logger.ERROR) - else: - logger.logMessageOnly(line.__str__()) - loc = reprentry.reprfileloc - logger.logMessageOnly("\n" + loc.path + ":" + loc.lineno.__str__() + ": " + loc.message, logLevel=logger.ERROR) - logger.logMessageOnly(line_separator("", symbol="- "), logLevel=logger.info) + logger.logException(session.config.fails[failTest].reprtraceback.reprentries) logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) visited_modules = set() @@ -207,8 +211,20 @@ def pytest_runtest_logreport(report): if report.passed: logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: - logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n {report.longrepr}") + logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n") + logger.logException(report.longrepr.reprtraceback.reprentries) elif report.skipped: logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") else: - logger.info(f"{testString} IDK what happened!?!?: {report}") \ No newline at end of file + logger.info(f"{testString} IDK what happened!?!?: {report}") + +def pytest_collectreport(report): + if report.failed: + intLogger.logMessageOnly(f"Collection failed:", logLevel=intLogger.ERROR) + if not hasattr(report.longrepr, "reprtraceback"): + intLogger.logException(report.longrepr.longrepr) + return + if not hasattr(report.longrepr.reprtraceback, "reprentries"): + intLogger.logException(report.longrepr.reprtraceback) + return + else: intLogger.logException(report.longrepr.reprtraceback.reprentries) \ No newline at end of file From 3d2ef0edcd3e541eb9c78084f8b70cf08408deaa Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 14:49:49 -0500 Subject: [PATCH 089/194] fix: updated to have proper logging. --- server/Classes/Fabricators/Device.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 8bb06ee4..04f718ce 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -21,7 +21,7 @@ class Device(ABC): def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): self.serialPort: ListPortInfo | SysFS | None = serialPort self.serialID: str | None = serialPort.serial_number - self.logger = Logger(self.serialPort.device, self.DESCRIPTION, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger) self.status = "idle" self.verdict = "" @@ -34,13 +34,9 @@ def connect(self): self.serialConnection.reset_input_buffer() return True except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error connecting:") - self.logger.error(e) - return False - + from app import app + with app.app_context(): + return app.handle_error_and_logging(e, self) def disconnect(self): if self.serialConnection: self.serialConnection.close() From c0dd71f9b1ef98b0f91369f81185f104b78dac52 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 14:50:29 -0500 Subject: [PATCH 090/194] fix: updated to wait during setup, in the case of ender hard crashing for reasons. --- server/Classes/Fabricators/Printers/Ender/EnderPrinter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index 2b4c9311..e2ed26f9 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,4 +1,6 @@ from abc import ABCMeta +from time import sleep + from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 @@ -11,7 +13,9 @@ class EnderPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMet homePosition = Vector3(-3.0,-10.0,0.0) def connect(self): - return usesMarlinGcode.connect(self) + ret = usesMarlinGcode.connect(self) + sleep(7) + return ret def disconnect(self): return usesMarlinGcode.disconnect(self) From fb035b14e25d7f961ffa96362a64b4d17dded1aa Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:27:28 -0500 Subject: [PATCH 091/194] fix: updated to return errors if they occur and to log properly --- server/Classes/Fabricators/Fabricator.py | 126 +++++++++++++---------- 1 file changed, 74 insertions(+), 52 deletions(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index dda07089..244913da 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -124,23 +124,26 @@ def addToDB(self): def begin(self): """starts the fabrication process""" - assert self.status == "idle" - assert self.queue is not None - assert len(self.queue) > 0 - - self.job = self.queue.getNext() - if self.job is None: - return # TODO: return error message - - #TODO: check filament type - self.checkValidJob() - - # if isinstance(self.device, hasStartupSequence): - # self.device.startupSequence() + try: + assert self.status == "idle" + assert self.queue is not None + assert len(self.queue) > 0 + self.job = self.queue.getNext() + assert self.job is not None, "Job is None" + self.checkValidJob() + assert self.status != "error", "Invalid job" + # if isinstance(self.device, hasStartupSequence): + # self.device.startupSequence() + assert self.setStatus("printing"), "Failed to set status to printing" + assert self.device.parseGcode(self.job.file_name_original), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. + self.handleVerdict() + return True + except Exception as e: + from app import app + with app.app_context(): + app.handle_error_and_logging(e, self) + return e - assert self.setStatus("printing"), "Failed to set status to printing" - assert self.device.parseGcode(self.job.file_name_original), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. - self.handleVerdict() def pause(self): """pauses the fabrication process if the fabricator supports it""" @@ -173,11 +176,9 @@ def cancel(self): self.setStatus("cancelled") return self.status == self.device.status == "cancelled" except Exception as e: - if self.device is None: - print(e) - else: - self.device.logger.error("Error cancelling job:") - self.device.logger.error(e) + from app import app + with app.app_context(): + app.handle_error_and_logging(e, self) return False def getStatus(self): @@ -201,11 +202,8 @@ def setStatus(self, newStatus): # ) return True except Exception as e: - if self.device is None: - print(e) - else: - self.device.logger.error("Error setting status:") - self.device.logger.error(e) + from app import app + app.handle_error_and_logging(e, self) return False def resetToIdle(self): @@ -257,38 +255,62 @@ def getDescription(self): def checkValidJob(self): """checks if the job is valid for the fabricator""" - settingsDict = getFileConfig(self.job.file_name_original) - from Classes.Fabricators.Printers.Printer import Printer - from Classes.Fabricators.CNCMachines.CNCMachine import CNCMachine - from Classes.Fabricators.LaserCutters.LaserCutter import LaserCutter - if isinstance(self.device, Printer): - assert self.device.filamentType is not None, "Filament type not set" - assert self.device.filamentDiameter is not None, "Filament diameter not set" - assert self.device.nozzleDiameter is not None, "Nozzle diameter not set" - assert self.device.filamentType == settingsDict["filament_type"], f"Filament type mismatch: {self.device.filamentType} != {settingsDict['filament_type']}" - assert self.device.filamentDiameter == float(settingsDict["filament_diameter"]), f"Filament diameter mismatch: {self.device.filamentDiameter} != {float(settingsDict['filament_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0" - assert self.device.nozzleDiameter == float(settingsDict["nozzle_diameter"]), f"Nozzle diameter mismatch: {self.device.nozzleDiameter} != {float(settingsDict['nozzle_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0.0" - - elif isinstance(self.device, CNCMachine): - # if self.device.bitDiameter is not None and self.device.bitDiameter != float(settingsDict["bit_diameter"]): - # return False - pass - elif isinstance(self.device, LaserCutter): - # if self.device.laserPower is not None and self.device.laserPower != int(settingsDict["laser_power"]): - # return False - pass + try: + settingsDict = getFileConfig(self.job.file_name_original) + from Classes.Fabricators.Printers.Printer import Printer + from Classes.Fabricators.CNCMachines.CNCMachine import CNCMachine + from Classes.Fabricators.LaserCutters.LaserCutter import LaserCutter + if isinstance(self.device, Printer): + assert self.device.filamentType is not None, "Filament type not set" + assert self.device.filamentDiameter is not None, "Filament diameter not set" + assert self.device.nozzleDiameter is not None, "Nozzle diameter not set" + assert self.device.filamentType == settingsDict["filament_type"], f"Filament type mismatch: {self.device.filamentType} != {settingsDict['filament_type']}" + assert self.device.filamentDiameter == float(settingsDict["filament_diameter"]), f"Filament diameter mismatch: {self.device.filamentDiameter} != {float(settingsDict['filament_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0" + assert self.device.nozzleDiameter == float(settingsDict["nozzle_diameter"]), f"Nozzle diameter mismatch: {self.device.nozzleDiameter} != {float(settingsDict['nozzle_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0.0" + elif isinstance(self.device, CNCMachine): + # if self.device.bitDiameter is not None and self.device.bitDiameter != float(settingsDict["bit_diameter"]): + # return False + pass + elif isinstance(self.device, LaserCutter): + # if self.device.laserPower is not None and self.device.laserPower != int(settingsDict["laser_power"]): + # return False + pass + except AssertionError as e: + self.device.logger.error(f"Invalid job: {e}") + self.setStatus("error") + self.queue.removeJob() + self.job = None def getFileConfig(file): """Get the config lines from the job file.""" with open(file, 'r') as f: lines = f.readlines() - comment_lines = [line.lstrip('; ').strip() for line in lines if line.startswith(';')] - if "prusaslicer" in comment_lines[0].lower(): - return {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines[-358:] if '=' in line} - elif "cura" in comment_lines[11].lower(): - settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} + comment_lines = [line.lstrip(';').strip() for line in lines if line.startswith(';') or ':' in line] + if len(comment_lines) > 0 and "prusaslicer" in comment_lines[0].lower(): + settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines[-358:] if '=' in line} + import re + days, hours, minutes, seconds = 0, 0, 0, 0 + timeList = re.findall(r"\d+", settingsDict["estimated printing time (normal mode)"]) + if len(timeList) == 1: + seconds = map(int, timeList) + elif len(timeList) == 2: + minutes, seconds = map(int, timeList) + elif len(timeList) == 3: + hours, minutes, seconds = map(int, timeList) + elif len(timeList) == 4: + days, hours, minutes, seconds = map(int, timeList) + settingsDict["expected_time"] = str(days * 86400 + hours * 3600 + (minutes + 2) * 60 + seconds) + elif len(comment_lines) >= 12 and "cura" in comment_lines[11].lower(): + equalsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} + colonDict = {line.split(':')[0].strip(): line.split(':')[1].strip() for line in comment_lines if ':' in line} + settingsDict = {**equalsDict, **colonDict} + settingsDict["expected_time"] = str(int(settingsDict["TIME"]) + 120) settingsDict["filament_type"] = settingsDict["material_type"] settingsDict["filament_diameter"] = settingsDict["material_diameter"] settingsDict["nozzle_diameter"] = settingsDict["machine_nozzle_size"] - return settingsDict + else: + equalsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} + colonDict = {line.split(':')[0].strip(): line.split(':')[1].strip() for line in comment_lines if ':' in line} + settingsDict = {**equalsDict, **colonDict} + return settingsDict From 64cb15bf66e9f3717d3b5654f5132a8e4bab6387 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:28:22 -0500 Subject: [PATCH 092/194] feat: updated to use concurrent.futures instead of threading, nad changed args list to its own list. --- Tests/parallel_test_runner.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 3caa394d..afaa1dde 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -30,25 +30,23 @@ PORTS = glob.glob("/dev/tty[A-Za-z]*") # Function to run pytest for a specific port -testLevel = 10 +testLevel = 1 verbosity = 2 -showAnything = False -verbosityCommand = "-p no:terminal" if not showAnything else "-vvv" +runFlags = 0b00 # 0b01: -s, 0b10: -vvv or -p no:terminal + def run_tests_for_port(comm_port): env = os.environ.copy() env["PORT"] = comm_port - subprocess.Popen(["pytest", ".", verbosityCommand, f"--myVerbose={verbosity}", f"--port={comm_port}"], env=env).wait() + args = ["pytest", ".", f"--myVerbose={verbosity}", f"--port={comm_port}"] + if runFlags & 0b1: args.append("-s") + if runFlags & 0b10: args.append("-vvv") + else: args.append("-p no:terminal") + subprocess.Popen(args, env=env).wait() if __name__ == "__main__": - # Create and start a thread for each port - threads = [] - for port in PORTS: - if Ports.getPortByName(port) is None: - continue - thread = threading.Thread(target=run_tests_for_port, args=(port,)) - thread.start() - threads.append(thread) + from concurrent.futures import ThreadPoolExecutor, as_completed - # Wait for all threads to complete - for thread in threads: - thread.join() \ No newline at end of file + with ThreadPoolExecutor(max_workers=len(PORTS)) as executor: + futures = [executor.submit(run_tests_for_port, port) for port in PORTS if Ports.getPortByName(port) is not None] + for future in as_completed(futures): + future.result() \ No newline at end of file From ba3b274f9c18f861c8b48f023e2c8a2821dac47c Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:29:36 -0500 Subject: [PATCH 093/194] fix: removed home so it actually pauses in the test, and added settings to the end of the file so it passes the tests. --- server/pauseAndResumeTest.gcode | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/pauseAndResumeTest.gcode b/server/pauseAndResumeTest.gcode index f673d91a..07acb549 100644 --- a/server/pauseAndResumeTest.gcode +++ b/server/pauseAndResumeTest.gcode @@ -1,5 +1,3 @@ -G28 ; home all axes - G1 X50 Y50 Z2 F36000 ; move to a point G1 X200 Y50 Z2 F36000 ; move to a point G1 X200 Y150 Z2 F36000 ; move to a point @@ -198,4 +196,7 @@ G1 X50 Y150 Z2 F36000 ; move to a point G1 X50 Y50 Z2 F36000 ; move to a point G1 X200 Y50 Z2 F36000 ; move to a point G1 X200 Y150 Z2 F36000 ; move to a point -G1 X50 Y150 Z2 F36000 ; move to a point \ No newline at end of file +G1 X50 Y150 Z2 F36000 ; move to a point +; filament_type = PLA +; filament_diameter = 1.75 +; nozzle_diameter = 0.4 \ No newline at end of file From 5242b8006a03014285cdf9fb47a077145b8c47ed Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:30:13 -0500 Subject: [PATCH 094/194] feat: updated to use proper constructor, and to log properly. --- .../Classes/Fabricators/Printers/Printer.py | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 0fadbea7..d25a78d1 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -3,31 +3,26 @@ from Classes.Fabricators.Device import Device class Printer(Device, metaclass=ABCMeta): - filamentType: str | None = None - filamentDiameter: float | None = None - nozzleDiameter: float | None = None bedTemperature: int | None = None nozzleTemperature: int | None = None + def __init__(self, serialPort, consoleLogger=None, fileLogger=None): + super().__init__(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.filamentType = None + self.filamentDiameter = None + self.nozzleDiameter = None + def changeFilament(self, filamentType: str, filamentDiameter: float): if not isinstance(filamentDiameter, float): filamentDiameter = float(filamentDiameter) try: - if self.status is "idle": - self.filamentType = filamentType - self.filamentDiameter = filamentDiameter - else: - raise Exception("Printer is not idle") + assert self.status is "idle", "Printer is not idle" + self.filamentType = filamentType + self.filamentDiameter = filamentDiameter except Exception as e: - if self.logger is None: - with app.app_context(): - app.logger.error("Error changing filament:") - app.logger.error(e) - else: - self.logger.error("Error changing filament:") - self.logger.error(e) - self.filamentType = filamentType - self.filamentDiameter = filamentDiameter + with app.app_context(): + app.logger.error("Error changing filament:") + app.logger.error(e) def changeNozzle(self, nozzleDiameter: float): if not isinstance(nozzleDiameter, float): From b156c0baa926eb6e87b09d068e1c13fd8abf9ad2 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:32:26 -0500 Subject: [PATCH 095/194] feat: added built-in method for logging exceptions --- server/Classes/Logger.py | 45 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 04677de3..1246c4f6 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -2,7 +2,8 @@ import os import sys import traceback -from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionChainRepr, ReprEntry, ReprEntryNative +from typing_extensions import Sequence class Logger(logging.Logger): @@ -12,8 +13,13 @@ class Logger(logging.Logger): ERROR = logging.ERROR CRITICAL = logging.CRITICAL - def __init__(self, port, deviceName, consoleLogger=None, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): - super().__init__(f"_".join(["Logger", port, deviceName])) + def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): + title = [] + if port: + title.append(port) + if deviceName: + title.append(deviceName) + super().__init__(f"_".join(["Logger"] + title)) self.setLevel(loggingLevel) info = [] if showDate: @@ -110,6 +116,39 @@ def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *a for handler, formatter in zip(self.handlers, oldFormatters): handler.setFormatter(formatter) + def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRepr | list[ReprEntry | ReprEntryNative] | Sequence[ReprEntry | ReprEntryNative] | ReprEntry | str): + if isinstance(reprentries, ExceptionChainRepr) or isinstance(reprentries, ReprEntry): + reprentries = [reprentries] + if isinstance(reprentries, list): + for index, reprentry in enumerate(reprentries): + if isinstance(reprentry, ReprEntry): + if reprentry.reprfuncargs is not None: + things = list(reprentry.reprfuncargs.args) + for args in things: + self.logMessageOnly(f"{args[0]} = {args[1]}") + self.logMessageOnly("") + for line in list(reprentry.lines): + if line.startswith("E") or line.startswith(">"): + self.logMessageOnly(line.__str__(), logLevel=self.ERROR) + else: + self.logMessageOnly(line.__str__()) + loc = reprentry.reprfileloc + self.logMessageOnly("\n" + loc.path + ":" + loc.lineno.__str__() + ": " + loc.message, logLevel=self.ERROR) + elif isinstance(reprentry, ExceptionChainRepr): + chain = reprentry.chain + for link in chain: + self.logException(link[0].reprentries) + + if index < len(reprentries) - 1: + from conftest import line_separator + self.logMessageOnly(line_separator("", symbol="- "), logLevel=self.INFO) + elif isinstance(reprentries, str): + for line in reprentries.split("\n"): + if line.startswith("E") or line.startswith(">"): + self.logMessageOnly(line, logLevel=self.ERROR) + else: + self.logMessageOnly(line) + class CustomFormatter(logging.Formatter): # ANSI escape codes for colors COLOR_CODES = { From bd099d28895a592e15a8973220ff09ead28292fb Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:33:11 -0500 Subject: [PATCH 096/194] fix: fixed formatting --- Tests/test_app.py | 1 - Tests/test_fabricator.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Tests/test_app.py b/Tests/test_app.py index 4d98f7f6..49442578 100644 --- a/Tests/test_app.py +++ b/Tests/test_app.py @@ -1,7 +1,6 @@ import os import re import pytest - from Classes.Logger import Logger from parallel_test_runner import testLevel from app import app diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index e040a7db..8009343c 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -57,9 +57,7 @@ def test_status(): def test_add_job(): file = cali_cube_setup() with open(file, "r") as f: - assert fabricator.queue.addToFront( - Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name), - 3), f"Failed to add job on {fabricator.getDescription()}" + assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name)), f"Failed to add job on {fabricator.getDescription()}" for job in fabricator.queue.getQueue(): assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" fabricator.queue.removeJob() @@ -140,7 +138,6 @@ def test_gcode_print_time(): expectedHours, expectedTime = divmod(expectedTime, 3600) if expectedTime >= 60: expectedMinutes, expectedTime = divmod(expectedTime, 60) - time = datetime.now() with open(file, "r") as f: fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)) time = datetime.now() From af339dadbec4d6d761ecb241a80db2ad1e7567b7 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:34:28 -0500 Subject: [PATCH 097/194] fix: changed the job import to pull from the updated version --- server/controllers/jobs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 0d5a0b20..1ddee7e0 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -4,7 +4,7 @@ import shutil import tempfile from flask import Blueprint, Response, jsonify, request, make_response, send_file -from models.jobs import Job +from Classes.Jobs import Job from models.printers import Printer from app import printer_status_service import json @@ -96,9 +96,9 @@ def add_job_to_queue(): priority = request.form['priority'] # if priotiry is '1' then add to front of queue, else add to back if priority == 'true': - findPrinterObject(printer_id).getQueue().addToFront(job, printer_id) + findPrinterObject(printer_id).getQueue().addToFront(job) else: - findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) + findPrinterObject(printer_id).getQueue().addToBack(job) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 From 2967a9a8238b91a1eea5e39c5579ea51a3c9d530 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 15:34:57 -0500 Subject: [PATCH 098/194] feat: added more lines to avoid adding the logs to the git. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a29cf8bf..e0fe30c1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ server/dist .DS_Store /qodana.yaml /Tests/logs/ +/Tests/server/ +/logs/ +/server/logs/ From 93460554c88fb8b07bd286713c48920e96108a59 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:03:44 -0500 Subject: [PATCH 099/194] fix: on error, should return false --- server/app.py | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/server/app.py b/server/app.py index f04d45dc..ff8ed9aa 100644 --- a/server/app.py +++ b/server/app.py @@ -63,7 +63,7 @@ def handle_errors_and_logging(e: Exception, fabricator = None): else: device.logger.error("Error:") device.logger.error(e) - return True + return False app.handle_error_and_logging = handle_errors_and_logging @@ -107,33 +107,31 @@ def handle_ping(): # own thread with app.app_context(): try: - # Create in-memory uploads folder - uploads_folder = os.path.join('../uploads') - tempcsv = os.path.join('../tempcsv') - - # Creating printer threads from registered printers on server start - res = getRegisteredFabricators() # gets registered fabricators from DB - data = res[0].get_json() # converts to JSON - printers_data = data.get("printers", []) # gets the values w/ printer data - printer_status_service.create_printer_threads(printers_data) - - if os.path.exists(uploads_folder): - # Remove the uploads folder and all its contents - shutil.rmtree(uploads_folder) - shutil.rmtree(tempcsv) - - # Recreate it as an empty directory - os.makedirs(uploads_folder) - os.makedirs(tempcsv) - - app.logger.info("Uploads folder recreated as an empty directory.") - else: - # Create the uploads folder if it doesn't exist - os.makedirs(uploads_folder) - os.makedirs(tempcsv) - app.logger.info("Uploads folder created successfully.") + # Define directory paths for uploads and tempcsv + uploads_folder = os.path.abspath('../uploads') + tempcsv = os.path.abspath('../tempcsv') + + # Create printer threads from registered printers on server start + from Classes.FabricatorList import FabricatorList + printer_status_service.create_printer_threads(FabricatorList.fabricators) + + # Check if directories exist and handle them accordingly + for folder in [uploads_folder, tempcsv]: + if os.path.exists(folder): + # Remove the folder and all its contents + import shutil + shutil.rmtree(folder) + app.logger.info(f"{folder} removed and will be recreated.") + # Recreate the folder + os.makedirs(folder) + app.logger.info(f"{folder} recreated as an empty directory.") + except Exception as e: + # Log any exceptions for troubleshooting + import traceback app.logger.error(f"Unexpected error: {e}") + app.logger.error(traceback.format_exception(e)) + if __name__ == "__main__": From 04dcf8750619edeb6718c79fba56698afa4c0ad4 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:03:57 -0500 Subject: [PATCH 100/194] fix: formatted time better --- Tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 2d037b36..6ee29b36 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -119,9 +119,9 @@ def pytest_sessionfinish(session, exitstatus) -> None: summary += f"\033[32m in {session_duration:.2f}s" if session_duration > 3600: - summary += f" ({session_duration // 3600:.0f}:{session_duration % 3600 // 60:.0f}:{session_duration % 60:.2f})" + summary += f" ({session_duration // 3600:02.0f}:{session_duration % 3600 // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" elif session_duration > 60: - summary += f" ({session_duration // 60:02.0f}:{session_duration % 60:02.2f})" + summary += f" ({session_duration // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" if session.config.failed_count > 0: headerText = "\n" + line_separator("FAILURES", symbol="=") From f8e8d5b555d1a7db588b812490b56594e7a890c2 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:04:51 -0500 Subject: [PATCH 101/194] fix: removed arbitrary limit on stuff to add to settingsDict. --- server/Classes/Fabricators/Fabricator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 244913da..593da1cb 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -288,7 +288,7 @@ def getFileConfig(file): lines = f.readlines() comment_lines = [line.lstrip(';').strip() for line in lines if line.startswith(';') or ':' in line] if len(comment_lines) > 0 and "prusaslicer" in comment_lines[0].lower(): - settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines[-358:] if '=' in line} + settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} import re days, hours, minutes, seconds = 0, 0, 0, 0 timeList = re.findall(r"\d+", settingsDict["estimated printing time (normal mode)"]) From d87036bb9eb998b6317d63506f405b9ec110fa68 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:05:07 -0500 Subject: [PATCH 102/194] fix: made logging errors work properly --- server/Classes/Logger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 1246c4f6..082fd28f 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -59,7 +59,8 @@ def formatLog(self, msg): if isinstance(msg, str): pass elif isinstance(msg, Exception): - msg = traceback.format_exception(msg.__traceback__) + msg = traceback.format_exception(msg) + msg = "".join(msg) elif isinstance(msg, ExceptionChainRepr): msg = msg.reprtraceback.__repr__() elif isinstance(msg, list) or isinstance(msg, tuple): From 7e03d0d225cc4ee42c81d38fd8b76adc0e991a8e Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:05:39 -0500 Subject: [PATCH 103/194] fix: fully removed threading in favor of concurrent.futures --- Tests/parallel_test_runner.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index afaa1dde..a3b059ce 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -1,7 +1,6 @@ import os import re import subprocess -import threading import platform from server.Classes.Ports import Ports From c3dbe62365944deee94179647c3a6c141d37ccdf Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:08:09 -0500 Subject: [PATCH 104/194] feat: added all the things MK3 needed to function, now that its working. --- .../Fabricators/Printers/Prusa/PrusaMK3.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index e711ee63..f121a8ef 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -1,16 +1,44 @@ +from serial.tools.list_ports_common import ListPortInfo +from serial.tools.list_ports_linux import SysFS + from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter +from Classes.Vector3 import Vector3 +from Mixins.hasResponseCodes import alwaysTrue, checkOK + class PrusaMK3(PrusaPrinter): MODEL = "Prusa MK3" PRODUCTID = 0x0002 DESCRIPTION = "Original Prusa MK3 - CDC" + MAXFEEDRATE = 12000 + homePosition = Vector3(0.2, -3.78, 0.15) + cancelCMD = b"M603\n" + + def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): + super().__init__(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.callablesHashtable["G28"] = [checkOK, checkOK] def endSequence(self): self.sendGcode(b"M104 S0\n") # turn off extruder self.sendGcode(b"M140 S0\n") # turn off heatbed self.sendGcode(b"M107\n") # turn off fan - self.sendGcode(b"G1 X0 Y210\n") # home X axis and push Y forward + self.sendGcode(b"G1 X0 Y210 F36000\n") # home X axis and push Y forward self.sendGcode(b"M84\n") # disable motors def getPrintTime(self): - pass \ No newline at end of file + pass + + def connect(self): + try: + import serial + self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) + self.serialConnection.reset_input_buffer() + from time import sleep + sleep(7) + if self.serialConnection and self.serialConnection.is_open: + self.serialConnection.write(b"M155 S1\n") + return True + except Exception as e: + from app import app + with app.app_context(): + return app.handle_error_and_logging(e, self) \ No newline at end of file From fbc8b9028122eeb2c9dc30ca339239d528c29013 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:10:27 -0500 Subject: [PATCH 105/194] fix: changed model to one word for easier parsing --- server/Classes/Fabricators/Printers/Ender/Ender3.py | 2 +- server/Classes/Fabricators/Printers/Ender/Ender3Pro.py | 2 +- server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py | 2 +- server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py | 2 +- server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3.py b/server/Classes/Fabricators/Printers/Ender/Ender3.py index 65444390..2acfeb39 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3.py @@ -1,7 +1,7 @@ from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter class Ender3(EnderPrinter): - MODEL = "Ender 3" + MODEL = "Ender3" PRODUCTID = 0x7523 DESCRIPTION = "Ender 3 - CDC" MAXFEEDRATE = 12000 diff --git a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py index a34fcca5..c1de9325 100644 --- a/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py +++ b/server/Classes/Fabricators/Printers/Ender/Ender3Pro.py @@ -1,5 +1,5 @@ from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 class Ender3Pro(Ender3): - MODEL = "Ender 3 Pro" + MODEL = "Ender3Pro" DESCRIPTION = "Ender 3 Pro - CDC" \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index f121a8ef..9d27dedc 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -7,7 +7,7 @@ class PrusaMK3(PrusaPrinter): - MODEL = "Prusa MK3" + MODEL = "MK3" PRODUCTID = 0x0002 DESCRIPTION = "Original Prusa MK3 - CDC" MAXFEEDRATE = 12000 diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index bf78e4b5..80cf82e8 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -2,7 +2,7 @@ from Classes.Vector3 import Vector3 class PrusaMK4(PrusaPrinter): - MODEL = "Prusa MK4" + MODEL = "MK4" PRODUCTID = 0x000D DESCRIPTION = "Original Prusa MK4 - CDC" MAXFEEDRATE = 36000 diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py index 06624b4a..d4ac894e 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4S.py @@ -1,7 +1,7 @@ from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 class PrusaMK4S(PrusaMK4): - MODEL = "Prusa MK4S" + MODEL = "MK4S" PRODUCTID = 0x001A DESCRIPTION = "Original Prusa MK4S - CDC" MAXFEEDRATE = 36000 From c5b1f62dfac0e40e4c50b3a1e8ae0015e0a0b8af Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:20:05 -0500 Subject: [PATCH 106/194] feat: added verbosity to begin, so logging is easier. --- server/Classes/Fabricators/Fabricator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 593da1cb..f4025ac7 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -122,7 +122,7 @@ def addToDB(self): db.session.add(self) db.session.commit() - def begin(self): + def begin(self, isVerbose: bool = False): """starts the fabrication process""" try: assert self.status == "idle" @@ -135,7 +135,7 @@ def begin(self): # if isinstance(self.device, hasStartupSequence): # self.device.startupSequence() assert self.setStatus("printing"), "Failed to set status to printing" - assert self.device.parseGcode(self.job.file_name_original), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. + assert self.device.parseGcode(self.job.file_name_original, isVerbose=isVerbose), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. self.handleVerdict() return True except Exception as e: From b799be3960332c01436342fecaaa85efa33ca900 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 19:23:30 -0500 Subject: [PATCH 107/194] feat: optimized code and imports for gcode --- server/Mixins/gcode/usesMarlinGcode.py | 48 ++++++++++++------------- server/Mixins/gcode/usesPrusaGcode.py | 24 ++++++------- server/Mixins/gcode/usesVanillaGcode.py | 6 ++-- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/server/Mixins/gcode/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py index 0e1ef762..3261122c 100644 --- a/server/Mixins/gcode/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -12,14 +12,14 @@ import re class LocationResponse: def __init__(self, response: str): - loc = [item for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item and item.strip()] + loc = [item.strip() for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item] self.x = float(loc[0]) self.y = float(loc[1]) self.z = float(loc[2]) self.e = float(loc[3]) - self.count_x = int(loc[4]) - self.count_y = int(loc[5]) - self.count_z = int(loc[6]) + self.count_x = float(loc[4]) if '.' in loc[4] else int(loc[4]) + self.count_y = float(loc[5]) if '.' in loc[5] else int(loc[5]) + self.count_z = float(loc[6]) if '.' in loc[6] else int(loc[6]) class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=ABCMeta): @@ -33,12 +33,12 @@ class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=AB getMachineNameCMD: Buffer = b"M997\n" callablesHashtable = { - "M104": [alwaysTrue, alwaysTrue], # Set hotend temp - "M109": [alwaysTrue, checkExtruderTemp], # Wait for hotend to reach target temp - "M114": [alwaysTrue, checkXYZ], # Get current position - "M140": [alwaysTrue, alwaysTrue], # Set bed temp - "M155": [alwaysTrue, alwaysTrue], # Set temperature auto report - "M190": [alwaysTrue, checkBedTemp], # Wait for bed to reach target temp + "M104": [], # Set hotend temp + "M109": [checkExtruderTemp], # Wait for hotend to reach target temp + "M114": [checkXYZ], # Get current position + "M140": [], # Set bed temp + "M155": [], # Set temperature auto report + "M190": [checkBedTemp], # Wait for bed to reach target temp } callablesHashtable = {**usesVanillaGcode.callablesHashtable, **callablesHashtable} @@ -47,7 +47,7 @@ def goTo(self: Device, loc: Vector3, isVerbose: bool = False): assert isinstance(loc, Vector3) assert isinstance(isVerbose, bool) assert isinstance(self, Device) - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{int(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{str(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) return loc == self.getToolHeadLocation() @@ -101,20 +101,18 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] if hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") - callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) - if callables[0] != alwaysTrue: - line = "" - while not callables[0](line): - line = self.serialConnection.readline() - if isVerbose: self.logger.debug(line) - if callables[1] != alwaysTrue: + elif hashIndex == "G28": + self.logger.info("Homing...") + callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [checkOK]) + for func in callables: while True: + if self.status == "cancelled": return True try: line = self.serialConnection.readline() if "processing" in line.decode("utf-8"): continue if isVerbose: self.logger.debug(line) - if callables[1](line): + if func(line): self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) return True except UnicodeDecodeError as e: @@ -139,14 +137,12 @@ def home(self, isVerbose: bool = False): assert isinstance(isVerbose, bool) assert isinstance(self, Device) self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) - return self.getHomePosition() == self.getToolHeadLocation() + assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" + return True except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error homing:") - self.logger.error(e) - return + from app import app + with app.app_context(): + return app.handle_error_and_logging(e, self) def pause(self): diff --git a/server/Mixins/gcode/usesPrusaGcode.py b/server/Mixins/gcode/usesPrusaGcode.py index 539a87af..fe876bd5 100644 --- a/server/Mixins/gcode/usesPrusaGcode.py +++ b/server/Mixins/gcode/usesPrusaGcode.py @@ -2,13 +2,14 @@ from typing_extensions import Buffer from Classes.Fabricators.Device import Device from Mixins.gcode.usesMarlinGcode import usesMarlinGcode -from Mixins.hasResponseCodes import alwaysTrue, checkOK +from Mixins.hasResponseCodes import checkOK, checkXYZ class usesPrusaGcode(usesMarlinGcode, metaclass=ABCMeta): callablesHashtable = { - "G29 P1": [alwaysTrue, checkOK], # Auto bed leveling + "G28": [checkOK, checkXYZ, checkOK], # Home + "G29 P1": [checkOK], # Auto bed leveling "G29 P9": [checkOK, checkOK], # Auto bed leveling - "M73": [alwaysTrue, checkOK], # Set build percentage + "M73": [checkOK], # Set build percentage } callablesHashtable = {**usesMarlinGcode.callablesHashtable, **callablesHashtable} @@ -26,22 +27,19 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): hashIndex += " P1" elif hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") + elif hashIndex == "G28": + self.logger.info("Homing...") assert isinstance(self, usesPrusaGcode) - callables = self.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [alwaysTrue, checkOK]) + callables = self.callablesHashtable.get(hashIndex, [checkOK]) assert isinstance(self, Device) - if callables[0] != alwaysTrue: - line = "" - while not callables[0](line): - line = self.serialConnection.readline() - if isVerbose: self.logger.debug(line) - if callables[1] != alwaysTrue: + for func in callables: while True: + if self.status == "cancelled": return True try: line = self.serialConnection.readline() - if "processing" in line.decode("utf-8"): - continue + if "processing" in line.decode("utf-8"): continue if isVerbose: self.logger.debug(line) - if callables[1](line): + if func(line): self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) return True except UnicodeDecodeError as e: diff --git a/server/Mixins/gcode/usesVanillaGcode.py b/server/Mixins/gcode/usesVanillaGcode.py index d53f1d9e..6f22ac2f 100644 --- a/server/Mixins/gcode/usesVanillaGcode.py +++ b/server/Mixins/gcode/usesVanillaGcode.py @@ -4,21 +4,21 @@ from Classes.Fabricators.Device import Device from Classes.Vector3 import Vector3 -from Mixins.hasResponseCodes import alwaysTrue, checkOK, checkXYZ, checkExtruderTemp, checkBedTemp +from Mixins.hasResponseCodes import checkXYZ class usesVanillaGcode: homeCMD: Buffer = b"G28\n" callablesHashtable = { - "G28": [alwaysTrue, checkXYZ], # Home + "G28": [checkXYZ], # Home } def goTo(self: Device, loc: Vector3, isVerbose: bool = False): assert isinstance(loc, Vector3) assert isinstance(isVerbose, bool) assert isinstance(self, Device) - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F36000\n".encode("utf-8"), isVerbose=isVerbose) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{str(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) def home(self, isVerbose: bool = False): try: From ba740c315562b2823b0987774d6f41bffae77629 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Wed, 13 Nov 2024 20:08:43 -0500 Subject: [PATCH 108/194] feat: added new logging options --- Tests/parallel_test_runner.py | 6 +- Tests/test_device.py | 3 + Tests/test_fabricator.py | 118 ++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 58 deletions(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index a3b059ce..50208c65 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -31,15 +31,15 @@ # Function to run pytest for a specific port testLevel = 1 verbosity = 2 -runFlags = 0b00 # 0b01: -s, 0b10: -vvv or -p no:terminal +runFlags = 0b000 # 0b001: -s, 0b010: -vvv or -p no:terminal, 0b100: debug or info def run_tests_for_port(comm_port): env = os.environ.copy() env["PORT"] = comm_port args = ["pytest", ".", f"--myVerbose={verbosity}", f"--port={comm_port}"] if runFlags & 0b1: args.append("-s") - if runFlags & 0b10: args.append("-vvv") - else: args.append("-p no:terminal") + args.append("-vvv") if runFlags & 0b10 else args.append("-p no:terminal") + env["LEVEL"] = "DEBUG" if runFlags & 0b100 else "INFO" subprocess.Popen(args, env=env).wait() if __name__ == "__main__": diff --git a/Tests/test_device.py b/Tests/test_device.py index 48d9dfc4..33868b99 100644 --- a/Tests/test_device.py +++ b/Tests/test_device.py @@ -8,6 +8,9 @@ def device_setup(port): if not port: return None + dev = Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) + if os["LEVEL"] == "DEBUG": + dev.logger.setLevel(dev.logger.DEBUG) return Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 8009343c..112bc35f 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -5,6 +5,7 @@ import pytest from Classes.Jobs import Job from Classes.Fabricators.Fabricator import Fabricator +from Classes.Logger import Logger from parallel_test_runner import testLevel testLevelToRun = testLevel @@ -16,23 +17,17 @@ def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" def cali_cube_setup(): file = "../server/xyz-cali-cube" - from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 - from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 - from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S - if shortTest: - file = file + "-mini" - if isinstance(fabricator.device, Ender3): - file = file + "_ENDER3.gcode" - elif isinstance(fabricator.device, PrusaMK4S): - file = file + "_MK4S.gcode" - elif isinstance(fabricator.device, PrusaMK4): - file = file + "_MK4.gcode" + if shortTest: file = file + "-mini" + file = file + f"_{fabricator.device.MODEL}.gcode" return file def fabricator_setup(port): if not port: return None from Classes.Ports import Ports - return Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) + fab = Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) + if os.getenv("LEVEL") == "DEBUG": + fab.device.logger.setLevel(fab.device.logger.DEBUG) + return fab @pytest.fixture(scope="module", autouse=True) def function_setup(request): @@ -71,37 +66,50 @@ def test_pause_and_resume(): pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") def parse_gcode(): - file = "../server/pauseAndResumeTest.gcode" - from Classes.Fabricators.Fabricator import getFileConfig - config = getFileConfig(file) - from Classes.Fabricators.Printers.Printer import Printer - if isinstance(fabricator.device, Printer): - assert config["filament_type"] is not None, "Failed to get filament_type from {file}" - assert config["filament_diameter"] is not None, f"Failed to get filament_diameter from {file}" - assert config["nozzle_diameter"] is not None, f"Failed to get nozzle_diameter from {file}" - fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) - fabricator.device.changeNozzle(float(config["nozzle_diameter"])) - with open(file, "r") as f: - job = Job(f.read(), "pauseAndResumeTest", fabricator.dbID, "ready", file, False, 1, fabricator.name) - fabricator.queue.addToFront(job) - result = fabricator.begin() - import traceback - assert not isinstance(result, Exception), f"Failed to begin on {fabricator.getDescription()}: {result}:\n{''.join(traceback.format_exception(None, result, result.__traceback__))}" - assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" + try: + file = "../server/pauseAndResumeTest.gcode" + from Classes.Fabricators.Fabricator import getFileConfig + config = getFileConfig(file) + from Classes.Fabricators.Printers.Printer import Printer + if isinstance(fabricator.device, Printer): + assert config["filament_type"] is not None, "Failed to get filament_type from {file}" + assert config["filament_diameter"] is not None, f"Failed to get filament_diameter from {file}" + assert config["nozzle_diameter"] is not None, f"Failed to get nozzle_diameter from {file}" + fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) + fabricator.device.changeNozzle(float(config["nozzle_diameter"])) + with open(file, "r") as f: + job = Job(f.read(), "pauseAndResumeTest", fabricator.dbID, "ready", file, False, 1, fabricator.name) + fabricator.queue.addToFront(job) + print("Beginning") + result = fabricator.begin() + print("Finished") + import traceback + assert not isinstance(result, Exception), f"Failed to begin on {fabricator.getDescription()}: {result}:\n{''.join(traceback.format_exception(None, result, result.__traceback__))}" + assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.job is None, f"Failed to complete on {fabricator.getDescription()}, expected job to be None, got {fabricator.job}" + except Exception as f: + fabricator.setStatus("error") + raise f def pause_and_resume_fabricator(): - from time import sleep - while fabricator.getStatus() != "printing": - assert fabricator.getStatus() != "error", f"Failed to print on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + try: + from time import sleep + while fabricator.getStatus() != "printing": + assert fabricator.getStatus() != "error", f"Failed to print on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(1) + print("Pausing") + assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(30) + print("Resuming") + assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(1) - assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(30) - assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(1) - assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(10) + print("Cancelling") + assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" + sleep(10) + except Exception as f: + fabricator.setStatus("error") + raise f fabricator.resetToIdle() assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" @@ -113,13 +121,10 @@ def pause_and_resume_fabricator(): pause_future = executor.submit(pause_and_resume_fabricator) for future in as_completed([parse_future, pause_future]): - try: - future.result() - except Exception as e: - raise e + future.result() -@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") -@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +#@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") +#@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") def test_gcode_print_time(): from Classes.Fabricators.Printers.Printer import Printer if not isinstance(fabricator.device, Printer): @@ -128,20 +133,21 @@ def test_gcode_print_time(): # expectedTime = 2040 # for my personal home test, 1072 from Classes.Fabricators.Fabricator import getFileConfig config = getFileConfig(file) - expectedTime = int(config["estimated_time"]) + expectedTime = int(config["expected_time"]) fabricator.device.changeFilament(config["filament_type"], float(config["filament_diameter"])) fabricator.device.changeNozzle(float(config["nozzle_diameter"])) expectedDays, expectedHours, expectedMinutes, expectedSeconds = 0, 0, 0, 0 - if expectedTime >= 86400: - expectedDays, expectedTime = divmod(expectedTime, 86400) - if expectedTime >= 3600: - expectedHours, expectedTime = divmod(expectedTime, 3600) if expectedTime >= 60: - expectedMinutes, expectedTime = divmod(expectedTime, 60) + expectedMinutes, expectedSeconds = divmod(expectedTime, 60) + if expectedMinutes >= 60: + expectedHours, expectedMinutes = divmod(expectedMinutes, 60) + if expectedHours >= 24: + expectedDays, expectedHours = divmod(expectedHours, 24) + with open(file, "r") as f: fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)) time = datetime.now() - fabricator.begin() + fabricator.begin(isVerbose=True) time = datetime.now() - time fabricator.device.serialConnection.write(b"M31\n") line = "" @@ -212,5 +218,7 @@ def test_gcode_print_time(): expectedString = ":".join(map(str, expectedList)) timeBoundary = 120 - assert printTime - expectedTime < timeBoundary, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {printString}" - assert time.seconds - expectedTime < timeBoundary, f"Failed to print within time boundary of measured time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {measuredTimeString}" \ No newline at end of file + expectedTimeBool = (printTime - expectedTime) < timeBoundary + measuredTimeBool = (time.seconds - expectedTime) < timeBoundary + assert expectedTimeBool, f"Failed to print within time boundary of expected time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {printString}" + assert measuredTimeBool, f"Failed to print within time boundary of measured time on {fabricator.getDescription()}. Expected: {expectedString}, Actual: {measuredTimeString}" \ No newline at end of file From eae61639f47cd8abf1f7a672c9f435f9d02428b4 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 14 Nov 2024 11:51:42 -0500 Subject: [PATCH 109/194] feat: add the ability to add attributes and data to the printer from json --- printeremu/data/printers.json | 20 ++++++++++++++++++-- printeremu/src/emulator.go | 6 ++++++ printeremu/src/printer.go | 18 ++++++++++++++---- printeremu/src/printerregistry.go | 20 ++++++++++++++------ 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/printeremu/data/printers.json b/printeremu/data/printers.json index e70e24e8..b8ef1e1c 100644 --- a/printeremu/data/printers.json +++ b/printeremu/data/printers.json @@ -6,7 +6,15 @@ "printerName": "Prusa", "printerModel": "MK4" }, - "usesMarlin": true + "data": { + "length": 250, + "width": 210, + "height": 220, + "startTemp": 0 + }, + "attributes": { + "marlin": true + } }, { "id": 2, @@ -15,7 +23,15 @@ "printerName": "Creality", "printerModel": "Ender 3" }, - "usesMarlin": true + "data": { + "length": 220, + "width": 220, + "height": 250, + "startTemp": 0 + }, + "attributes": { + "marlin": true + } } ] \ No newline at end of file diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 97cb4c8d..52e67c1a 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -51,6 +51,12 @@ func Init(id int, device string, description string, hwid string, name string, s return extruder, printer, nil } +func PostRegistry(printer *Printer) { + printer.Heatbed.Length = printer.GetData("length").(float64) + printer.Heatbed.Width = printer.GetData("width").(float64) + printer.Extruder.MaxZHeight = printer.GetData("height").(float64) +} + func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, settings *EmulatorSettings) { loadedAddress := func() string { if settings.DefaultAddress != "" && settings.DefaultPort != 0 { diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 960ffa41..a20ad134 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -27,7 +27,8 @@ type Printer struct { LinearAdvanceFactor float64 HeatbreakTemp float64 EnabledMotors map[string]bool - Attributes map[string]string + Attributes map[string]interface{} + Data map[string]interface{} WSConnection *websocket.Conn } @@ -90,7 +91,8 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex LinearAdvanceFactor: 0.0, // Default linear advance factor HeatbreakTemp: 0.0, // Default heatbreak temp EnabledMotors: make(map[string]bool), - Attributes: make(map[string]string), + Attributes: make(map[string]interface{}), + Data: make(map[string]interface{}), WSConnection: nil, // Default will be offline emulator } @@ -208,14 +210,22 @@ func (printer *Printer) MoveExtruder(targetPos Vector3) error { return nil } -func (printer *Printer) AddAttribute(key string, value string) { +func (printer *Printer) AddAttribute(key string, value interface{}) { printer.Attributes[key] = value } -func (printer *Printer) GetAttribute(key string) string { +func (printer *Printer) GetAttribute(key string) interface{} { return printer.Attributes[key] } +func (printer *Printer) AddData(key string, value interface{}) { + printer.Data[key] = value +} + +func (printer *Printer) GetData(key string) interface{} { + return printer.Data[key] +} + func (printer *Printer) WriteSerial(event string, data interface{}) error { if printer.WSConnection == nil { return fmt.Errorf("WebSocket connection not established") diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index 519db238..8b82fede 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -11,10 +11,11 @@ import ( ) type PrinterConfig struct { - Id int `json:"id"` - Name string `json:"name"` - Brand PrinterBrand `json:"brand"` - UsesMarlin bool `json:"usesMarlin"` + Id int `json:"id"` + Name string `json:"name"` + Brand PrinterBrand `json:"brand"` + Data map[string]interface{} `json:"data"` + Attributes map[string]interface{} `json:"attributes"` } type PrinterBrand struct { @@ -50,16 +51,23 @@ func LoadPrinters(filePath string) ([]Printer, error) { for _, printerConfig := range printersConfig { _, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", "EMU-"+RandomString(8), printerConfig.Name, "Init") + // always add the model as an attribute so we always know what the model is printer.AddAttribute("model", printerConfig.Brand.PrinterName) - if printerConfig.UsesMarlin { - printer.AddAttribute("usesMarlin", "true") + for key, value := range printerConfig.Attributes { + printer.AddAttribute(key, value) + } + + for key, value := range printerConfig.Data { + printer.AddData(key, value) } if err != nil { return nil, fmt.Errorf("failed to initialize printer %d: %v", printerConfig.Id, err) } + PostRegistry(printer) + printers = append(printers, *printer) } From cb054258cb19fa4f6d8576da0a0081a91d0fa903 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 14 Nov 2024 11:55:40 -0500 Subject: [PATCH 110/194] fix: add start temp --- printeremu/src/emulator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 52e67c1a..68e49188 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -55,6 +55,7 @@ func PostRegistry(printer *Printer) { printer.Heatbed.Length = printer.GetData("length").(float64) printer.Heatbed.Width = printer.GetData("width").(float64) printer.Extruder.MaxZHeight = printer.GetData("height").(float64) + printer.Heatbed.Temp = printer.GetData("startTemp").(float64) } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, settings *EmulatorSettings) { From 48bc7dfd3ac4f48fffb714a6e7d48de19a215d78 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 14 Nov 2024 15:31:41 -0500 Subject: [PATCH 111/194] fix: undo the deletion? --- server/Classes/Ports.py | 172 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index d66c1861..d09f0d58 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -2,6 +2,11 @@ import serial.tools.list_ports from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS +from flask import Blueprint, jsonify, request +from sqlalchemy.exc import SQLAlchemyError +from Classes.Fabricators.Fabricator import Fabricator +from Classes.Fabricators.Device import Device +from Classes.serialCommunication import sendGcode class Ports: @staticmethod @@ -27,4 +32,169 @@ def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: for port in ports: if hwid in port.hwid: return port - return None \ No newline at end of file + return None + + @staticmethod + def getRegisteredFabricators() -> list[Fabricator]: + """Get a list of all registered fabricators.""" + fabricators = Fabricator.queryAll() # Assuming Fabricator has a method to query all instances + registered_fabricators = [] + for fab in fabricators: + port = Ports.getPortByName(fab.devicePort) + if port: + registered_fabricators.append(fab) + return registered_fabricators + + @staticmethod + def diagnosePort(port: ListPortInfo | SysFS) -> str: + """Diagnose a port to check if it is functional by sending basic G-code commands.""" + try: + device = Fabricator.createDevice(port) # Create a Device instance using Fabricator logic + if not device: + return "Device creation failed." + + device.connect() + sendGcode("M115") # Standard G-code command to get firmware information + response = device.getSerialConnection().readline().decode("utf-8").strip() + device.disconnect() + + return f"Diagnosis result for {port.device}: {response}" + except Exception as e: + return f"Error diagnosing port {port.device}: {e}" + +# Blueprint for ports routes +ports_bp = Blueprint("ports", __name__) + +@ports_bp.route("/getports", methods=["GET"]) +def getPorts(): + """Get a list of all connected ports.""" + try: + ports = Ports.getPorts() + return jsonify([port.device for port in ports]) + except Exception as e: + print(f"Error getting ports: {e}") + return jsonify({"error": "Failed to retrieve ports"}), 500 + +@ports_bp.route("/getfabricators", methods=["GET"]) +def getRegisteredFabricators(): + """Get a list of all registered fabricators.""" + try: + fabricators = Ports.getRegisteredFabricators() + return jsonify([{ + "name": fab.name, + "description": fab.description, + "hwid": fab.hwid, + "devicePort": fab.devicePort, + "status": fab.getStatus() + } for fab in fabricators]) + except Exception as e: + print(f"Error getting registered fabricators: {e}") + return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 + +@ports_bp.route("/register", methods=["POST"]) +def registerFabricator(): + """Register a new fabricator with the system.""" + try: + data = request.get_json() + device = data['fabricator']['device'] + description = data['fabricator']['description'] + hwid = data['fabricator']['hwid'] + name = data['fabricator']['name'] + + # Create a new fabricator instance using the Fabricator class + new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) + new_fabricator.description = description + new_fabricator.hwid = hwid + + return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) + except SQLAlchemyError as db_err: + print(f"Database error during registration: {db_err}") + return jsonify({"error": "Database error occurred"}), 500 + except Exception as e: + print(f"Error registering fabricator: {e}") + return jsonify({"error": "Failed to register fabricator"}), 500 + +@ports_bp.route("/deletefabricator", methods=["POST"]) +def deleteFabricator(): + """Delete a fabricator from the system.""" + try: + data = request.get_json() + fabricator_id = data['fabricator_id'] + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + + if fabricator: + Fabricator.query.filter_by(dbID=fabricator_id).delete() + Fabricator.addToDB() + return jsonify({"success": True, "message": "Fabricator deleted successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 + except Exception as e: + print(f"Error deleting fabricator: {e}") + return jsonify({"error": "Failed to delete fabricator"}), 500 + +@ports_bp.route("/editname", methods=["POST"]) +def editName(): + """Edit the name of a registered fabricator.""" + try: + data = request.get_json() + fabricator_id = data['fabricator_id'] + new_name = data['name'] + + fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() + if fabricator: + fabricator.setName(new_name) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 + except Exception as e: + print(f"Error editing fabricator name: {e}") + return jsonify({"error": "Failed to edit fabricator name"}), 500 + +@ports_bp.route("/diagnose", methods=["POST"]) +def diagnoseFabricator(): + """Diagnose a fabricator based on its port.""" + try: + data = request.get_json() + device_name = data['device'] + port = Ports.getPortByName(device_name) + + if port: + diagnosis_result = Ports.diagnosePort(port) + return jsonify({"success": True, "message": diagnosis_result}) + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error diagnosing fabricator: {e}") + return jsonify({"error": "Failed to diagnose fabricator"}), 500 + +@ports_bp.route("/movehead", methods=["POST"]) +def moveHead(): + """Move the head of a fabricator. Deprecated??????""" + try: + data = request.get_json() + device_name = data['port'] + port = Ports.getPortByName(device_name) + + if port: + fabricator = Fabricator(port) + result = fabricator.device.home() # Ensuring `home()` method from Device is used + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + else: + return jsonify({"error": "Device not found"}), 404 + except Exception as e: + print(f"Error moving head: {e}") + return jsonify({"error": "Failed to move fabricator head"}), 500 + +@ports_bp.route("/movefabricatorlist", methods=["POST"]) +def moveFabricatorList(): + """Change the order of fabricators.""" + try: + from app import printer_status_service # Ensure integration aligns with the new naming convention + data = request.get_json() + fabricator_ids = data['fabricator_ids'] + + result = printer_status_service.moveFabricatorList(fabricator_ids) + return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) + except Exception as e: + print(f"Error moving fabricator list: {e}") + return jsonify({"error": "Failed to move fabricator list"}), 500 From 546aec367ddca5e1baa66805865d339acca7a9b1 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:21:13 -0500 Subject: [PATCH 112/194] fix: log captured output --- Tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 6ee29b36..ce172251 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -101,8 +101,8 @@ def pytest_sessionfinish(session, exitstatus) -> None: captured = capture_manager.read_global_and_disable() # Print the captured stdout and stderr - print("\nCaptured output during tests:\n") - print(captured) + logger.logMessageOnly("\nCaptured output during tests:\n") + logger.logMessageOnly(captured) # Re-enable capture if needed for further use capture_manager.resume_global_capture() From 2f2e8a934b0f37cff0e33f6e66e34ba5252f05ba Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:21:50 -0500 Subject: [PATCH 113/194] feat: added verbose debug logging --- server/Classes/Fabricators/Fabricator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index f4025ac7..9e548f5a 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -136,7 +136,9 @@ def begin(self, isVerbose: bool = False): # self.device.startupSequence() assert self.setStatus("printing"), "Failed to set status to printing" assert self.device.parseGcode(self.job.file_name_original, isVerbose=isVerbose), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. + if isVerbose: self.device.logger.debug(f"Job complete, verdict: {self.device.verdict}") self.handleVerdict() + if isVerbose: self.device.logger.debug(f"Verdict handled, status: {self.status}") return True except Exception as e: from app import app From df5c0c287c5b4aff604453615207dc0c87133229 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:26:12 -0500 Subject: [PATCH 114/194] feat: optimized response codes and added tracing back everything. --- server/Mixins/hasResponseCodes.py | 46 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 406a72e4..23e85908 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -1,3 +1,5 @@ +import re +import traceback from abc import ABCMeta, abstractmethod from Classes.Vector3 import Vector3 @@ -13,37 +15,47 @@ def getToolHeadLocation(self) -> Vector3: pass def checkOK(line): - return line == b'ok\n' + line = (line.decode() if isinstance(line, bytes) else line).strip() + return line == "ok" def checkXYZ(line): - line = line.decode("utf-8") + line = (line.decode() if isinstance(line, bytes) else line).strip() return ("X:" in line) and ("Y:" in line) and ("Z:" in line) -def alwaysTrue(line): - return True - -def anyResponse(line): - return line != b'' - def checkEcho(line): - return line.decode("utf-8").startswith("echo") + line = (line.decode() if isinstance(line, bytes) else line).strip() + return line.startswith("echo") def checkBedTemp(line): + line = (line.decode() if isinstance(line, bytes) else line).strip() try: - temps = line.decode("utf-8").split("B:")[1].split("X:")[0].split("/") - if len(temps) == 2: - if float(temps[1]) == 0.0: return True - return float(temps[1]) - float(temps[0]) < 0.25 - return True + return checkTemp([temp.strip() for temp in line.split("B:")[1].split("T0:")[0].split("X:")[0].split("/")]) + except IndexError as e: + return False except Exception as e: + traceback.print_exc() return False def checkExtruderTemp(line): + line = (line.decode() if isinstance(line, bytes) else line).strip() + try: + return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")]) + except IndexError as e: + return False + except Exception as e: + traceback.print_exc() + return False + +def checkTemp(temps): try: - temps = line.decode("utf-8").split("T:")[1].split("B:")[0].split("/") if len(temps) == 2: if float(temps[1]) == 0.0: return True return float(temps[1]) - float(temps[0]) < 0.25 - return True + return False except Exception as e: - return False \ No newline at end of file + traceback.print_exc() + return False + +def checkTime(line): + line = (line.decode() if isinstance(line, bytes) else line).strip() + return re.search(r"\d+m \d+s", line) or re.search(r"\d+ min, \d+ sec", line) \ No newline at end of file From 3d5c6b6384c526624f3753bcc2dc02aff3b7412f Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:27:25 -0500 Subject: [PATCH 115/194] fix: overrode set level --- server/Classes/Logger.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 082fd28f..ac927782 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -20,7 +20,7 @@ def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, l if deviceName: title.append(deviceName) super().__init__(f"_".join(["Logger"] + title)) - self.setLevel(loggingLevel) + super().setLevel(loggingLevel) info = [] if showDate: info.append("%(asctime)s") @@ -150,6 +150,11 @@ def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRep else: self.logMessageOnly(line) + def setLevel(self, level): + super().setLevel(level) + self.consoleLogger.setLevel(level) + self.fileLogger.setLevel(level) + class CustomFormatter(logging.Formatter): # ANSI escape codes for colors COLOR_CODES = { From 0a9a13fb6328b6f0c6e100ae2bcd4577db4523e8 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:28:33 -0500 Subject: [PATCH 116/194] feat: updated to run all tests --- Tests/parallel_test_runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 50208c65..8d5fa00e 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -2,7 +2,6 @@ import re import subprocess import platform - from server.Classes.Ports import Ports PORTS = [] @@ -29,7 +28,7 @@ PORTS = glob.glob("/dev/tty[A-Za-z]*") # Function to run pytest for a specific port -testLevel = 1 +testLevel = 10 verbosity = 2 runFlags = 0b000 # 0b001: -s, 0b010: -vvv or -p no:terminal, 0b100: debug or info From 1ee9f8f880077e8e3801b13a323b8d35ce193b11 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:29:38 -0500 Subject: [PATCH 117/194] feat: fixed update loop. --- server/Classes/Ports.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index d09f0d58..9d2adbc5 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -5,7 +5,6 @@ from flask import Blueprint, jsonify, request from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Fabricator import Fabricator -from Classes.Fabricators.Device import Device from Classes.serialCommunication import sendGcode class Ports: @@ -124,7 +123,7 @@ def deleteFabricator(): if fabricator: Fabricator.query.filter_by(dbID=fabricator_id).delete() - Fabricator.addToDB() + Fabricator.updateDB() return jsonify({"success": True, "message": "Fabricator deleted successfully"}) else: return jsonify({"error": "Fabricator not found"}), 404 From 23661deb4bf3e8815ea5befd3f699981c9638775 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:30:28 -0500 Subject: [PATCH 118/194] fix: optimized class --- .../Fabricators/Printers/Prusa/PrusaMK3.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index 9d27dedc..bc62b194 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -3,7 +3,7 @@ from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 -from Mixins.hasResponseCodes import alwaysTrue, checkOK +from Mixins.hasResponseCodes import checkOK, checkTime, checkXYZ class PrusaMK3(PrusaPrinter): @@ -13,10 +13,16 @@ class PrusaMK3(PrusaPrinter): MAXFEEDRATE = 12000 homePosition = Vector3(0.2, -3.78, 0.15) cancelCMD = b"M603\n" + homeCMD = b"G28 W\n" - def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): - super().__init__(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) - self.callablesHashtable["G28"] = [checkOK, checkOK] + callablesHashtable = { + "M31": [checkTime, checkOK], # Print time + "G28": [checkOK], + "G29.02": [checkOK, checkOK], + "G29.01": [checkOK, checkXYZ, checkXYZ, checkOK], # Auto bed leveling + } + + callablesHashtable = {**PrusaPrinter.callablesHashtable, **callablesHashtable} def endSequence(self): self.sendGcode(b"M104 S0\n") # turn off extruder @@ -34,9 +40,9 @@ def connect(self): self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) self.serialConnection.reset_input_buffer() from time import sleep - sleep(7) + sleep(4) if self.serialConnection and self.serialConnection.is_open: - self.serialConnection.write(b"M155 S1\n") + self.sendGcode(b"M155 S1\n") return True except Exception as e: from app import app From 30596096d0e8daf04d2b6a4500d1d035dcee162d Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:32:20 -0500 Subject: [PATCH 119/194] fix: optimized class --- Tests/test_fabricator.py | 57 ++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 112bc35f..29c38823 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -5,12 +5,12 @@ import pytest from Classes.Jobs import Job from Classes.Fabricators.Fabricator import Fabricator -from Classes.Logger import Logger from parallel_test_runner import testLevel testLevelToRun = testLevel shortTest = True fabricator = Fabricator(None, "Test Printer", addToDB=False, consoleLogger=sys.stdout) +isVerbose = False def __desc__(): return "Fabricator Tests" def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" @@ -33,6 +33,8 @@ def fabricator_setup(port): def function_setup(request): global fabricator fabricator = fabricator_setup(request.session.config.port) + global isVerbose + isVerbose = fabricator.device.logger.level <= fabricator.device.logger.DEBUG if fabricator is None: pytest.skip("No port specified") yield @@ -126,6 +128,8 @@ def pause_and_resume_fabricator(): #@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") #@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") def test_gcode_print_time(): + print("Testing gcode print time") + print(fabricator.device.logger.level) from Classes.Fabricators.Printers.Printer import Printer if not isinstance(fabricator.device, Printer): pytest.skip(f"{fabricator.getDescription()} doesn't support printing gcode") @@ -143,35 +147,19 @@ def test_gcode_print_time(): expectedHours, expectedMinutes = divmod(expectedMinutes, 60) if expectedHours >= 24: expectedDays, expectedHours = divmod(expectedHours, 24) - + if isVerbose: fabricator.device.logger.debug(f"Expected print time: {expectedDays:02}:{expectedHours:02}:{expectedMinutes:02}:{expectedSeconds:02}") with open(file, "r") as f: fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)) time = datetime.now() - fabricator.begin(isVerbose=True) + fabricator.begin(isVerbose=isVerbose) time = datetime.now() - time + if isVerbose: fabricator.device.logger.debug(f"Actual print time: {time}") fabricator.device.serialConnection.write(b"M31\n") line = "" - while not re.search(r"\d+m \d+s", line): + from Mixins.hasResponseCodes import checkTime + while not checkTime(line): line = fabricator.device.serialConnection.readline().decode("utf-8") - minutes, seconds = divmod(time.seconds, 60) - hours, minutes = divmod(minutes, 60) - days, hours = divmod(hours, 24) - measuredTimeList = [] - if days > 0: - measuredTimeList.append(f"{days:02}") - measuredTimeList.append(f"{hours:02}") - measuredTimeList.append(f"{minutes:02}") - measuredTimeList.append(f"{seconds:02}") - elif hours > 0: - measuredTimeList.append(f"{hours:02}") - measuredTimeList.append(f"{minutes:02}") - measuredTimeList.append(f"{seconds:02}") - elif minutes > 0: - measuredTimeList.append(f"{minutes:02}") - measuredTimeList.append(f"{seconds:02}") - else: - measuredTimeList.append(f"{seconds:02}") - measuredTimeString = ":".join(measuredTimeList) + if isVerbose: fabricator.device.logger.debug(f"not stuck in print time loop: time: {line}") actualTimeList = re.findall(r"\d+", line) printDays, printHours, printMinutes, printSeconds = 0, 0, 0, 0 @@ -200,6 +188,29 @@ def test_gcode_print_time(): else: printList.append(f"{printSeconds:02}") printString = ":".join(map(str, printList)) + + + minutes, seconds = divmod(time.seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + measuredTimeList = [] + if days > 0: + measuredTimeList.append(f"{days:02}") + measuredTimeList.append(f"{hours:02}") + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + elif hours > 0: + measuredTimeList.append(f"{hours:02}") + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + elif minutes > 0: + measuredTimeList.append(f"{minutes:02}") + measuredTimeList.append(f"{seconds:02}") + else: + measuredTimeList.append(f"{seconds:02}") + measuredTimeString = ":".join(measuredTimeList) + + expectedList = [] if expectedDays > 0: expectedList.append(f"{expectedDays:02}") From 1812699d78c673518cad4935468bf1c9e2e8ad89 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:33:05 -0500 Subject: [PATCH 120/194] fix: bad line fixed --- Tests/test_device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_device.py b/Tests/test_device.py index 33868b99..b09d2a02 100644 --- a/Tests/test_device.py +++ b/Tests/test_device.py @@ -9,7 +9,7 @@ def device_setup(port): if not port: return None dev = Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) - if os["LEVEL"] == "DEBUG": + if os.environ["LEVEL"] == "DEBUG": dev.logger.setLevel(dev.logger.DEBUG) return Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) From 71334df20be9718d5b25a2ba52288caeb9077f38 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:35:00 -0500 Subject: [PATCH 121/194] fix: optimized gcode classes. --- server/Mixins/gcode/usesMarlinGcode.py | 6 +++-- server/Mixins/gcode/usesPrusaGcode.py | 33 +++++++++++++++----------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/server/Mixins/gcode/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py index 3261122c..e3fe9fb6 100644 --- a/server/Mixins/gcode/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -7,7 +7,7 @@ from Mixins.canPause import canPause from Mixins.gcode.usesVanillaGcode import usesVanillaGcode from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import checkOK, checkXYZ, alwaysTrue, checkBedTemp, checkExtruderTemp, hasResponsecodes +from Mixins.hasResponseCodes import checkOK, checkXYZ, checkBedTemp, checkExtruderTemp, hasResponsecodes, checkTime import re class LocationResponse: @@ -33,11 +33,11 @@ class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=AB getMachineNameCMD: Buffer = b"M997\n" callablesHashtable = { + "M31": [checkTime], # Print time "M104": [], # Set hotend temp "M109": [checkExtruderTemp], # Wait for hotend to reach target temp "M114": [checkXYZ], # Get current position "M140": [], # Set bed temp - "M155": [], # Set temperature auto report "M190": [checkBedTemp], # Wait for bed to reach target temp } @@ -80,6 +80,8 @@ def parseGcode(self: Device, file: str, isVerbose: bool = False): self.verdict = "cancelled" self.logger.info("Job cancelled") return True + if ";" in line: + line = line.split(";")[0].strip() + "\n" self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" self.logger.info("Job complete") diff --git a/server/Mixins/gcode/usesPrusaGcode.py b/server/Mixins/gcode/usesPrusaGcode.py index fe876bd5..e8c6ea9c 100644 --- a/server/Mixins/gcode/usesPrusaGcode.py +++ b/server/Mixins/gcode/usesPrusaGcode.py @@ -2,13 +2,14 @@ from typing_extensions import Buffer from Classes.Fabricators.Device import Device from Mixins.gcode.usesMarlinGcode import usesMarlinGcode -from Mixins.hasResponseCodes import checkOK, checkXYZ +from Mixins.hasResponseCodes import checkOK, checkXYZ, checkTime class usesPrusaGcode(usesMarlinGcode, metaclass=ABCMeta): callablesHashtable = { - "G28": [checkOK, checkXYZ, checkOK], # Home - "G29 P1": [checkOK], # Auto bed leveling - "G29 P9": [checkOK, checkOK], # Auto bed leveling + "G28": [checkXYZ, checkOK], # Home + "G29.01": [checkXYZ, checkOK], # Auto bed leveling + "G29.02": [checkOK], # Auto bed leveling + "M31": [checkOK, checkTime, checkOK], # Print time "M73": [checkOK], # Set build percentage } @@ -20,30 +21,34 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): self.serialConnection.write(gcode) hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] if hashIndex == "G29": - g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] - if g29addon == "P9": - hashIndex += " " + g29addon - else: - hashIndex += " P1" - elif hashIndex == "M109" or hashIndex == "M190": + try: + g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] + hashIndex += ".01" if g29addon == "P1" else ".02" + except IndexError as e: + hashIndex += ".01" + if hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") elif hashIndex == "G28": self.logger.info("Homing...") assert isinstance(self, usesPrusaGcode) callables = self.callablesHashtable.get(hashIndex, [checkOK]) assert isinstance(self, Device) + line='' for func in callables: while True: if self.status == "cancelled": return True try: line = self.serialConnection.readline() if "processing" in line.decode("utf-8"): continue - if isVerbose: self.logger.debug(line) + if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.decode().strip()}") if func(line): - self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) - return True + break except UnicodeDecodeError as e: + if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") continue except Exception as e: self.logger.error(e) - return False \ No newline at end of file + return False + if not callables: self.logger.info(f"{gcode.decode().strip()}: Always True") + else: self.logger.info(gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) + return True \ No newline at end of file From 51abd87b3bb7a86846fcf6ef7384f11388e290f6 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:35:43 -0500 Subject: [PATCH 122/194] feat: external tests to pytests. --- server/test.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/server/test.py b/server/test.py index ed0b32b5..d57d8a8b 100644 --- a/server/test.py +++ b/server/test.py @@ -5,7 +5,6 @@ from Classes.Jobs import Job from Classes.Vector3 import Vector3 from Classes.Ports import Ports -#from app import app red = '\033[31m' green = '\033[32m' @@ -69,7 +68,7 @@ def runTests(fabricator: Fabricator, isVerbose=False): time = datetime.now() file = "server/xyz-cali-cube-mini_MK4.gcode" with open(file, "r") as f: - fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name), fabricator.dbID) + fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)) fabricator.begin() time = datetime.now() - time minutes, seconds = divmod(time.seconds, 60) @@ -115,6 +114,7 @@ def runTests(fabricator: Fabricator, isVerbose=False): # with app.app_context(): PrusaMK4S = None PrusaMK4 = None +PrusaMK3 = None Ender3 = None MakerBot = None EnderPro = None @@ -126,37 +126,36 @@ def runTests(fabricator: Fabricator, isVerbose=False): # ari's MK4S if Ports.getPortByName("COM5") is not None: PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S", addToDB=False) - #runTests(PrusaMK4S) # nate's Ender 3 Pro if Ports.getPortByName("COM6") is not None: EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro", addToDB=False) - #runTests(EnderPro) # school prusa mk4 if Ports.getPortByName("COM3") is not None: PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4", addToDB=False) - runTests(PrusaMK4) + # PrusaMK4.device.logger.setLevel(PrusaMK4.device.logger.DEBUG) + # PrusaMK4.device.sendGcode(b'G29 A\n', isVerbose=True) # school Ender 3 if Ports.getPortByName("COM4") is not None: Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3", addToDB=False) - # runTests(Ender3) # Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) # school Makerbot if Ports.getPortByName("COM7") is not None: MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot", addToDB=False) - #runTests(MakerBot) -if PrusaMK4 is not None and PrusaMK4.device.serialConnection.is_open: - PrusaMK4.device.disconnect() +# school MK3 +if Ports.getPortByName("COM9") is not None: + PrusaMK3 = Fabricator(Ports.getPortByName("COM9"), "PrusaMK3", addToDB=False) + PrusaMK3.device.logger.setLevel(PrusaMK3.device.logger.DEBUG) + PrusaMK3.device.sendGcode(b'G28\n', isVerbose=True) -if Ender3 is not None and Ender3.device.serialConnection.is_open: - Ender3.device.disconnect() +# for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: +# if fab is not None: +# runTests(fab) -if MakerBot is not None and MakerBot.device.serialConnection.is_open: - MakerBot.device.disconnect() - -if EnderPro is not None and EnderPro.device.serialConnection.is_open: - EnderPro.device.disconnect() +for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: + if fab is not None and fab.device.serialConnection.is_open: + fab.device.disconnect() From a6c69fd820018ef7f5b51b438e45ed14e565a776 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:36:23 -0500 Subject: [PATCH 123/194] feat: disable socketIO until its integrated --- server/Classes/Queue.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 9ab1f8dc..926800c2 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,5 +1,5 @@ from collections import deque -from flask import jsonify, current_app +#from flask import jsonify, current_app class Queue: @@ -23,17 +23,20 @@ def addToBack(self, job, printerid): print("Adding job to back of queue ", printerid) if self.__queue.count(job) > 0: - raise Exception("Job ID already in queue.") + #raise Exception("Job ID already in queue.") + return False self.__queue.append( job ) # appending on the right is the "front" because popping takes out from right d - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - ) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + # ) + return True - def addToFront(self, job, printerid): + def addToFront(self, job): if self.__queue.count(job) > 0: - raise Exception("Job ID already in queue.") + #raise Exception("Job ID already in queue.") + return False # If the queue has at least one job and the first job is printing, # insert at the second position because we don't want to interrupt it. if len(self.__queue) >= 1 and self.__queue[0].status == "printing": @@ -42,9 +45,10 @@ def addToFront(self, job, printerid): # add the job to the front else: self.__queue.appendleft(job) - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - ) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + # ) + return True def bump(self, up, jobid): # up = boolean. if up = true bump up, else bump down index = next( @@ -81,10 +85,10 @@ def deleteJob(self, jobid, printerid): if job.getJobId() == jobid: deletedjob = job self.__queue.remove(job) - current_app.socketio.emit( - "queue_update", - {"queue": self.convertQueueToJson(), "printerid": printerid}, - ) + # current_app.socketio.emit( + # "queue_update", + # {"queue": self.convertQueueToJson(), "printerid": printerid}, + # ) return deletedjob return "Job not found in queue." @@ -97,7 +101,7 @@ def convertQueueToJson(self): "name": job.name, "status": job.status, "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), - "printerid": job.printer_id, + "printerid": job.fabricator_id, "errorid": job.error_id, "file_name_original": job.file_name_original, "progress": job.progress, From 1882526f6dd348fea1d76a99fba1e25d576b2eee Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Fri, 15 Nov 2024 15:37:27 -0500 Subject: [PATCH 124/194] dep: to be removed. --- server/models/jobs.py | 1322 ++++++++++++++++++++--------------------- 1 file changed, 654 insertions(+), 668 deletions(-) diff --git a/server/models/jobs.py b/server/models/jobs.py index f07c49a8..d1d9ea5a 100644 --- a/server/models/jobs.py +++ b/server/models/jobs.py @@ -1,668 +1,654 @@ -import asyncio -import base64 -from operator import or_ -import os -import re -from models.db import db -from models.printers import Printer - -from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory -from datetime import datetime, timezone, timedelta -from sqlalchemy import Column, String, LargeBinary, DateTime, ForeignKey -from sqlalchemy.orm import relationship -from flask import jsonify, current_app -from sqlalchemy.exc import SQLAlchemyError -from datetime import datetime -from tzlocal import get_localzone -from io import BytesIO -from werkzeug.datastructures import FileStorage -import time -import gzip -import csv -from flask import send_file - -from app import printer_status_service -# model for job history table - - -class Job(db.Model): - id = db.Column(db.Integer, primary_key=True) - file = db.Column(db.LargeBinary(16777215), nullable=True) - name = db.Column(db.String(50), nullable=False) - status = db.Column(db.String(50), nullable=False) - date = db.Column(db.DateTime, default=lambda: datetime.now( - timezone.utc).astimezone(), nullable=False) - # foreign key relationship to match jobs to the printer printed on - printer_id = db.Column(db.Integer, db.ForeignKey('printer.id'), nullable=True) - - printer = db.relationship('Printer', backref='Job') - - printer_name = db.Column(db.String(50), nullable=True) - - # TeamDynamics ID - td_id = db.Column(db.Integer, nullable=True) - - #FK to issue - error_id = db.Column(db.Integer, db.ForeignKey('issue.id'), nullable=True) - error = db.relationship('Issue', backref='Issue') - - # comments - comments = db.Column(db.String(500), nullable=True) - - file_name_original = db.Column(db.String(50), nullable=False) - favorite = db.Column(db.Boolean, nullable=False) - file_name_pk = None - max_layer_height = 0.0 - current_layer_height = 0.0 - filament = '' - released = 0 - filePause = 0 - progress = 0.0 - sent_lines = 0 - time_started = 0 - extruded = 0 - #total, eta, timestart, pause time - job_time = [0, datetime.min, datetime.min, datetime.min] - - - - def __init__(self, file, name, printer_id, status, file_name_original, favorite, td_id, printer_name): - self.file = file - self.name = name - self.printer_id = printer_id - self.status = status - self.file_name_original = file_name_original # original file name without PK identifier - self.td_id = td_id - self.file_name_pk = None - self.favorite = favorite - self.released = 0 - self.filePause = 0 - self.progress = 0.0 - self.sent_lines = 0 - self.time_started = 0 - self.extruded = 0 - self.job_time = [0, datetime.min, datetime.min, datetime.min] - self.error_id = 0 - self.printer_name = printer_name - self.max_layer_height = 0.0 - self.current_layer_height = 0.0 - self.filament = '' - - def __repr__(self): - return f"Job(id={self.id}, name={self.name}, printer_id={self.printer_id}, status={self.status})" - - def getPrinterId(self): - return self.printer_id - - - @classmethod - def get_job_history( - cls, - page, - pageSize, - printerIds=None, - oldestFirst=False, - searchJob="", - searchCriteria="", - searchTicketId=None, - favoriteOnly=False, - issueIds = None, - startDate=None, - endDate=None, - fromError = None, - countOnly = None - ): - try: - query = cls.query - - print("fromError: ", fromError) - if(fromError==1): - print("here") - query = cls.query.filter_by(status="error") - - if printerIds: - query = query.filter(cls.printer_id.in_(printerIds)) - - if issueIds: - query = query.filter(cls.error_id.in_(issueIds)) - - if searchJob: - searchJob = f"%{searchJob}%" - query = query.filter( - or_( - cls.name.ilike(searchJob), - cls.file_name_original.ilike(searchJob), - ) - ) - - if "searchByJobName" in searchCriteria: - searchByJobName = f"%{searchJob}%" - query = query.filter(cls.name.ilike(searchByJobName)) - elif "searchByFileName" in searchCriteria: - searchByFileName = f"%{searchJob}%" - query = query.filter(cls.file_name_original.ilike(searchByFileName)) - - if searchTicketId: - searchTicketId = int(searchTicketId) - query = query.filter(cls.td_id == searchTicketId) - - if favoriteOnly: - query = query.filter(cls.favorite == True) - - if oldestFirst: - query = query.order_by(cls.date.asc()) - else: - query = query.order_by(cls.date.desc()) # Change this line - - - if startDate!='' or endDate!='': - if(endDate==''): - cls.date.between( - datetime.fromisoformat(startDate), - datetime.fromisoformat(startDate), - ) - - query = query.filter( - cls.date.between( - datetime.fromisoformat(startDate), - datetime.fromisoformat(endDate), - ) - ) - - pagination = query.paginate(page=page, per_page=pageSize, error_out=False) - jobs = pagination.items - - jobs_data = [ - { - "id": job.id, - "name": job.name, - "status": job.status, - "date": job.date.strftime("%a, %d %b %Y %H:%M:%S"), - "printerid": job.printer_id, - "errorid": job.error_id, - "file_name_original": job.file_name_original, - "comments": job.comments, - "td_id": job.td_id, - "printer": job.printer.name if job.printer else "None", - "error": job.error.issue if job.error else 'None', - "printer_name": job.printer_name - } - for job in jobs - ] - if(countOnly == 0): - return jobs_data, pagination.total - else: - return pagination.total - - except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve jobs. Database error"}), 500 - - @classmethod - def jobHistoryInsert(cls, name, printer_id, status, file, file_name_original, favorite, td_id): - try: - if isinstance(file, bytes): - file_data = file - else: - file.seek(0) - file_data = file.read() - - try: - gzip.decompress(file_data) - # If it decompresses successfully, it's already compressed - compressed_data = file_data - except OSError: - compressed_data = gzip.compress(file_data) - - printer = Printer.query.get(printer_id) - - job = cls( - file=compressed_data, - name=name, - printer_id=printer_id, - status=status, - file_name_original = file_name_original, - favorite = favorite, - td_id = td_id, - printer_name = printer.name - ) - - db.session.add(job) - db.session.commit() - - return {"success": True, "message": "Job added to collection.", "id": job.id} - except SQLAlchemyError as e: - print(f"Database error: {e}") - return ( - jsonify({"error": "Failed to add job. Database error"}), - 500, - ) - - @classmethod - def update_job_status(cls, job_id, new_status): - try: - # Retrieve the job from the database based on its primary key - job = cls.query.get(job_id) - if job: - # Update the status attribute of the job - job.status = new_status - # Commit the changes to the database - db.session.commit() - - current_app.socketio.emit('job_status_update', { - 'job_id': job_id, 'status': new_status}) - - return {"success": True, "message": f"Job {job_id} status updated successfully."} - else: - return {"success": False, "message": f"Job {job_id} not found."}, 404 - except SQLAlchemyError as e: - print(f"Database error: {e}") - return ( - jsonify({"error": "Failed to update job status. Database error"}), - 500, - ) - - @classmethod - def delete_job(cls, job_id): - try: - job = cls.query.get(job_id) - if job: - db.session.delete(job) - db.session.commit() - return {"success": True, "message": f"Job with ID {job_id} deleted from the database."} - else: - return {"error": f"Job with ID {job_id} not found in the database."} - except Exception as e: - print(f"Unexpected error: {e}") - # When an error occurs or an exception is raised during a database operation (such as adding, - # updating, or deleting records), it may leave the database in an inconsistent state. To handle such - # situations, a rollback is performed to revert any changes made within the current session to maintain the integrity of the database. - db.session.rollback() - return {"error": "Unexpected error occurred during job deletion."} - - @classmethod - def findJob(cls, job_id): - try: - job = cls.query.filter_by(id=job_id).first() - return job - except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve job. Database error"}), 500 - - @classmethod - def findPrinterObject(self, printer_id): - threads = printer_status_service.getThreadArray() - return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer - - @classmethod - def removeFileFromPath(cls, file_path): - # file_path = self.generatePath() # Get the file path - if os.path.exists(file_path): # Check if the file exists - os.remove(file_path) # Remove the file - - @classmethod - def setDBstatus(cls, jobid, status): - cls.update_job_status(jobid, status) - - @classmethod - def getPathForDelete(cls, file_name): - return os.path.join('../uploads', file_name) - - @classmethod - def nullifyPrinterId(cls, printer_id): - try: - jobs = cls.query.filter_by(printer_id=printer_id).all() - for job in jobs: - job.printer_id = 0 - db.session.commit() - return {"success": True, "message": "Printer ID nullified successfully."} - except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to nullify printer ID. Database error"}), 500 - - @classmethod - def clearSpace(cls): - try: - six_months_ago = datetime.now() - timedelta(days=182) # 6 months ago - old_jobs = Job.query.filter(Job.date < six_months_ago).all() - - # thirty_seconds_ago = datetime.now() - timedelta(seconds=30) # 30 seconds ago - # old_jobs = Job.query.filter(Job.date < thirty_seconds_ago).all() - - for job in old_jobs: - if(job.favorite==0): - job.file = None # Set file to None - if "Removed after 6 months" not in job.file_name_original: - job.file_name_original = f"{job.file_name_original}: Removed after 6 months" - db.session.commit() # Commit the changes - return {"success": True, "message": "Space cleared successfully."} - except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to clear space. Database error"}), 500 - - @classmethod - def setDBstatus(cls, jobid, status): - cls.update_job_status(jobid, status) - - @classmethod - def getPathForDelete(cls, file_name): - return os.path.join('../uploads', file_name) - - @classmethod - def getFavoriteJobs(cls): - try: - jobs = cls.query.filter_by(favorite=True).all() - - jobs_data = [{ - "id": job.id, - "name": job.name, - "status": job.status, - "date": f"{job.date.strftime('%a, %d %b %Y %H:%M:%S')} {get_localzone().tzname(job.date)}", - "printer": job.printer.name if job.printer else 'None', - "file_name_original": job.file_name_original, - "favorite": job.favorite - } for job in jobs] - - return jobs_data - except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve favorite jobs. Database error"}), 500 - - @classmethod - def setIssue(cls, job_id, issue_id): - job = cls.query.get(job_id) - - if job is None: - return None - - # Set the job's error_id to the given issue_id - job.error_id = issue_id - - # Commit the changes to the database - try: - db.session.commit() - return {"success": True, "message": "Issue assigned successfully."} - except Exception as e: - db.session.rollback() - print(f"Error setting issue: {e}") - return None - - @classmethod - def unsetIssue(cls, job_id): - job = cls.query.get(job_id) - - if job is None: - return None - - # Set the job's error_id to None - job.error_id = None - - # Commit the changes to the database - try: - db.session.commit() - return {"success": True, "message": "Issue removed successfully."} - except Exception as e: - db.session.rollback() - print(f"Error unsetting issue: {e}") - return None - - @classmethod - def setComment(cls, job_id, comments): - job = cls.query.get(job_id) - - if job is None: - return None - - # Set the job's comments to the given comments - job.comments = comments - - # Commit the changes to the database - try: - db.session.commit() - return {"success": True, "message": "Comments added successfully."} - except Exception as e: - db.session.rollback() - print(f"Error setting comments: {e}") - return None - - @classmethod - def downloadCSV(cls, alljobs, jobids=None): - try: - if(jobids!=None): - # Join Job and Issue on error_id and filter by jobids - jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).filter(cls.id.in_(jobids)).all() - else: - # Join Job and Issue on error_id - jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).all() - - # Specify the columns you want to include - column_names = ['td_id', 'printer', 'name','file_name_original', 'status', 'date', 'issue', 'comments'] - - date_string = datetime.now().strftime("%m%d%Y") - - csv_file_name = f'../tempcsv/jobs_{date_string}.csv' - - # Write to CSV - - with open(csv_file_name, 'w', newline='') as file: - writer = csv.writer(file) - writer.writerow(column_names) # write headers - for job, issue in jobs: - row = [getattr(job, 'td_id', ''), getattr(job, 'printer_name', ''), getattr(job, 'name', ''), getattr(job, 'file_name_original', ''), getattr(job, 'status', ''), getattr(job, 'date', ''), getattr(issue, 'issue', '') if issue else '', getattr(job, 'comments', '')] - writer.writerow(row) # write data rows - - csv_file_path = f'./{csv_file_name}' - - return send_file(csv_file_path, as_attachment=True) - - - except Exception as e: - print(f"Error downloading CSV: {e}") - return {"status": "error", "message": f"Error downloading CSV: {e}"} - - def saveToFolder(self): - file_data = self.getFile() - decompressed_data = gzip.decompress(file_data) - with open(self.generatePath(), 'wb') as f: - f.write(decompressed_data) - - def generatePath(self): - return os.path.join('../uploads', self.getFileNamePk()) - - # getters - def getName(self): - return self.name - - def getFilePath(self): - return self.path - - def getFile(self): - return self.file - - def getStatus(self): - return self.status - - def getFileNamePk(self): - return self.file_name_pk - - def getFileNameOriginal(self): - return self.file_name_original - - def getFileFavorite(self): - return self.favorite - - def setFileFavorite(self, favorite): - self.favorite = favorite - db.session.commit() - return {"success": True, "message": "Favorite status updated successfully."} - - def getPrinterId(self): - return self.printer_id - - def getJobId(self): - return self.id - - def getFilePause(self): - return self.filePause - - def setFilePause(self, pause): - self.filePause = pause - current_app.socketio.emit('file_pause_update', { - 'job_id': self.id, 'file_pause': self.filePause}) - - def getExtruded(self): - return self.extruded - - def setExtruded(self, extruded): - self.extruded = extruded - current_app.socketio.emit('extruded_update', { - 'job_id': self.id, 'extruded': self.extruded}) - - # setters - - def setStatus(self, status): - self.status = status - # self.setDBstatus(self.id, status) - - # added a setProgress method to update the progress of a job - # which sends it to the frontend using socketio - def setProgress(self, progress): - if self.status == 'printing': - self.progress = progress - # Emit a 'progress_update' event with the new progress - current_app.socketio.emit( - 'progress_update', {'job_id': self.id, 'progress': self.progress}) - - # added a getProgress method to get the progress of a job - def getProgress(self): - return self.progress - - def setSentLines(self, sent_lines): - self.sent_lines = sent_lines - current_app.socketio.emit('gcode_viewer', {'job_id': self.id, 'gcode_num': self.sent_lines}) - - def getSentLines(self): - return self.sent_lines - - def getTimeFromFile(self, comment_lines): - # job_line can look two ways: - # 1. ;TIME:seconds - # 2. ; estimated printing time (normal mode) = minutes seconds - # if first line contains "FLAVOR", then the second line contains the time estimate in the format of ";TIME:seconds" - if "FLAVOR" in comment_lines[0]: - time_line = comment_lines[1] - time_seconds = int(time_line.split(":")[1]) - else: - # search for the line that contains "printing time", then the time estimate is in the format of "; estimated printing time (normal mode) = minutes seconds" - time_line = next(line for line in comment_lines if "time" in line) - time_values = re.findall(r'\d+', time_line) - - # Initialize all time units to 0 - time_days = time_hours = time_minutes = time_seconds = 0 - - # Assign values from right to left (seconds, minutes, hours, days) - time_values = time_values[::-1] - if len(time_values) > 0: - time_seconds = int(time_values[0]) - if len(time_values) > 1: - time_minutes = int(time_values[1]) - if len(time_values) > 2: - time_hours = int(time_values[2]) - if len(time_values) > 3: - time_days = int(time_values[3]) - - # Calculate total time in seconds - time_seconds = time_days * 24 * 60 * 60 + time_hours * 60 * 60 + time_minutes * 60 + time_seconds - # date = datetime.fromtimestamp(time_seconds) - return time_seconds - - def getTimeStarted(self): - return self.time_started - - def calculateEta(self): - now = datetime.now() - eta = timedelta(seconds=self.job_time[0]) + now - return eta - - def updateEta(self): - now = datetime.now() - pause_time = self.getJobTime()[3] - - duration = now - pause_time - - new_eta = self.getJobTime()[1] + timedelta(seconds=1) - return new_eta - - def colorEta(self): - print("before ETA: ", self.getJobTime()[1]) - - now = datetime.now() - pause_time = self.getJobTime()[3] - duration = now - pause_time - eta = self.getJobTime()[1] + duration - return eta - - def calculateTotalTime(self): - total_time = self.getJobTime()[0] - - # Add one second to total_time - total_time+=1 - return total_time - - def calculateColorChangeTotal(self): - print("before Total Time: ", self.getJobTime()[0]) - - now = datetime.now() - pause_time = self.getJobTime()[3] - duration = now - pause_time - duration_in_seconds = duration.total_seconds() - total_time = self.getJobTime()[0] + duration_in_seconds - return total_time - - def getJobTime(self): - return self.job_time - - def getReleased(self): - return self.released - - def getTdId(self): - return self.td_id - - def setMaxLayerHeight(self, max_layer_height): - self.max_layer_height = max_layer_height - current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) - - def setCurrentLayerHeight(self, current_layer_height): - print("Current Layer Height: ", current_layer_height) - self.current_layer_height = current_layer_height - current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) - - def setFilament(self, filament): - self.filament = filament - - def setPath(self, path): - self.path = path - - def setFileName(self, filename): - self.file_name_pk = filename - - def setFile(self, file): - self.file = file - - def setReleased(self, released): - self.released = released - current_app.socketio.emit('release_job', {'job_id': self.id, 'released': released}) - - def setTimeStarted(self, time_started): - self.time_started = time_started - current_app.socketio.emit('set_time_started', {'job_id': self.id, 'started': time_started}) - - - def setTime(self, timeData, index): - # timeData = datetime(y, m, d, h, min, s) - # print("TimeData: ", timeData, " Index: ", index) - self.job_time[index] = timeData - if index==0: - current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData, 'index': index}) - else: - current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData.isoformat(), 'index': index}) \ No newline at end of file +# from operator import or_ +# import os +# import re +# from models.db import db +# #from models.printers import Printer +# +# from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory +# from datetime import timezone, timedelta +# from flask import jsonify, current_app +# from sqlalchemy.exc import SQLAlchemyError +# from datetime import datetime +# from tzlocal import get_localzone +# import gzip +# import csv +# from flask import send_file +# +# from models.printers import Printer +# +# +# # from app import printer_status_service +# # model for job history table +# +# +# class Job(db.Model): +# id = db.Column(db.Integer, primary_key=True) +# file = db.Column(db.LargeBinary(16777215), nullable=True) +# name = db.Column(db.String(50), nullable=False) +# status = db.Column(db.String(50), nullable=False) +# date = db.Column(db.DateTime, default=lambda: datetime.now( +# timezone.utc).astimezone(), nullable=False) +# # foreign key relationship to match jobs to the printer printed on +# #printer_id = db.Column(db.Integer, db.ForeignKey('printer.id'), nullable=True) +# +# #printer = db.relationship('Printer', backref='Job') +# +# printer_name = db.Column(db.String(50), nullable=True) +# +# # TeamDynamics ID +# td_id = db.Column(db.Integer, nullable=True) +# +# #FK to issue +# error_id = db.Column(db.Integer, db.ForeignKey('issue.id'), nullable=True) +# error = db.relationship('Issue', backref='Issue') +# +# # comments +# comments = db.Column(db.String(500), nullable=True) +# +# file_name_original = db.Column(db.String(50), nullable=False) +# favorite = db.Column(db.Boolean, nullable=False) +# file_name_pk = None +# max_layer_height = 0.0 +# current_layer_height = 0.0 +# filament = '' +# released = 0 +# filePause = 0 +# progress = 0.0 +# sent_lines = 0 +# time_started = 0 +# extruded = 0 +# #total, eta, timestart, pause time +# job_time = [0, datetime.min, datetime.min, datetime.min] +# +# +# +# def __init__(self, file, name, printer_id, status, file_name_original, favorite, td_id, printer_name): +# self.path = None +# self.file = file +# self.name = name +# self.printer_id = printer_id +# self.status = status +# self.file_name_original = file_name_original # original file name without PK identifier +# self.td_id = td_id +# self.file_name_pk = None +# self.favorite = favorite +# self.released = 0 +# self.filePause = 0 +# self.progress = 0.0 +# self.sent_lines = 0 +# self.time_started = 0 +# self.extruded = 0 +# self.job_time = [0, datetime.min, datetime.min, datetime.min] +# self.error_id = 0 +# self.printer_name = printer_name +# self.max_layer_height = 0.0 +# self.current_layer_height = 0.0 +# self.filament = '' +# +# def __repr__(self): +# return f"Job(id={self.id}, name={self.name}, printer_id={self.printer_id}, status={self.status})" +# +# def getPrinterId(self): +# return self.printer_id +# +# +# @classmethod +# def get_job_history( +# cls, +# page, +# pageSize, +# printerIds=None, +# oldestFirst=False, +# searchJob="", +# searchCriteria="", +# searchTicketId=None, +# favoriteOnly=False, +# issueIds = None, +# startDate=None, +# endDate=None, +# fromError = None, +# countOnly = None +# ): +# try: +# query = cls.query +# +# print("fromError: ", fromError) +# if(fromError==1): +# print("here") +# query = cls.query.filter_by(status="error") +# +# if printerIds: +# query = query.filter(cls.printer_id.in_(printerIds)) +# +# if issueIds: +# query = query.filter(cls.error_id.in_(issueIds)) +# +# if searchJob: +# searchJob = f"%{searchJob}%" +# query = query.filter( +# or_( +# cls.name.ilike(searchJob), +# cls.file_name_original.ilike(searchJob), +# ) +# ) +# +# if "searchByJobName" in searchCriteria: +# searchByJobName = f"%{searchJob}%" +# query = query.filter(cls.name.ilike(searchByJobName)) +# elif "searchByFileName" in searchCriteria: +# searchByFileName = f"%{searchJob}%" +# query = query.filter(cls.file_name_original.ilike(searchByFileName)) +# +# if searchTicketId: +# searchTicketId = int(searchTicketId) +# query = query.filter(cls.td_id == searchTicketId) +# +# if favoriteOnly: +# query = query.filter(cls.favorite == True) +# +# if oldestFirst: +# query = query.order_by(cls.date.asc()) +# else: +# query = query.order_by(cls.date.desc()) # Change this line +# +# +# if startDate!='' or endDate!='': +# if(endDate==''): +# cls.date.between( +# datetime.fromisoformat(startDate), +# datetime.fromisoformat(startDate), +# ) +# +# query = query.filter( +# cls.date.between( +# datetime.fromisoformat(startDate), +# datetime.fromisoformat(endDate), +# ) +# ) +# +# pagination = query.paginate(page=page, per_page=pageSize, error_out=False) +# jobs = pagination.items +# +# jobs_data = [ +# { +# "id": job.id, +# "name": job.name, +# "status": job.status, +# "date": job.date.strftime("%a, %d %b %Y %H:%M:%S"), +# "printerid": job.fabricator_id, +# "errorid": job.error_id, +# "file_name_original": job.file_name_original, +# "comments": job.comments, +# "td_id": job.td_id, +# "printer": job.printer.name if job.printer else "None", +# "error": job.error.issue if job.error else 'None', +# "printer_name": job.printer_name +# } +# for job in jobs +# ] +# if(countOnly == 0): +# return jobs_data, pagination.total +# else: +# return pagination.total +# +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return jsonify({"error": "Failed to retrieve jobs. Database error"}), 500 +# +# @classmethod +# def jobHistoryInsert(cls, name, printer_id, status, file, file_name_original, favorite, td_id): +# try: +# if isinstance(file, bytes): +# file_data = file +# else: +# file.seek(0) +# file_data = file.read() +# +# try: +# gzip.decompress(file_data) +# # If it decompresses successfully, it's already compressed +# compressed_data = file_data +# except OSError: +# compressed_data = gzip.compress(file_data) +# +# printer = Printer.query.get(printer_id) +# +# job = cls( +# file=compressed_data, +# name=name, +# printer_id=printer_id, +# status=status, +# file_name_original = file_name_original, +# favorite = favorite, +# td_id = td_id, +# printer_name = printer.name +# ) +# +# db.session.add(job) +# db.session.commit() +# +# return {"success": True, "message": "Job added to collection.", "id": job.id} +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return ( +# jsonify({"error": "Failed to add job. Database error"}), +# 500, +# ) +# +# @classmethod +# def update_job_status(cls, job_id, new_status): +# try: +# # Retrieve the job from the database based on its primary key +# job = cls.query.get(job_id) +# if job: +# # Update the status attribute of the job +# job.status = new_status +# # Commit the changes to the database +# db.session.commit() +# +# current_app.socketio.emit('job_status_update', { +# 'job_id': job_id, 'status': new_status}) +# +# return {"success": True, "message": f"Job {job_id} status updated successfully."} +# else: +# return {"success": False, "message": f"Job {job_id} not found."}, 404 +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return ( +# jsonify({"error": "Failed to update job status. Database error"}), +# 500, +# ) +# +# @classmethod +# def delete_job(cls, job_id): +# try: +# job = cls.query.get(job_id) +# if job: +# db.session.delete(job) +# db.session.commit() +# return {"success": True, "message": f"Job with ID {job_id} deleted from the database."} +# else: +# return {"error": f"Job with ID {job_id} not found in the database."} +# except Exception as e: +# print(f"Unexpected error: {e}") +# # When an error occurs or an exception is raised during a database operation (such as adding, +# # updating, or deleting records), it may leave the database in an inconsistent state. To handle such +# # situations, a rollback is performed to revert any changes made within the current session to maintain the integrity of the database. +# db.session.rollback() +# return {"error": "Unexpected error occurred during job deletion."} +# +# @classmethod +# def findJob(cls, job_id): +# try: +# job = cls.query.filter_by(id=job_id).first() +# return job +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return jsonify({"error": "Failed to retrieve job. Database error"}), 500 +# +# # @classmethod +# # def findPrinterObject(self, printer_id): +# # threads = printer_status_service.getThreadArray() +# # return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer +# +# @classmethod +# def removeFileFromPath(cls, file_path): +# # file_path = self.generatePath() # Get the file path +# if os.path.exists(file_path): # Check if the file exists +# os.remove(file_path) # Remove the file +# +# @classmethod +# def setDBstatus(cls, jobid, status): +# cls.update_job_status(jobid, status) +# +# @classmethod +# def getPathForDelete(cls, file_name): +# return os.path.join('../uploads', file_name) +# +# @classmethod +# def nullifyPrinterId(cls, printer_id): +# try: +# jobs = cls.query.filter_by(printer_id=printer_id).all() +# for job in jobs: +# job.fabricator_id = 0 +# db.session.commit() +# return {"success": True, "message": "Printer ID nullified successfully."} +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return jsonify({"error": "Failed to nullify printer ID. Database error"}), 500 +# +# @classmethod +# def clearSpace(cls): +# try: +# six_months_ago = datetime.now() - timedelta(days=182) # 6 months ago +# old_jobs = Job.query.filter(Job.date < six_months_ago).all() +# +# # thirty_seconds_ago = datetime.now() - timedelta(seconds=30) # 30 seconds ago +# # old_jobs = Job.query.filter(Job.date < thirty_seconds_ago).all() +# +# for job in old_jobs: +# if(job.favorite==0): +# job.file = None # Set file to None +# if "Removed after 6 months" not in job.file_name_original: +# job.file_name_original = f"{job.file_name_original}: Removed after 6 months" +# db.session.commit() # Commit the changes +# return {"success": True, "message": "Space cleared successfully."} +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return jsonify({"error": "Failed to clear space. Database error"}), 500 +# +# @classmethod +# def getFavoriteJobs(cls): +# try: +# jobs = cls.query.filter_by(favorite=True).all() +# +# jobs_data = [{ +# "id": job.id, +# "name": job.name, +# "status": job.status, +# "date": f"{job.date.strftime('%a, %d %b %Y %H:%M:%S')} {get_localzone().tzname(job.date)}", +# "printer": job.printer.name if job.printer else 'None', +# "file_name_original": job.file_name_original, +# "favorite": job.favorite +# } for job in jobs] +# +# return jobs_data +# except SQLAlchemyError as e: +# print(f"Database error: {e}") +# return jsonify({"error": "Failed to retrieve favorite jobs. Database error"}), 500 +# +# @classmethod +# def setIssue(cls, job_id, issue_id): +# job = cls.query.get(job_id) +# +# if job is None: +# return None +# +# # Set the job's error_id to the given issue_id +# job.error_id = issue_id +# +# # Commit the changes to the database +# try: +# db.session.commit() +# return {"success": True, "message": "Issue assigned successfully."} +# except Exception as e: +# db.session.rollback() +# print(f"Error setting issue: {e}") +# return None +# +# @classmethod +# def unsetIssue(cls, job_id): +# job = cls.query.get(job_id) +# +# if job is None: +# return None +# +# # Set the job's error_id to None +# job.error_id = None +# +# # Commit the changes to the database +# try: +# db.session.commit() +# return {"success": True, "message": "Issue removed successfully."} +# except Exception as e: +# db.session.rollback() +# print(f"Error unsetting issue: {e}") +# return None +# +# @classmethod +# def setComment(cls, job_id, comments): +# job = cls.query.get(job_id) +# +# if job is None: +# return None +# +# # Set the job's comments to the given comments +# job.comments = comments +# +# # Commit the changes to the database +# try: +# db.session.commit() +# return {"success": True, "message": "Comments added successfully."} +# except Exception as e: +# db.session.rollback() +# print(f"Error setting comments: {e}") +# return None +# +# @classmethod +# def downloadCSV(cls, alljobs, jobids=None): +# try: +# if(jobids!=None): +# # Join Job and Issue on error_id and filter by jobids +# jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).filter(cls.id.in_(jobids)).all() +# else: +# # Join Job and Issue on error_id +# jobs = db.session.query(cls, Issue).outerjoin(Issue, cls.error_id == Issue.id).all() +# +# # Specify the columns you want to include +# column_names = ['td_id', 'printer', 'name','file_name_original', 'status', 'date', 'issue', 'comments'] +# +# date_string = datetime.now().strftime("%m%d%Y") +# +# csv_file_name = f'../tempcsv/jobs_{date_string}.csv' +# +# # Write to CSV +# +# with open(csv_file_name, 'w', newline='') as file: +# writer = csv.writer(file) +# writer.writerow(column_names) # write headers +# for job, issue in jobs: +# row = [getattr(job, 'td_id', ''), getattr(job, 'printer_name', ''), getattr(job, 'name', ''), getattr(job, 'file_name_original', ''), getattr(job, 'status', ''), getattr(job, 'date', ''), getattr(issue, 'issue', '') if issue else '', getattr(job, 'comments', '')] +# writer.writerow(row) # write data rows +# +# csv_file_path = f'./{csv_file_name}' +# +# return send_file(csv_file_path, as_attachment=True) +# +# +# except Exception as e: +# print(f"Error downloading CSV: {e}") +# return {"status": "error", "message": f"Error downloading CSV: {e}"} +# +# def saveToFolder(self): +# file_data = self.getFile() +# decompressed_data = gzip.decompress(file_data) +# with open(self.generatePath(), 'wb') as f: +# f.write(decompressed_data) +# +# def generatePath(self): +# return os.path.join('../uploads', self.getFileNamePk()) +# +# # getters +# def getName(self): +# return self.name +# +# def getFilePath(self): +# return self.path +# +# def getFile(self): +# return self.file +# +# def getStatus(self): +# return self.status +# +# def getFileNamePk(self): +# return self.file_name_pk +# +# def getFileNameOriginal(self): +# return self.file_name_original +# +# def getFileFavorite(self): +# return self.favorite +# +# def setFileFavorite(self, favorite): +# self.favorite = favorite +# db.session.commit() +# return {"success": True, "message": "Favorite status updated successfully."} +# +# def getJobId(self): +# return self.id +# +# def getFilePause(self): +# return self.filePause +# +# def setFilePause(self, pause): +# self.filePause = pause +# current_app.socketio.emit('file_pause_update', { +# 'job_id': self.id, 'file_pause': self.filePause}) +# +# def getExtruded(self): +# return self.extruded +# +# def setExtruded(self, extruded): +# self.extruded = extruded +# current_app.socketio.emit('extruded_update', { +# 'job_id': self.id, 'extruded': self.extruded}) +# +# # setters +# +# def setStatus(self, status): +# self.status = status +# # self.setDBstatus(self.id, status) +# +# # added a setProgress method to update the progress of a job +# # which sends it to the frontend using socketio +# def setProgress(self, progress): +# if self.status == 'printing': +# self.progress = progress +# # Emit a 'progress_update' event with the new progress +# current_app.socketio.emit( +# 'progress_update', {'job_id': self.id, 'progress': self.progress}) +# +# # added a getProgress method to get the progress of a job +# def getProgress(self): +# return self.progress +# +# def setSentLines(self, sent_lines): +# self.sent_lines = sent_lines +# current_app.socketio.emit('gcode_viewer', {'job_id': self.id, 'gcode_num': self.sent_lines}) +# +# def getSentLines(self): +# return self.sent_lines +# +# def getTimeFromFile(self, comment_lines): +# # job_line can look two ways: +# # 1. ;TIME:seconds +# # 2. ; estimated printing time (normal mode) = minutes seconds +# # if first line contains "FLAVOR", then the second line contains the time estimate in the format of ";TIME:seconds" +# if "FLAVOR" in comment_lines[0]: +# time_line = comment_lines[1] +# time_seconds = int(time_line.split(":")[1]) +# else: +# # search for the line that contains "printing time", then the time estimate is in the format of "; estimated printing time (normal mode) = minutes seconds" +# time_line = next(line for line in comment_lines if "time" in line) +# time_values = re.findall(r'\d+', time_line) +# +# # Initialize all time units to 0 +# time_days = time_hours = time_minutes = time_seconds = 0 +# +# # Assign values from right to left (seconds, minutes, hours, days) +# time_values = time_values[::-1] +# if len(time_values) > 0: +# time_seconds = int(time_values[0]) +# if len(time_values) > 1: +# time_minutes = int(time_values[1]) +# if len(time_values) > 2: +# time_hours = int(time_values[2]) +# if len(time_values) > 3: +# time_days = int(time_values[3]) +# +# # Calculate total time in seconds +# time_seconds = time_days * 24 * 60 * 60 + time_hours * 60 * 60 + time_minutes * 60 + time_seconds +# # date = datetime.fromtimestamp(time_seconds) +# return time_seconds +# +# def getTimeStarted(self): +# return self.time_started +# +# def calculateEta(self): +# now = datetime.now() +# eta = timedelta(seconds=self.job_time[0]) + now +# return eta +# +# def updateEta(self): +# now = datetime.now() +# pause_time = self.getJobTime()[3] +# +# duration = now - pause_time +# +# new_eta = self.getJobTime()[1] + timedelta(seconds=1) +# return new_eta +# +# def colorEta(self): +# print("before ETA: ", self.getJobTime()[1]) +# +# now = datetime.now() +# pause_time = self.getJobTime()[3] +# duration = now - pause_time +# eta = self.getJobTime()[1] + duration +# return eta +# +# def calculateTotalTime(self): +# total_time = self.getJobTime()[0] +# +# # Add one second to total_time +# total_time+=1 +# return total_time +# +# def calculateColorChangeTotal(self): +# print("before Total Time: ", self.getJobTime()[0]) +# +# now = datetime.now() +# pause_time = self.getJobTime()[3] +# duration = now - pause_time +# duration_in_seconds = duration.total_seconds() +# total_time = self.getJobTime()[0] + duration_in_seconds +# return total_time +# +# def getJobTime(self): +# return self.job_time +# +# def getReleased(self): +# return self.released +# +# def getTdId(self): +# return self.td_id +# +# def setMaxLayerHeight(self, max_layer_height): +# self.max_layer_height = max_layer_height +# current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) +# +# def setCurrentLayerHeight(self, current_layer_height): +# print("Current Layer Height: ", current_layer_height) +# self.current_layer_height = current_layer_height +# current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) +# +# def setFilament(self, filament): +# self.filament = filament +# +# def setPath(self, path): +# self.path = path +# +# def setFileName(self, filename): +# self.file_name_pk = filename +# +# def setFile(self, file): +# self.file = file +# +# def setReleased(self, released): +# self.released = released +# current_app.socketio.emit('release_job', {'job_id': self.id, 'released': released}) +# +# def setTimeStarted(self, time_started): +# self.time_started = time_started +# current_app.socketio.emit('set_time_started', {'job_id': self.id, 'started': time_started}) +# +# +# def setTime(self, timeData, index): +# # timeData = datetime(y, m, d, h, min, s) +# # print("TimeData: ", timeData, " Index: ", index) +# self.job_time[index] = timeData +# if index==0: +# current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData, 'index': index}) +# else: +# current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData.isoformat(), 'index': index}) \ No newline at end of file From 57929e14aabb61c7d60119c1ee5af5e500a30cc6 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Sat, 16 Nov 2024 18:41:41 -0500 Subject: [PATCH 125/194] Refactor FabricatorStatusService.py into FabricatorList.py + other edits for it --- server/Classes/FabricatorList.py | 232 ++++++++++++++++-- server/Classes/Ports.py | 13 +- server/Classes/Queue.py | 80 +++--- .../FabricatorStatusService.py | 2 +- 4 files changed, 254 insertions(+), 73 deletions(-) rename server/{Classes => models}/FabricatorStatusService.py (99%) diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 28f7cf93..7d498d9e 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -6,9 +6,20 @@ from Classes.Fabricators.Device import Device from Classes.Ports import Ports from Classes.Fabricators.Fabricator import Fabricator +from Classes.Queue import Queue +from threading import Thread +import time + +class FabricatorThread(Thread): + def __init__(self, fabricator, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fabricator = fabricator class FabricatorList(): fabricators: [Fabricator] = [] + fabricator_threads = [] + ping_thread = None + @staticmethod def __iter__(): return iter(FabricatorList.fabricators) @@ -47,24 +58,7 @@ def deleteFabricator(printerid): """delete a printer from the list, and from the database""" # TODO: Implement deleteFabricator pass - # try: - # ports = serial.tools.list_ports.comports() - # for port in ports: - # hwid = port["hwid"] # get hwid - # if hwid == Printer.query.get(printerid).hwid: - # ser = serial.Serial(port["device"], 115200, timeout=1) - # ser.close() - # break - # printer = PrinterList.query.get(printerid) - # db.session.delete(printer) - # db.session.commit() - # return {"success": True, "message": "Printer successfully deleted."} - # except SQLAlchemyError as e: - # print(f"Database error: {e}") - # return ( - # jsonify({"error": "Failed to delete printer. Database error"}), - # 500, - # ) + @staticmethod def getFabricatorByName(name) -> Fabricator | None: """find the first printer with the given name""" @@ -89,7 +83,6 @@ def diagnose(device: Device): diagnoseString += f"



Device {port.device} is registered with the following details:

Name: {printer.name}
Device: {printer.device},
Description: {printer.description},
HWID: {printer.hwid}" if diagnoseString == "": diagnoseString = "The port this printer is registered under is not found. Please check the connection and try again." - # return diagnoseString return { "success": True, "message": "Printer successfully diagnosed.", @@ -99,5 +92,202 @@ def diagnose(device: Device): except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - - \ No newline at end of file + + @staticmethod + def start_fabricator_thread(fabricator, app): + thread = FabricatorThread(fabricator, target=FabricatorList.update_thread, args=(fabricator, app)) + thread.daemon = True + thread.start() + return thread + + @staticmethod + def create_fabricator_threads(fabricators_data, app): + for fabricator_info in fabricators_data: + fabricator = Fabricator( + id=fabricator_info["id"], + device=fabricator_info["device"], + description=fabricator_info["description"], + hwid=fabricator_info["hwid"], + name=fabricator_info["name"], + status='configuring', + ) + fabricator.setQueue(Queue()) # Ensure each fabricator has its own queue + fabricator_thread = FabricatorList.start_fabricator_thread(fabricator, app) + FabricatorList.fabricator_threads.append(fabricator_thread) + + FabricatorList.ping_thread = Thread(target=FabricatorList.pingForStatus) + + @staticmethod + def queue_restore(fabricators_data, status, queue, app): + for fabricator_info in fabricators_data: + fabricator = Fabricator( + id=fabricator_info["id"], + device=fabricator_info["device"], + description=fabricator_info["description"], + hwid=fabricator_info["hwid"], + name=fabricator_info["name"], + ) + for job in queue: + if job.status != 'inqueue': + job.setStatus('inqueue') + job.setDBstatus(job.id, 'inqueue') + fabricator.setQueue(queue) + fabricator.setStatus(status) + fabricator_thread = FabricatorList.start_fabricator_thread(fabricator, app) + FabricatorList.fabricator_threads.append(fabricator_thread) + + @staticmethod + def update_thread(fabricator, app): + with app.app_context(): + while True: + time.sleep(2) + status = fabricator.getStatus() + queueSize = fabricator.getQueue().getSize() + fabricator.responseCount = 0 + if status == "ready" and queueSize > 0: + time.sleep(2) + if status != "offline": + fabricator.printNextInQueue() + + @staticmethod + def resetThread(fabricator_id, app): + try: + for thread in FabricatorList.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.terminated = 1 + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + } + FabricatorList.fabricator_threads.remove(thread) + FabricatorList.create_fabricator_threads([thread_data], app) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + @staticmethod + def queueRestore(fabricator_id, status, app): + try: + for thread in FabricatorList.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.terminated = 1 + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + } + FabricatorList.fabricator_threads.remove(thread) + FabricatorList.queue_restore([thread_data], status, fabricator.getQueue(), app) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + @staticmethod + def deleteThread(fabricator_id): + try: + for thread in FabricatorList.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + thread_data = { + "id": fabricator.id, + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name + } + FabricatorList.fabricator_threads.remove(thread) + break + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + @staticmethod + def editName(fabricator_id, name): + try: + for thread in FabricatorList.fabricator_threads: + if thread.fabricator.id == fabricator_id: + fabricator = thread.fabricator + fabricator.name = name + break + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 + + @staticmethod + def retrieve_fabricator_info(): + fabricator_info_list = [] + for thread in FabricatorList.fabricator_threads: + fabricator = thread.fabricator + fabricator_info = { + "device": fabricator.device, + "description": fabricator.description, + "hwid": fabricator.hwid, + "name": fabricator.name, + "status": fabricator.status, + "id": fabricator.id, + "error": fabricator.error, + "canPause": fabricator.canPause, + "queue": [], + "colorChangeBuffer": fabricator.colorbuff + } + queue = fabricator.getQueue() + for job in queue: + job_info = { + "id": job.id, + "name": job.name, + "status": job.status, + "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), + "fabricatorid": job.fabricator_id, + "errorid": job.error_id, + "file_name_original": job.file_name_original, + "progress": job.progress, + "sent_lines": job.sent_lines, + "favorite": job.favorite, + "released": job.released, + "file_pause": job.filePause, + "comments": job.comments, + "extruded": job.extruded, + "td_id": job.td_id, + "time_started": job.time_started, + "fabricator_name": job.fabricator_name, + "max_layer_height": job.max_layer_height, + "current_layer_height": job.current_layer_height, + "filament": job.filament, + } + fabricator_info['queue'].append(job_info) + + fabricator_info_list.append(fabricator_info) + + return fabricator_info_list + + @staticmethod + def getThreadArray(): + return FabricatorList.fabricator_threads + + @staticmethod + def pingForStatus(): + pass + + @staticmethod + def moveFabricatorList(fabricator_ids): + new_thread_list = [] + for id in fabricator_ids: + for thread in FabricatorList.fabricator_threads: + if thread.fabricator.id == id: + new_thread_list.append(thread) + break + FabricatorList.fabricator_threads = new_thread_list + return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) \ No newline at end of file diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 9d2adbc5..0254f2b5 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -6,6 +6,7 @@ from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode +from Classes.Fabricators.FabricatorList import FabricatorList class Ports: @staticmethod @@ -36,7 +37,7 @@ def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: @staticmethod def getRegisteredFabricators() -> list[Fabricator]: """Get a list of all registered fabricators.""" - fabricators = Fabricator.queryAll() # Assuming Fabricator has a method to query all instances + fabricators = Fabricator.queryAll() registered_fabricators = [] for fab in fabricators: port = Ports.getPortByName(fab.devicePort) @@ -48,12 +49,12 @@ def getRegisteredFabricators() -> list[Fabricator]: def diagnosePort(port: ListPortInfo | SysFS) -> str: """Diagnose a port to check if it is functional by sending basic G-code commands.""" try: - device = Fabricator.createDevice(port) # Create a Device instance using Fabricator logic + device = Fabricator.createDevice(port) if not device: return "Device creation failed." device.connect() - sendGcode("M115") # Standard G-code command to get firmware information + sendGcode("M115") response = device.getSerialConnection().readline().decode("utf-8").strip() device.disconnect() @@ -100,7 +101,6 @@ def registerFabricator(): hwid = data['fabricator']['hwid'] name = data['fabricator']['name'] - # Create a new fabricator instance using the Fabricator class new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) new_fabricator.description = description new_fabricator.hwid = hwid @@ -188,12 +188,11 @@ def moveHead(): def moveFabricatorList(): """Change the order of fabricators.""" try: - from app import printer_status_service # Ensure integration aligns with the new naming convention data = request.get_json() fabricator_ids = data['fabricator_ids'] - result = printer_status_service.moveFabricatorList(fabricator_ids) + result = FabricatorList.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: print(f"Error moving fabricator list: {e}") - return jsonify({"error": "Failed to move fabricator list"}), 500 + return jsonify({"error": "Failed to move fabricator list"}), 500 \ No newline at end of file diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 926800c2..5b6b0c37 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,9 +1,7 @@ from collections import deque -#from flask import jsonify, current_app - +from flask import current_app class Queue: - # Only adding ID to the queue def __init__(self): self.__queue = deque() # use Python double-ended queue @@ -13,44 +11,35 @@ def __iter__(self): # iterate over queue def __len__(self): return len(self.__queue) - # def setToInQueue(self): - # for job in self.__queue: - # job.status = "inqueue" + def setToInQueue(self): + for job in self.__queue: + job.status = "inqueue" - # if no priority add to end of queue. If priority add to front of queue. def addToBack(self, job, printerid): print("Adding job to back of queue ", job.id) print("Adding job to back of queue ", printerid) if self.__queue.count(job) > 0: - #raise Exception("Job ID already in queue.") return False - self.__queue.append( - job - ) # appending on the right is the "front" because popping takes out from right d - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - # ) + self.__queue.append(job) + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + ) return True def addToFront(self, job): if self.__queue.count(job) > 0: - #raise Exception("Job ID already in queue.") return False - # If the queue has at least one job and the first job is printing, - # insert at the second position because we don't want to interrupt it. if len(self.__queue) >= 1 and self.__queue[0].status == "printing": self.__queue.insert(1, job) - # If the queue is empty or the first job is not printing, - # add the job to the front else: self.__queue.appendleft(job) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - # ) + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} + ) return True - def bump(self, up, jobid): # up = boolean. if up = true bump up, else bump down + def bump(self, up, jobid): index = next( ( index @@ -64,20 +53,25 @@ def bump(self, up, jobid): # up = boolean. if up = true bump up, else bump down return job_to_move = self.__queue[index] self.__queue.remove(job_to_move) - if up == True and index > 0: + if up and index > 0: self.__queue.insert(index - 1, job_to_move) elif not up and index < len(self.__queue): self.__queue.insert(index + 1, job_to_move) - - def reorder(self, arr): - # arr is an array of job ids in the order they should be in the queue + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": job_to_move.fabricator_id} + ) + + def reorder(self, arr): new_queue = deque() - for jobid in arr: - for job in self.__queue: - if job.getJobId() == jobid: + for jobid in arr: + for job in self.__queue: + if job.getJobId() == jobid: new_queue.append(job) break self.__queue = new_queue + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} + ) def deleteJob(self, jobid, printerid): deletedjob = None @@ -85,16 +79,15 @@ def deleteJob(self, jobid, printerid): if job.getJobId() == jobid: deletedjob = job self.__queue.remove(job) - # current_app.socketio.emit( - # "queue_update", - # {"queue": self.convertQueueToJson(), "printerid": printerid}, - # ) + current_app.socketio.emit( + "queue_update", + {"queue": self.convertQueueToJson(), "printerid": printerid}, + ) return deletedjob return "Job not found in queue." def convertQueueToJson(self): queue = [] - # job_info = {} for job in self.__queue: job_info = { "id": job.id, @@ -121,7 +114,7 @@ def convertQueueToJson(self): queue.append(job_info) return queue - def bumpExtreme(self, front, jobid, printerid): # bump to back/front of queue + def bumpExtreme(self, front, jobid, printerid): index = next( ( index @@ -135,29 +128,28 @@ def bumpExtreme(self, front, jobid, printerid): # bump to back/front of queue return job_to_move = self.__queue[index] self.__queue.remove(job_to_move) - if front == True: - # If the queue has at least one job and the first job is printing, - # insert at the second position because we don't want to interrupt it. + if front: if len(self.__queue) >= 1 and self.__queue[0].status == "printing": self.__queue.insert(1, job_to_move) - # If the queue is empty or the first job is not printing, - # add the job to the front else: self.__queue.insert(0, job_to_move) else: self.addToBack(job_to_move, printerid) + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + ) def getJob(self, job_to_find): for job in self.__queue: if job.getJobId() == job_to_find.getJobId(): return job - return None # Return None if job is not found in the queue + return None def getJobById(self, job_to_find): for job in self.__queue: if job.getJobId() == job_to_find: return job - return None # Return None if job is not found in the queue + return None def jobExists(self, jobid): for job in self.__queue: @@ -176,4 +168,4 @@ def getSize(self): def removeJob(self): self.__queue.pop() - # current_app.socketio.emit('job_removed', {'queue': list(self.__queue)}, broadcast=True) + current_app.socketio.emit('job_removed', {'queue': list(self.__queue)}, broadcast=True) \ No newline at end of file diff --git a/server/Classes/FabricatorStatusService.py b/server/models/FabricatorStatusService.py similarity index 99% rename from server/Classes/FabricatorStatusService.py rename to server/models/FabricatorStatusService.py index 6f44fcd6..8f21dd3b 100644 --- a/server/Classes/FabricatorStatusService.py +++ b/server/models/FabricatorStatusService.py @@ -4,7 +4,7 @@ from Classes.Fabricators.Fabricator import Fabricator - +# DEAD JUST MOVED HERE BC I DONT TRUST MYSELF class FabricatorThread(Thread): def __init__(self, fabricator, *args, **kwargs): super().__init__(*args, **kwargs) From deff174c597dfe31c37ac9231ec91ae923c9cabf Mon Sep 17 00:00:00 2001 From: iron768 Date: Sun, 17 Nov 2024 21:20:09 -0500 Subject: [PATCH 126/194] fix: update exp go library --- printeremu/go.mod | 2 +- printeremu/go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/printeremu/go.mod b/printeremu/go.mod index da0d32bc..69472aff 100644 --- a/printeremu/go.mod +++ b/printeremu/go.mod @@ -7,5 +7,5 @@ require github.com/gorilla/websocket v1.5.0 require ( github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gomodule/redigo v1.8.9 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect ) diff --git a/printeremu/go.sum b/printeremu/go.sum index b9c852b5..949e5b72 100644 --- a/printeremu/go.sum +++ b/printeremu/go.sum @@ -27,6 +27,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= From bd92512cc5edd0ffb27958e500b2c5a52a2c8812 Mon Sep 17 00:00:00 2001 From: L10nhunter Date: Thu, 21 Nov 2024 14:11:02 -0500 Subject: [PATCH 127/194] feat: updated tests for integration with FabricatorList --- Tests/conftest.py | 73 +++++++++++++++- Tests/test_app.py | 154 ++++++++++++++++++++++++++++------ Tests/test_device.py | 57 +++++-------- Tests/test_fabricator.py | 55 +++--------- Tests/test_fabricator_list.py | 21 +++++ 5 files changed, 251 insertions(+), 109 deletions(-) create mode 100644 Tests/test_fabricator_list.py diff --git a/Tests/conftest.py b/Tests/conftest.py index ce172251..787b9447 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -6,6 +6,36 @@ import time import pluggy import pytest +from app import fabricator_list + +@pytest.fixture(scope="session") +def fabricator(request, app): + port = request.session.config.port + if not port: return None + from serial.tools.list_ports_common import ListPortInfo + from serial.tools.list_ports_linux import SysFS + if isinstance(port, str): + fabricator = fabricator_list.getFabricatorByPort(port) + elif isinstance(port, ListPortInfo) or isinstance(port, SysFS): + fabricator = fabricator_list.getFabricatorByPort(port.device) + else: + fabricator = None + if fabricator is None: + pytest.skip("No port specified") + if os.getenv("LEVEL") == "DEBUG": + fabricator.device.logger.setLevel(fabricator.device.logger.DEBUG) + fabricator.device.connect() + yield fabricator + fabricator.device.disconnect() + +@pytest.fixture(scope="session") +def app(): + from app import app + app = app + with app.app_context(): + yield app + fabricator_list.teardown() + from Classes.Logger import Logger @@ -29,7 +59,7 @@ def pytest_addoption(parser): help="port to test" ) -def line_separator(interrupter: str, symbol: str = "-", length: int = 104) -> str: +def line_separator(interrupter: str, symbol: str = "-", length: int = 136) -> str: if not interrupter: return symbol * (length//len(symbol)) interrupterNoColor = re.sub(r'\033\[[0-9;]*m', '', interrupter) @@ -85,6 +115,19 @@ def pytest_sessionstart(session) -> None: def pytest_collection_modifyitems(session, config, items): session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...") + file_order = [ + "test_app.py", + "test_fabricator_list.py", + "test_device.py", + "test_fabricator.py", + ] + + def get_file_order(item): + file_name = item.location[0] + return file_order.index(file_name) if file_name in file_order else len(file_order) + + # Sort the items based on the file order + items.sort(key=get_file_order) def pytest_sessionfinish(session, exitstatus) -> None: session_duration = time.time() - session.config.start_time @@ -128,7 +171,18 @@ def pytest_sessionfinish(session, exitstatus) -> None: logger.logMessageOnly(headerText, logLevel=logger.ERROR) for failTest in session.config.failNames: logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) - logger.logException(session.config.fails[failTest].reprtraceback.reprentries) + if not hasattr(session.config.fails[failTest], "reprtraceback"): + if not hasattr(session.config.fails[failTest], "longrepr"): + if hasattr(session.config.fails[failTest], "errorstring"): + logger.error(session.config.fails[failTest].errorstring) + else: + logger.error(session.config.fails[failTest]) + else: + logger.error(session.config.fails[failTest].longrepr) + elif not hasattr(session.config.fails[failTest].reprtraceback, "reprentries"): + logger.error(session.config.fails[failTest].reprtraceback) + else: + logger.logException(session.config.fails[failTest].reprtraceback.reprentries) logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) visited_modules = set() @@ -192,7 +246,7 @@ def pytest_runtest_logreport(report): logger.info(f"IDK what happened!?!?: {report}") elif verbosity == 1: loc = report.nodeid.split("::")[-1] - testString = f"{loc}[{port}]{' ' * (27 - len(loc) - len(str(port)) - 2)}" + testString = f"{loc}[{port}]{' ' * (59 - len(loc) - len(str(port)) - 2)}" if report.passed: logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: @@ -207,11 +261,22 @@ def pytest_runtest_logreport(report): logger.info(f"{testString} IDK what happened!?!?: {report}") elif verbosity >= 2: loc = report.nodeid - testString = f"{loc}[{port}]{' ' * (47 - len(loc) - len(str(port)) - 2)}" + testString = f"{loc}[{port}]{' ' * (79 - len(loc) - len(str(port)) - 2)}" if report.passed: logger.info(f"{testString} \033[32mPASSED\033[0m") elif report.failed: logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n") + if not hasattr(report, "longrepr"): + if hasattr(report, "errorstring"): + logger.error(report.errorstring) + else: + logger.error(report) + elif not hasattr(report.longrepr, "reprtraceback"): + logger.error(report.longrepr) + elif not hasattr(report.longrepr.reprtraceback, "reprentries"): + logger.error(report.longrepr.reprtraceback) + else: + logger.logException(report.longrepr.reprtraceback.reprentries) logger.logException(report.longrepr.reprtraceback.reprentries) elif report.skipped: logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") diff --git a/Tests/test_app.py b/Tests/test_app.py index 49442578..39dc111a 100644 --- a/Tests/test_app.py +++ b/Tests/test_app.py @@ -3,31 +3,131 @@ import pytest from Classes.Logger import Logger from parallel_test_runner import testLevel -from app import app + def __desc__(): return "App Tests" -with app.app_context(): - @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") - def test_db(): - db_file_no_path = app.config["SQLALCHEMY_DATABASE_URI"].split("/")[-1].split("\\")[-1] - assert db_file_no_path, "database_uri doesn't exist?" - assert db_file_no_path == "hvamc.db", f"database_uri is {db_file_no_path}" - assert os.path.exists(app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]), f"Database file {app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]} does not exist" - - @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") - def test_base_url(): - assert app.config["base_url"], "base_url doesnt exist?" - assert re.match(r"http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$", app.config["base_url"]) or re.match(r"http://localhost:\d{1,5}$", app.config["base_url"]), f"base_url is {app.config['base_url']}" - - @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") - def test_environment(): - assert app.config["environment"], "environment doesnt exist?" - assert app.config["environment"] == "development", f"environment is {app.config['environment']}" - - @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") - def test_logger(): - assert app.logger, "myLogger doesnt exist?" - assert app.logger.name, "name doesnt exist?" - assert str(app.logger.name) == "Logger_App", f"myLogger is {str(app.logger.name)}" - assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" - assert app.logger.fileLogger, "fileLogger doesnt exist?" - assert app.logger.consoleLogger, "consoleLogger doesn't exists?" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_db_to_make_sure_it_has_valid_file_path(app): + db_file_no_path = app.config["SQLALCHEMY_DATABASE_URI"].split("/")[-1].split("\\")[-1] + assert db_file_no_path, "database_uri doesn't exist?" + assert db_file_no_path == "hvamc.db", f"database_uri is {db_file_no_path}" + assert os.path.exists(app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[ + -1]), f"Database file {app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]} does not exist" + + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_base_url_for_http_responses_has_valid_format(app): + assert app.config["base_url"], "base_url doesnt exist?" + assert re.match(r"http://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$", app.config["base_url"]) or re.match( + r"http://localhost:\d{1,5}$", app.config["base_url"]), f"base_url is {app.config['base_url']}" + + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_environment_for_development(app): + assert app.config["environment"], "environment doesnt exist?" + assert app.config["environment"] == "development", f"environment is {app.config['environment']}" + + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_logger_is_custom_implementation_and_exists(app): + assert app.logger, "myLogger doesnt exist?" + assert app.logger.name, "name doesnt exist?" + assert str(app.logger.name) == "Logger_App", f"myLogger is {str(app.logger.name)}" + assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" + assert app.logger.fileLogger, "fileLogger doesnt exist?" + assert app.logger.consoleLogger, "consoleLogger doesn't exists?" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_socketio_exists_and_works(app): + assert app.socketio, "socketio doesnt exist?" + assert app.socketio.async_mode, "async_mode doesnt exist?" + assert app.socketio.async_mode == "threading", f"async_mode is {app.socketio.async_mode}" + socketio_test_client = app.socketio.test_client(app) + assert socketio_test_client.is_connected(), "socketio_test_client is not connected?" + socketio_test_client.emit('my_event', {'data': 'test'}) + received = socketio_test_client.get_received() + assert len(received) == 0, "Response received from socketio" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_handle_errors_and_logging(app): + assert app.handle_errors_and_logging, "handle_errors_and_logging doesnt exist?" + assert callable(app.handle_errors_and_logging), "handle_errors_and_logging is not callable?" + assert app.handle_errors_and_logging(Exception("Test Exception")) is False, "handle_errors_and_logging did not return False?" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_static_loading_for_client(app): + assert app.static_folder, "static_folder doesnt exist?" + assert "../client/dist" in app.static_folder, f"static_folder is {app.static_folder}" + assert os.path.exists(app.static_folder), f"static_folder {app.static_folder} does not exist" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_index_html_exists_in_the_static_files(app): + assert os.path.exists(os.path.join(app.static_folder, "index.html")), f"index.html does not exist in {app.static_folder}" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_main_view_response_is_200(app): + with app.test_client() as client: + response = client.get('/') + assert response.status_code == 200, f"Response status code is {response.status_code}" + assert response.data, "Response data is empty?" + assert b'' in response.data, "Response data does not contain ?" + assert b'?" + assert b'' in response.data, "Response data does not contain ?" + assert b'' in response.data, "Response data does not contain <title>?" + assert b'<body>' in response.data, "Response data does not contain <body>?" + assert b'<div id="app">' in response.data, 'Response data does not contain <div id="app">?' + assert b'<script type="module" crossorigin src=' in response.data, 'Response data does not contain <script type="module" crossorigin src=?' + assert b'<link rel="stylesheet" crossorigin href=' in response.data, 'Response data does not contain <link rel="stylesheet" crossorigin href=?' + assert b'</body>' in response.data, "Response data does not contain </body>?" + assert b'</html>' in response.data, "Response data does not contain </html>?" + +# @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +# def test_queue_view_response(app): +# with app.test_client() as client: +# response = client.get('/queue') +# assert response.status_code == 200, f"Response status code is {response.status_code}" +# assert response.data, "Response data is empty?" +# assert b'<!DOCTYPE html>' in response.data, "Response data does not contain <!DOCTYPE html>?" +# assert b'<html' in response.data, "Response data does not contain <html>?" +# assert b'<head>' in response.data, "Response data does not contain <head>?" +# assert b'<title>' in response.data, "Response data does not contain <title>?" +# assert b'<body>' in response.data, "Response data does not contain <body>?" +# assert b'<div id="app">' in response.data, 'Response data does not contain <div id="app">?' +# assert b'<script type="module" crossorigin src=' in response.data, 'Response data does not contain <script type="module" crossorigin src=?' +# assert b'<link rel="stylesheet" crossorigin href=' in response.data, 'Response data does not contain <link rel="stylesheet" crossorigin href=?' +# assert b'</body>' in response.data, "Response data does not contain </body>?" +# assert b'</html>' in response.data, "Response data does not contain </html>?" +# +# @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +# def test_registered_view_response(app): +# with app.test_client() as client: +# response = client.get('/registration') +# assert response.status_code == 200, f"Response status code is {response.status_code}" +# assert response.data, "Response data is empty?" +# assert b'<!DOCTYPE html>' in response.data, "Response data does not contain <!DOCTYPE html>?" +# assert b'<html' in response.data, "Response data does not contain <html>?" +# assert b'<head>' in response.data, "Response data does not contain <head>?" +# assert b'<title>' in response.data, "Response data does not contain <title>?" +# assert b'<body>' in response.data, "Response data does not contain <body>?" +# assert b'<div id="app">' in response.data, 'Response data does not contain <div id="app">?' +# assert b'<script type="module" crossorigin src=' in response.data, 'Response data does not contain <script type="module" crossorigin src=?' +# assert b'<link rel="stylesheet" crossorigin href=' in response.data, 'Response data does not contain <link rel="stylesheet" crossorigin href=?' +# assert b'</body>' in response.data, "Response data does not contain </body>?" +# assert b'</html>' in response.data, "Response data does not contain </html>?" +# +# @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +# def test_error_view_response(app): +# with app.test_client() as client: +# response = client.get('/error') +# assert response.status_code == 200, f"Response status code is {response.status_code}" +# assert response.data, "Response data is empty?" +# assert b'<!DOCTYPE html>' in response.data, "Response data does not contain <!DOCTYPE html>?" +# assert b'<html' in response.data, "Response data does not contain <html>?" +# assert b'<head>' in response.data, "Response data does not contain <head>?" +# assert b'<title>' in response.data, "Response data does not contain <title>?" +# assert b'<body>' in response.data, "Response data does not contain <body>?" +# assert b'<div id="app">' in response.data, 'Response data does not contain <div id="app">?' +# assert b'<script type="module" crossorigin src=' in response.data, 'Response data does not contain <script type="module" crossorigin src=?' +# assert b'<link rel="stylesheet" crossorigin href=' in response.data, 'Response data does not contain <link rel="stylesheet" crossorigin href=?' +# assert b'</body>' in response.data, "Response data does not contain </body>?" +# assert b'</html>' in response.data, "Response data does not contain </html>?" \ No newline at end of file diff --git a/Tests/test_device.py b/Tests/test_device.py index b09d2a02..7f3a5f15 100644 --- a/Tests/test_device.py +++ b/Tests/test_device.py @@ -1,23 +1,11 @@ import os -import sys import pytest - from Classes.Ports import Ports -from Classes.Fabricators.Fabricator import Fabricator from Classes.Vector3 import Vector3 - -def device_setup(port): - if not port: return None - dev = Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) - if os.environ["LEVEL"] == "DEBUG": - dev.logger.setLevel(dev.logger.DEBUG) - return Fabricator.createDevice(Ports.getPortByName(port), consoleLogger=sys.stdout) - - from parallel_test_runner import testLevel + testLevelToRun = testLevel shortTest = True -device = Fabricator.createDevice(None, consoleLogger=sys.stdout) def __desc__(): return "Device Tests" @@ -25,47 +13,44 @@ def __desc__(): def __repr__(): return f"test_device.py running on port {Ports.getPortByName(os.getenv('PORT'))}" -@pytest.fixture(scope="module", autouse=True) -def function_setup(request): - global device - device = device_setup(request.session.config.port) - if device is None: - pytest.skip("No port specified") - device.connect() - yield - device.disconnect() - @pytest.mark.dependency() @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") -def test_connection(): - assert device is not None, f"No printer connected on {device.DESCRIPTION}" - +def test_connection(app, fabricator): + assert fabricator.device is not None, f"No printer connected on {fabricator.device.DESCRIPTION}" + assert fabricator.device.serialConnection is not None, f"No serial connection on {fabricator.device.DESCRIPTION}" + assert fabricator.device.serialConnection.isOpen(), f"Serial connection not open on {fabricator.device.DESCRIPTION}" @pytest.mark.dependency(depends=["test_connection"]) @pytest.mark.skipif(condition=testLevelToRun < 3, reason="Not doing lvl 3 tests") -def test_home(): - assert device.home(), f"Failed to home {device.DESCRIPTION}" +def test_home(app, fabricator): + assert fabricator.device.home(), f"Failed to home {fabricator.device.DESCRIPTION}" @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_square(): +def test_draw_square(app, fabricator): squarePasses = 0 for point in [Vector3(50.0, 50.0, 2.0), Vector3(200.0, 50.0, 2.0), Vector3(200.0, 150.0, 2.0), Vector3(50.0, 150.0, 2.0)]: - squarePasses += 1 if device.goTo(point, isVerbose=True) else 0 - assert squarePasses == 4, f"Failed to draw square on {device.DESCRIPTION}" + squarePasses += 1 if fabricator.device.goTo(point, isVerbose=True) else 0 + assert squarePasses == 4, f"Failed to draw square on {fabricator.device.DESCRIPTION}" @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_octagon(): +def test_draw_octagon(app, fabricator): octagonPasses = 0 for point in [Vector3(50.0, 100.0, 2.0), Vector3(100.0, 50.0, 2.0), Vector3(150.0, 50.0, 2.0), Vector3(200.0, 100.0, 2), Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)]: - octagonPasses += 1 if device.goTo(point) else 0 - assert octagonPasses == 8, f"Failed to draw octagon on {device.DESCRIPTION}" + octagonPasses += 1 if fabricator.device.goTo(point) else 0 + assert octagonPasses == 8, f"Failed to draw octagon on {fabricator.device.DESCRIPTION}" @pytest.mark.dependency(depends=["test_home"]) @pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") -def test_center(): - assert device.goTo(Vector3(125.0, 100.0, 2.0)), f"Failed to go to location on {device.DESCRIPTION}" \ No newline at end of file +def test_go_to_center(app, fabricator): + assert fabricator.device.goTo(Vector3(125.0, 100.0, 2.0)), f"Failed to go to location on {fabricator.device.DESCRIPTION}" + +@pytest.mark.dependency(depends=["test_go_to_center"]) +@pytest.mark.skipif(condition=testLevelToRun < 5, reason="Not doing lvl 5 tests") +def test_draw_circle(app, fabricator): + assert fabricator.device.sendGcode(b"G2 X125 Y100 I25 J0\n"), f"Failed to draw circle on {fabricator.device.DESCRIPTION}" + assert fabricator.device.sendGcode(b"G2 X125 Y100 I-25 J0\n"), f"Failed to draw circle on {fabricator.device.DESCRIPTION}" \ No newline at end of file diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index 29c38823..c4841961 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -1,49 +1,28 @@ import os -import sys from datetime import datetime import re import pytest +from app import fabricator_list from Classes.Jobs import Job -from Classes.Fabricators.Fabricator import Fabricator from parallel_test_runner import testLevel testLevelToRun = testLevel shortTest = True -fabricator = Fabricator(None, "Test Printer", addToDB=False, consoleLogger=sys.stdout) -isVerbose = False def __desc__(): return "Fabricator Tests" def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" -def cali_cube_setup(): +def cali_cube_setup(fabricator=None): + if fabricator is None: + fabricator = fabricator_list.getFabricatorByPort(os.getenv("PORT")) file = "../server/xyz-cali-cube" if shortTest: file = file + "-mini" file = file + f"_{fabricator.device.MODEL}.gcode" return file -def fabricator_setup(port): - if not port: return None - from Classes.Ports import Ports - fab = Fabricator(Ports.getPortByName(port), "Test Printer", addToDB=False, consoleLogger=sys.stdout) - if os.getenv("LEVEL") == "DEBUG": - fab.device.logger.setLevel(fab.device.logger.DEBUG) - return fab - -@pytest.fixture(scope="module", autouse=True) -def function_setup(request): - global fabricator - fabricator = fabricator_setup(request.session.config.port) - global isVerbose - isVerbose = fabricator.device.logger.level <= fabricator.device.logger.DEBUG - if fabricator is None: - pytest.skip("No port specified") - yield - fabricator.device.disconnect() - fabricator = None - @pytest.mark.dependency(depends=["test_device.py::test_connection"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") -def test_status(): +def test_status(app, fabricator): assert fabricator.getStatus() is not None, f"Failed to get status on {fabricator.getDescription()}" assert fabricator.device.status is not None, f"Failed to get status on device of {fabricator.getDescription()}" assert fabricator.getStatus() == fabricator.device.status, f"Internal status mismatch: fabricator: {fabricator.getDescription()}, device: {fabricator.device.status}" @@ -51,8 +30,8 @@ def test_status(): @pytest.mark.dependency(depends=["test_device.py::test_connection"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") -def test_add_job(): - file = cali_cube_setup() +def test_add_job(app, fabricator): + file = cali_cube_setup(fabricator=fabricator) with open(file, "r") as f: assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name)), f"Failed to add job on {fabricator.getDescription()}" for job in fabricator.queue.getQueue(): @@ -62,7 +41,7 @@ def test_add_job(): @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 7, reason="Not doing lvl 7 tests") -def test_pause_and_resume(): +def test_pause_and_resume(app, fabricator): from Mixins.canPause import canPause if not isinstance(fabricator.device, canPause): pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") @@ -82,9 +61,7 @@ def parse_gcode(): with open(file, "r") as f: job = Job(f.read(), "pauseAndResumeTest", fabricator.dbID, "ready", file, False, 1, fabricator.name) fabricator.queue.addToFront(job) - print("Beginning") result = fabricator.begin() - print("Finished") import traceback assert not isinstance(result, Exception), f"Failed to begin on {fabricator.getDescription()}: {result}:\n{''.join(traceback.format_exception(None, result, result.__traceback__))}" assert fabricator.getStatus() == fabricator.device.status == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, expected cancel, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" @@ -99,13 +76,10 @@ def pause_and_resume_fabricator(): while fabricator.getStatus() != "printing": assert fabricator.getStatus() != "error", f"Failed to print on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(1) - print("Pausing") assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(30) - print("Resuming") assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(1) - print("Cancelling") assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(10) @@ -125,15 +99,14 @@ def pause_and_resume_fabricator(): for future in as_completed([parse_future, pause_future]): future.result() -#@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") -#@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") -def test_gcode_print_time(): - print("Testing gcode print time") - print(fabricator.device.logger.level) +@pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") +@pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") +def test_gcode_print_time(app, fabricator): + isVerbose = os.getenv("LEVEL") == "DEBUG" from Classes.Fabricators.Printers.Printer import Printer if not isinstance(fabricator.device, Printer): pytest.skip(f"{fabricator.getDescription()} doesn't support printing gcode") - file = cali_cube_setup() + file = cali_cube_setup(fabricator=fabricator) # expectedTime = 2040 # for my personal home test, 1072 from Classes.Fabricators.Fabricator import getFileConfig config = getFileConfig(file) @@ -189,7 +162,6 @@ def test_gcode_print_time(): printList.append(f"{printSeconds:02}") printString = ":".join(map(str, printList)) - minutes, seconds = divmod(time.seconds, 60) hours, minutes = divmod(minutes, 60) days, hours = divmod(hours, 24) @@ -210,7 +182,6 @@ def test_gcode_print_time(): measuredTimeList.append(f"{seconds:02}") measuredTimeString = ":".join(measuredTimeList) - expectedList = [] if expectedDays > 0: expectedList.append(f"{expectedDays:02}") diff --git a/Tests/test_fabricator_list.py b/Tests/test_fabricator_list.py new file mode 100644 index 00000000..f7c6d5b8 --- /dev/null +++ b/Tests/test_fabricator_list.py @@ -0,0 +1,21 @@ +import pytest + +from parallel_test_runner import testLevel + +def __desc__(): + return "Fabricator List Tests" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_fabricator_list_has_at_least_one_fabricator(app): + assert len(app.FabricatorList) > 0, "Fabricator list is empty" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_fabricator_list_has_fabricator_from_fixture(app, fabricator): + assert fabricator is not None, "Fabricator is None" + assert fabricator in app.fabricator_list, "Fabricator not in list" + +@pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") +def test_fabricator_thread_is_running(app, fabricator): + thread = app.fabricator_list.get_fabricator_thread(fabricator) + assert thread.is_alive(), "Fabricator thread is not running" + assert thread.daemon, "Fabricator thread is not daemon" From ac4b547065bd1bb830055cfc0943ff166cf7593b Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 14:11:37 -0500 Subject: [PATCH 128/194] fix: use abs path for config file loading so server and tests are both happy. --- server/models/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/models/config.py b/server/models/config.py index b8436523..960ae85a 100644 --- a/server/models/config.py +++ b/server/models/config.py @@ -10,7 +10,8 @@ def load_config(file_path): config = json.load(config_file) return config -config = load_config('./config/config.json') +configFileLoc = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config', 'config.json')) +config = load_config(configFileLoc) environment = config.get('environment', 'development') ip = config.get('ip', '127.0.0.1') database_uri = config.get('databaseURI', 'hvamc') + ".db" From 51eb52b4418c213dbbad0eb8d85796f1bb37a84d Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:12:49 -0500 Subject: [PATCH 129/194] fix: added logging for fixture errors --- server/Classes/Fabricators/Device.py | 7 ++-- server/Classes/Fabricators/Fabricator.py | 44 ++++++++++++++---------- server/Classes/Logger.py | 34 ++++++++++-------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 04f718ce..d7ac6a46 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -34,9 +34,10 @@ def connect(self): self.serialConnection.reset_input_buffer() return True except Exception as e: - from app import app - with app.app_context(): - return app.handle_error_and_logging(e, self) + from app import handle_errors_and_logging + handle_errors_and_logging(e, self) + return False + def disconnect(self): if self.serialConnection: self.serialConnection.close() diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 9e548f5a..1f00bcb5 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -3,7 +3,6 @@ from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError - from Classes.Fabricators.Device import Device from Mixins.canPause import canPause from Mixins.hasEndingSequence import hasEndingSequence @@ -42,14 +41,12 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", addToDB: b self.description = self.device.getDescription() self.hwid = self.device.getHWID() self.devicePort = self.device.getSerialPort().device - - self.device.connect() if addToDB: db.session.add(self) db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}" @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: """returns the model of the printer based on the response to M997""" @@ -104,8 +101,12 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi return None @classmethod - def queryAll(cls) -> list["Fabricator"]: - """return all fabricators in the database""" + def queryAll(cls): + """ + Returns all fabricators in the database as a list of the Fabricator objects + + :return (list[Fabricator]): list of Fabricator objects + """ fabList = [] from Classes.Ports import Ports for fab in cls.query.all(): @@ -141,18 +142,19 @@ def begin(self, isVerbose: bool = False): if isVerbose: self.device.logger.debug(f"Verdict handled, status: {self.status}") return True except Exception as e: - from app import app - with app.app_context(): - app.handle_error_and_logging(e, self) - return e + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) def pause(self): """pauses the fabrication process if the fabricator supports it""" if not isinstance(self.device, canPause): - return # TODO: return error message + from app import handle_errors_and_logging + return handle_errors_and_logging("Fabricator doesn't support pausing", self) + assert isinstance(self.device, Device) if self.status != "printing": - return # TODO: return error message + from app import handle_errors_and_logging + return handle_errors_and_logging("Fabricator doesn't support pausing", self) self.setStatus("paused") assert isinstance(self.device, Device) return self.status == self.device.status == "paused" @@ -178,10 +180,8 @@ def cancel(self): self.setStatus("cancelled") return self.status == self.device.status == "cancelled" except Exception as e: - from app import app - with app.app_context(): - app.handle_error_and_logging(e, self) - return False + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) def getStatus(self): return self.status @@ -204,9 +204,8 @@ def setStatus(self, newStatus): # ) return True except Exception as e: - from app import app - app.handle_error_and_logging(e, self) - return False + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) def resetToIdle(self): #TODO: send message to front end insuring that the print bed is clear and that the job is done @@ -254,6 +253,13 @@ def getHwid(self): def getDescription(self): return self.description + def getSerialPort(self): + if self.device is None: + return None + return self.device.getSerialPort() + + def getQueue(self): + return self.queue def checkValidJob(self): """checks if the job is valid for the fabricator""" diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index ac927782..b538395e 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -3,6 +3,7 @@ import sys import traceback from _pytest._code.code import ExceptionChainRepr, ReprEntry, ReprEntryNative +from _pytest.fixtures import FixtureLookupErrorRepr from typing_extensions import Sequence @@ -32,12 +33,9 @@ def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, l if consoleLogger is not None: consoleLogger = logging.StreamHandler(consoleLogger) consoleLogger.setLevel(loggingLevel) - else: - consoleLogger = logging.StreamHandler(sys.stdout) - consoleLogger.setLevel(self.ERROR) - consoleLogger.setFormatter(CustomFormatter(formatString)) - self.consoleLogger = consoleLogger - self.addHandler(consoleLogger) + consoleLogger.setFormatter(CustomFormatter(formatString)) + self.consoleLogger = consoleLogger + self.addHandler(consoleLogger) if fileLogger is None: log_folder = "./server/logs" os.makedirs(log_folder, exist_ok=True) @@ -58,16 +56,12 @@ def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, l def formatLog(self, msg): if isinstance(msg, str): pass - elif isinstance(msg, Exception): - msg = traceback.format_exception(msg) - msg = "".join(msg) elif isinstance(msg, ExceptionChainRepr): msg = msg.reprtraceback.__repr__() + elif isinstance(msg, Exception): + msg = "".join(traceback.format_exception(None, msg, msg.__traceback__)) elif isinstance(msg, list) or isinstance(msg, tuple): - msgList = msg - msg = "" - for line in msgList: - msg += self.formatLog(line) + msg = "".join(msg) else: msg = str(msg) return msg @@ -117,8 +111,8 @@ def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *a for handler, formatter in zip(self.handlers, oldFormatters): handler.setFormatter(formatter) - def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRepr | list[ReprEntry | ReprEntryNative] | Sequence[ReprEntry | ReprEntryNative] | ReprEntry | str): - if isinstance(reprentries, ExceptionChainRepr) or isinstance(reprentries, ReprEntry): + def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRepr | list[ReprEntry | ReprEntryNative] | Sequence[ReprEntry | ReprEntryNative]| list[FixtureLookupErrorRepr] | FixtureLookupErrorRepr | ReprEntry | str): + if isinstance(reprentries, ExceptionChainRepr) or isinstance(reprentries, ReprEntry) or isinstance(reprentries, FixtureLookupErrorRepr): reprentries = [reprentries] if isinstance(reprentries, list): for index, reprentry in enumerate(reprentries): @@ -139,6 +133,16 @@ def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRep chain = reprentry.chain for link in chain: self.logException(link[0].reprentries) + elif isinstance(reprentry, FixtureLookupErrorRepr): + for line in reprentry.tblines: + self.logMessageOnly(line.rstrip()) + lines = reprentry.errorstring.split("\n") + if lines: + for line in lines: + if line.startswith("E") or line.startswith(">"): + self.logMessageOnly(line, logLevel=self.ERROR) + else: + self.logMessageOnly(line) if index < len(reprentries) - 1: from conftest import line_separator From 06eb85a30ba838079b275635d3224bfb0f30e07f Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:20:56 -0500 Subject: [PATCH 130/194] fix: added important locations to path for calling, and added catches and logging for threads --- Tests/parallel_test_runner.py | 37 ++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 8d5fa00e..efb5117a 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -1,8 +1,22 @@ import os +import sys import re import subprocess import platform + +# Add test root to sys.path if needed +rootpath=os.path.abspath(os.path.join(os.path.dirname(__file__).split("QView3D")[0], 'QView3D')) +if rootpath not in sys.path: + sys.path.append(rootpath) +serverpath = os.path.join(rootpath, "server") +if serverpath not in sys.path: + sys.path.append(serverpath) +testpath = os.path.join(rootpath, "Tests") +if testpath not in sys.path: + sys.path.append(testpath) + from server.Classes.Ports import Ports +from app import app PORTS = [] # List of available ports for testing @@ -28,23 +42,28 @@ PORTS = glob.glob("/dev/tty[A-Za-z]*") # Function to run pytest for a specific port -testLevel = 10 +testLevel = 0 verbosity = 2 -runFlags = 0b000 # 0b001: -s, 0b010: -vvv or -p no:terminal, 0b100: debug or info +runFlags = 0b010 # 0b001: -s, 0b010: -vvv or -p no:terminal, 0b100: debug or info def run_tests_for_port(comm_port): env = os.environ.copy() env["PORT"] = comm_port - args = ["pytest", ".", f"--myVerbose={verbosity}", f"--port={comm_port}"] + args = ["pytest", testpath, f"--myVerbose={verbosity}", f"--port={comm_port}"] if runFlags & 0b1: args.append("-s") - args.append("-vvv") if runFlags & 0b10 else args.append("-p no:terminal") + args.append("-vv") if runFlags & 0b10 else args.append("-p no:terminal") env["LEVEL"] = "DEBUG" if runFlags & 0b100 else "INFO" subprocess.Popen(args, env=env).wait() if __name__ == "__main__": from concurrent.futures import ThreadPoolExecutor, as_completed - - with ThreadPoolExecutor(max_workers=len(PORTS)) as executor: - futures = [executor.submit(run_tests_for_port, port) for port in PORTS if Ports.getPortByName(port) is not None] - for future in as_completed(futures): - future.result() \ No newline at end of file + with app.app_context(): + if len(PORTS) != 0: + with ThreadPoolExecutor(max_workers=len(PORTS)) as executor: + futures = [executor.submit(run_tests_for_port, port) for port in PORTS if Ports.getPortByName(port) is not None] + for future in as_completed(futures): + try: + future.result() + except Exception as e: + from app import handle_errors_and_logging + handle_errors_and_logging(e) \ No newline at end of file From 0a45956b382105cb774eff32bdabfbad850d69ac Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:24:24 -0500 Subject: [PATCH 131/194] fix: fixed logging --- server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index bc62b194..ae675e55 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -45,6 +45,5 @@ def connect(self): self.sendGcode(b"M155 S1\n") return True except Exception as e: - from app import app - with app.app_context(): - return app.handle_error_and_logging(e, self) \ No newline at end of file + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) \ No newline at end of file From 00e000fbe8bd46ca9a476b89901f9c9c3d9c5de3 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:25:00 -0500 Subject: [PATCH 132/194] fix: changed queue to extend deque instead of just having one --- server/Classes/Queue.py | 112 ++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 5b6b0c37..bf8c34cc 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,49 +1,44 @@ from collections import deque from flask import current_app -class Queue: - def __init__(self): - self.__queue = deque() # use Python double-ended queue +from Classes.Jobs import Job - def __iter__(self): # iterate over queue - return iter(self.__queue) - - def __len__(self): - return len(self.__queue) - +class Queue(deque): def setToInQueue(self): - for job in self.__queue: + for job in self: job.status = "inqueue" - def addToBack(self, job, printerid): + def addToBack(self, job: Job, printerid): + assert isinstance(job, Job) print("Adding job to back of queue ", job.id) print("Adding job to back of queue ", printerid) - if self.__queue.count(job) > 0: + if self.count(job) > 0: return False - self.__queue.append(job) - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - ) + self.append(job) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + # ) return True def addToFront(self, job): - if self.__queue.count(job) > 0: + assert isinstance(job, Job) + if self.count(job) > 0: return False - if len(self.__queue) >= 1 and self.__queue[0].status == "printing": - self.__queue.insert(1, job) + if len(self) >= 1 and self[0].status == "printing": + self.insert(1, job) else: - self.__queue.appendleft(job) - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} - ) + self.appendleft(job) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} + # ) return True def bump(self, up, jobid): index = next( ( index - for index, queued_job in enumerate(self.__queue) + for index, queued_job in enumerate(self) if queued_job.id == jobid ), -1, @@ -51,34 +46,36 @@ def bump(self, up, jobid): if index == -1: print("Job not found in queue.") return - job_to_move = self.__queue[index] - self.__queue.remove(job_to_move) + job_to_move = self[index] + self.remove(job_to_move) if up and index > 0: - self.__queue.insert(index - 1, job_to_move) - elif not up and index < len(self.__queue): - self.__queue.insert(index + 1, job_to_move) - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": job_to_move.fabricator_id} - ) + self.insert(index - 1, job_to_move) + elif not up and index < len(self): + self.insert(index + 1, job_to_move) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": job_to_move.fabricator_id} + # ) def reorder(self, arr): new_queue = deque() for jobid in arr: - for job in self.__queue: + for job in self: if job.getJobId() == jobid: new_queue.append(job) break - self.__queue = new_queue - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} - ) + self.clear() + self.extend(new_queue) + + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} + # ) def deleteJob(self, jobid, printerid): deletedjob = None - for job in self.__queue: + for job in self: if job.getJobId() == jobid: deletedjob = job - self.__queue.remove(job) + self.remove(job) current_app.socketio.emit( "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid}, @@ -88,7 +85,7 @@ def deleteJob(self, jobid, printerid): def convertQueueToJson(self): queue = [] - for job in self.__queue: + for job in self: job_info = { "id": job.id, "name": job.name, @@ -118,7 +115,7 @@ def bumpExtreme(self, front, jobid, printerid): index = next( ( index - for index, queued_job in enumerate(self.__queue) + for index, queued_job in enumerate(self) if queued_job.id == jobid ), -1, @@ -126,46 +123,47 @@ def bumpExtreme(self, front, jobid, printerid): if index == -1: print("Job not found in queue.") return - job_to_move = self.__queue[index] - self.__queue.remove(job_to_move) + job_to_move = self[index] + self.remove(job_to_move) if front: - if len(self.__queue) >= 1 and self.__queue[0].status == "printing": - self.__queue.insert(1, job_to_move) + if len(self) >= 1 and self[0].status == "printing": + self.insert(1, job_to_move) else: - self.__queue.insert(0, job_to_move) + self.insert(0, job_to_move) else: self.addToBack(job_to_move, printerid) - current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - ) + # current_app.socketio.emit( + # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + # ) def getJob(self, job_to_find): - for job in self.__queue: + for job in self: if job.getJobId() == job_to_find.getJobId(): return job return None def getJobById(self, job_to_find): - for job in self.__queue: + for job in self: if job.getJobId() == job_to_find: return job return None def jobExists(self, jobid): - for job in self.__queue: + for job in self: + if job.id == jobid: return True return False def getQueue(self): - return self.__queue + return self def getNext(self): - return self.__queue[0] + return self[0] def getSize(self): - return len(self.__queue) + return len(self) def removeJob(self): - self.__queue.pop() - current_app.socketio.emit('job_removed', {'queue': list(self.__queue)}, broadcast=True) \ No newline at end of file + self.pop() + # current_app.socketio.emit('job_removed', {'queue': list(self)}, broadcast=True) \ No newline at end of file From 2390e3737fd0389206ab571930b940267b125c66 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:25:16 -0500 Subject: [PATCH 133/194] fix: removed old reqs --- server/requirements.txt | 84 ----------------------------------------- 1 file changed, 84 deletions(-) delete mode 100644 server/requirements.txt diff --git a/server/requirements.txt b/server/requirements.txt deleted file mode 100644 index acbe091c..00000000 --- a/server/requirements.txt +++ /dev/null @@ -1,84 +0,0 @@ - -Babel==2.8.0 -bcrypt==3.2.0 -bitarray==2.9.2 -bitstring==4.1.4 -blinker==1.7.0 - -certifi==2020.6.20 -chardet==4.0.0 -click==8.1.7 -colorama==0.4.4 - -cryptography==3.4.8 -cupshelpers==1.0 -dbus-python==1.2.18 - - -ecdsa==0.19.0 -fasteners==0.14.1 -Flask==3.0.3 -Flask-Cors==4.0.0 -Flask-SQLAlchemy==3.1.1 -future==0.18.2 -greenlet==3.0.3 -httplib2==0.20.2 -idna==3.3 -importlib-metadata==4.6.4 -intelhex==2.3.0 -iso8601==2.1.0 -itsdangerous==2.2.0 -jeepney==0.7.1 -Jinja2==3.1.3 -keyring==23.5.0 - -launchpadlib==1.10.16 -lazr.restfulclient==0.14.4 -lazr.uri==1.0.6 -lockfile==0.12.2 - -macaroonbakery==1.3.1 -Mako==1.1.3 -MarkupSafe==2.1.5 -monotonic==1.6 -more-itertools==8.10.0 -netifaces==0.11.0 -oauthlib==3.2.0 -olefile==0.46 -paramiko==2.9.3 -pexpect==4.8.0 -Pillow==9.0.1 -protobuf==3.12.4 -ptyprocess==0.7.0 -pycairo==1.20.1 -pycups==2.0.1 -PyGObject==3.42.1 -pyinotify==0.9.6 -PyJWT==2.3.0 -pymacaroons==0.13.0 -PyNaCl==1.5.0 -pyOpenSSL==21.0.0 -pyparsing==2.4.7 -pyRFC3339==1.1 -pyserial==3.5 -python-dateutil==2.8.1 -pytz==2022.1 -pyxdg==0.27 -PyYAML==5.4.1 -reedsolo==1.7.0 -reportlab==3.6.8 -requests==2.25.1 -SecretStorage==3.3.1 -simplejson==3.17.6 -six==1.16.0 -SQLAlchemy==2.0.29 -systemd-python==234 -typing_extensions==4.11.0 -tzlocal==5.2 -urllib3==1.26.5 -usb-creator==0.3.7 -wadllib==1.3.6 -Werkzeug==3.0.2 -xdg==5 -xkit==0.0.0 -zipp==1.0.0 \ No newline at end of file From 8818ff0eba2eb1b52110018c634174d4c56a0fe9 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:26:11 -0500 Subject: [PATCH 134/194] fix: removed all calls to printer status service --- server/controllers/statusService.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index b8c40ed5..4969757d 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -1,9 +1,6 @@ -from flask import Blueprint, jsonify -from app import printer_status_service # import the instance from app.py from flask import Blueprint, jsonify, request -from models.jobs import Job import os - +from app import fabricator_list status_bp = Blueprint("status", __name__) @status_bp.route('/ping', methods=["GET"]) @@ -18,7 +15,7 @@ def getOpenThreads(): @status_bp.route('/getprinterinfo', methods=["GET"]) def getPrinterInfo(): try: - printer_info = printer_status_service.retrieve_printer_info() # call the method on the instance + printer_info = fabricator_list.fabricators # call the method on the instance return jsonify(printer_info) except Exception as e: print(f"Unexpected error: {e}") @@ -29,7 +26,7 @@ def hardreset(): try: data = request.get_json() # get json data id = data['printerid'] - res = printer_status_service.resetThread(id) + res = fabricator_list.resetThread(id) return res except Exception as e: print(f"Unexpected error: {e}") @@ -41,7 +38,7 @@ def queueRestore(): data = request.get_json() # get json data id = data['printerid'] status = data['status'] - res = printer_status_service.queueRestore(id, status) + res = fabricator_list.queueRestore(id, status) return res except Exception as e: print(f"Unexpected error: {e}") @@ -52,7 +49,7 @@ def removeThread(): try: data = request.get_json() # get json data printerid = data['printerid'] - res = printer_status_service.deleteThread(printerid) + res = fabricator_list.deleteThread(printerid) return res except Exception as e: print(f"Unexpected error: {e}") @@ -64,7 +61,8 @@ def editName(): data = request.get_json() printerid = data['printerid'] name = data['newname'] - res = printer_status_service.editName(printerid, name) + fabricator_list.getFabricatorByHwid() + res = fabricator_list.editName(printerid, name) return res except Exception as e: print(f"Unexpected error: {e}") From 6a716f632f68db6923e1d9a3453ca395abf7388c Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:27:35 -0500 Subject: [PATCH 135/194] fix: removed test for console logger, since that isn't guaranteed --- Tests/test_app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_app.py b/Tests/test_app.py index 39dc111a..3ef8c36a 100644 --- a/Tests/test_app.py +++ b/Tests/test_app.py @@ -35,7 +35,6 @@ def test_logger_is_custom_implementation_and_exists(app): assert str(app.logger.name) == "Logger_App", f"myLogger is {str(app.logger.name)}" assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" assert app.logger.fileLogger, "fileLogger doesnt exist?" - assert app.logger.consoleLogger, "consoleLogger doesn't exists?" @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_socketio_exists_and_works(app): From d729c02902acbe54e418d7279c0c69640ba266fc Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:31:53 -0500 Subject: [PATCH 136/194] feat: reworked FabricatorList to no longer be static, and to be testable in app context. --- server/Classes/FabricatorList.py | 328 ++++++++++++++----------------- 1 file changed, 149 insertions(+), 179 deletions(-) diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 7d498d9e..44545826 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -11,81 +11,127 @@ import time class FabricatorThread(Thread): - def __init__(self, fabricator, *args, **kwargs): + def __init__(self, fabricator, app=None, *args, **kwargs): super().__init__(*args, **kwargs) self.fabricator = fabricator + if app: + self.app = app + else: + from app import app + self.app = app -class FabricatorList(): - fabricators: [Fabricator] = [] - fabricator_threads = [] - ping_thread = None + def run(self): + with self.app.app_context(): + while True: + time.sleep(2) + status = self.fabricator.getStatus() + queueSize = self.fabricator.getQueue().getSize() + self.fabricator.responseCount = 0 + if status == "ready" and queueSize > 0: + time.sleep(2) + if status != "offline": + self.fabricator.printNextInQueue() + + def stop(self): + self.fabricator.terminated = 1 - @staticmethod - def __iter__(): - return iter(FabricatorList.fabricators) +class FabricatorList: + def __init__(self, app=None): + self.app = app + with self.app.app_context(): + self.fabricators = Fabricator.queryAll() + self.fabricator_threads = [] + self.ping_thread = None + for fabricator in self.fabricators: + self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) - @staticmethod - def __len__(): - return len(FabricatorList.fabricators) + def __iter__(self): + return iter(self.fabricators) - @staticmethod - def init(): - """initialize the list of printers""" - FabricatorList.fabricators = Fabricator.queryAll() + def __len__(self): + return len(self.fabricators) - @staticmethod - def addFabricator(serialPortName: str, name: str = ""): - """add a printer to the list, and to the database""" + def __getitem__(self, key): + return self.fabricators[key] + + def teardown(self): + """stop all fabricator threads""" + [thread.stop() for thread in self.fabricator_threads] + self.fabricator_threads = [] + + def addFabricator(self, serialPortName: str, name: str = ""): + """add a fabricator to the list, and to the database, then start a thread for it""" serialPort: ListPortInfo | SysFS | None = Ports.getPortByName(serialPortName) - dbFab: Fabricator | None = next((fabricator for fabricator in Fabricator.queryAll() if fabricator.hwid == serialPort.hwid.split(' LOCATION=')[0]), None) - listFab: Fabricator | None = next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) + dbFab: Fabricator | None = next((fabricator for fabricator in Fabricator.queryAll() if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) + listFab: Fabricator | None = next((fabricator for fabricator in self if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) + newFab: Fabricator | None = None if dbFab is not None: # means that the fabricator is in the db if listFab is not None: # means that the fabricator is in the list and the db - print("Fabricator is already in the list and the db") + from app import handle_errors_and_logging + handle_errors_and_logging(Exception(f"Fabricator {dbFab.getname()} already exists in the list"), listFab) else: # means that the fabricator is in the db but not in the list - FabricatorList.fabricators.append(Fabricator(serialPort, name=dbFab.getname())) + newFab = Fabricator(serialPort, name=dbFab.getname()) + self.fabricators.append(newFab) else: # means that the fabricator is not in the db if listFab is not None: # means that the fabricator is in the list but not in the db - listFab.addToDB() + newFab = listFab + newFab.addToDB() else: # means that the fabricator is not in the list or the db - FabricatorList.fabricators.append(Fabricator(serialPort, name=name, addToDB=True)) - dbPrinters = Fabricator.queryAll() - assert(len(FabricatorList.fabricators) == len(dbPrinters)) - assert all(printer in FabricatorList.fabricators for printer in dbPrinters) - - @staticmethod - def deleteFabricator(printerid): - """delete a printer from the list, and from the database""" + newFab = Fabricator(serialPort, name=name, addToDB=True) + self.fabricators.append(newFab) + dbfabricators = Fabricator.queryAll() + assert(len(self) == len(dbfabricators)) + assert all(fabricator in self for fabricator in dbfabricators) + if newFab: self.start_fabricator_thread(newFab) + + + def deleteFabricator(self, fabricatorid): + """delete a fabricator from the list, and from the database""" # TODO: Implement deleteFabricator pass - @staticmethod - def getFabricatorByName(name) -> Fabricator | None: - """find the first printer with the given name""" - return next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getName() == name), None) - @staticmethod - def getPrinterByHwid(hwid) -> Fabricator | None: - """find the first printer with the given hwid""" - return next((fabricator for fabricator in FabricatorList.fabricators if fabricator.getHwid() == hwid), None) + def getFabricatorByName(self, name) -> Fabricator | None: + """find the first fabricator with the given name""" + return next((fabricator for fabricator in self if fabricator.getName() == name), None) + + + def getFabricatorByHwid(self, hwid) -> Fabricator | None: + """find the first fabricator with the given hwid""" + return next((fabricator for fabricator in self if fabricator.getHwid() == hwid), None) - @staticmethod - def diagnose(device: Device): + def getFabricatorById(self, id) -> Fabricator | None: + """find the first fabricator with the given id""" + return next((fabricator for fabricator in self if fabricator.dbID == id), None) + + + def getFabricatorByPort(self, port) -> Fabricator | None: + """find the first fabricator with the given port""" + for fabricator in self: + if fabricator.getSerialPort().device == port: + return fabricator + return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) + + + def diagnose(self, device: Device | Fabricator): + """diagnose a fabricator""" + if isinstance(device, Fabricator): + device = device.device try: diagnoseString = "" for port in serial.tools.list_ports.comports(): if port.device == device.getSerialPort().device: diagnoseString += f"The system has found a <b>matching port</b> with the following details: <br><br> <b>Device:</b> {port.device}, <br> <b>Description:</b> {port.description}, <br> <b>HWID:</b> {port.hwid}" hwid = device.getHWID() - printerExists = FabricatorList.getPrinterByHwid(hwid) - if printerExists: - printer = FabricatorList.getPrinterByHwid(hwid) - diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {printer.name} <br> <b>Device:</b> {printer.device}, <br> <b>Description:</b> {printer.description}, <br><b> HWID:</b> {printer.hwid}" + fabricatorExists = self.getFabricatorByHwid(hwid) + if fabricatorExists: + fabricator = self.getFabricatorByHwid(hwid) + diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {fabricator.name} <br> <b>Device:</b> {fabricator.device}, <br> <b>Description:</b> {fabricator.description}, <br><b> HWID:</b> {fabricator.hwid}" if diagnoseString == "": - diagnoseString = "The port this printer is registered under is <b>not found</b>. Please check the connection and try again." + diagnoseString = "The port this fabricator is registered under is <b>not found</b>. Please check the connection and try again." return { "success": True, - "message": "Printer successfully diagnosed.", + "message": "fabricator successfully diagnosed.", "diagnoseString": diagnoseString, } @@ -93,66 +139,56 @@ def diagnose(device: Device): print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 - @staticmethod - def start_fabricator_thread(fabricator, app): - thread = FabricatorThread(fabricator, target=FabricatorList.update_thread, args=(fabricator, app)) + + def start_fabricator_thread(self, fabricator: Fabricator): + thread = FabricatorThread(fabricator, app=self.app, target=self.update_thread, args=(fabricator,)) thread.daemon = True thread.start() return thread - @staticmethod - def create_fabricator_threads(fabricators_data, app): - for fabricator_info in fabricators_data: - fabricator = Fabricator( - id=fabricator_info["id"], - device=fabricator_info["device"], - description=fabricator_info["description"], - hwid=fabricator_info["hwid"], - name=fabricator_info["name"], - status='configuring', - ) + + def create_fabricator_threads(self): + for fabricator in self: fabricator.setQueue(Queue()) # Ensure each fabricator has its own queue - fabricator_thread = FabricatorList.start_fabricator_thread(fabricator, app) - FabricatorList.fabricator_threads.append(fabricator_thread) - - FabricatorList.ping_thread = Thread(target=FabricatorList.pingForStatus) - - @staticmethod - def queue_restore(fabricators_data, status, queue, app): - for fabricator_info in fabricators_data: - fabricator = Fabricator( - id=fabricator_info["id"], - device=fabricator_info["device"], - description=fabricator_info["description"], - hwid=fabricator_info["hwid"], - name=fabricator_info["name"], - ) + fabricator_thread = self.start_fabricator_thread(fabricator) + self.fabricator_threads.append(fabricator_thread) + self.ping_thread = Thread(target=self.pingForStatus) + + def get_fabricator_thread(self, fabricator): + assert fabricator in self + thread = next(thread for thread in self.fabricator_threads if thread.fabricator == fabricator) + assert thread.is_alive() + assert thread.daemon + return thread + + + + def queue_restore(self, status, queue): + for fabricator in self.fabricators: for job in queue: if job.status != 'inqueue': job.setStatus('inqueue') job.setDBstatus(job.id, 'inqueue') fabricator.setQueue(queue) fabricator.setStatus(status) - fabricator_thread = FabricatorList.start_fabricator_thread(fabricator, app) - FabricatorList.fabricator_threads.append(fabricator_thread) + fabricator_thread = self.start_fabricator_thread(fabricator) + self.fabricator_threads.append(fabricator_thread) - @staticmethod - def update_thread(fabricator, app): - with app.app_context(): - while True: + def update_thread(self, fabricator): + # thread = next(thread for thread in self.fabricator_threads if thread.fabricator.id == fabricator.id) + while True: + time.sleep(2) + status = fabricator.getStatus() + queueSize = fabricator.getQueue().getSize() + fabricator.responseCount = 0 + if status == "ready" and queueSize > 0: time.sleep(2) - status = fabricator.getStatus() - queueSize = fabricator.getQueue().getSize() - fabricator.responseCount = 0 - if status == "ready" and queueSize > 0: - time.sleep(2) - if status != "offline": - fabricator.printNextInQueue() + if status != "offline": + fabricator.printNextInQueue() - @staticmethod - def resetThread(fabricator_id, app): + def resetThread(self, fabricator_id): try: - for thread in FabricatorList.fabricator_threads: + for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: fabricator = thread.fabricator fabricator.terminated = 1 @@ -163,18 +199,17 @@ def resetThread(fabricator_id, app): "hwid": fabricator.hwid, "name": fabricator.name, } - FabricatorList.fabricator_threads.remove(thread) - FabricatorList.create_fabricator_threads([thread_data], app) + self.fabricator_threads.remove(thread) + self.create_fabricator_threads() break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - @staticmethod - def queueRestore(fabricator_id, status, app): + def queueRestore(self, fabricator_id, status): try: - for thread in FabricatorList.fabricator_threads: + for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: fabricator = thread.fabricator fabricator.terminated = 1 @@ -185,109 +220,44 @@ def queueRestore(fabricator_id, status, app): "hwid": fabricator.hwid, "name": fabricator.name, } - FabricatorList.fabricator_threads.remove(thread) - FabricatorList.queue_restore([thread_data], status, fabricator.getQueue(), app) + self.fabricator_threads.remove(thread) + self.queue_restore(status, fabricator.getQueue()) break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - @staticmethod - def deleteThread(fabricator_id): + + def deleteThread(self, fabricator_id): try: - for thread in FabricatorList.fabricator_threads: + for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: fabricator = thread.fabricator - thread_data = { - "id": fabricator.id, - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name - } - FabricatorList.fabricator_threads.remove(thread) + if fabricator.getStatus() == "ready": + fabricator.terminated = 1 + self.fabricator_threads.remove(thread) break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - @staticmethod - def editName(fabricator_id, name): - try: - for thread in FabricatorList.fabricator_threads: - if thread.fabricator.id == fabricator_id: - fabricator = thread.fabricator - fabricator.name = name - break - return jsonify({"success": True, "message": "Fabricator name updated successfully"}) - except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - @staticmethod - def retrieve_fabricator_info(): - fabricator_info_list = [] - for thread in FabricatorList.fabricator_threads: - fabricator = thread.fabricator - fabricator_info = { - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name, - "status": fabricator.status, - "id": fabricator.id, - "error": fabricator.error, - "canPause": fabricator.canPause, - "queue": [], - "colorChangeBuffer": fabricator.colorbuff - } - queue = fabricator.getQueue() - for job in queue: - job_info = { - "id": job.id, - "name": job.name, - "status": job.status, - "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), - "fabricatorid": job.fabricator_id, - "errorid": job.error_id, - "file_name_original": job.file_name_original, - "progress": job.progress, - "sent_lines": job.sent_lines, - "favorite": job.favorite, - "released": job.released, - "file_pause": job.filePause, - "comments": job.comments, - "extruded": job.extruded, - "td_id": job.td_id, - "time_started": job.time_started, - "fabricator_name": job.fabricator_name, - "max_layer_height": job.max_layer_height, - "current_layer_height": job.current_layer_height, - "filament": job.filament, - } - fabricator_info['queue'].append(job_info) - - fabricator_info_list.append(fabricator_info) - - return fabricator_info_list - - @staticmethod - def getThreadArray(): - return FabricatorList.fabricator_threads - - @staticmethod - def pingForStatus(): + def getThreadArray(self): + return self.fabricator_threads + + + def pingForStatus(self): pass - @staticmethod - def moveFabricatorList(fabricator_ids): + + def moveFabricatorList(self, fabricator_ids): new_thread_list = [] for id in fabricator_ids: - for thread in FabricatorList.fabricator_threads: + for thread in self.fabricator_threads: if thread.fabricator.id == id: new_thread_list.append(thread) break - FabricatorList.fabricator_threads = new_thread_list + self.fabricator_threads = new_thread_list return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) \ No newline at end of file From a45cbdd856f7d9a130281fd3feee1cf81f280f49 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:33:05 -0500 Subject: [PATCH 137/194] feat: updated to use custom logging with built-in terminal writers --- Tests/conftest.py | 493 +++++++++++++++++++++++++++------------------- 1 file changed, 286 insertions(+), 207 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 787b9447..1cc6473e 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -1,12 +1,66 @@ import math import os -import platform import re import sys -import time -import pluggy import pytest from app import fabricator_list +from _pytest.terminal import TerminalWriter, TerminalReporter + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config): + for arg in config.invocation_params.args: + if arg.startswith("--port="): + config.port = arg.split("=")[1] + config.logger = setup_logger(config.port) + logger = config.logger + myTR = TerminalReporter(config) + myTR.__repr__ = lambda: "My Terminal Reporter" + myTW = myTR._tw + myTW.__repr__ = lambda: "My Terminal Writer" + myTW.logLine: str = "" + myTW.fullwidth = 113 + myTR._screen_width = myTW.fullwidth + myTW.hasmarkup = True + + def custom_write(self: TerminalWriter, msg: str, *, flush: bool = False, **markup: bool): + if msg: + current_line = msg.rsplit("\n", 1)[-1] + if "\n" in msg: + self._current_line = current_line + else: + self._current_line += current_line + + msg = self.markup(msg, **markup) + log = msg.strip() + + try: + if log: + self.logLine = " ".join([self.logLine, log]) + if flush: + if "red" in markup and markup["red"] == True: + logger.error(self.logLine) + elif "yellow" in markup and markup["yellow"] == True: + logger.warning(self.logLine) + else: + logger.info(self.logLine) + self.logLine = "" + self._file.write(msg) + except UnicodeEncodeError: + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) + + if flush: + self.flush() + + myTW.write = custom_write.__get__(myTW, TerminalWriter) + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter: + config.pluginmanager.unregister(terminal_reporter) + config.pluginmanager.register(myTR, "terminalreporter") + else: + config.pluginmanager.register(myTR, "terminalreporter") + @pytest.fixture(scope="session") def fabricator(request, app): @@ -41,9 +95,9 @@ def app(): intLogger = Logger("Internal Errors", consoleLogger=sys.stdout, fileLogger="internal_errors.log", loggingLevel=Logger.INFO) -def pytest_internalerror(excrepr, excinfo): - # This hook is called when pytest encounters an internal error - intLogger.error(f"Internal pytest error:\n{excrepr}") +# def pytest_internalerror(excrepr, excinfo): +# # This hook is called when pytest encounters an internal error +# intLogger.error(f"Internal pytest error:\n{excrepr}") def pytest_addoption(parser): parser.addoption( @@ -59,11 +113,20 @@ def pytest_addoption(parser): help="port to test" ) -def line_separator(interrupter: str, symbol: str = "-", length: int = 136) -> str: + +def line_separator(interrupter: str, symbol: str = "-", length: int = 136, color: int | None = None, colorAll: bool = False) -> str: if not interrupter: + if color: + return f"\033[{color}m" + symbol * (length//len(symbol)) + "\033[0m" return symbol * (length//len(symbol)) interrupterNoColor = re.sub(r'\033\[[0-9;]*m', '', interrupter) side = (length - 2 - len(interrupterNoColor)) / 2 + if color: + color = f"\033[{color}m" + if colorAll: + return color + symbol * math.ceil(side) + " " + interrupter + " " + symbol * math.floor(side) + "\033[0m" + else: + return color + symbol * math.ceil(side) + "\033[0m" + " " + interrupter + " " + color + symbol * math.floor(side) + "\033[0m" return symbol * math.ceil(side) + " " + interrupter + " " + symbol * math.floor(side) def setup_logger(port): @@ -75,46 +138,46 @@ def setup_logger(port): subfolder = os.path.join(log_folder, timestamp) os.makedirs(subfolder, exist_ok=True) log_file_path = os.path.join(subfolder, f"test_{port}.log") - return Logger(port, "Test Printer", consoleLogger=sys.stdout, fileLogger=log_file_path, showFile=False, showLevel=False) - -@pytest.hookimpl(tryfirst=True) -def pytest_sessionstart(session) -> None: - for arg in session.config.invocation_params.args: - if not hasattr(session.config, "verbosity") and arg.startswith("--myVerbose="): - session.config.verbosity = int(arg.split("=")[1]) - elif not hasattr(session.config, "port") and arg.startswith("--port="): - session.config.port = arg.split("=")[1] - elif not hasattr(session.config, "testLevel") and arg.startswith("--testLevel="): - session.config.testLevel = int(arg.split("=")[1]) - if session.config.verbosity > 2: - session.config.verbosity = 2 + return Logger(port, "Test Printer", fileLogger=log_file_path, showFile=False, showLevel=False) - session.config.start_time = time.time() - session.config.passed_count = 0 - session.config.failed_count = 0 - session.config.skipped_count = 0 - session.config.xfailed_count = 0 - session.config.xpassed_count = 0 - session.config.failNames = [] - session.config.fails = {} - session.config.logger = setup_logger(session.config.port) - - - if session.config.verbosity >= 0: - logger = session.config.logger - logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") - verinfo = platform.python_version() - msg = f"platform {sys.platform} -- Python {verinfo}" - pypy_version_info = getattr(sys, "pypy_version_info", None) - if pypy_version_info: - verinfo = ".".join(map(str, pypy_version_info[:3])) - msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" - msg += f", pytest-{pytest.__version__}, pluggy-{pluggy.__version__}" - logger.logMessageOnly(msg) - logger.logMessageOnly(f"rootdir: {session.config.rootdir}") +# @pytest.hookimpl(tryfirst=True) +# def pytest_sessionstart(session) -> None: +# for arg in session.config.invocation_params.args: +# if not hasattr(session.config, "verbosity") and arg.startswith("--myVerbose="): +# session.config.verbosity = int(arg.split("=")[1]) +# elif not hasattr(session.config, "port") and arg.startswith("--port="): +# session.config.port = arg.split("=")[1] +# elif not hasattr(session.config, "testLevel") and arg.startswith("--testLevel="): +# session.config.testLevel = int(arg.split("=")[1]) +# if session.config.verbosity > 2: +# session.config.verbosity = 2 +# +# session.config.start_time = time.time() +# session.config.passed_count = 0 +# session.config.failed_count = 0 +# session.config.skipped_count = 0 +# session.config.xfailed_count = 0 +# session.config.xpassed_count = 0 +# session.config.failNames = [] +# session.config.fails = {} +# session.config.logger = setup_logger(session.config.port) +# +# +# if session.config.verbosity >= 0: +# logger = session.config.logger +# logger.logMessageOnly("\033[1m" + line_separator("test session starts", symbol="=") + "\033[0m") +# verinfo = platform.python_version() +# msg = f"platform {sys.platform} -- Python {verinfo}" +# pypy_version_info = getattr(sys, "pypy_version_info", None) +# if pypy_version_info: +# verinfo = ".".join(map(str, pypy_version_info[:3])) +# msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" +# msg += f", pytest-{pytest.__version__}, pluggy-{pluggy.__version__}" +# logger.logMessageOnly(msg) +# logger.logMessageOnly(f"rootdir: {session.config.rootdir}") def pytest_collection_modifyitems(session, config, items): - session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...") + # session.config.logger.logMessageOnly(f"\033[1m...collected {len(items)} items...") file_order = [ "test_app.py", "test_fabricator_list.py", @@ -129,167 +192,183 @@ def get_file_order(item): # Sort the items based on the file order items.sort(key=get_file_order) -def pytest_sessionfinish(session, exitstatus) -> None: - session_duration = time.time() - session.config.start_time - passes = session.config.passed_count - fails = session.config.failed_count - skips = session.config.skipped_count - xfails = session.config.xfailed_count - xpasses = session.config.xpassed_count - logger = session.config.logger - - if hasattr(session.config, "_capturemanager"): - capture_manager = session.config._capturemanager - # Suspend capturing to retrieve the output - captured = capture_manager.read_global_and_disable() - - # Print the captured stdout and stderr - logger.logMessageOnly("\nCaptured output during tests:\n") - logger.logMessageOnly(captured) - - # Re-enable capture if needed for further use - capture_manager.resume_global_capture() - - stats = [] - if passes > 0: stats.append(f"\033[32m\033[1m{passes} passed") - if fails > 0: stats.append(f"\033[31m\033[1m{fails} failed") - if skips > 0: stats.append(f"\033[33m{skips} skipped") - if xfails > 0: stats.append(f"\033[33m{xfails} xfailed") - if xpasses > 0: stats.append(f"\031[33m{xpasses} xpassed") - - if len(stats) > 0: summary = ", ".join(stats) - else: summary = "\033[33mno tests ran" - - summary += f"\033[32m in {session_duration:.2f}s" - if session_duration > 3600: - summary += f" ({session_duration // 3600:02.0f}:{session_duration % 3600 // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" - elif session_duration > 60: - summary += f" ({session_duration // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" - - if session.config.failed_count > 0: - headerText = "\n" + line_separator("FAILURES", symbol="=") - logger.logMessageOnly(headerText, logLevel=logger.ERROR) - for failTest in session.config.failNames: - logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) - if not hasattr(session.config.fails[failTest], "reprtraceback"): - if not hasattr(session.config.fails[failTest], "longrepr"): - if hasattr(session.config.fails[failTest], "errorstring"): - logger.error(session.config.fails[failTest].errorstring) - else: - logger.error(session.config.fails[failTest]) - else: - logger.error(session.config.fails[failTest].longrepr) - elif not hasattr(session.config.fails[failTest].reprtraceback, "reprentries"): - logger.error(session.config.fails[failTest].reprtraceback) - else: - logger.logException(session.config.fails[failTest].reprtraceback.reprentries) - logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) - -visited_modules = set() - -@pytest.hookimpl(tryfirst=True, hookwrapper=True) -def pytest_runtest_makereport(item, call): - outcome = yield - report = outcome.get_result() # Retrieve the TestReport object - # Only check the outcome after the "call" phase (i.e., after the test ran) - if (report.when == "setup" and not report.passed) or (report.when == "call"): - if report.passed: - if hasattr(report, "wasxfail"): - report.outcome = "xpassed" - report.xpassed = True - item.config.xpassed_count += 1 - else: - item.config.passed_count += 1 - elif report.failed: - item.config.failed_count += 1 - failName = report.nodeid.split("::")[1] + "." + item.name - item.config.failNames.append(failName) - item.config.fails[failName] = report.longrepr - elif report.skipped: - if hasattr(report, "wasxfail"): - report.outcome = "xfailed" - report.xfailed = True - item.config.xfailed_count += 1 - else: - item.config.skipped_count += 1 - report.port = item.config.port - report.verbosity = item.config.verbosity - report.logger = item.config.logger - module_name = item.module.__name__ - if module_name not in visited_modules: - visited_modules.add(module_name) - report.logger.logMessageOnly("\n" + line_separator(item.module.__desc__(), symbol="-")) - -@pytest.hookimpl(hookwrapper=True) -def pytest_runtest_logreport(report): - verbosity = report.verbosity - yield - logger = report.logger - port = report.port - if (report.when == "setup" and not report.passed) or (report.when == "call"): - if port is None: - # Retrieve port from the test function if it's set as an attribute - port = os.getenv("PORT") - - if verbosity == 0: - if report.passed: - logger.info("\033[32m.\033[0m") - elif report.failed: - logger.info("\033[31mF\033[0m") - elif report.skipped: - logger.info("\033[33ms\033[0m") - elif hasattr(report, "xfailed") and report.xfailed: - logger.info("\033[33mX\033[0m") - elif hasattr(report, "xpassed") and report.xpassed: - logger.info("\033[31mx\033[0m") - else: - logger.info(f"IDK what happened!?!?: {report}") - elif verbosity == 1: - loc = report.nodeid.split("::")[-1] - testString = f"{loc}[{port}]{' ' * (59 - len(loc) - len(str(port)) - 2)}" - if report.passed: - logger.info(f"{testString} \033[32mPASSED\033[0m") - elif report.failed: - logger.info(f"{testString} \033[31mFAILED\033[0m") - elif report.skipped: - logger.info(f"{testString} \033[33mSKIPPED\033[0m") - elif hasattr(report, "xfailed") and report.xfailed: - logger.info(f"{testString} \033[33mXFAILED\033[0m") - elif hasattr(report, "xpassed") and report.xpassed: - logger.info(f"{testString} \033[31mXPASSED\033[0m") - else: - logger.info(f"{testString} IDK what happened!?!?: {report}") - elif verbosity >= 2: - loc = report.nodeid - testString = f"{loc}[{port}]{' ' * (79 - len(loc) - len(str(port)) - 2)}" - if report.passed: - logger.info(f"{testString} \033[32mPASSED\033[0m") - elif report.failed: - logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n") - if not hasattr(report, "longrepr"): - if hasattr(report, "errorstring"): - logger.error(report.errorstring) - else: - logger.error(report) - elif not hasattr(report.longrepr, "reprtraceback"): - logger.error(report.longrepr) - elif not hasattr(report.longrepr.reprtraceback, "reprentries"): - logger.error(report.longrepr.reprtraceback) - else: - logger.logException(report.longrepr.reprtraceback.reprentries) - logger.logException(report.longrepr.reprtraceback.reprentries) - elif report.skipped: - logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") - else: - logger.info(f"{testString} IDK what happened!?!?: {report}") +# def pytest_sessionfinish(session, exitstatus) -> None: +# session_duration = time.time() - session.config.start_time +# passes = session.config.passed_count +# fails = session.config.failed_count +# skips = session.config.skipped_count +# xfails = session.config.xfailed_count +# xpasses = session.config.xpassed_count +# logger = session.config.logger +# +# if hasattr(session.config, "_capturemanager"): +# capture_manager = session.config._capturemanager +# # Suspend capturing to retrieve the output +# captured = capture_manager.read_global_and_disable() +# +# # Print the captured stdout and stderr +# logger.logMessageOnly("\nCaptured output during tests:\n") +# logger.logMessageOnly(captured) +# +# # Re-enable capture if needed for further use +# capture_manager.resume_global_capture() +# +# stats = [] +# if passes > 0: stats.append(f"\033[32m\033[1m{passes} passed") +# if fails > 0: stats.append(f"\033[31m\033[1m{fails} failed") +# if skips > 0: stats.append(f"\033[33m{skips} skipped") +# if xfails > 0: stats.append(f"\033[33m{xfails} xfailed") +# if xpasses > 0: stats.append(f"\031[33m{xpasses} xpassed") +# +# if len(stats) > 0: summary = ", ".join(stats) +# else: summary = "\033[33mno tests ran" +# +# summary += f"\033[32m in {session_duration:.2f}s" +# if session_duration > 3600: +# summary += f" ({session_duration // 3600:02.0f}:{session_duration % 3600 // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" +# elif session_duration > 60: +# summary += f" ({session_duration // 60:02.0f}:{(session_duration % 60)//1:02.0f}.{(session_duration % 1).__round__(2) * 100 // 1:02.0f})" +# +# if session.config.failed_count > 0: +# headerText = "\n" + line_separator("FAILURES", symbol="=") +# logger.logMessageOnly(headerText, logLevel=logger.ERROR) +# for failTest in session.config.failNames: +# logger.logMessageOnly(line_separator(failTest, symbol="_"), end="\n", logLevel=logger.ERROR) +# if not hasattr(session.config.fails[failTest], "reprtraceback"): +# if not hasattr(session.config.fails[failTest], "longrepr"): +# if hasattr(session.config.fails[failTest], "errorstring"): +# logger.error(session.config.fails[failTest].errorstring) +# else: +# logger.error(session.config.fails[failTest]) +# else: +# logger.error(session.config.fails[failTest].longrepr) +# elif not hasattr(session.config.fails[failTest].reprtraceback, "reprentries"): +# logger.error(session.config.fails[failTest].reprtraceback) +# else: +# logger.logException(session.config.fails[failTest].reprtraceback.reprentries) +# logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) +# +# visited_modules = set() +# +# @pytest.hookimpl(tryfirst=True, hookwrapper=True) +# def pytest_runtest_makereport(item, call): +# outcome = yield +# report = outcome.get_result() # Retrieve the TestReport object +# # Only check the outcome after the "call" phase (i.e., after the test ran) +# if (report.when == "setup" and not report.passed) or (report.when == "call"): +# if report.passed: +# if hasattr(report, "wasxfail"): +# report.outcome = "xpassed" +# report.xpassed = True +# item.config.xpassed_count += 1 +# else: +# item.config.passed_count += 1 +# elif report.failed: +# item.config.failed_count += 1 +# failName = report.nodeid.split("::")[1] + "." + item.name +# item.config.failNames.append(failName) +# item.config.fails[failName] = report.longrepr +# elif report.skipped: +# if hasattr(report, "wasxfail"): +# report.outcome = "xfailed" +# report.xfailed = True +# item.config.xfailed_count += 1 +# else: +# item.config.skipped_count += 1 +# report.port = item.config.port +# report.verbosity = item.config.verbosity +# report.logger = item.config.logger +# module_name = item.module.__name__ +# if module_name not in visited_modules: +# visited_modules.add(module_name) +# report.logger.logMessageOnly("\n" + line_separator(item.module.__desc__(), symbol="-")) +# +# @pytest.hookimpl(hookwrapper=True) +# def pytest_runtest_logreport(report): +# verbosity = report.verbosity +# yield +# logger = report.logger +# port = report.port +# if (report.when == "setup" and not report.passed) or (report.when == "call"): +# if port is None: +# # Retrieve port from the test function if it's set as an attribute +# port = os.getenv("PORT") +# +# if verbosity == 0: +# if report.passed: +# logger.info("\033[32m.\033[0m") +# elif report.failed: +# logger.info("\033[31mF\033[0m") +# elif report.skipped: +# logger.info("\033[33ms\033[0m") +# elif hasattr(report, "xfailed") and report.xfailed: +# logger.info("\033[33mX\033[0m") +# elif hasattr(report, "xpassed") and report.xpassed: +# logger.info("\033[31mx\033[0m") +# else: +# logger.info(f"IDK what happened!?!?: {report}") +# elif verbosity == 1: +# loc = report.nodeid.split("::")[-1] +# testString = f"{loc}[{port}]{' ' * (59 - len(loc) - len(str(port)) - 2)}" +# if report.passed: +# logger.info(f"{testString} \033[32mPASSED\033[0m") +# elif report.failed: +# logger.info(f"{testString} \033[31mFAILED\033[0m") +# elif report.skipped: +# logger.info(f"{testString} \033[33mSKIPPED\033[0m") +# elif hasattr(report, "xfailed") and report.xfailed: +# logger.info(f"{testString} \033[33mXFAILED\033[0m") +# elif hasattr(report, "xpassed") and report.xpassed: +# logger.info(f"{testString} \033[31mXPASSED\033[0m") +# else: +# logger.info(f"{testString} IDK what happened!?!?: {report}") +# elif verbosity >= 2: +# loc = report.nodeid +# testString = f"{loc}[{port}]{' ' * (79 - len(loc) - len(str(port)) - 2)}" +# if report.passed: +# logger.info(f"{testString} \033[32mPASSED\033[0m") +# elif report.failed: +# logger.info(f"{testString} \033[31mFAILED\033[0m:\n\n") +# if not hasattr(report, "longrepr"): +# if hasattr(report, "errorstring"): +# logger.error(report.errorstring) +# else: +# logger.error(report) +# elif not hasattr(report.longrepr, "reprtraceback"): +# logger.error(report.longrepr) +# elif not hasattr(report.longrepr.reprtraceback, "reprentries"): +# logger.error(report.longrepr.reprtraceback) +# else: +# logger.logException(report.longrepr.reprtraceback.reprentries) +# logger.logException(report.longrepr.reprtraceback.reprentries) +# elif report.skipped: +# logger.info(f"{testString} \033[33mSKIPPED\033[0m: {report.longrepr[-1].split('Skipped: ')[-1]}") +# else: +# logger.info(f"{testString} IDK what happened!?!?: {report}") +# +# def pytest_collectreport(report): +# if report.failed: +# intLogger.logMessageOnly(f"Collection failed:", logLevel=intLogger.ERROR) +# if not hasattr(report.longrepr, "reprtraceback"): +# intLogger.logException(report.longrepr.longrepr) +# return +# if not hasattr(report.longrepr.reprtraceback, "reprentries"): +# intLogger.logException(report.longrepr.reprtraceback) +# return +# else: intLogger.logException(report.longrepr.reprtraceback.reprentries) -def pytest_collectreport(report): - if report.failed: - intLogger.logMessageOnly(f"Collection failed:", logLevel=intLogger.ERROR) - if not hasattr(report.longrepr, "reprtraceback"): - intLogger.logException(report.longrepr.longrepr) - return - if not hasattr(report.longrepr.reprtraceback, "reprentries"): - intLogger.logException(report.longrepr.reprtraceback) - return - else: intLogger.logException(report.longrepr.reprtraceback.reprentries) \ No newline at end of file +def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus, config): + import time + from _pytest.terminal import format_session_duration + session_duration = time.time() - terminalreporter._sessionstarttime + (parts, main_color) = terminalreporter.build_summary_stats_line() + line_parts = [] + for text, markup in parts: + with_markup = terminalreporter._tw.markup(text, **markup) + line_parts.append(with_markup) + msg = ", ".join(line_parts) + main_markup = {main_color: True} + duration = f" in {format_session_duration(session_duration)}" + duration_with_markup = terminalreporter._tw.markup(duration, **main_markup) + msg += duration_with_markup + config.logger.logMessageOnly("\n" + line_separator(msg, symbol="=", length=terminalreporter._tw.fullwidth, color=terminalreporter._tw._esctable[main_color])) From d0305efc165ea9b30b4a1010b928d15e81fa1653 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Thu, 21 Nov 2024 17:35:29 -0500 Subject: [PATCH 138/194] feat: updated app to use new features in other places --- server/app.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/server/app.py b/server/app.py index ff8ed9aa..96fdaf8b 100644 --- a/server/app.py +++ b/server/app.py @@ -9,13 +9,14 @@ import shutil from flask_socketio import SocketIO from models.config import Config +from Classes.FabricatorList import FabricatorList # moved this up here so we can pass the app to the PrinterStatusService # Basic app setup app = Flask(__name__, static_folder='../client/dist') app.config.from_object(__name__) # update application instantly -logs = os.path.join('./logs') +logs = os.path.join(os.path.dirname(__file__),'logs') from Classes.Logger import Logger app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.join(logs, "app.log")) # start database connection @@ -37,6 +38,8 @@ migrate = Migrate(app, db) # moved this before importing the blueprints so that it can be accessed by the PrinterStatusService printer_status_service = PrinterStatusService(app) +fabricator_list = FabricatorList(app) +app.fabricator_list = fabricator_list # Initialize SocketIO, which will be used to send printer status updates to the frontend # and this specific socket it will be used throughout the backend @@ -49,26 +52,33 @@ socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object -def handle_errors_and_logging(e: Exception, fabricator = None): +def handle_errors_and_logging(e: Exception | str, fabricator = None): from Classes.Fabricators.Fabricator import Fabricator device = fabricator if isinstance(fabricator, Fabricator): device = fabricator.device - if device.logger is None: - if app.logger is None: + if device is not None and device.logger is not None: + device.logger.error(e, stacklevel=3) + elif app.logger is None: + if isinstance(e, str): print(e) else: - app.logger.error("Error:") - app.logger.error(e) + import traceback + print(traceback.format_exception(None, e, e.__traceback__)) else: - device.logger.error("Error:") - device.logger.error(e) + app.logger.error(e, stacklevel=3) return False -app.handle_error_and_logging = handle_errors_and_logging +app.handle_errors_and_logging = handle_errors_and_logging CORS(app) +@app.cli.command("test") +def run_tests(): + """Run all tests.""" + import subprocess + subprocess.run(["python", "../Tests/parallel_test_runner.py"]) + @app.before_request def handle_preflight(): if request.method == "OPTIONS": @@ -110,10 +120,7 @@ def handle_ping(): # Define directory paths for uploads and tempcsv uploads_folder = os.path.abspath('../uploads') tempcsv = os.path.abspath('../tempcsv') - - # Create printer threads from registered printers on server start - from Classes.FabricatorList import FabricatorList - printer_status_service.create_printer_threads(FabricatorList.fabricators) + app.FabricatorList = fabricator_list # Check if directories exist and handle them accordingly for folder in [uploads_folder, tempcsv]: From f3b33f2b0ef6daf436e9f75628f402022bbe87e5 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Sun, 24 Nov 2024 01:06:42 -0500 Subject: [PATCH 139/194] fix routes --- server/controllers/statusService.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index 4969757d..39b137b8 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -4,12 +4,21 @@ status_bp = Blueprint("status", __name__) @status_bp.route('/ping', methods=["GET"]) -def getStatus(Printer): - pass +def getStatus(): + try: + return jsonify({"status": "pong"}), 200 + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 -@status_bp.route('/getopenthreads') +@status_bp.route('/getopenthreads', methods=["GET"]) def getOpenThreads(): - pass + try: + open_threads = fabricator_list.getOpenThreads() + return jsonify(open_threads), 200 + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 # this is the route that will be called by the UI to get the printers that have threads information @status_bp.route('/getprinterinfo', methods=["GET"]) From 0794e7e7c78f8310dce9189f81c3e9881620acff Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:07:05 -0500 Subject: [PATCH 140/194] fixed and added gcode commands --- printeremu/src/extruder.go | 13 +- printeremu/src/gcode_commands.go | 615 ++++++++++++++++++------------- printeremu/src/printer.go | 17 +- 3 files changed, 380 insertions(+), 265 deletions(-) diff --git a/printeremu/src/extruder.go b/printeremu/src/extruder.go index 8a7ceade..6c53d79b 100644 --- a/printeremu/src/extruder.go +++ b/printeremu/src/extruder.go @@ -13,12 +13,13 @@ type Vector3 struct { // Extruder struct to handle extruder settings, including temperature and fan speed. type Extruder struct { - Position Vector3 - FanSpeed float64 - ExtruderTemp float64 - TargetTemp float64 // Desired temperature for the extruder. - AbsolutePositioning bool - MaxZHeight float64 // Maximum height of the extruder + Position Vector3 + FanSpeed float64 + ExtruderTemp float64 + TargetTemp float64 // Desired temperature for the extruder. + AbsolutePositioning bool + MaxZHeight float64 // Maximum height of the extruder + AllowColdExtrusion bool // Allow cold extrusion } // Heatbed struct to handle heatbed temperature. diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 7aeaaf2c..477e6ea6 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -1,46 +1,46 @@ package src import ( - "fmt" - "math" - "regexp" - "strconv" - "strings" - "time" + "fmt" + "math" + "regexp" + "strconv" + "strings" + "time" ) type Command interface { - Execute(printer *Printer) string + Execute(printer *Printer) string } // CommandHandler parses and executes commands func CommandHandler(command string, printer *Printer) string { - if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { - command = command[:semicolonIndex] - } + if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { + command = command[:semicolonIndex] + } - if command == "" { - return "" - } + if command == "" { + return "" + } - command = strings.TrimSpace(command) - cmd := NewCommand(command, printer) + command = strings.TrimSpace(command) + cmd := NewCommand(command, printer) - if cmd == nil { - return "Unknown command\n" - } + if cmd == nil { + return "Unknown command\n" + } - return cmd.Execute(printer) + return cmd.Execute(printer) } // NewCommand parses a command string and returns the appropriate Command func NewCommand(command string, printer *Printer) Command { - for key, factory := range commandRegistry { - if strings.HasPrefix(command, key) { - return factory(command, printer) - } - } - return nil + for key, factory := range commandRegistry { + if strings.HasPrefix(command, key) { + return factory(command, printer) + } + } + return nil } // ==================== Movement and Positioning Commands ==================== @@ -48,114 +48,114 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { - if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return "Homing completed\n" + return "Homing completed\n" } type G0G1Command struct { - target Vector3 - feedRate float64 + target Vector3 + feedRate float64 } func NewG0G1Command(command string, printer *Printer) *G0G1Command { - target, feedRate := parseMoveCommand(command, printer.Extruder.Position) + target, feedRate := parseMoveCommand(command, printer.Extruder.Position) - return &G0G1Command{target: target, feedRate: feedRate} + return &G0G1Command{target: target, feedRate: feedRate} } func (cmd *G0G1Command) Execute(printer *Printer) string { - // TODO: Implement feed rate - if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + // TODO: Implement feed rate + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } type G2G3Command struct { - target Vector3 - radius float64 - clockwise bool + target Vector3 + radius float64 + clockwise bool } func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Command { - target, radius := parseArcCommand(command, printer.Extruder.Position) + target, radius := parseArcCommand(command, printer.Extruder.Position) - return &G2G3Command{target: target, radius: radius, clockwise: clockwise} + return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } func (cmd *G2G3Command) Execute(printer *Printer) string { - if printer.Paused { - return "Printer is paused\n" - } + if printer.Paused { + return "Printer is paused\n" + } - currentPos := printer.Extruder.Position - arcAngle := 180.0 - angleRad := arcAngle * math.Pi / 180.0 + currentPos := printer.Extruder.Position + arcAngle := 180.0 + angleRad := arcAngle * math.Pi / 180.0 - if cmd.clockwise { - cmd.target.X = currentPos.X + cmd.radius*math.Cos(angleRad) - cmd.target.Y = currentPos.Y - cmd.radius*math.Sin(angleRad) - } else { - cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) - cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) - } + if cmd.clockwise { + cmd.target.X = currentPos.X + cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y - cmd.radius*math.Sin(angleRad) + } else { + cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) + } - if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } type G4Command struct { - duration int + duration int } func NewG4Command(command string) *G4Command { - duration := parseDuration(command) - return &G4Command{duration: duration} + duration := parseDuration(command) + return &G4Command{duration: duration} } func (cmd *G4Command) Execute(printer *Printer) string { - return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) + return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) } type G90Command struct{} func (cmd *G90Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = true + printer.Extruder.AbsolutePositioning = true - return "Set to Absolute Positioning\n" + return "Set to Absolute Positioning\n" } type G91Command struct{} func (cmd *G91Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = false + printer.Extruder.AbsolutePositioning = false - return "Set to Relative Positioning\n" + return "Set to Relative Positioning\n" } type G92Command struct { - position Vector3 + position Vector3 } func NewG92Command(command string) *G92Command { - position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) + position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) - return &G92Command{position: position} + return &G92Command{position: position} } func (cmd *G92Command) Execute(printer *Printer) string { - if err := printer.MoveExtruder(cmd.position); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(cmd.position); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) + return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) } // ==================== Unit Conversion Commands ==================== @@ -163,149 +163,185 @@ func (cmd *G92Command) Execute(printer *Printer) string { type G20Command struct{} func (cmd *G20Command) Execute(printer *Printer) string { - printer.Units = "inches" - return "Units set to inches\n" + printer.Units = "inches" + return "Units set to inches\n" } type G21Command struct{} func (cmd *G21Command) Execute(printer *Printer) string { - printer.Units = "mm" - return "Units set to millimeters\n" + printer.Units = "mm" + return "Units set to millimeters\n" } // ==================== Temperature and Fan Control Commands ==================== type M104Command struct { - temperature float64 + temperature float64 } func NewM104Command(command string) *M104Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M104Command{temperature: temperature} + return &M104Command{temperature: temperature} } func (cmd *M104Command) Execute(printer *Printer) string { - if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Extruder temperature set to %.2f°C\n", cmd.temperature) + return fmt.Sprintf("Extruder temperature set to %.2f°C\n", cmd.temperature) } +// M106Command sets the fan speed type M106Command struct { - fanSpeed float64 + fanSpeed float64 } func NewM106Command(command string) *M106Command { - fanSpeed := parseFanSpeed(command) - - return &M106Command{fanSpeed: fanSpeed} + fanSpeed := parseFanSpeed(command) + return &M106Command{fanSpeed: fanSpeed} } func (cmd *M106Command) Execute(printer *Printer) string { - if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } - - return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) + if cmd.fanSpeed > 255 { + return "Error: invalid fan speed. Valid range: 0 to 255\n" + } + if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) } + type M107Command struct{} func (cmd *M107Command) Execute(printer *Printer) string { - printer.SetFanSpeed(0) + printer.SetFanSpeed(0) - return "Fan turned off\n" + return "Fan turned off\n" } type M140Command struct { - temperature float64 + temperature float64 } func NewM140Command(command string) *M140Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M140Command{temperature: temperature} + return &M140Command{temperature: temperature} } func (cmd *M140Command) Execute(printer *Printer) string { - // Set the target temperature of the bed, but do not block - if err := printer.SetBedTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + // Set the target temperature of the bed, but do not block + if err := printer.SetBedTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - printer.Heatbed.Heating = true // Start heating + printer.Heatbed.Heating = true // Start heating - return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) + return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) } // M190Command waits until the bed reaches the target temperature before allowing further commands type M190Command struct { - temperature float64 + temperature float64 } func NewM190Command(command string) *M190Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M190Command{temperature: temperature} + return &M190Command{temperature: temperature} } func (cmd *M190Command) Execute(printer *Printer) string { - // Set the target temperature and check if the bed is heated - // TODO: use error handling! - printer.Heatbed.TargetTemp = cmd.temperature + // Set the target temperature and check if the bed is heated + // TODO: use error handling! + printer.Heatbed.TargetTemp = cmd.temperature - // Check if the current temperature is below the target - if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { - return "Waiting for bed to reach target temperature...\n" - } + // Check if the current temperature is below the target + if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { + return "Waiting for bed to reach target temperature...\n" + } - // If target temperature reached, continue - printer.Heatbed.Heating = false - return fmt.Sprintf("Bed temperature reached %.2f\n", cmd.temperature) + // If target temperature reached, continue + printer.Heatbed.Heating = false + return fmt.Sprintf("Bed temperature reached %.2f\n", cmd.temperature) +} + +type M109Command struct { + temperature float64 +} + +func NewM109Command(command string) *M109Command { + temperature := parseTemperature(command) + + return &M109Command{temperature: temperature} +} + +func (cmd *M109Command) Execute(printer *Printer) string { + // Set the target temperature and check if the extruder is heated + printer.Extruder.TargetTemp = cmd.temperature + + // Check if the current temperature is below the target + if printer.Extruder.ExtruderTemp < printer.Extruder.TargetTemp { + return "Waiting for extruder to reach target temperature...\n" + } + + // If target temperature reached, continue + return fmt.Sprintf("Extruder temperature reached %.2f\n", cmd.temperature) } type M113Command struct{} func NewM113Command(command string) *M113Command { - return &M113Command{} + return &M113Command{} } func (cmd *M113Command) Execute(printer *Printer) string { - printer.KeepAliveTime = time.Now() - return "Keepalive signal sent\n" + printer.KeepAliveTime = time.Now() + return "Keepalive signal sent\n" } type M204Command struct { - acceleration float64 + acceleration float64 } func NewM204Command(command string) *M204Command { - acceleration := parseAcceleration(command) - return &M204Command{acceleration: acceleration} + acceleration := parseAcceleration(command) + return &M204Command{acceleration: acceleration} } func (cmd *M204Command) Execute(printer *Printer) string { - printer.Acceleration = cmd.acceleration - return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) + printer.Acceleration = cmd.acceleration + return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) } type M73Command struct { - progress int + progress int + remaining int } func NewM73Command(command string) *M73Command { - progress := parseProgress(command) - - return &M73Command{progress: progress} + progress := parseProgress(command) + remaining := parseRemainingTime(command) + return &M73Command{progress: progress, remaining: remaining} } func (cmd *M73Command) Execute(printer *Printer) string { - printer.UpdateProgress(cmd.progress) + printer.UpdateProgress(cmd.progress) + return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\n", cmd.progress, cmd.remaining) +} - return fmt.Sprintf("Progress set to %d%%\n", cmd.progress) +// Parsing helpers +func parseRemainingTime(command string) int { + reR := regexp.MustCompile(`R([0-9]+)`) + remaining := 0 + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + remaining, _ = strconv.Atoi(rMatch[1]) + } + return remaining } // ==================== Control and Status Commands ==================== @@ -313,231 +349,310 @@ func (cmd *M73Command) Execute(printer *Printer) string { type CancelCommand struct{} func (cmd *CancelCommand) Execute(printer *Printer) string { - printer.Pause() + printer.Pause() - return "Emergency stop activated, printer paused\n" + return "Emergency stop activated, printer paused\n" } type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) string { - position := printer.Extruder.Position - return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) + position := printer.Extruder.Position + return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) +} + +type M115Command struct{} + +func (cmd *M115Command) Execute(printer *Printer) string { + return "Firmware: TotallyRealMarlin 2.1.2.5\n" +} + +type M17Command struct{} + +func (cmd *M17Command) Execute(printer *Printer) string { + printer.EnableMotor("x") + printer.EnableMotor("y") + printer.EnableMotor("z") + return "Motors enabled\n" +} + +type M18Command struct{} + +func (cmd *M18Command) Execute(printer *Printer) string { + printer.DisableMotor("x") + printer.DisableMotor("y") + printer.DisableMotor("z") + return "Motors disabled\n" +} + +type M82Command struct{} + +func (cmd *M82Command) Execute(printer *Printer) string { + printer.Extruder.AbsolutePositioning = true + return "Extruder set to absolute positioning\n" +} + +type M83Command struct{} + +func (cmd *M83Command) Execute(printer *Printer) string { + printer.Extruder.AbsolutePositioning = false + return "Extruder set to relative positioning\n" +} + +type M302Command struct { + allowColdExtrusion bool +} + +func NewM302Command(command string) *M302Command { + allowColdExtrusion := parseAllowColdExtrusion(command) + return &M302Command{allowColdExtrusion: allowColdExtrusion} +} + +func (cmd *M302Command) Execute(printer *Printer) string { + if cmd.allowColdExtrusion { + return "Cold extrusion allowed\n" + } + return "Cold extrusion not allowed\n" +} + +type M503Command struct{} + +func (cmd *M503Command) Execute(printer *Printer) string { + return printer.String() } type M997Command struct{} func (cmd *M997Command) Execute(printer *Printer) string { - return fmt.Sprintf("Machine name: %s", printer.Device) + return fmt.Sprintf("Machine name: %s", printer.Device) } type M601Command struct{} func (cmd *M601Command) Execute(printer *Printer) string { - printer.Pause() + printer.Pause() - return "Printer paused\n" + return "Printer paused\n" } type M602Command struct{} func (cmd *M602Command) Execute(printer *Printer) string { - printer.Resume() + printer.Resume() - return "Printer not paused\n" + return "Printer not paused\n" } type M900Command struct { - kFactor float64 + kFactor float64 } func NewM900Command(command string) *M900Command { - kFactor := parseKFactor(command) // Extracts the K value from the command + kFactor := parseKFactor(command) // Extracts the K value from the command - return &M900Command{kFactor: kFactor} + return &M900Command{kFactor: kFactor} } func (cmd *M900Command) Execute(printer *Printer) string { - printer.LinearAdvanceFactor = cmd.kFactor + printer.LinearAdvanceFactor = cmd.kFactor - return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) + return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) } type M142Command struct { - temperature float64 + temperature float64 } func NewM142Command(command string) *M142Command { - temperature := parseTemperature(command) // Uses parseTemperature to get S value + temperature := parseTemperature(command) // Uses parseTemperature to get S value - return &M142Command{temperature: temperature} + return &M142Command{temperature: temperature} } func (cmd *M142Command) Execute(printer *Printer) string { - printer.HeatbreakTemp = cmd.temperature + printer.HeatbreakTemp = cmd.temperature - return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) + return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) } type M84Command struct { - axes []string + axes []string } func NewM84Command(command string) *M84Command { - axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command + axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command - return &M84Command{axes: axes} + return &M84Command{axes: axes} } func (cmd *M84Command) Execute(printer *Printer) string { - for _, axis := range cmd.axes { - printer.DisableMotor(axis) - } + for _, axis := range cmd.axes { + printer.DisableMotor(axis) + } - return fmt.Sprintf("Motors %v disabled\n", cmd.axes) + return fmt.Sprintf("Motors %v disabled\n", cmd.axes) } // ==================== Parsing Helpers ==================== func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { - reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) - reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) - reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) - reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) + reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) + reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) + reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) + reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) - target := currentPos - feedRate := 3600.0 + target := currentPos + feedRate := 3600.0 - if xMatch := reX.FindStringSubmatch(command); xMatch != nil { - target.X, _ = strconv.ParseFloat(xMatch[1], 64) - } + if xMatch := reX.FindStringSubmatch(command); xMatch != nil { + target.X, _ = strconv.ParseFloat(xMatch[1], 64) + } - if yMatch := reY.FindStringSubmatch(command); yMatch != nil { - target.Y, _ = strconv.ParseFloat(yMatch[1], 64) - } + if yMatch := reY.FindStringSubmatch(command); yMatch != nil { + target.Y, _ = strconv.ParseFloat(yMatch[1], 64) + } - if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { - target.Z, _ = strconv.ParseFloat(zMatch[1], 64) - } + if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { + target.Z, _ = strconv.ParseFloat(zMatch[1], 64) + } - if fMatch := reF.FindStringSubmatch(command); fMatch != nil { - feedRate, _ = strconv.ParseFloat(fMatch[1], 64) - } + if fMatch := reF.FindStringSubmatch(command); fMatch != nil { + feedRate, _ = strconv.ParseFloat(fMatch[1], 64) + } - return target, feedRate + return target, feedRate } func parseArcCommand(command string, currentPos Vector3) (Vector3, float64) { - reR := regexp.MustCompile(`R([-+]?[0-9]*\.?[0-9]+)`) - radius := 0.0 - target := currentPos + reR := regexp.MustCompile(`R([-+]?[0-9]*\.?[0-9]+)`) + radius := 0.0 + target := currentPos - if rMatch := reR.FindStringSubmatch(command); rMatch != nil { - radius, _ = strconv.ParseFloat(rMatch[1], 64) - } + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + radius, _ = strconv.ParseFloat(rMatch[1], 64) + } - return target, radius + return target, radius } func parseDuration(command string) int { - reP := regexp.MustCompile(`P([0-9]+)`) - duration := 0 + reP := regexp.MustCompile(`P([0-9]+)`) + duration := 0 - if pMatch := reP.FindStringSubmatch(command); pMatch != nil { - duration, _ = strconv.Atoi(pMatch[1]) - } - return duration + if pMatch := reP.FindStringSubmatch(command); pMatch != nil { + duration, _ = strconv.Atoi(pMatch[1]) + } + return duration } func parseTemperature(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - temperature := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + temperature := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - temperature, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + temperature, _ = strconv.ParseFloat(sMatch[1], 64) + } - return temperature + return temperature } func parseFanSpeed(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - fanSpeed := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + fanSpeed := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) + } - return fanSpeed + return fanSpeed } func parseAcceleration(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - acceleration := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + acceleration := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - acceleration, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + acceleration, _ = strconv.ParseFloat(sMatch[1], 64) + } - return acceleration + return acceleration } func parseProgress(command string) int { - reS := regexp.MustCompile(`S([0-9]+)`) - progress := 0 + reS := regexp.MustCompile(`S([0-9]+)`) + progress := 0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - progress, _ = strconv.Atoi(sMatch[1]) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + progress, _ = strconv.Atoi(sMatch[1]) + } - return progress + return progress } func parseKFactor(command string) float64 { - reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) - kFactor := 0.0 + reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) + kFactor := 0.0 + + if kMatch := reK.FindStringSubmatch(command); kMatch != nil { + kFactor, _ = strconv.ParseFloat(kMatch[1], 64) + } + + return kFactor +} + +func parseAllowColdExtrusion(command string) bool { + reP := regexp.MustCompile(`P([01])`) + allowColdExtrusion := false - if kMatch := reK.FindStringSubmatch(command); kMatch != nil { - kFactor, _ = strconv.ParseFloat(kMatch[1], 64) - } + if pMatch := reP.FindStringSubmatch(command); pMatch != nil { + allowColdExtrusion, _ = strconv.ParseBool(pMatch[1]) + } - return kFactor + return allowColdExtrusion } func parseAxes(command string) []string { - reAxes := regexp.MustCompile(`[XYZE]`) + reAxes := regexp.MustCompile(`[XYZE]`) - return reAxes.FindAllString(command, -1) + return reAxes.FindAllString(command, -1) } // ==================== Command Registry ==================== var commandRegistry = map[string]func(string, *Printer) Command{ - "G28": func(cmd string, p *Printer) Command { return &G28Command{} }, - "G0": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, - "G1": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, - "G2": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, true, p) }, - "G3": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, false, p) }, - "G4": func(cmd string, p *Printer) Command { return NewG4Command(cmd) }, - "G20": func(cmd string, p *Printer) Command { return &G20Command{} }, - "G21": func(cmd string, p *Printer) Command { return &G21Command{} }, - "G90": func(cmd string, p *Printer) Command { return &G90Command{} }, - "G91": func(cmd string, p *Printer) Command { return &G91Command{} }, - "G92": func(cmd string, p *Printer) Command { return NewG92Command(cmd) }, - "M104": func(cmd string, p *Printer) Command { return NewM104Command(cmd) }, - "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, - "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, - "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, - "M113": func(cmd string, p *Printer) Command { return &M113Command{} }, - "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, - "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, - "M190": func(cmd string, p *Printer) Command { return NewM190Command(cmd) }, - "M204": func(cmd string, p *Printer) Command { return NewM204Command(cmd) }, - "M73": func(cmd string, p *Printer) Command { return NewM73Command(cmd) }, - "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, - "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, - "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, - "M900": func(cmd string, p *Printer) Command { return NewM900Command(cmd) }, - "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, - "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, + "G28": func(cmd string, p *Printer) Command { return &G28Command{} }, + "G0": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G1": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G2": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, true, p) }, + "G3": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, false, p) }, + "G4": func(cmd string, p *Printer) Command { return NewG4Command(cmd) }, + "G20": func(cmd string, p *Printer) Command { return &G20Command{} }, + "G21": func(cmd string, p *Printer) Command { return &G21Command{} }, + "G90": func(cmd string, p *Printer) Command { return &G90Command{} }, + "G91": func(cmd string, p *Printer) Command { return &G91Command{} }, + "G92": func(cmd string, p *Printer) Command { return NewG92Command(cmd) }, + "M104": func(cmd string, p *Printer) Command { return NewM104Command(cmd) }, + "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, + "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, + "M109": func(cmd string, p *Printer) Command { return NewM109Command(cmd) }, + "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, + "M113": func(cmd string, p *Printer) Command { return &M113Command{} }, + "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, + "M115": func(cmd string, p *Printer) Command { return &M115Command{} }, + "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, + "M190": func(cmd string, p *Printer) Command { return NewM190Command(cmd) }, + "M204": func(cmd string, p *Printer) Command { return NewM204Command(cmd) }, + "M73": func(cmd string, p *Printer) Command { return NewM73Command(cmd) }, + "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, + "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, + "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, + "M900": func(cmd string, p *Printer) Command { return NewM900Command(cmd) }, + "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, + "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, + "M17": func(cmd string, p *Printer) Command { return &M17Command{} }, + "M18": func(cmd string, p *Printer) Command { return &M18Command{} }, + "M302": func(cmd string, p *Printer) Command { return NewM302Command(cmd) }, + "M503": func(cmd string, p *Printer) Command { return &M503Command{} }, + "M82": func(cmd string, p *Printer) Command { return &M82Command{} }, + "M83": func(cmd string, p *Printer) Command { return &M83Command{} }, } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index a20ad134..e855bebb 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -152,8 +152,8 @@ func (printer *Printer) UpdateBedTemperature(currentTemp float64) { // SetFanSpeed sets the fan speed on the extruder func (printer *Printer) SetFanSpeed(speed float64) error { - if speed < 0 || speed > 100 { - return fmt.Errorf("invalid fan speed: %.2f. Valid range: 0 to 100", speed) + if speed < 0 || speed > 255 { + return fmt.Errorf("invalid fan speed: %.2f. Valid range: 0 to 255", speed) } printer.Extruder.SetFanSpeed(speed) @@ -178,13 +178,12 @@ func (printer *Printer) Resume() { // UpdateProgress updates the progress of the print job func (printer *Printer) UpdateProgress(progress int) error { - if progress >= 0 && progress <= 100 { - printer.Progress = progress - } else { - return fmt.Errorf("invalid progress: %d. Valid range: 0 to 100", progress) - } - - return nil + if progress >= 0 && progress <= 100 { + printer.Progress = progress + } else { + return fmt.Errorf("invalid progress: %d. Valid range: 0 to 100", progress) + } + return nil } // MoveExtruder moves the extruder to the specified position. From a17ca33231322503e9a0b6f8a573c9c403ebcf6f Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Fri, 29 Nov 2024 15:51:07 -0500 Subject: [PATCH 141/194] feat: worked on frontend integration. (WIP) --- client/src/components/GCode3DImageViewer.vue | 7 +- client/src/components/GCode3DLiveViewer.vue | 9 +- client/src/components/GCodeThumbnail.vue | 9 +- client/src/components/ThemePanel.vue | 1 - client/src/model/ports.ts | 8 +- client/src/views/JobHistory.vue | 5 +- client/src/views/MainView.vue | 16 +- client/src/views/RegisteredView.vue | 12 +- server/Classes/FabricatorList.py | 50 ++- server/Classes/Fabricators/Device.py | 212 +++++++++- server/Classes/Fabricators/Fabricator.py | 149 ++++--- .../Printers/Ender/EnderPrinter.py | 27 +- .../Printers/MakerBot/MakerBotPrinter.py | 31 +- .../Classes/Fabricators/Printers/Printer.py | 392 +++++++++++++++++- .../Fabricators/Printers/Prusa/PrusaMK3.py | 8 +- .../Printers/Prusa/PrusaPrinter.py | 69 +-- server/Classes/Jobs.py | 204 +++++---- server/Classes/Logger.py | 25 +- server/Classes/Ports.py | 11 +- server/Classes/Queue.py | 90 ++-- server/Classes/Vector3.py | 3 + server/Mixins/gcode/usesMarlinGcode.py | 209 ---------- server/Mixins/gcode/usesPrusaGcode.py | 54 --- server/Mixins/gcode/usesVanillaGcode.py | 2 +- server/app.py | 50 +-- server/controllers/jobs.py | 143 ++++--- server/controllers/ports.py | 72 ++-- server/controllers/statusService.py | 78 ++-- server/routes.py | 15 + 29 files changed, 1180 insertions(+), 781 deletions(-) create mode 100644 server/routes.py diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index 6e470e4f..84d241f2 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -38,9 +38,14 @@ onMounted(async () => { extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--bs-primary-color').trim() || '#7561A9', backgroundColor: 'black', buildVolume: { x: 250, y: 210, z: 220 }, + lineWidth: 1, + lineHeight: 1, + extrusionWidth: 1, + renderExtrusion: true, + renderTubes: true, }); - preview.camera.position.set(0, 410, 365); + preview.camera.position.set(-200, 232, 200); preview.camera.lookAt(0, 0, 0); if (canvas.value) { diff --git a/client/src/components/GCode3DLiveViewer.vue b/client/src/components/GCode3DLiveViewer.vue index 2a9fac20..981184a4 100644 --- a/client/src/components/GCode3DLiveViewer.vue +++ b/client/src/components/GCode3DLiveViewer.vue @@ -62,12 +62,17 @@ onMounted(async () => { if (canvas.value) { preview = GCodePreview.init({ canvas: canvas.value, - extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--bs-primary-color').trim() || '#7561A9', + extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim() || '#7561A9', backgroundColor: 'black', buildVolume: { x: 250, y: 210, z: 220 }, + lineWidth: 1, + lineHeight: 1, + extrusionWidth: 1, + renderExtrusion: true, + renderTubes: true, }); - preview.camera.position.set(0, 475, 0); + preview.camera.position.set(-200, 232, 200); preview.camera.lookAt(0, 0, 0); if (job.value?.current_layer_height && preview) { diff --git a/client/src/components/GCodeThumbnail.vue b/client/src/components/GCodeThumbnail.vue index 1e625565..844c959d 100644 --- a/client/src/components/GCodeThumbnail.vue +++ b/client/src/components/GCodeThumbnail.vue @@ -44,8 +44,13 @@ onMounted(async () => { try { // Extract the thumbnail from the metadata const { metadata } = preview.parser.parseGCode(gcode); - if (metadata.thumbnails && metadata.thumbnails['640x480']) { - const thumbnailData = metadata.thumbnails['640x480']; + console.debug('GCode metadata:', metadata); + let thumbnailData = null; + if (metadata.thumbnails) { + if(metadata.thumbnails['640x480']) thumbnailData = metadata.thumbnails['640x480']; + else if(metadata.thumbnails['320x240']) thumbnailData = metadata.thumbnails['320x240']; + else if(metadata.thumbnails['160x120']) thumbnailData = metadata.thumbnails['160x120']; + else thumbnailData = metadata.thumbnails[Object.keys(metadata.thumbnails)[0]]; thumbnailSrc.value = thumbnailData.src; } } catch (error) { diff --git a/client/src/components/ThemePanel.vue b/client/src/components/ThemePanel.vue index 936c3df2..931005d7 100644 --- a/client/src/components/ThemePanel.vue +++ b/client/src/components/ThemePanel.vue @@ -236,7 +236,6 @@ const brightness = (rgb: string) => { const fontColor = (rgb: string) => { const bright = brightness(rgb); - console.log("rgb", rgb, "brightness", bright); return bright < 155 ? getComputedStyle(document.documentElement, null).getPropertyValue('--vt-c-text-dark-1') : getComputedStyle(document.documentElement, null).getPropertyValue('--vt-c-text-light-1'); } diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index e93a24a3..76c2f8fb 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -9,7 +9,7 @@ export function api(action: string, body?: unknown, method?: string, headers?: a } export interface Device { - device: string + device: Record<string, any> description: string hwid: string name?: string @@ -70,8 +70,7 @@ export function useRetrievePrinters() { return { async retrieve() { try { - const response = await api('getprinters') - return response.printers + return await api('getprinterinfo') } catch (error) { console.error(error) } @@ -84,8 +83,7 @@ export function useRetrievePrintersInfo() { return { async retrieveInfo() { try { - const response = await api('getprinterinfo') - return response // return the response directly + return await api('getprinterinfo') } catch (error) { console.error(error) } diff --git a/client/src/views/JobHistory.vue b/client/src/views/JobHistory.vue index 4479c449..0851315a 100644 --- a/client/src/views/JobHistory.vue +++ b/client/src/views/JobHistory.vue @@ -1,8 +1,8 @@ <script setup lang="ts"> -import { printers, type Device } from '../model/ports' +import { printers, type Device } from '@/model/ports' import { pageSize, useGetJobs, type Job, useGetJobFile, useDeleteJob, useClearSpace, useFavoriteJob, useGetFile, useAssignComment, useDownloadCsv, useRemoveIssue, isLoading } from '../model/jobs'; import { computed, onMounted, onBeforeUnmount, ref, watchEffect, onUnmounted } from 'vue'; -import { type Issue, useGetIssues, useAssignIssue } from '../model/issues' +import { type Issue, useGetIssues, useAssignIssue } from '@/model/issues' import { useRouter } from 'vue-router'; import GCode3DImageViewer from '@/components/GCode3DImageViewer.vue' import GCodeThumbnail from '@/components/GCodeThumbnail.vue'; @@ -88,6 +88,7 @@ let filteredJobs = computed(() => { } }) + let offcanvasElement: HTMLElement | null = null; onMounted(async () => { diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index 94390a31..25295b1d 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -310,7 +310,7 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="printer.status === 'printing' && printer.queue?.[0]?.released === 0" style="color: #ad6060" + <p v-if="printer.status === 'idle' && printer.queue?.[0]?.released === 0" style="color: #ad6060" class="mb-0 me-2"> Waiting release </p> @@ -342,13 +342,13 @@ const handleDragEnd = async () => { </button> <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete'" + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" @click="setPrinterStatus(printer, 'offline')"> Turn Offline </button> <button class="btn btn-secondary" - v-if="printer.status == 'printing' && printer.queue?.[0].released == 0" + v-if="printer.status == 'idle' && printer.queue?.[0].released == 0" @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> @@ -568,7 +568,7 @@ const handleDragEnd = async () => { </div> <tr v-else :id="printer.id"> <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0].td_id }} </td> <td v-else><i>idle</i></td> @@ -587,7 +587,7 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="printer.status === 'printing' && printer.queue?.[0]?.released === 0" style="color: #ad6060" + <p v-if="printer.status === 'idle' && printer.queue?.[0]?.released === 0" style="color: #ad6060" class="mb-0 me-2"> Waiting release </p> @@ -598,7 +598,7 @@ const handleDragEnd = async () => { </td> <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.name }} </td> <td v-else></td> @@ -619,12 +619,12 @@ const handleDragEnd = async () => { </button> <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete'" + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" @click="setPrinterStatus(printer, 'offline')"> Turn Offline </button> - <button class="btn btn-secondary" v-if="printer.status == 'printing' && printer.queue?.[0].released == 0" + <button class="btn btn-secondary" v-if="printer.status == 'idle' && printer.queue?.[0].released == 0" @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> diff --git a/client/src/views/RegisteredView.vue b/client/src/views/RegisteredView.vue index a8b1b0fe..317beee2 100644 --- a/client/src/views/RegisteredView.vue +++ b/client/src/views/RegisteredView.vue @@ -113,9 +113,9 @@ const doRepair = async () => { isLoading.value = false } -const doMove = async (printer: Device) => { +const doMove = async (port: string) => { isLoading.value = true - await move(printer.device).then(() => { + await move(port).then(() => { toast.success('Printer moved to home position') }).catch(() => { toast.error('Failed to move printer to home position') @@ -125,9 +125,9 @@ const doMove = async (printer: Device) => { const doDiagnose = async (printer: Device) => { isLoading.value = true - message.value = `Diagnosing <b>${printer.name}</b>:<br/><br/>This printer is registered under port <b>${printer.device}</b>.` + message.value = `Diagnosing <b>${printer.name}</b>:<br/><br/>This printer is registered under port <b>${printer.device['serialPort']}</b>.` showMessage.value = true - let str = await diagnose(printer.device) + let str = await diagnose(printer.device['serialPort']) let resstr = str.diagnoseString message.value += "<br><br>" + resstr isLoading.value = false @@ -239,7 +239,7 @@ const doCloseRegisterModal = async () => { </a> </li> <li> - <a class="dropdown-item d-flex align-items-center" @click="doMove(printer)"> + <a class="dropdown-item d-flex align-items-center" @click="doMove(printer.device['serialPort'])"> <i class="fas fa-home"></i> <span class="ms-2">Home Printer</span> </a> @@ -255,7 +255,7 @@ const doCloseRegisterModal = async () => { @click="editMode = false; editNum = undefined; newName = ''">Cancel</button> </div> </div> - <h6 class="card-text mb-0"> <b>Printer device:</b> {{ printer.device }}</h6> + <h6 class="card-text mb-0"> <b>Printer device:</b> {{ printer.name }}</h6> <h6 class="card-text mb-0"> <b>Printer description:</b> {{ printer.description }}</h6> <h6 class="card-text mb-0"> <b>Date registered:</b> {{ printer.date }}</h6> <h6 class="card-text mt-0"> <b>HWID:</b> {{ printer.hwid }}</h6> diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 44545826..661c072a 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -20,6 +20,14 @@ def __init__(self, fabricator, app=None, *args, **kwargs): from app import app self.app = app + def __to_JSON__(self): + return { + "fabricator": self.fabricator, + "app": self.app, + "running": self.is_alive(), + "daemon": self.daemon, + } + def run(self): with self.app.app_context(): while True: @@ -38,11 +46,13 @@ def stop(self): class FabricatorList: def __init__(self, app=None): self.app = app + assert self.app is not None, "app is None" with self.app.app_context(): self.fabricators = Fabricator.queryAll() self.fabricator_threads = [] self.ping_thread = None for fabricator in self.fabricators: + fabricator.device.connect() self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) def __iter__(self): @@ -54,6 +64,25 @@ def __len__(self): def __getitem__(self, key): return self.fabricators[key] + def __to_JSON__(self): + """ + Convert the FabricatorList to a JSON object + :return: JSON object + :rtype: dict + """ + fab_list = [] + for fabricator in self: + fab_list.append(fabricator.__to_JSON__()) + thread_list = [] + for thread in self.fabricator_threads: + thread_list.append(thread.__to_JSON__()) + return { + "fabricators": fab_list, + "fabricator_threads": thread_list, + "ping_thread": self.ping_thread, + "app": self.app, + } + def teardown(self): """stop all fabricator threads""" [thread.stop() for thread in self.fabricator_threads] @@ -77,18 +106,18 @@ def addFabricator(self, serialPortName: str, name: str = ""): newFab = listFab newFab.addToDB() else: # means that the fabricator is not in the list or the db - newFab = Fabricator(serialPort, name=name, addToDB=True) + newFab = Fabricator(serialPort, name=name) self.fabricators.append(newFab) - dbfabricators = Fabricator.queryAll() - assert(len(self) == len(dbfabricators)) - assert all(fabricator in self for fabricator in dbfabricators) + dbFabricators = Fabricator.queryAll() + assert(len(self) == len(dbFabricators)), f"len(self)={len(self)}, len(dbFabricators)={len(dbFabricators)}" + assert all(fabricator in self for fabricator in dbFabricators), f"self={self}, dbFabricators={dbFabricators}" if newFab: self.start_fabricator_thread(newFab) def deleteFabricator(self, fabricatorid): """delete a fabricator from the list, and from the database""" # TODO: Implement deleteFabricator - pass + raise NotImplementedError("deleteFabricator is not implemented") def getFabricatorByName(self, name) -> Fabricator | None: @@ -107,8 +136,11 @@ def getFabricatorById(self, id) -> Fabricator | None: def getFabricatorByPort(self, port) -> Fabricator | None: """find the first fabricator with the given port""" + assert isinstance(port, str), f"port={port}, type(port)={type(port)}" for fabricator in self: - if fabricator.getSerialPort().device == port: + assert isinstance(fabricator.devicePort, str), f"fabricator.devicePort={fabricator.devicePort}, type(fabricator.devicePort)={type(fabricator.devicePort)}" + print(fabricator.devicePort + " ?= " + port) + if fabricator.devicePort == port: return fabricator return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) @@ -155,10 +187,10 @@ def create_fabricator_threads(self): self.ping_thread = Thread(target=self.pingForStatus) def get_fabricator_thread(self, fabricator): - assert fabricator in self + assert fabricator in self, f"fabricator {fabricator} not in self" thread = next(thread for thread in self.fabricator_threads if thread.fabricator == fabricator) - assert thread.is_alive() - assert thread.daemon + assert thread.is_alive(), f"thread {thread} is not alive" + assert thread.daemon, f"thread {thread} is not daemon" return thread diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index d7ac6a46..2a4b9eb8 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -1,12 +1,17 @@ -from abc import ABC, abstractmethod +import sys +from abc import ABC +from time import sleep from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS -from typing_extensions import Buffer + +from Classes.Jobs import Job from Classes.Vector3 import Vector3 from Classes.Logger import Logger import serial import serial.tools.list_ports -from Mixins.canPause import canPause +from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import checkXYZ + class Device(ABC): # static variables @@ -18,17 +23,55 @@ class Device(ABC): serialConnection: serial.Serial | None = None homePosition: Vector3 | None = None - def __init__(self, serialPort: ListPortInfo | SysFS, consoleLogger=None, fileLogger=None): + homeCMD: bytes | None= b"G28\n" + cancelCMD: bytes | None = None + keepAliveCMD: bytes | None = None + doNotKeepAliveCMD: bytes | None = None + statusCMD: bytes | None = None + getLocationCMD: bytes | None = None + pauseCMD: bytes | None = None + resumeCMD: bytes | None = None + getMachineNameCMD: bytes | None = None + + callablesHashtable = { + "G28": [checkXYZ], # Home + } + + def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sys.stdout, fileLogger=None): + self.dbID: int = dbID self.serialPort: ListPortInfo | SysFS | None = serialPort self.serialID: str | None = serialPort.serial_number - self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG) self.status = "idle" self.verdict = "" def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" + def __to_JSON__(self): + return { + "MODEL": self.MODEL, + "VENDORID": self.VENDORID, + "PRODUCTID": self.PRODUCTID, + "DESCRIPTION": self.DESCRIPTION, + "MAXFEEDRATE": self.MAXFEEDRATE, + "serialConnection": { + "port": self.serialConnection.port, + "baudrate": self.serialConnection.baudrate, + "timeout": self.serialConnection.timeout, + "is_open": self.serialConnection.is_open, + } if self.serialConnection else None, + "homePosition": self.homePosition.__to_JSON__() if self.homePosition else None, + "dbID": self.dbID, + "serialPort": self.serialPort.name if self.serialPort else None, + "serialID": self.serialID, + "logger": self.logger.name, + "status": self.status, + "verdict": self.verdict + } + def connect(self): + """Connect to the hardware using the serial port.""" try: self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) self.serialConnection.reset_input_buffer() @@ -39,34 +82,140 @@ def connect(self): return False def disconnect(self): + """Disconnect from the hardware by closing the serial connection.""" if self.serialConnection: self.serialConnection.close() self.serialConnection = None - @abstractmethod - def home(self, isVerbose: bool = False): - pass + def home(self, isVerbose: bool = True): + """ + Home the device. + :param isVerbose: Whether to log the command. + :type isVerbose: bool + """ + try: + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + self.sendGcode(self.homeCMD, isVerbose=isVerbose) + assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" + return True + except Exception as e: + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) - @abstractmethod def goTo(self, loc: Vector3, isVerbose: bool = False): - pass + """ + Move the tool head to a specific location. + :param loc: the coordinates to move to + :type loc: Vector3 + :param isVerbose: whether to log the command + :type isVerbose: bool + :raises AssertionError: if the location is not a Vector3, if isVerbose is not a bool, or if self is not a Device + """ + assert isinstance(loc, Vector3) + assert isinstance(isVerbose, bool) + assert isinstance(self, Device) + self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{str(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) + self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) + if hasattr(self, "getLocationCMD"): + return loc == self.getToolHeadLocation() + return True - def parseGcode(self, file, isVerbose=False): - pass + def parseGcode(self, job: Job, isVerbose: bool = False): + """ + Parse a G-code file and send the commands to the device. + :param job: The Job object, with file name to parse. + :param isVerbose: Whether to log the commands + :type job: Job + :type isVerbose: bool + :raises AssertionError: if the file is not a string or if isVerbose is not a bool + """ + assert isinstance(job, Job) + file = job.file_name_original + assert isinstance(file, str) + assert isinstance(isVerbose, bool) + try: + with open(file, "r") as f: + self.logger.info(f"Printing {file}") + for line in f: + if line.startswith(";") or line == "\n": + continue + if isVerbose: self.logger.debug(line.strip("\n")) + if self.status == "paused": + self.pause() + while self.status == "paused": + sleep(1) + if self.status == "cancelled": + if isinstance(self, hasEndingSequence): + self.endSequence() + self.verdict = "cancelled" + self.logger.info("Job cancelled") + return True + elif self.status == "printing": + self.resume() + if self.status == "cancelled": + if isinstance(self, hasEndingSequence): + self.endSequence() + self.verdict = "cancelled" + self.logger.info("Job cancelled") + return True + if ";" in line: + line = line.split(";")[0].strip() + "\n" + self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) + self.verdict = "complete" + self.logger.info("Job complete") + return True + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error cancelling job:") + self.logger.error(e) + self.verdict = "error" + return True - def pause(self: canPause): - pass - def resume(self: canPause): + def pause(self): pass - @abstractmethod - def sendGcode(self, gcode: Buffer, isVerbose: bool = False): + def resume(self): pass - @abstractmethod - def getToolHeadLocation(self) -> Vector3: - pass + def sendGcode(self, gcode: bytes, isVerbose: bool = False): + """ + Send a G-code command to the device. + :param gcode: The line to send to the hardware + :type gcode: bytes + :param isVerbose: Whether to log the command + :type isVerbose: bool + :return: True if the command was sent successfully, else False + :rtype: bool + :raises AssertionError: if the serial connection is not open, if gcode is not bytes, or if isVerbose is not a bool + """ + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + assert isinstance(gcode, bytes) + assert isinstance(isVerbose, bool) + self.serialConnection.write(gcode) + if isVerbose: self.logger.debug(gcode.decode("utf-8")) + return True + + def getToolHeadLocation(self, isVerbose: bool = False) -> Vector3: + """ + Get the current location of the tool head. + :param isVerbose: Whether to log the command + :return: the current location of the tool head + :rtype: Vector3 + """ + assert hasattr(self, "getLocationCMD") + self.serialConnection.write(self.getLocationCMD) + response = "" + while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): + response = self.serialConnection.readline().decode("utf-8") + if isVerbose: self.logger.info(response) + loc = LocationResponse(response) + return Vector3(loc.x, loc.y, loc.z) def repair(self): """Attempt to repair the device connection by closing and reopening the serial connection.""" @@ -146,3 +295,26 @@ def getHomePosition(self): def getMaxFeedRate(self): return self.MAXFEEDRATE + +class LocationResponse: + def __init__(self, response: str): + import re + loc = [item.strip() for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item] + self.x = float(loc[0]) + self.y = float(loc[1]) + self.z = float(loc[2]) + self.e = float(loc[3]) + self.count_x = float(loc[4]) if '.' in loc[4] else int(loc[4]) + self.count_y = float(loc[5]) if '.' in loc[5] else int(loc[5]) + self.count_z = float(loc[6]) if '.' in loc[6] else int(loc[6]) + + def __to_JSON__(self): + return { + "x": self.x, + "y": self.y, + "z": self.z, + "e": self.e, + "count_x": self.count_x, + "count_y": self.count_y, + "count_z": self.count_z, + } \ No newline at end of file diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 1f00bcb5..1ecb9a18 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -4,7 +4,7 @@ from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Device import Device -from Mixins.canPause import canPause +from typing_extensions import TextIO from Mixins.hasEndingSequence import hasEndingSequence from models.db import db from datetime import datetime, timezone @@ -22,31 +22,60 @@ class Fabricator(db.Model): devicePort = db.Column(db.String(50), nullable=False) - def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", addToDB: bool = False, consoleLogger=None, fileLogger=None): + def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLogger=None, fileLogger=None): if port is None: return - assert isinstance(port, ListPortInfo) or isinstance(port, SysFS) - assert isinstance(name, str) - + assert isinstance(port, ListPortInfo) or isinstance(port, SysFS), f"Invalid port type: {type(port)}" + assert isinstance(name, str), f"Invalid name type: {type(name)}" from Classes.Queue import Queue from Classes.Jobs import Job - self.device: Device = Fabricator.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.dbID = None # Initialize dbID self.job: Job | None = None - self.queue: Queue = Queue() self.status: str = "idle" - + self.hwid = port.hwid.split(" LOCATION=")[0] + self.description = "New Fabricator" self.name: str = name - self.description = self.device.getDescription() - self.hwid = self.device.getHWID() - self.devicePort = self.device.getSerialPort().device - if addToDB: + self.devicePort = port.device + dbFab = Fabricator.query.filter_by(hwid=self.hwid).first() + if dbFab is None: db.session.add(self) db.session.commit() + self.dbID = self.dbID # Set dbID after committing to the database + else: + self.name = dbFab.name + self.description = dbFab.description + self.hwid = dbFab.hwid + self.devicePort = dbFab.devicePort + self.date = dbFab.date + self.dbID = dbFab.dbID + + self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger) + if self.description == "New Fabricator": self.description = self.device.getDescription() + db.session.commit() def __repr__(self): return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}" + + def __to_JSON__(self): + """ + Converts the Fabricator object to a JSON object + :return: JSON object + :rtype: dict + """ + return { + "name": self.name, + "description": self.description, + "hwid": self.hwid, + "status": self.status, + "id": self.dbID, + "date": self.date.strftime("%a, %d %b %Y %H:%M:%S"), + "queue": self.queue.convertQueueToJson(), + "job": self.job.__to_JSON__() if self.job is not None else None, + "device": self.device.__to_JSON__(), + } + @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: """returns the model of the printer based on the response to M997""" @@ -61,12 +90,22 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: response = response.decode("utf-8") return response - - @staticmethod - def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None) -> Device | None: - """creates the correct printer object based on the serial port info""" + def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None): + """ + creates the correct printer object based on the serial port info + :param serialPort: the serial port info + :type serialPort: ListPortInfo | SysFS | None + :param consoleLogger: the console stream to output to + :type consoleLogger: TextIO | None + :param fileLogger: the file path to output file logs to + :type fileLogger: str | None + :return: the printer object + :rtype: Device | None + """ if serialPort is None: return None + assert isinstance(self, Fabricator), f"self is not a Fabricator object: {self}" + assert self.dbID is not None, "dbID is None, so there is no way to add the fabricator to the database" from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter @@ -75,11 +114,11 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: - return PrusaMK4(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4S(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: return None elif serialPort.vid == EnderPrinter.VENDORID: @@ -87,15 +126,15 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3Pro(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) elif "Ender-3" in model: - return Ender3(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Replicator2(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) else: #TODO: assume generic printer, do stuff return None @@ -104,8 +143,8 @@ def createDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fi def queryAll(cls): """ Returns all fabricators in the database as a list of the Fabricator objects - - :return (list[Fabricator]): list of Fabricator objects + :return: list of Fabricator objects + :rtype: list[Fabricator] """ fabList = [] from Classes.Ports import Ports @@ -120,15 +159,25 @@ def updateDB(cls): db.session.commit() def addToDB(self): + """adds the fabricator to the db""" db.session.add(self) db.session.commit() def begin(self, isVerbose: bool = False): - """starts the fabrication process""" + """ + starts the fabrication process + :param isVerbose: whether to print verbose output + :type isVerbose: bool + :return: whether the fabrication process was successful + :rtype: bool + """ + from flask import current_app + if current_app is not None: + current_app.logger.critical(f"current app: {current_app}") try: - assert self.status == "idle" - assert self.queue is not None - assert len(self.queue) > 0 + assert self.status == "idle", f"Fabricator is not idle, status: {self.status}" + assert self.queue is not None, "Queue is None" + assert len(self.queue) > 0, "Queue is empty" self.job = self.queue.getNext() assert self.job is not None, "Job is None" self.checkValidJob() @@ -136,7 +185,7 @@ def begin(self, isVerbose: bool = False): # if isinstance(self.device, hasStartupSequence): # self.device.startupSequence() assert self.setStatus("printing"), "Failed to set status to printing" - assert self.device.parseGcode(self.job.file_name_original, isVerbose=isVerbose), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. + assert self.device.parseGcode(self.job, isVerbose=isVerbose), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. if isVerbose: self.device.logger.debug(f"Job complete, verdict: {self.device.verdict}") self.handleVerdict() if isVerbose: self.device.logger.debug(f"Verdict handled, status: {self.status}") @@ -148,35 +197,38 @@ def begin(self, isVerbose: bool = False): def pause(self): """pauses the fabrication process if the fabricator supports it""" - if not isinstance(self.device, canPause): + if not self.device.pauseCMD: from app import handle_errors_and_logging return handle_errors_and_logging("Fabricator doesn't support pausing", self) - assert isinstance(self.device, Device) + assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" if self.status != "printing": from app import handle_errors_and_logging - return handle_errors_and_logging("Fabricator doesn't support pausing", self) + return handle_errors_and_logging("Nothing to pause, Fabricator isn't printing", self) + assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" + assert self.device.pause(), "Failed to pause" self.setStatus("paused") - assert isinstance(self.device, Device) return self.status == self.device.status == "paused" def resume(self): """resumes the fabrication process if the fabricator supports it""" - if not isinstance(self.device, canPause): - return #TODO: return error message + if not self.device.resumeCMD: + from app import handle_errors_and_logging + return handle_errors_and_logging("Fabricator doesn't support pausing", self) if self.status != "paused": - return #TODO: return error message + from app import handle_errors_and_logging + return handle_errors_and_logging("Nothing to resume, Fabricator isn't paused", self) self.setStatus("printing") - assert isinstance(self.device, Device) + assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" return self.status == self.device.status == "printing" def cancel(self): """cancels the fabrication process""" try: - assert self.job is not None - assert self.device is not None + assert self.job is not None, "Job is None" + assert self.device is not None, "Device is None" if self.status != "printing" and self.status != "paused": - self.device.logger.error(f"Fabricator isn't in the middle of a job: current status: {self.status}") - return #TODO: return error message + from app import handle_errors_and_logging + return handle_errors_and_logging("Nothing to cancel, Fabricator isn't printing", self) self.setStatus("cancelled") return self.status == self.device.status == "cancelled" except Exception as e: @@ -188,8 +240,8 @@ def getStatus(self): def setStatus(self, newStatus): try: - assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint"] - assert self.device is not None + assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint"], f"Invalid status: {newStatus}" + assert self.device is not None, "Device is None" if self.status == "error" and newStatus!= "error": self.device.hardReset(newStatus) @@ -198,10 +250,11 @@ def setStatus(self, newStatus): if self.job is not None: self.job.status = newStatus - - # current_app.socketio.emit( - # "status_update", {"fabricator_id": self.dbID, "status": newStatus} - # ) + from flask import current_app + if current_app: + current_app.socketio.emit( + "status_update", {"fabricator_id": self.dbID, "status": newStatus} + ) return True except Exception as e: from app import handle_errors_and_logging @@ -213,7 +266,7 @@ def resetToIdle(self): def handleVerdict(self): assert self.device.verdict in ["complete", "error", "cancelled", "misprint"], f"Invalid verdict: {self.device.verdict}" - assert self.job is not None + assert self.job is not None, "Job is None" if self.device.verdict == "complete": #self.device.disconnect() self.setStatus("complete") diff --git a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py index e2ed26f9..c5d46e52 100644 --- a/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py +++ b/server/Classes/Fabricators/Printers/Ender/EnderPrinter.py @@ -1,25 +1,19 @@ from abc import ABCMeta from time import sleep - -from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.gcode.usesMarlinGcode import usesMarlinGcode -class EnderPrinter(Printer, hasEndingSequence, usesMarlinGcode, metaclass=ABCMeta): +class EnderPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): VENDORID = 0x1A86 homePosition = Vector3(-3.0,-10.0,0.0) def connect(self): - ret = usesMarlinGcode.connect(self) + ret = super().connect() sleep(7) return ret - def disconnect(self): - return usesMarlinGcode.disconnect(self) - def endSequence(self): self.sendGcode(b"G91\n") # Relative positioning self.sendGcode(b"G1 E-2 F2700\n") # Retract a bit @@ -33,20 +27,5 @@ def endSequence(self): self.sendGcode(b"M140 S0\n") # Turn-off bed self.sendGcode(b"M84 X Y E\n") # Disable all steppers but Z - def goTo(self, loc: Vector3, isVerbose: bool = False): - return usesMarlinGcode.goTo(self, loc, isVerbose) - def getPrintTime(self): - pass - - def getToolHeadLocation(self) -> Vector3: - return usesMarlinGcode.getToolHeadLocation(self) - - def parseGcode(self, file, isVerbose: bool = False): - return usesMarlinGcode.parseGcode(self, file, isVerbose) - - def sendGcode(self, gcode: Buffer, isVerbose: bool = False): - usesMarlinGcode.sendGcode(self, gcode, isVerbose) - - def home(self, isVerbose: bool = False): - return usesMarlinGcode.home(self, isVerbose) \ No newline at end of file + pass \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py index d24cd8be..41b8f13c 100644 --- a/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py +++ b/server/Classes/Fabricators/Printers/MakerBot/MakerBotPrinter.py @@ -1,38 +1,9 @@ from abc import ABCMeta -from typing_extensions import Buffer from Classes.Fabricators.Printers.Printer import Printer from Classes.Vector3 import Vector3 from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import hasResponsecodes -from Mixins.gcode.usesMarlinGcode import usesMarlinGcode class MakerBotPrinter(Printer, hasEndingSequence, hasResponsecodes, metaclass=ABCMeta): VENDORID = 0x23C1 - homePosition = Vector3(0.0, 0.0, 0.0) - - def connect(self): - return usesMarlinGcode.connect(self) - - def disconnect(self): - return usesMarlinGcode.disconnect(self) - - def endSequence(self): - pass - - def goTo(self, loc: Vector3, isVerbose: bool = False): - pass - - def getPrintTime(self): - pass - - def getToolHeadLocation(self) -> Vector3: - pass - - def parseGcode(self, file, isVerbose: bool = False): - pass - - def sendGcode(self, gcode: Buffer, isVerbose: bool = False): - pass - - def home(self, isVerbose: bool = False): - pass \ No newline at end of file + homePosition = Vector3(0.0, 0.0, 0.0) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index d25a78d1..c031c354 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -1,30 +1,398 @@ from abc import ABCMeta -from app import app +import re +from datetime import datetime +from time import sleep from Classes.Fabricators.Device import Device +from Classes.Jobs import Job +from Mixins.hasResponseCodes import checkTime, checkExtruderTemp, checkXYZ, checkBedTemp, checkOK + class Printer(Device, metaclass=ABCMeta): - bedTemperature: int | None = None - nozzleTemperature: int | None = None + cancelCMD: bytes = b"M112\n" + keepAliveCMD: bytes = b"M113 S1\n" + doNotKeepAliveCMD: bytes = b"M113 S0\n" + statusCMD: bytes = b"M115\n" + getLocationCMD: bytes = b"M114\n" + pauseCMD: bytes = b"M601\n" + resumeCMD: bytes = b"M602\n" + getMachineNameCMD: bytes = b"M997\n" + + callablesHashtable = { + "M31": [checkTime], # Print time + "M104": [], # Set hotend temp + "M109": [checkExtruderTemp], # Wait for hotend to reach target temp + "M114": [checkXYZ], # Get current position + "M140": [], # Set bed temp + "M190": [checkBedTemp], # Wait for bed to reach target temp + } + callablesHashtable = {**Device.callablesHashtable, **callablesHashtable} + + bedTemperature: int | float | None = None + nozzleTemperature: int | float | None = None - def __init__(self, serialPort, consoleLogger=None, fileLogger=None): - super().__init__(serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None): + super().__init__(dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) self.filamentType = None self.filamentDiameter = None self.nozzleDiameter = None + def parseGcode(self, job: Job, isVerbose: bool = False): + assert isinstance(job, Job) + file = job.file_name_original + assert isinstance(file, str) + assert isinstance(isVerbose, bool) + assert self.serialConnection.is_open + assert self.status == "printing" + try: + with open(file, "r") as g: + # Read the file and store the lines in a list + if self.status == "cancelled": + self.sendGcode(self.cancelCMD) + self.verdict = "cancelled" + self.logger.debug("Job cancelled") + return True + + lines = g.readlines() + + # Time handling + comment_lines = [line for line in lines if line.strip() and line.startswith(";")] + + max_layer_height = 0 + for i in reversed(range(len(comment_lines))): + # Check if the line contains ";LAYER_CHANGE" + if ";LAYER_CHANGE" in comment_lines[i]: + # Check if the next line exists + if i < len(comment_lines) - 1: + # Save the next line + line = comment_lines[i + 1] + # Use regex to find the numerical value after ";Z:" + match = re.search(r";Z:(\d+\.?\d*)", line) + if match: + max_layer_height = float(match.group(1)) + break + if max_layer_height != 0: + job.setMaxLayerHeight(max_layer_height) + + total_time = job.getTimeFromFile(comment_lines) + job.setTime(total_time, 0) + # job.setTime(total_time, 0) + + # Only send the lines that are not empty and don't start with ";" + # so we can correctly get the progress + command_lines = [ + line for line in lines if line.strip() and not line.startswith(";") + ] + # store the total to find the percentage later on + total_lines = len(command_lines) + # set the sent lines to 0 + sent_lines = 0 + # previous line to check for layer height + prev_line = "" + # Replace file with the path to the file. "r" means read mode. + # now instead of reading from 'g', we are reading line by line + for line in lines: + if self.status == "cancelled": + self.sendGcode(self.cancelCMD) + self.verdict = "cancelled" + self.logger.debug("Job cancelled") + return True + + # print("LINE: ", line, " STATUS: ", self.status, " FILE PAUSE: ", job.getFilePause()) + if "layer" in line.lower() and self.status == 'colorchange': + #TODO: implement color change + pass + + # if line contains ";LAYER_CHANGE", do job.currentLayerHeight(the next line) + if prev_line and ";LAYER_CHANGE" in prev_line: + match = re.search(r";Z:(\d+\.?\d*)", line) + if match: + current_layer_height = float(match.group(1)) + job.setCurrentLayerHeight(current_layer_height) + prev_line = line + + # remove whitespace + line = line.strip() + # Don't send empty lines and comments. ";" is a comment in gcode. + if ";" in line: # Remove inline comments + line = line.split(";")[ + 0 + ].strip() # Remove comments starting with ";" + + if len(line) == 0 or line.startswith(";"): + continue + + if ("M569" in line) and job.getTimeStarted() == 0: + job.setTimeStarted(1) + job.setTime(job.calculateEta(), 1) + job.setTime(datetime.now(), 2) + + assert self.sendGcode(line), f"Failed to send {line}" + + if job.getFilePause() == 1: + # self.setStatus("printing") + job.setTime(job.colorEta(), 1) + job.setTime(job.calculateColorChangeTotal(), 0) + job.setTime(datetime.min, 3) + job.setFilePause(0) + if self.status == "cancelled": + self.sendGcode(self.cancelCMD) + self.verdict = "cancelled" + self.logger.debug("Job cancelled") + return True + self.status = "printing" + + if "M600" in line: + job.setTime(datetime.now(), 3) + # job.setTime(job.calculateTotalTime(), 0) + # job.setTime(job.updateEta(), 1) + self.status = "colorchange" + # self.setColorChangeBuffer(3) + # self.setColorChangeBuffer(1) + job.setFilePause(1) + + if ("M569" in line) and (job.getExtruded() == 0): + job.setExtruded(1) + + # software pausing + if self.status == "paused": + self.pause() + job.setTime(datetime.now(), 3) + while self.status == "paused": + sleep(.5) + readline = self.serialConnection.readline().decode("utf-8").strip() + if readline: + self.logger.debug(readline) + if "T:" in readline and "B:" in readline: + self.logger.warning(f"Temperature line: {readline}") + self.handleTempLine(readline) + if self.status == "cancelled": + self.sendGcode(self.cancelCMD) + self.verdict = "cancelled" + self.logger.debug("Job cancelled") + return True + elif self.status == "printing": + self.resume() + job.setTime(job.colorEta(), 1) + job.setTime(job.calculateColorChangeTotal(), 0) + job.setTime(datetime.min, 3) + # software color change + if self.status == "colorchange" and job.getFilePause() == 0: + job.setTime(datetime.now(), 3) + # job.setTime(job.calculateTotalTime(), 0) + # job.setTime(job.updateEta(), 1) + print("SENDING COLORCHANGE") + self.sendGcode("M600") # color change command + job.setTime(job.colorEta(), 1) + job.setTime(job.calculateColorChangeTotal(), 0) + job.setTime(datetime.min, 3) + job.setFilePause(1) + #self.setColorChangeBuffer(0) + # self.setStatus("printing") + + # Increment the sent lines + sent_lines += 1 + job.setSentLines(sent_lines) + # Calculate the progress + progress = (sent_lines / total_lines) * 100 + + # Call the setProgress method + job.setProgress(progress) + + # if self.status == "complete" and job.extruded != 0: + if self.status == "complete": + self.verdict = "complete" + self.logger.debug("Job complete") + return True + + if self.status == "error": + self.verdict = "error" + self.logger.debug("Job error") + return False + self.verdict = "complete" + self.status = "complete" + self.logger.debug("Job complete") + return True + except Exception as e: + # self.setStatus("error") + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) + + def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): + assert self.serialConnection is not None, "Serial connection is None" + assert self.serialConnection.is_open, "Serial connection is not open" + if isinstance(gcode, str): + if gcode[-1] != "\n": gcode += "\n" + gcode = gcode.encode("utf-8") + assert isinstance(gcode, bytes), f"Expected bytes, got {type(gcode)}" + self.serialConnection.write(gcode) + callables = self.callablesHashtable.get(self.extractIndex(gcode), [checkOK]) + line = '' + for func in callables: + while True: + if self.status == "cancelled": return True + try: + line = self.serialConnection.readline() + decLine = line.decode("utf-8").strip() + if "processing" in decLine or "echo" in decLine: continue + if "T:" in decLine and "B:" in decLine: + self.logger.warning(f"Temperature line: {decLine}") + self.handleTempLine(decLine) + if func != checkBedTemp and func != checkExtruderTemp: + continue + self.logger.debug(f"{gcode.decode().strip()}: {decLine}") + if func(line): + break + except UnicodeDecodeError: + if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") + continue + except Exception as e: + self.logger.error(e) + return False + if not callables: + self.logger.info(f"{gcode.decode().strip()}: Always True") + else: + self.logger.info( + gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) + return True + def changeFilament(self, filamentType: str, filamentDiameter: float): + """ + Method to change filament + :param filamentType: type of plastic the filament is made of + :param filamentDiameter: diameter of the filament in mm + :type filamentType: str + :type filamentDiameter: float + :return: None + """ if not isinstance(filamentDiameter, float): filamentDiameter = float(filamentDiameter) try: - assert self.status is "idle", "Printer is not idle" + assert self.status == "idle", "Printer is not idle" self.filamentType = filamentType self.filamentDiameter = filamentDiameter except Exception as e: - with app.app_context(): - app.logger.error("Error changing filament:") - app.logger.error(e) + from app import handle_errors_and_logging + handle_errors_and_logging(e, self) def changeNozzle(self, nozzleDiameter: float): - if not isinstance(nozzleDiameter, float): - nozzleDiameter = float(nozzleDiameter) - self.nozzleDiameter = nozzleDiameter \ No newline at end of file + """ + Method to change nozzle size + :param nozzleDiameter: The diameter of the nozzle in mm + :type nozzleDiameter: float + """ + try: + if not isinstance(nozzleDiameter, float): + nozzleDiameter = float(nozzleDiameter) + assert self.status == "idle", "Printer is not idle" + self.nozzleDiameter = nozzleDiameter + except Exception as e: + from app import handle_errors_and_logging + handle_errors_and_logging(e, self) + + def handleTempLine(self, line: str): + """ + Method to handle temperature lines in the serial response + :param line: + :type line: str + """ + try: + temp_t = re.search(r'T:(\d+.\d+)', line) + temp_b = re.search(r'B:(\d+.\d+)', line) + if not temp_t: + temp_t = re.search(r'T:(\d+)', line) + if not temp_b: + temp_b = re.search(r'B:(\d+)', line) + if temp_t: + self.nozzleTemperature = float(temp_t.group(1)) + if temp_b: + self.bedTemperature = float(temp_b.group(1)) + from flask import current_app + if current_app: + current_app.socketio.emit('temp_update', {'printerid': self.dbID, 'extruder_temp': self.nozzleTemperature, + 'bed_temp': self.bedTemperature}) + except ValueError: + pass + except Exception as e: + from app import handle_errors_and_logging + handle_errors_and_logging(e, self) + def extractIndex(self, gcode: bytes) -> str: + """ + Method to extract the index of the gcode for use in the callablesHashtable + :param gcode: the line of gcode to extract the index from + :type gcode: bytes + :return: the hash index of the gcode + :rtype: str + """ + hashIndex = gcode.decode().split("\n")[0].split(" ")[0] + if hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") + elif hashIndex == "G28": + self.logger.info("Homing...") + return hashIndex + + def pause(self): + """ + Pause the device, if the pause command is implemented. + :return: True if the device was successfully paused, else False + :rtype: bool + """ + if not self.pauseCMD: + self.logger.error("Pause command not implemented.") + return True + try: + assert self.pauseCMD is not None + assert isinstance(self, Device) + assert self.serialConnection is not None + assert self.serialConnection.is_open + if hasattr(self, "keepAliveCMD") and self.keepAliveCMD: + self.sendGcode(self.keepAliveCMD) + self.sendGcode(self.pauseCMD) + self.logger.info("Job Paused") + return True + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error pausing job:") + self.logger.error(e) + return False + + def resume(self): + """Resume the device, if the resume command is implemented.""" + if self.resumeCMD is None: + self.logger.error("Resume command not implemented.") + return False + try: + assert isinstance(self, Device), "self is not an instance of Device" + assert self.serialConnection is not None, "Serial connection is None" + assert self.serialConnection.is_open, "Serial connection is not open" + if hasattr(self, "doNotKeepAliveCMD") and self.doNotKeepAliveCMD: self.sendGcode(self.doNotKeepAliveCMD) + self.sendGcode(self.resumeCMD) + self.logger.info("Job Resumed") + return True + except Exception as e: + if self.logger is None: + print(e) + else: + self.logger.error("Error resuming job:") + self.logger.error(e) + return False + + def connect(self): + super().connect() + try: + if self.serialConnection and self.serialConnection.is_open: + self.serialConnection.write(b"M155 S1\n") + return True + except Exception as e: + from app import handle_errors_and_logging + return handle_errors_and_logging(e, self) + + def disconnect(self: Device): + if self.serialConnection and self.serialConnection.is_open: + self.sendGcode(b"M155 S100\n") + self.sendGcode(b"M155 S0\n") + self.sendGcode(b"M104 S0\n") + self.sendGcode(b"M140 S0\n") + self.sendGcode(b"M84\n") + self.serialConnection.close() \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index ae675e55..2493b69d 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -1,6 +1,3 @@ -from serial.tools.list_ports_common import ListPortInfo -from serial.tools.list_ports_linux import SysFS - from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 from Mixins.hasResponseCodes import checkOK, checkTime, checkXYZ @@ -13,13 +10,16 @@ class PrusaMK3(PrusaPrinter): MAXFEEDRATE = 12000 homePosition = Vector3(0.2, -3.78, 0.15) cancelCMD = b"M603\n" - homeCMD = b"G28 W\n" + homeCMD = b"G28\n" + keepAliveCMD = None + doNotKeepAliveCMD = None callablesHashtable = { "M31": [checkTime, checkOK], # Print time "G28": [checkOK], "G29.02": [checkOK, checkOK], "G29.01": [checkOK, checkXYZ, checkXYZ, checkOK], # Auto bed leveling + "M601": [] # Pause } callablesHashtable = {**PrusaPrinter.callablesHashtable, **callablesHashtable} diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index c8a9062f..0a9688ba 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -1,39 +1,40 @@ -from abc import ABCMeta, abstractmethod -from typing_extensions import Buffer -from Classes.Vector3 import Vector3 +from abc import ABCMeta from Classes.Fabricators.Printers.Printer import Printer -from Mixins.gcode.usesPrusaGcode import usesPrusaGcode from Mixins.hasEndingSequence import hasEndingSequence +from Mixins.hasResponseCodes import checkXYZ, checkOK, checkTime -class PrusaPrinter(Printer, hasEndingSequence, usesPrusaGcode, metaclass=ABCMeta): +class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): VENDORID = 0x2C99 - - def sendGcode(self, gcode: Buffer, isVerbose: bool = False): - return usesPrusaGcode.sendGcode(self, gcode, isVerbose) - - def parseGcode(self, file, isVerbose=False): - return usesPrusaGcode.parseGcode(self, file, isVerbose) - - def connect(self): - return usesPrusaGcode.connect(self) - - def disconnect(self): - usesPrusaGcode.disconnect(self) - - def home(self, isVerbose: bool = False): - return usesPrusaGcode.home(self, isVerbose) - - def goTo(self, loc: Vector3, isVerbose: bool = False): - return usesPrusaGcode.goTo(self, loc, isVerbose) - - @abstractmethod - def endSequence(self): - pass - - @abstractmethod - def getPrintTime(self): - pass - - def getToolHeadLocation(self) -> Vector3: - return usesPrusaGcode.getToolHeadLocation(self) \ No newline at end of file + cancelCMD: bytes = b"M112\n" + keepAliveCMD: bytes = b"M113 S1\n" + doNotKeepAliveCMD: bytes = b"M113 S0\n" + statusCMD: bytes = b"M115\n" + getLocationCMD: bytes = b"M114\n" + pauseCMD: bytes = b"M601\n" + resumeCMD: bytes = b"M602\n" + + callablesHashtable = { + "G28": [checkXYZ, checkOK], # Home + "G29.01": [checkXYZ, checkOK], # Auto bed leveling + "G29.02": [checkOK], # Auto bed leveling + "M31": [checkOK, checkTime, checkOK], # Print time + "M73": [checkOK], # Set build percentage + } + + callablesHashtable = {**Printer.callablesHashtable, **callablesHashtable} + + + def extractIndex(self, gcode: bytes) -> str: + hashIndex = gcode.decode().split("\n")[0].split(" ")[0] + if hashIndex == "G29": + try: + g29addon = gcode.decode().split("\n")[0].split(" ")[1] + hashIndex += ".01" if g29addon == "P1" else ".02" + except IndexError as e: + hashIndex += ".01" + if hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") + elif hashIndex == "G28": + self.logger.info("Homing...") + return hashIndex diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index f54c74a4..709a4cec 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -6,13 +6,13 @@ from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory from datetime import timezone, timedelta from flask import jsonify, current_app +from traceback import format_exc from sqlalchemy.exc import SQLAlchemyError from datetime import datetime from tzlocal import get_localzone import gzip import csv from flask import send_file -# from app import printer_status_service # model for job history table class Job(db.Model): @@ -80,6 +80,29 @@ def __init__(self, file, name, fabricator_id, status, file_name_original, favori def __repr__(self): return f"Job(id={self.id}, name={self.name}, printer_id={self.fabricator_id}, status={self.status})" + def __to_JSON__(self): + return { + "id": self.id, + "name": self.name, + "status": self.status, + "date": self.date.strftime('%a, %d %b %Y %H:%M:%S'), + "printerid": self.fabricator_id, + "errorid": self.error_id, + "file_name_original": self.file_name_original, + "progress": self.progress, + "sent_lines": self.sent_lines, + "favorite": self.favorite, + "released": self.released, + "file_pause": self.filePause, + "comments": self.comments, + "extruded": self.extruded, + "td_id": self.td_id, + "time_started": self.time_started, + "printer_name": self.fabricator_name, + "max_layer_height": self.max_layer_height, + "current_layer_height": self.current_layer_height, + "filament": self.filament, + } def getPrinterId(self): return self.fabricator_id @@ -159,21 +182,7 @@ def get_job_history( pagination = query.paginate(page=page, per_page=pageSize, error_out=False) jobs = pagination.items - jobs_data = [ - { - "id": job.id, - "name": job.name, - "status": job.status, - "date": job.date.strftime("%a, %d %b %Y %H:%M:%S"), - "printerid": job.fabricator_id, - "errorid": job.error_id, - "file_name_original": job.file_name_original, - "comments": job.comments, - "td_id": job.td_id, - "printer": job.printer.name if job.printer else "None", - "error": job.error.issue if job.error else 'None', - "printer_name": job.printer_name - } + jobs_data = [job.__to_JSON__() for job in jobs ] if countOnly == 0: @@ -182,11 +191,14 @@ def get_job_history( return pagination.total except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve jobs. Database error"}), 500 + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod - def jobHistoryInsert(cls, name, fabricator_id, status, file, file_name_original, favorite, td_id): + def jobHistoryInsert(cls, name, fabricator_id, status, file, file_name_original, favorite=False, td_id=0): try: if isinstance(file, bytes): file_data = file @@ -219,11 +231,11 @@ def jobHistoryInsert(cls, name, fabricator_id, status, file, file_name_original, return {"success": True, "message": "Job added to collection.", "id": job.id} except SQLAlchemyError as e: - print(f"Database error: {e}") - return ( - jsonify({"error": "Failed to add job. Database error"}), - 500, - ) + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def update_job_status(cls, job_id, new_status): @@ -234,20 +246,20 @@ def update_job_status(cls, job_id, new_status): # Update the status attribute of the job job.status = new_status # Commit the changes to the database - db.session.commit() - - # current_app.socketio.emit('job_status_update', { - # 'job_id': job_id, 'status': new_status}) + if current_app: + db.session.commit() + current_app.socketio.emit('job_status_update', { + 'job_id': job_id, 'status': new_status}) return {"success": True, "message": f"Job {job_id} status updated successfully."} else: return {"success": False, "message": f"Job {job_id} not found."}, 404 except SQLAlchemyError as e: - print(f"Database error: {e}") - return ( - jsonify({"error": "Failed to update job status. Database error"}), - 500, - ) + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def delete_job(cls, job_id): @@ -260,12 +272,15 @@ def delete_job(cls, job_id): else: return {"error": f"Job with ID {job_id} not found in the database."} except Exception as e: - print(f"Unexpected error: {e}") # When an error occurs or an exception is raised during a database operation (such as adding, # updating, or deleting records), it may leave the database in an inconsistent state. To handle such # situations, a rollback is performed to revert any changes made within the current session to maintain the integrity of the database. - db.session.rollback() - return {"error": "Unexpected error occurred during job deletion."} + if current_app: + db.session.rollback() + current_app.handle_errors_and_logging(e) + else: + print(f"Unexpected error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def findJob(cls, job_id): @@ -273,8 +288,11 @@ def findJob(cls, job_id): job = cls.query.filter_by(id=job_id).first() return job except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve job. Database error"}), 500 + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 # @classmethod # def findPrinterObject(self, printer_id): @@ -304,8 +322,11 @@ def nullifyPrinterId(cls, printer_id): db.session.commit() return {"success": True, "message": "Printer ID nullified successfully."} except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to nullify printer ID. Database error"}), 500 + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def clearSpace(cls): @@ -324,28 +345,23 @@ def clearSpace(cls): db.session.commit() # Commit the changes return {"success": True, "message": "Space cleared successfully."} except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to clear space. Database error"}), 500 + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def getFavoriteJobs(cls): try: jobs = cls.query.filter_by(favorite=True).all() - - jobs_data = [{ - "id": job.id, - "name": job.name, - "status": job.status, - "date": f"{job.date.strftime('%a, %d %b %Y %H:%M:%S')} {get_localzone().tzname(job.date)}", - "printer": job.printer.name if job.printer else 'None', - "file_name_original": job.file_name_original, - "favorite": job.favorite - } for job in jobs] - - return jobs_data + return [job.__to_JSON__() for job in jobs] except SQLAlchemyError as e: - print(f"Database error: {e}") - return jsonify({"error": "Failed to retrieve favorite jobs. Database error"}), 500 + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def setIssue(cls, job_id, issue_id): @@ -362,9 +378,12 @@ def setIssue(cls, job_id, issue_id): db.session.commit() return {"success": True, "message": "Issue assigned successfully."} except Exception as e: - db.session.rollback() - print(f"Error setting issue: {e}") - return None + if current_app: + db.session.rollback() + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def unsetIssue(cls, job_id): @@ -381,9 +400,12 @@ def unsetIssue(cls, job_id): db.session.commit() return {"success": True, "message": "Issue removed successfully."} except Exception as e: - db.session.rollback() - print(f"Error unsetting issue: {e}") - return None + if current_app: + db.session.rollback() + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def setComment(cls, job_id, comments): @@ -400,9 +422,12 @@ def setComment(cls, job_id, comments): db.session.commit() return {"success": True, "message": "Comments added successfully."} except Exception as e: - db.session.rollback() - print(f"Error setting comments: {e}") - return None + if current_app: + db.session.rollback() + current_app.handle_errors_and_logging(e) + else: + print(f"Database error: {e}") + return jsonify({"error": format_exc()}), 500 @classmethod def downloadCSV(cls, alljobs, jobids=None): @@ -439,8 +464,11 @@ def downloadCSV(cls, alljobs, jobids=None): except Exception as e: - print(f"Error downloading CSV: {e}") - return {"status": "error", "message": f"Error downloading CSV: {e}"} + if current_app: + current_app.handle_errors_and_logging(e) + else: + print(f"Error downloading CSV: {e}") + return jsonify({"error": format_exc()}), 500 def saveToFolder(self): file_data = self.getFile() @@ -486,16 +514,18 @@ def getFilePause(self): def setFilePause(self, pause): self.filePause = pause - # current_app.socketio.emit('file_pause_update', { - # 'job_id': self.id, 'file_pause': self.filePause}) + if current_app: + current_app.socketio.emit('file_pause_update', { + 'job_id': self.id, 'file_pause': self.filePause}) def getExtruded(self): return self.extruded def setExtruded(self, extruded): self.extruded = extruded - # current_app.socketio.emit('extruded_update', { - # 'job_id': self.id, 'extruded': self.extruded}) + if current_app: + current_app.socketio.emit('extruded_update', { + 'job_id': self.id, 'extruded': self.extruded}) # setters @@ -508,9 +538,10 @@ def setStatus(self, status): def setProgress(self, progress): if self.status == 'printing': self.progress = progress - # # Emit a 'progress_update' event with the new progress - # current_app.socketio.emit( - # 'progress_update', {'job_id': self.id, 'progress': self.progress}) + # Emit a 'progress_update' event with the new progress + if current_app: + current_app.socketio.emit( + 'progress_update', {'job_id': self.id, 'progress': self.progress}) # added a getProgress method to get the progress of a job def getProgress(self): @@ -533,7 +564,9 @@ def getTimeFromFile(self, comment_lines): time_seconds = int(time_line.split(":")[1]) else: # search for the line that contains "printing time", then the time estimate is in the format of "; estimated printing time (normal mode) = minutes seconds" - time_line = next(line for line in comment_lines if "time" in line) + time_line = next((line for line in comment_lines if "time" in line), None) + if not time_line: + return 0 time_values = re.findall(r'\d+', time_line) # Initialize all time units to 0 @@ -609,12 +642,14 @@ def getTdId(self): def setMaxLayerHeight(self, max_layer_height): self.max_layer_height = max_layer_height - # current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) + if current_app: + current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) def setCurrentLayerHeight(self, current_layer_height): print("Current Layer Height: ", current_layer_height) self.current_layer_height = current_layer_height - # current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) + if current_app: + current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) def setFilament(self, filament): self.filament = filament @@ -630,17 +665,20 @@ def setFile(self, file): def setReleased(self, released): self.released = released - # current_app.socketio.emit('release_job', {'job_id': self.id, 'released': released}) + if current_app: + current_app.socketio.emit('release_job', {'job_id': self.id, 'released': released}) def setTimeStarted(self, time_started): self.time_started = time_started - # current_app.socketio.emit('set_time_started', {'job_id': self.id, 'started': time_started}) + if current_app: + current_app.socketio.emit('set_time_started', {'job_id': self.id, 'started': time_started}) def setTime(self, timeData, index): # timeData = datetime(y, m, d, h, min, s) # print("TimeData: ", timeData, " Index: ", index) self.job_time[index] = timeData - # if index == 0: - # current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData, 'index': index}) - # else: - # current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData.isoformat(), 'index': index}) \ No newline at end of file + if current_app: + if index == 0: + current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData, 'index': index}) + else: + current_app.socketio.emit('set_time', {'job_id': self.id, 'new_time': timeData.isoformat(), 'index': index}) \ No newline at end of file diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index b538395e..fa133f0d 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -14,7 +14,7 @@ class Logger(logging.Logger): ERROR = logging.ERROR CRITICAL = logging.CRITICAL - def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): + def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): title = [] if port: title.append(port) @@ -36,19 +36,21 @@ def __init__(self, deviceName, port=None, consoleLogger=None, fileLogger=None, l consoleLogger.setFormatter(CustomFormatter(formatString)) self.consoleLogger = consoleLogger self.addHandler(consoleLogger) + else: self.consoleLogger = None if fileLogger is None: - log_folder = "./server/logs" + from app import root_path + log_folder = os.path.abspath(os.path.join(root_path, "server","logs")) os.makedirs(log_folder, exist_ok=True) subfolder = os.path.join(log_folder, deviceName) os.makedirs(subfolder, exist_ok=True) from datetime import datetime - fileLogger = logging.FileHandler(os.path.join(subfolder, f"{datetime.now().strftime('%m-%d-%Y__%H-%M-%S')}.log")) + fileLogger = CustomFileHandler(os.path.join(subfolder, f"{datetime.now().strftime('%m-%d-%Y__%H-%M-%S')}.log")) else: if not os.path.exists(fileLogger): - fileLogger = logging.FileHandler(fileLogger, mode='w') + fileLogger = CustomFileHandler(fileLogger, mode='w') else: - fileLogger = logging.FileHandler(fileLogger) - fileLogger.setFormatter(logging.Formatter(formatString)) + fileLogger = CustomFileHandler(fileLogger) + fileLogger.setFormatter(CustomFormatter(formatString)) fileLogger.setLevel(loggingLevel) self.fileLogger = fileLogger self.addHandler(fileLogger) @@ -156,7 +158,7 @@ def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRep def setLevel(self, level): super().setLevel(level) - self.consoleLogger.setLevel(level) + if self.consoleLogger is not None: self.consoleLogger.setLevel(level) self.fileLogger.setLevel(level) class CustomFormatter(logging.Formatter): @@ -175,3 +177,12 @@ def format(self, record): color = self.COLOR_CODES.get(record.levelname, self.RESET_CODE) message = super().format(record) return f"{color}{message}{self.RESET_CODE}" + +class CustomFileHandler(logging.FileHandler): + def emit(self, record): + try: + msg = self.format(record) + self.stream.write(msg + "\n") + self.flush() + except RecursionError: + pass diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 0254f2b5..137216ce 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -6,7 +6,6 @@ from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode -from Classes.Fabricators.FabricatorList import FabricatorList class Ports: @staticmethod @@ -17,7 +16,7 @@ def getPorts() -> list[ListPortInfo | SysFS]: @staticmethod def getPortByName(name: str) -> ListPortInfo | SysFS | None: """Get a specific port by its device name.""" - assert isinstance(name, str) + assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" ports = Ports.getPorts() for port in ports: if port.device == name: @@ -27,7 +26,7 @@ def getPortByName(name: str) -> ListPortInfo | SysFS | None: @staticmethod def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: """Get a specific port by its hardware ID.""" - assert isinstance(hwid, str) + assert isinstance(hwid, str), f"HWID must be a string: {hwid} : {type(hwid)}" ports = Ports.getPorts() for port in ports: if hwid in port.hwid: @@ -101,7 +100,7 @@ def registerFabricator(): hwid = data['fabricator']['hwid'] name = data['fabricator']['name'] - new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) + new_fabricator = Fabricator(Ports.getPortByName(device), name) new_fabricator.description = description new_fabricator.hwid = hwid @@ -190,8 +189,8 @@ def moveFabricatorList(): try: data = request.get_json() fabricator_ids = data['fabricator_ids'] - - result = FabricatorList.moveFabricatorList(fabricator_ids) + from app import fabricator_list + result = fabricator_list.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: print(f"Error moving fabricator list: {e}") diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index bf8c34cc..f9a8571e 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -16,9 +16,10 @@ def addToBack(self, job: Job, printerid): if self.count(job) > 0: return False self.append(job) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - # ) + if current_app: + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + ) return True def addToFront(self, job): @@ -29,9 +30,10 @@ def addToFront(self, job): self.insert(1, job) else: self.appendleft(job) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} - # ) + if current_app: + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} + ) return True def bump(self, up, jobid): @@ -52,9 +54,10 @@ def bump(self, up, jobid): self.insert(index - 1, job_to_move) elif not up and index < len(self): self.insert(index + 1, job_to_move) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": job_to_move.fabricator_id} - # ) + if current_app: + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": job_to_move.fabricator_id} + ) def reorder(self, arr): new_queue = deque() @@ -66,9 +69,10 @@ def reorder(self, arr): self.clear() self.extend(new_queue) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} - # ) + if current_app: + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} + ) def deleteJob(self, jobid, printerid): deletedjob = None @@ -84,31 +88,31 @@ def deleteJob(self, jobid, printerid): return "Job not found in queue." def convertQueueToJson(self): - queue = [] - for job in self: - job_info = { - "id": job.id, - "name": job.name, - "status": job.status, - "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), - "printerid": job.fabricator_id, - "errorid": job.error_id, - "file_name_original": job.file_name_original, - "progress": job.progress, - "sent_lines": job.sent_lines, - "favorite": job.favorite, - "released": job.released, - "file_pause": job.filePause, - "comments": job.comments, - "extruded": job.extruded, - "td_id": job.td_id, - "time_started": job.time_started, - "printer_name": job.printer_name, - "max_layer_height": job.max_layer_height, - "current_layer_height": job.current_layer_height, - "filament": job.filament, - } - queue.append(job_info) + queue = [job.__to_JSON__() for job in self] + # for job in self: + # job_info = { + # "id": job.id, + # "name": job.name, + # "status": job.status, + # "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), + # "printerid": job.fabricator_id, + # "errorid": job.error_id, + # "file_name_original": job.file_name_original, + # "progress": job.progress, + # "sent_lines": job.sent_lines, + # "favorite": job.favorite, + # "released": job.released, + # "file_pause": job.filePause, + # "comments": job.comments, + # "extruded": job.extruded, + # "td_id": job.td_id, + # "time_started": job.time_started, + # "printer_name": job.fabricator_name, + # "max_layer_height": job.max_layer_height, + # "current_layer_height": job.current_layer_height, + # "filament": job.filament, + # } + # queue.append(job_info) return queue def bumpExtreme(self, front, jobid, printerid): @@ -132,9 +136,10 @@ def bumpExtreme(self, front, jobid, printerid): self.insert(0, job_to_move) else: self.addToBack(job_to_move, printerid) - # current_app.socketio.emit( - # "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} - # ) + if current_app: + current_app.socketio.emit( + "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + ) def getJob(self, job_to_find): for job in self: @@ -166,4 +171,7 @@ def getSize(self): def removeJob(self): self.pop() - # current_app.socketio.emit('job_removed', {'queue': list(self)}, broadcast=True) \ No newline at end of file + if current_app: + current_app.socketio.emit( + "job_removed", {"queue": list(self)}, broadcast=True + ) \ No newline at end of file diff --git a/server/Classes/Vector3.py b/server/Classes/Vector3.py index c28a376b..f64b4eb8 100644 --- a/server/Classes/Vector3.py +++ b/server/Classes/Vector3.py @@ -10,6 +10,9 @@ def __init__(self, x=0.0, y=0.0, z=0.0): def __repr__(self): return f"X:{self.x:.2f} Y:{self.y:.2f} Z:{self.z:.2f}" + def __to_JSON__(self): + return {"x": self.x, "y": self.y, "z": self.z} + # Adding two vectors def __add__(self, other): return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) diff --git a/server/Mixins/gcode/usesMarlinGcode.py b/server/Mixins/gcode/usesMarlinGcode.py index e3fe9fb6..e69de29b 100644 --- a/server/Mixins/gcode/usesMarlinGcode.py +++ b/server/Mixins/gcode/usesMarlinGcode.py @@ -1,209 +0,0 @@ -from abc import ABCMeta -from time import sleep -from typing_extensions import Buffer - -from Classes.Fabricators.Device import Device -from Classes.Vector3 import Vector3 -from Mixins.canPause import canPause -from Mixins.gcode.usesVanillaGcode import usesVanillaGcode -from Mixins.hasEndingSequence import hasEndingSequence -from Mixins.hasResponseCodes import checkOK, checkXYZ, checkBedTemp, checkExtruderTemp, hasResponsecodes, checkTime - -import re -class LocationResponse: - def __init__(self, response: str): - loc = [item.strip() for item in re.split(r'X:| Y:| Z:| E:| Count X:|\n', response) if item] - self.x = float(loc[0]) - self.y = float(loc[1]) - self.z = float(loc[2]) - self.e = float(loc[3]) - self.count_x = float(loc[4]) if '.' in loc[4] else int(loc[4]) - self.count_y = float(loc[5]) if '.' in loc[5] else int(loc[5]) - self.count_z = float(loc[6]) if '.' in loc[6] else int(loc[6]) - - -class usesMarlinGcode(usesVanillaGcode, canPause, hasResponsecodes, metaclass=ABCMeta): - cancelCMD: Buffer = b"M112\n" - keepAliveCMD: Buffer = b"M113 S1\n" - doNotKeepAliveCMD: Buffer = b"M113 S0\n" - statusCMD: Buffer = b"M115\n" - getLocationCMD: Buffer = b"M114\n" - pauseCMD: Buffer = b"M601\n" - resumeCMD: Buffer = b"M602\n" - getMachineNameCMD: Buffer = b"M997\n" - - callablesHashtable = { - "M31": [checkTime], # Print time - "M104": [], # Set hotend temp - "M109": [checkExtruderTemp], # Wait for hotend to reach target temp - "M114": [checkXYZ], # Get current position - "M140": [], # Set bed temp - "M190": [checkBedTemp], # Wait for bed to reach target temp - } - - callablesHashtable = {**usesVanillaGcode.callablesHashtable, **callablesHashtable} - - def goTo(self: Device, loc: Vector3, isVerbose: bool = False): - assert isinstance(loc, Vector3) - assert isinstance(isVerbose, bool) - assert isinstance(self, Device) - self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{str(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) - self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) - return loc == self.getToolHeadLocation() - - def parseGcode(self: Device, file: str, isVerbose: bool = False): - assert isinstance(file, str) - assert isinstance(isVerbose, bool) - assert isinstance(self, Device) - try: - with open(file, "r") as f: - self.logger.info(f"Printing {file}") - for line in f: - if line.startswith(";") or line == "\n": - continue - if isVerbose: self.logger.debug(line.strip("\n")) - if self.status == "paused": - self.pause() - while self.status == "paused": - sleep(1) - if self.status == "cancelled": - if isinstance(self, hasEndingSequence): - self.endSequence() - self.verdict = "cancelled" - self.logger.info("Job cancelled") - return True - elif self.status == "printing": - self.resume() - if self.status == "cancelled": - if isinstance(self, hasEndingSequence): - self.endSequence() - self.verdict = "cancelled" - self.logger.info("Job cancelled") - return True - if ";" in line: - line = line.split(";")[0].strip() + "\n" - self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) - self.verdict = "complete" - self.logger.info("Job complete") - return True - except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error cancelling job:") - self.logger.error(e) - self.verdict = "error" - return True - - - def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): - assert self.serialConnection.is_open - assert isinstance(gcode, bytes) - self.serialConnection.write(gcode) - hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") - callables = usesMarlinGcode.callablesHashtable.get(gcode.decode("utf-8").split("\n")[0].split(" ")[0], [checkOK]) - for func in callables: - while True: - if self.status == "cancelled": return True - try: - line = self.serialConnection.readline() - if "processing" in line.decode("utf-8"): - continue - if isVerbose: self.logger.debug(line) - if func(line): - self.logger.info(gcode.decode().strip() + ": " + line.decode().strip()) - return True - except UnicodeDecodeError as e: - continue - except Exception as e: - self.logger.error(e) - return False - - - def getToolHeadLocation(self: Device, isVerbose: bool = False) -> Vector3: - self.serialConnection.write(usesMarlinGcode.getLocationCMD) - response = "" - while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): - response = self.serialConnection.readline().decode("utf-8") - if isVerbose: self.logger.info(response) - loc = LocationResponse(response) - return Vector3(loc.x, loc.y, loc.z) - - - def home(self, isVerbose: bool = False): - try: - assert isinstance(isVerbose, bool) - assert isinstance(self, Device) - self.sendGcode(usesMarlinGcode.homeCMD, isVerbose=isVerbose) - assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" - return True - except Exception as e: - from app import app - with app.app_context(): - return app.handle_error_and_logging(e, self) - - - def pause(self): - try: - assert isinstance(self, canPause) - assert isinstance(self, Device) - assert self.serialConnection is not None - assert self.serialConnection.is_open - assert self.status == "printing" - self.sendGcode(usesMarlinGcode.keepAliveCMD) - self.sendGcode(usesMarlinGcode.pauseCMD) - self.logger.info("Job Paused") - return True - except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error pausing job:") - self.logger.error(e) - return False - - def resume(self): - try: - assert isinstance(self, canPause) - assert isinstance(self, Device) - assert self.serialConnection is not None - assert self.serialConnection.is_open - assert self.status == "paused" - self.sendGcode(usesMarlinGcode.doNotKeepAliveCMD) - self.sendGcode(usesMarlinGcode.resumeCMD) - self.logger.info("Job Resumed") - return True - except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error resuming job:") - self.logger.error(e) - return False - - - def connect(self: Device): - Device.connect(self) - try: - if self.serialConnection and self.serialConnection.is_open: - self.serialConnection.write(b"M155 S1\n") - return True - except Exception as e: - from app import app - with app.app_context(): - return app.handle_error_and_logging(e, self) - - - - def disconnect(self: Device): - if self.serialConnection and self.serialConnection.is_open: - self.sendGcode(b"M155 S100\n") - self.sendGcode(b"M155 S0\n") - self.sendGcode(b"M104 S0\n") - self.sendGcode(b"M140 S0\n") - self.sendGcode(b"M84\n") - self.serialConnection.close() diff --git a/server/Mixins/gcode/usesPrusaGcode.py b/server/Mixins/gcode/usesPrusaGcode.py index e8c6ea9c..e69de29b 100644 --- a/server/Mixins/gcode/usesPrusaGcode.py +++ b/server/Mixins/gcode/usesPrusaGcode.py @@ -1,54 +0,0 @@ -from abc import ABCMeta -from typing_extensions import Buffer -from Classes.Fabricators.Device import Device -from Mixins.gcode.usesMarlinGcode import usesMarlinGcode -from Mixins.hasResponseCodes import checkOK, checkXYZ, checkTime - -class usesPrusaGcode(usesMarlinGcode, metaclass=ABCMeta): - callablesHashtable = { - "G28": [checkXYZ, checkOK], # Home - "G29.01": [checkXYZ, checkOK], # Auto bed leveling - "G29.02": [checkOK], # Auto bed leveling - "M31": [checkOK, checkTime, checkOK], # Print time - "M73": [checkOK], # Set build percentage - } - - callablesHashtable = {**usesMarlinGcode.callablesHashtable, **callablesHashtable} - - def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): - assert self.serialConnection.is_open - assert isinstance(gcode, bytes) - self.serialConnection.write(gcode) - hashIndex = gcode.decode("utf-8").split("\n")[0].split(" ")[0] - if hashIndex == "G29": - try: - g29addon = gcode.decode("utf-8").split("\n")[0].split(" ")[1] - hashIndex += ".01" if g29addon == "P1" else ".02" - except IndexError as e: - hashIndex += ".01" - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") - assert isinstance(self, usesPrusaGcode) - callables = self.callablesHashtable.get(hashIndex, [checkOK]) - assert isinstance(self, Device) - line='' - for func in callables: - while True: - if self.status == "cancelled": return True - try: - line = self.serialConnection.readline() - if "processing" in line.decode("utf-8"): continue - if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.decode().strip()}") - if func(line): - break - except UnicodeDecodeError as e: - if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") - continue - except Exception as e: - self.logger.error(e) - return False - if not callables: self.logger.info(f"{gcode.decode().strip()}: Always True") - else: self.logger.info(gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) - return True \ No newline at end of file diff --git a/server/Mixins/gcode/usesVanillaGcode.py b/server/Mixins/gcode/usesVanillaGcode.py index 6f22ac2f..8ccc3be6 100644 --- a/server/Mixins/gcode/usesVanillaGcode.py +++ b/server/Mixins/gcode/usesVanillaGcode.py @@ -77,5 +77,5 @@ def sendGcode(self: Device, gcode: Buffer, isVerbose: bool = False): assert self.serialConnection.is_open assert isinstance(gcode, bytes) self.serialConnection.write(gcode) - self.logger.debug(gcode.decode("utf-8")) + if isVerbose: self.logger.debug(gcode.decode("utf-8")) return True diff --git a/server/app.py b/server/app.py index 96fdaf8b..365e9589 100644 --- a/server/app.py +++ b/server/app.py @@ -2,23 +2,25 @@ from flask_cors import CORS import os from models.db import db -from models.PrinterStatusService import PrinterStatusService from flask_migrate import Migrate from dotenv import load_dotenv -from controllers.ports import getRegisteredFabricators import shutil from flask_socketio import SocketIO from models.config import Config from Classes.FabricatorList import FabricatorList +from routes import defineRoutes + +# Global variables +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) # moved this up here so we can pass the app to the PrinterStatusService # Basic app setup -app = Flask(__name__, static_folder='../client/dist') +app = Flask(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) app.config.from_object(__name__) # update application instantly -logs = os.path.join(os.path.dirname(__file__),'logs') +logs = os.path.join(root_path,"server", "logs") from Classes.Logger import Logger -app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.join(logs, "app.log")) +app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.abspath(os.path.join(logs, "app.log"))) # start database connection app.config["environment"] = Config.get('environment') app.config["ip"] = Config.get('ip') @@ -26,8 +28,8 @@ app.config["base_url"] = Config.get('base_url') load_dotenv() -basedir = os.path.abspath(os.path.dirname(__file__)) -database_file = os.path.join(basedir, Config.get('database_uri')) +basedir = os.path.abspath(os.path.join(root_path, "server")) +database_file = os.path.abspath(os.path.join(basedir, Config.get('database_uri'))) if isinstance(database_file, bytes): database_file = database_file.decode('utf-8') databaseuri = 'sqlite:///' + database_file @@ -37,9 +39,7 @@ migrate = Migrate(app, db) # moved this before importing the blueprints so that it can be accessed by the PrinterStatusService -printer_status_service = PrinterStatusService(app) -fabricator_list = FabricatorList(app) -app.fabricator_list = fabricator_list +#printer_status_service = PrinterStatusService(app) # Initialize SocketIO, which will be used to send printer status updates to the frontend # and this specific socket it will be used throughout the backend @@ -61,7 +61,7 @@ def handle_errors_and_logging(e: Exception | str, fabricator = None): device.logger.error(e, stacklevel=3) elif app.logger is None: if isinstance(e, str): - print(e) + print(e.strip()) else: import traceback print(traceback.format_exception(None, e, e.__traceback__)) @@ -73,6 +73,9 @@ def handle_errors_and_logging(e: Exception | str, fabricator = None): CORS(app) +# Register all routes +defineRoutes(app) + @app.cli.command("test") def run_tests(): """Run all tests.""" @@ -98,18 +101,6 @@ def serve_static(path='index.html'): def serve_assets(filename): return send_from_directory(os.path.join(app.static_folder, 'assets'), filename) -# IMPORTING BLUEPRINTS -from controllers.ports import ports_bp -from controllers.jobs import jobs_bp -from controllers.statusService import status_bp -from controllers.issues import issue_bp - -# # Register the display_bp Blueprint -app.register_blueprint(ports_bp) -app.register_blueprint(jobs_bp) -app.register_blueprint(status_bp) -app.register_blueprint(issue_bp) - @app.socketio.on('ping') def handle_ping(): app.socketio.emit('pong') @@ -120,7 +111,8 @@ def handle_ping(): # Define directory paths for uploads and tempcsv uploads_folder = os.path.abspath('../uploads') tempcsv = os.path.abspath('../tempcsv') - app.FabricatorList = fabricator_list + fabricator_list = FabricatorList(app) + app.fabricator_list = fabricator_list # Check if directories exist and handle them accordingly for folder in [uploads_folder, tempcsv]: @@ -135,11 +127,11 @@ def handle_ping(): except Exception as e: # Log any exceptions for troubleshooting - import traceback - app.logger.error(f"Unexpected error: {e}") - app.logger.error(traceback.format_exception(e)) + app.handle_errors_and_logging(e) - +def run_socketio(app): + # host=app.config["ip"], port=app.config["port"] + socketio.run(app, Debug=True, allow_unsafe_werkzeug=True) if __name__ == "__main__": # If hits last line in GCode file: @@ -147,4 +139,4 @@ def handle_ping(): # Before sending to printer, query for status. If error, throw error. # since we are using socketio, we need to use socketio.run instead of app.run # which passes the app anyways - socketio.run(app, debug=True) # Replace app.run with socketio.run \ No newline at end of file + run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 1ddee7e0..15e6d64a 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -1,19 +1,14 @@ -import base64 -from io import BytesIO -import io import shutil -import tempfile -from flask import Blueprint, Response, jsonify, request, make_response, send_file +from flask import Blueprint, jsonify, request from Classes.Jobs import Job from models.printers import Printer -from app import printer_status_service -import json -from werkzeug.utils import secure_filename +import json import os import gzip -from flask import current_app import serial import serial.tools.list_ports +from app import handle_errors_and_logging +from traceback import format_exc # get data for jobs jobs_bp = Blueprint("jobs", __name__) @@ -50,8 +45,8 @@ def getJobs(): res = Job.get_job_history(page, pageSize, printerIds, oldestFirst, searchJob, searchCriteria, searchTicketId, favoriteOnly, issueIds, startdate, enddate, fromError, countOnly) return jsonify(res) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 # add job to queue @jobs_bp.route('/addjobtoqueue', methods=["POST"]) @@ -96,15 +91,15 @@ def add_job_to_queue(): priority = request.form['priority'] # if priotiry is '1' then add to front of queue, else add to back if priority == 'true': - findPrinterObject(printer_id).getQueue().addToFront(job) + findPrinterObject(printer_id).getQueue().addToFront(job, printer_id) else: - findPrinterObject(printer_id).getQueue().addToBack(job) + findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/autoqueue', methods=["POST"]) def auto_queue(): @@ -150,8 +145,8 @@ def auto_queue(): return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/rerunjob', methods=["POST"]) def rerun_job(): @@ -163,8 +158,8 @@ def rerun_job(): rerunjob(printerpk, jobpk, "back") return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 # route to insert job into database @jobs_bp.route('/jobdbinsert', methods=["POST"]) @@ -178,16 +173,16 @@ def job_db_insert(): name = jobdata.get('name') printer_id = jobdata.get('printer_id') status = jobdata.get('status') - # file_name=jobdata.get("file_name") + file_name=jobdata.get("file_name") file_path=jobdata.get("file_path") # Insert the job data into the database - res = Job.jobHistoryInsert(name, printer_id, status, file_path) + res = Job.jobHistoryInsert(name, printer_id, status, file_path, file_name) return "success" except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 # cancel queued job @jobs_bp.route('/canceljob', methods=["POST"]) @@ -217,8 +212,8 @@ def remove_job(): return jsonify({"success": True, "message": "Job removed from printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 # cancel queued job @@ -251,8 +246,8 @@ def remove_job_from_queue(): return jsonify({"success": True, "message": "Job removed from printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/releasejob', methods=["POST"]) @@ -291,8 +286,8 @@ def releasejob(): return jsonify({"success": True, "message": "Job released successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/bumpjob', methods=["POST"]) def bumpjob(): @@ -317,8 +312,8 @@ def bumpjob(): return jsonify({"success": True, "message": "Job bumped up in printer queue."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/movejob', methods=["POST"]) def moveJob(): @@ -331,8 +326,8 @@ def moveJob(): printerobject.queue.reorder(arr) return jsonify({"success": True, "message": "Queue updated successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/updatejobstatus', methods=["POST"]) def updateJobStatus(): @@ -352,8 +347,8 @@ def updateJobStatus(): return jsonify(res), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/assigntoerror', methods=["POST"]) def assignToError(): @@ -373,8 +368,8 @@ def assignToError(): return jsonify(res), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/deletejob', methods=["POST"]) def delete_job(): @@ -401,8 +396,8 @@ def delete_job(): return jsonify({"success": True, "message": f"Job with ID {job_id} deleted successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/setstatus", methods=["POST"]) def setStatus(): @@ -418,8 +413,8 @@ def setStatus(): return jsonify({"success": True, "message": "Status updated successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/getfile', methods=["GET"]) def getFile(): @@ -431,8 +426,8 @@ def getFile(): return jsonify({"file": decompressed_file, "file_name": job.getFileNameOriginal()}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/nullifyjobs', methods=["POST"]) def nullifyJobs(): @@ -442,8 +437,8 @@ def nullifyJobs(): res = Job.nullifyPrinterId(printerid) return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/clearspace', methods=["GET"]) def clearSpace(): @@ -451,8 +446,8 @@ def clearSpace(): res = Job.clearSpace() return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/getfavoritejobs', methods=["GET"]) def getFavoriteJobs(): @@ -460,8 +455,8 @@ def getFavoriteJobs(): res = Job.getFavoriteJobs() return jsonify(res) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/favoritejob', methods=["POST"]) def favoriteJob(): @@ -473,8 +468,8 @@ def favoriteJob(): res = job.setFileFavorite(favorite) return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/assignissue', methods=["POST"]) def assignIssue(): @@ -487,8 +482,8 @@ def assignIssue(): res = job.setIssue(jobid, issueid) return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/removeissue', methods=["POST"]) def removeIssue(): @@ -500,8 +495,8 @@ def removeIssue(): res = job.unsetIssue(jobid) return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/startprint', methods=["POST"]) def startPrint(): @@ -514,11 +509,13 @@ def startPrint(): inmemjob = queue.getJobById(jobid) print(inmemjob) inmemjob.setReleased(1) + printerobject.setStatus("printing") + return jsonify({"success": True, "message": "Job started successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected ersetupPortRepairSocketror occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/savecomment', methods=["POST"]) def saveComment(): @@ -532,8 +529,8 @@ def saveComment(): return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/downloadcsv', methods=["GET", "POST"]) def downloadCSV(): @@ -553,9 +550,8 @@ def downloadCSV(): return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/removeCSV', methods=["GET", "POST"]) def removeCSV(): @@ -574,8 +570,8 @@ def removeCSV(): return jsonify({"success": True, "message": "CSV file removed successfully."}), 200 except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/repairports", methods=["POST", "GET"]) def repair_ports(): @@ -592,8 +588,8 @@ def repair_ports(): printerthread.setDevice(port.device) return {"success": True, "message": "Printer port(s) successfully updated."} except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/refetchtimedata", methods=['POST', 'GET']) def refetch_time(): @@ -617,15 +613,16 @@ def refetch_time(): return jsonify(timejson) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 def findPrinterObject(printer_id): - threads = printer_status_service.getThreadArray() - return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer + from app import fabricator_list + threads = fabricator_list.getThreadArray() + return list(filter(lambda thread: thread.fabricator.dbID == printer_id, threads))[0].fabricator def getSmallestQueue(): - threads = printer_status_service.getThreadArray() + from app import fabricator_list + threads = fabricator_list.getThreadArray() smallest_queue_thread = min(threads, key=lambda thread: thread.printer.queue.getSize()) return smallest_queue_thread.printer.id @@ -637,7 +634,7 @@ def rerunjob(printerpk, jobpk, position): favorite = job.getFileFavorite() # get favorite status td_id = job.getTdId() # Insert new job into DB and return new PK - res = Job.jobHistoryInsert(name=job.getName(), printer_id=printerpk, status=status, file=job.getFile(), file_name_original=file_name_original, favorite=favorite, td_id=td_id) # insert into DB + res = Job.jobHistoryInsert(name=job.getName(), fabricator_id=printerpk, status=status, file=job.getFile(), file_name_original=file_name_original, favorite=favorite, td_id=td_id) # insert into DB id = res['id'] file_name_pk = file_name_original + f"_{id}" # append id to file name to make it unique diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 6f3ac59e..b4d95237 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,7 +1,9 @@ from sqlalchemy.exc import SQLAlchemyError -from flask import Blueprint, jsonify, request +from flask import Blueprint, jsonify, request, current_app from Classes.Fabricators.Fabricator import Fabricator from Classes.Ports import Ports +from app import handle_errors_and_logging +from traceback import format_exc # Blueprint for ports routes ports_bp = Blueprint("ports", __name__) @@ -12,16 +14,10 @@ def getRegisteredFabricators(): """Get a list of all registered fabricators.""" try: fabricators = Fabricator.queryAll() - return jsonify([{ - "name": fab.name, - "description": fab.description, - "hwid": fab.hwid, - "devicePort": fab.devicePort, - "status": fab.getStatus() - } for fab in fabricators]) + return jsonify([fab.__to_JSON__() for fab in fabricators]) except Exception as e: - print(f"Error getting registered fabricators: {e}") - return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/register", methods=["POST"]) def registerFabricator(): @@ -32,14 +28,14 @@ def registerFabricator(): name = data['fabricator']['name'] # Create a new fabricator instance using the Fabricator class - new_fabricator = Fabricator(Ports.getPortByName(device), name, addToDB=True) + new_fabricator = Fabricator(Ports.getPortByName(device), name) return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") return jsonify({"error": "Database error occurred"}), 500 except Exception as e: - print(f"Error registering fabricator: {e}") - return jsonify({"error": "Failed to register fabricator"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/deletefabricator", methods=["POST"]) def deleteFabricator(): @@ -56,8 +52,8 @@ def deleteFabricator(): else: return jsonify({"error": "Fabricator not found"}), 404 except Exception as e: - print(f"Error deleting fabricator: {e}") - return jsonify({"error": "Failed to delete fabricator"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/editname", methods=["POST"]) def editName(): @@ -74,8 +70,8 @@ def editName(): else: return jsonify({"error": "Fabricator not found"}), 404 except Exception as e: - print(f"Error editing fabricator name: {e}") - return jsonify({"error": "Failed to edit fabricator name"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/diagnose", methods=["POST"]) def diagnoseFabricator(): @@ -86,7 +82,7 @@ def diagnoseFabricator(): port = Ports.getPortByName(device_name) if port: - fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + fabricator = Fabricator(port) # Ensure the Fabricator has this method if fabricator: diagnosis_result = fabricator.diagnose() return jsonify({"success": True, "message": diagnosis_result}) @@ -95,8 +91,8 @@ def diagnoseFabricator(): else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - print(f"Error diagnosing fabricator: {e}") - return jsonify({"error": "Failed to diagnose fabricator"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/repair", methods=["POST"]) def repairFabricator(): @@ -116,37 +112,43 @@ def repairFabricator(): else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - print(f"Error repairing fabricator: {e}") - return jsonify({"error": "Failed to repair fabricator"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/movehead", methods=["POST"]) def moveHead(): """Move the head of a fabricator. Deprecated if no longer needed.""" try: data = request.get_json() - device_name = data['port'] - port = Ports.getPortByName(device_name) - + port = data['port'] if port: - fabricator = Fabricator(port) - result = fabricator.device.home() # Use home() method from Device - return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + if current_app: + with current_app.app_context(): + fabricator = current_app.fabricator_list.getFabricatorByPort(port) + if fabricator: + result = fabricator.device.home() # Use home() method from Device + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + else: + return jsonify({"error": "Fabricator not found", "line number": 136}), 404 + else: + fabricator = Fabricator(port) + result = fabricator.device.home() # Use home() method from Device + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - print(f"Error moving head: {e}") - return jsonify({"error": "Failed to move fabricator head"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @ports_bp.route("/movefabricatorlist", methods=["POST"]) def moveFabricatorList(): """Change the order of fabricators.""" try: - from app import printer_status_service data = request.get_json() fabricator_ids = data['fabricator_ids'] - - result = printer_status_service.moveFabricatorList(fabricator_ids) + from app import app + result = app.fabricator_list.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: - print(f"Error moving fabricator list: {e}") - return jsonify({"error": "Failed to move fabricator list"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index 4969757d..055dca10 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -1,6 +1,7 @@ from flask import Blueprint, jsonify, request import os -from app import fabricator_list +from app import app, handle_errors_and_logging +from traceback import format_exc status_bp = Blueprint("status", __name__) @status_bp.route('/ping', methods=["GET"]) @@ -9,65 +10,72 @@ def getStatus(Printer): @status_bp.route('/getopenthreads') def getOpenThreads(): - pass + pass +@status_bp.route('/getprinters', methods=["GET"]) +def getPrinters(): + try: + printers = app.fabricator_list.fabricators # call the method on the instance + return jsonify({"printers": printers}) + except Exception as e: + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 # this is the route that will be called by the UI to get the printers that have threads information @status_bp.route('/getprinterinfo', methods=["GET"]) def getPrinterInfo(): - try: - printer_info = fabricator_list.fabricators # call the method on the instance - return jsonify(printer_info) + try: + return jsonify([fab.__to_JSON__() for fab in app.fabricator_list.fabricators]) except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 @status_bp.route('/hardreset', methods=["POST"]) def hardreset(): - try: - data = request.get_json() # get json data + try: + data = request.get_json() # get json data id = data['printerid'] - res = fabricator_list.resetThread(id) - return res + res = app.fabricator_list.resetThread(id) + return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 + @status_bp.route('/queuerestore', methods=["POST"]) def queueRestore(): - try: - data = request.get_json() # get json data + try: + data = request.get_json() # get json data id = data['printerid'] status = data['status'] - res = fabricator_list.queueRestore(id, status) - return res + res = app.fabricator_list.queueRestore(id, status) + return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 + @status_bp.route("/removethread", methods=["POST"]) def removeThread(): try: data = request.get_json() # get json data printerid = data['printerid'] - res = fabricator_list.deleteThread(printerid) - return res + res = app.fabricator_list.deleteThread(printerid) + return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 + @status_bp.route("/editNameInThread", methods=["POST"]) -def editName(): - try: - data = request.get_json() +def editName(): + try: + data = request.get_json() printerid = data['printerid'] name = data['newname'] - fabricator_list.getFabricatorByHwid() - res = fabricator_list.editName(printerid, name) - return res + app.fabricator_list.getFabricatorByHwid() + res = app.fabricator_list.editName(printerid, name) + return res except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 - + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 + @status_bp.route("/serverVersion", methods=["GET"]) def getVersion(): res = jsonify(os.environ.get('SERVER_VERSION')) diff --git a/server/routes.py b/server/routes.py new file mode 100644 index 00000000..59e446b4 --- /dev/null +++ b/server/routes.py @@ -0,0 +1,15 @@ +from flask import Flask + + +def defineRoutes(app: Flask): + # IMPORTING BLUEPRINTS + from controllers.ports import ports_bp + from controllers.jobs import jobs_bp + from controllers.statusService import status_bp + from controllers.issues import issue_bp + + # # Register the display_bp Blueprint + app.register_blueprint(ports_bp) + app.register_blueprint(jobs_bp) + app.register_blueprint(status_bp) + app.register_blueprint(issue_bp) \ No newline at end of file From 1eeb830544c36ef01c57e1bf6674e3f205eca7a9 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Fri, 29 Nov 2024 15:54:21 -0500 Subject: [PATCH 142/194] feat: more tests. (WIP) --- Tests/conftest.py | 76 +++++++++++------------- Tests/parallel_test_runner.py | 34 ++++++----- Tests/test_app.py | 16 ++++- Tests/test_device.py | 2 +- Tests/test_fabricator.py | 26 +++++---- Tests/test_fabricator_list.py | 7 ++- server/test.py | 106 ++++++++++++++++++---------------- 7 files changed, 146 insertions(+), 121 deletions(-) diff --git a/Tests/conftest.py b/Tests/conftest.py index 1cc6473e..d6cf9751 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -36,8 +36,12 @@ def custom_write(self: TerminalWriter, msg: str, *, flush: bool = False, **marku try: if log: - self.logLine = " ".join([self.logLine, log]) + if self.logLine == "": + self.logLine = msg + else: + self.logLine = " ".join([self.logLine, msg]) if flush: + self.logLine = self.logLine.strip() if "red" in markup and markup["red"] == True: logger.error(self.logLine) elif "yellow" in markup and markup["yellow"] == True: @@ -61,17 +65,19 @@ def custom_write(self: TerminalWriter, msg: str, *, flush: bool = False, **marku else: config.pluginmanager.register(myTR, "terminalreporter") + config.terminalReporter = myTR -@pytest.fixture(scope="session") + +@pytest.fixture(scope="session", autouse=True) def fabricator(request, app): port = request.session.config.port if not port: return None from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS if isinstance(port, str): - fabricator = fabricator_list.getFabricatorByPort(port) + fabricator = app.fabricator_list.getFabricatorByPort(port) elif isinstance(port, ListPortInfo) or isinstance(port, SysFS): - fabricator = fabricator_list.getFabricatorByPort(port.device) + fabricator = app.fabricator_list.getFabricatorByPort(port.device) else: fabricator = None if fabricator is None: @@ -82,10 +88,20 @@ def fabricator(request, app): yield fabricator fabricator.device.disconnect() -@pytest.fixture(scope="session") +# @pytest.fixture(scope="session", autouse=True) +# def client(request, app): +# client = Client(logger=True) +# port = request.session.config.port +# portNum = int(port.split("COM")[-1]) +# clientPort = portNum + 5000 +# client.connect(f"http://localhost:{clientPort}") +# app.client = client +# yield client +# client.disconnect() + +@pytest.fixture(scope="session", autouse=True) def app(): from app import app - app = app with app.app_context(): yield app fabricator_list.teardown() @@ -131,14 +147,15 @@ def line_separator(interrupter: str, symbol: str = "-", length: int = 136, color def setup_logger(port): # set up fie location for output logs - log_folder = "logs" + from app import root_path + log_folder = os.path.join(root_path,"Tests", "logs") os.makedirs(log_folder, exist_ok=True) from datetime import datetime timestamp = datetime.now().strftime("%m-%d-%Y__%H-%M-%S") subfolder = os.path.join(log_folder, timestamp) os.makedirs(subfolder, exist_ok=True) log_file_path = os.path.join(subfolder, f"test_{port}.log") - return Logger(port, "Test Printer", fileLogger=log_file_path, showFile=False, showLevel=False) + return Logger(port, "Test Printer", consoleLogger=None, fileLogger=log_file_path, showFile=False, showLevel=False) # @pytest.hookimpl(tryfirst=True) # def pytest_sessionstart(session) -> None: @@ -248,40 +265,15 @@ def get_file_order(item): # logger.logException(session.config.fails[failTest].reprtraceback.reprentries) # logger.logMessageOnly("\n\033[32m" + line_separator(summary, symbol="=")) # -# visited_modules = set() -# -# @pytest.hookimpl(tryfirst=True, hookwrapper=True) -# def pytest_runtest_makereport(item, call): -# outcome = yield -# report = outcome.get_result() # Retrieve the TestReport object -# # Only check the outcome after the "call" phase (i.e., after the test ran) -# if (report.when == "setup" and not report.passed) or (report.when == "call"): -# if report.passed: -# if hasattr(report, "wasxfail"): -# report.outcome = "xpassed" -# report.xpassed = True -# item.config.xpassed_count += 1 -# else: -# item.config.passed_count += 1 -# elif report.failed: -# item.config.failed_count += 1 -# failName = report.nodeid.split("::")[1] + "." + item.name -# item.config.failNames.append(failName) -# item.config.fails[failName] = report.longrepr -# elif report.skipped: -# if hasattr(report, "wasxfail"): -# report.outcome = "xfailed" -# report.xfailed = True -# item.config.xfailed_count += 1 -# else: -# item.config.skipped_count += 1 -# report.port = item.config.port -# report.verbosity = item.config.verbosity -# report.logger = item.config.logger -# module_name = item.module.__name__ -# if module_name not in visited_modules: -# visited_modules.add(module_name) -# report.logger.logMessageOnly("\n" + line_separator(item.module.__desc__(), symbol="-")) +visited_modules = set() + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_protocol(item, nextitem): + module_name = item.module.__name__ + if module_name not in visited_modules: + visited_modules.add(module_name) + item.config.terminalReporter.write("\n" + line_separator(item.module.__desc__(), symbol="-", length=item.config.terminalReporter._tw.fullwidth - 1), flush=True) + yield # # @pytest.hookimpl(hookwrapper=True) # def pytest_runtest_logreport(report): diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index efb5117a..79566917 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -5,13 +5,13 @@ import platform # Add test root to sys.path if needed -rootpath=os.path.abspath(os.path.join(os.path.dirname(__file__).split("QView3D")[0], 'QView3D')) -if rootpath not in sys.path: - sys.path.append(rootpath) -serverpath = os.path.join(rootpath, "server") +from app import root_path +if root_path not in sys.path: + sys.path.append(root_path) +serverpath = os.path.join(root_path, "server") if serverpath not in sys.path: sys.path.append(serverpath) -testpath = os.path.join(rootpath, "Tests") +testpath = os.path.join(root_path, "Tests") if testpath not in sys.path: sys.path.append(testpath) @@ -41,8 +41,10 @@ import glob PORTS = glob.glob("/dev/tty[A-Za-z]*") + + # Function to run pytest for a specific port -testLevel = 0 +testLevel = 10 verbosity = 2 runFlags = 0b010 # 0b001: -s, 0b010: -vvv or -p no:terminal, 0b100: debug or info @@ -57,13 +59,13 @@ def run_tests_for_port(comm_port): if __name__ == "__main__": from concurrent.futures import ThreadPoolExecutor, as_completed - with app.app_context(): - if len(PORTS) != 0: - with ThreadPoolExecutor(max_workers=len(PORTS)) as executor: - futures = [executor.submit(run_tests_for_port, port) for port in PORTS if Ports.getPortByName(port) is not None] - for future in as_completed(futures): - try: - future.result() - except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e) \ No newline at end of file + if len(PORTS) != 0: + with ThreadPoolExecutor(max_workers=len(PORTS)) as executor: + futures = [executor.submit(run_tests_for_port, port) for port in PORTS if + Ports.getPortByName(port) is not None] + for future in as_completed(futures): + try: + future.result() + except Exception as e: + from app import handle_errors_and_logging + handle_errors_and_logging(e) \ No newline at end of file diff --git a/Tests/test_app.py b/Tests/test_app.py index 3ef8c36a..32835748 100644 --- a/Tests/test_app.py +++ b/Tests/test_app.py @@ -6,6 +6,7 @@ def __desc__(): return "App Tests" +@pytest.mark.dependency() @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_db_to_make_sure_it_has_valid_file_path(app): db_file_no_path = app.config["SQLALCHEMY_DATABASE_URI"].split("/")[-1].split("\\")[-1] @@ -15,6 +16,7 @@ def test_db_to_make_sure_it_has_valid_file_path(app): -1]), f"Database file {app.config["SQLALCHEMY_DATABASE_URI"].split("sqlite:///")[-1]} does not exist" +@pytest.mark.dependency(depends=["test_db_to_make_sure_it_has_valid_file_path"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_base_url_for_http_responses_has_valid_format(app): assert app.config["base_url"], "base_url doesnt exist?" @@ -22,12 +24,14 @@ def test_base_url_for_http_responses_has_valid_format(app): r"http://localhost:\d{1,5}$", app.config["base_url"]), f"base_url is {app.config['base_url']}" +@pytest.mark.dependency(depends=["test_base_url_for_http_responses_has_valid_format"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_environment_for_development(app): assert app.config["environment"], "environment doesnt exist?" assert app.config["environment"] == "development", f"environment is {app.config['environment']}" +@pytest.mark.dependency(depends=["test_environment_for_development"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_logger_is_custom_implementation_and_exists(app): assert app.logger, "myLogger doesnt exist?" @@ -36,6 +40,8 @@ def test_logger_is_custom_implementation_and_exists(app): assert isinstance(app.logger, Logger), "myLogger is not an instance of Logger?" assert app.logger.fileLogger, "fileLogger doesnt exist?" + +@pytest.mark.dependency(depends=["test_logger_is_custom_implementation_and_exists"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_socketio_exists_and_works(app): assert app.socketio, "socketio doesnt exist?" @@ -47,22 +53,30 @@ def test_socketio_exists_and_works(app): received = socketio_test_client.get_received() assert len(received) == 0, "Response received from socketio" + +@pytest.mark.dependency(depends=["test_socketio_exists_and_works"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_handle_errors_and_logging(app): assert app.handle_errors_and_logging, "handle_errors_and_logging doesnt exist?" assert callable(app.handle_errors_and_logging), "handle_errors_and_logging is not callable?" assert app.handle_errors_and_logging(Exception("Test Exception")) is False, "handle_errors_and_logging did not return False?" + +@pytest.mark.dependency(depends=["test_handle_errors_and_logging"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_static_loading_for_client(app): assert app.static_folder, "static_folder doesnt exist?" - assert "../client/dist" in app.static_folder, f"static_folder is {app.static_folder}" + assert os.path.join("client","dist") in app.static_folder, f"static_folder is {app.static_folder}" assert os.path.exists(app.static_folder), f"static_folder {app.static_folder} does not exist" + +@pytest.mark.dependency(depends=["test_static_loading_for_client"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_index_html_exists_in_the_static_files(app): assert os.path.exists(os.path.join(app.static_folder, "index.html")), f"index.html does not exist in {app.static_folder}" + +@pytest.mark.dependency(depends=["test_index_html_exists_in_the_static_files"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_main_view_response_is_200(app): with app.test_client() as client: diff --git a/Tests/test_device.py b/Tests/test_device.py index 7f3a5f15..d74803cd 100644 --- a/Tests/test_device.py +++ b/Tests/test_device.py @@ -13,7 +13,7 @@ def __desc__(): def __repr__(): return f"test_device.py running on port {Ports.getPortByName(os.getenv('PORT'))}" -@pytest.mark.dependency() +@pytest.mark.dependency(depends=["test_app.py::test_main_view_response_is_200"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 1, reason="Not doing lvl 1 tests") def test_connection(app, fabricator): assert fabricator.device is not None, f"No printer connected on {fabricator.device.DESCRIPTION}" diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index c4841961..ae2f9843 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -33,7 +33,7 @@ def test_status(app, fabricator): def test_add_job(app, fabricator): file = cali_cube_setup(fabricator=fabricator) with open(file, "r") as f: - assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", 3, "ready", file, False, 1, fabricator.name)), f"Failed to add job on {fabricator.getDescription()}" + assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)), f"Failed to add job on {fabricator.getDescription()}" for job in fabricator.queue.getQueue(): assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" fabricator.queue.removeJob() @@ -42,8 +42,7 @@ def test_add_job(app, fabricator): @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 7, reason="Not doing lvl 7 tests") def test_pause_and_resume(app, fabricator): - from Mixins.canPause import canPause - if not isinstance(fabricator.device, canPause): + if not fabricator.device.pauseCMD or not fabricator.device.resumeCMD: pytest.skip(f"{fabricator.getDescription()} doesn't support pausing") def parse_gcode(): @@ -79,7 +78,7 @@ def pause_and_resume_fabricator(): assert fabricator.pause(), f"Failed to pause on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(30) assert fabricator.resume(), f"Failed to resume on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - sleep(1) + sleep(10) assert fabricator.cancel(), f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" assert fabricator.getStatus() == "cancelled", f"Failed to cancel on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" sleep(10) @@ -89,15 +88,22 @@ def pause_and_resume_fabricator(): fabricator.resetToIdle() assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - - assert fabricator.device.home(), f"Failed to home on {fabricator.getDescription()}" + from flask import current_app + fabricator.device.logger.critical(f"app: ,{current_app}") + fabricator.device.logger.critical(f"app.socketio: ,{current_app.socketio}") + from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 + if isinstance(fabricator.device, PrusaMK3): + fabricator.device.sendGcode(b"G28 W\n") + else: + assert fabricator.device.home(), f"Failed to home on {fabricator.getDescription()}" from concurrent.futures import ThreadPoolExecutor, as_completed with ThreadPoolExecutor(max_workers=2) as executor: - parse_future = executor.submit(parse_gcode) - pause_future = executor.submit(pause_and_resume_fabricator) + with current_app.app_context(): + parse_future = executor.submit(parse_gcode) + pause_future = executor.submit(pause_and_resume_fabricator) - for future in as_completed([parse_future, pause_future]): - future.result() + for future in as_completed([parse_future, pause_future]): + future.result() @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 9, reason="Not doing lvl 9 tests") diff --git a/Tests/test_fabricator_list.py b/Tests/test_fabricator_list.py index f7c6d5b8..27165a62 100644 --- a/Tests/test_fabricator_list.py +++ b/Tests/test_fabricator_list.py @@ -5,15 +5,20 @@ def __desc__(): return "Fabricator List Tests" +@pytest.mark.dependency(depends=["test_app.py::test_main_view_response_is_200"], scope="session") @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_fabricator_list_has_at_least_one_fabricator(app): - assert len(app.FabricatorList) > 0, "Fabricator list is empty" + assert len(app.fabricator_list) > 0, "Fabricator list is empty" + +@pytest.mark.dependency(depends=["test_fabricator_list_has_at_least_one_fabricator"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_fabricator_list_has_fabricator_from_fixture(app, fabricator): assert fabricator is not None, "Fabricator is None" assert fabricator in app.fabricator_list, "Fabricator not in list" + +@pytest.mark.dependency(depends=["test_fabricator_list_has_fabricator_from_fixture"]) @pytest.mark.skipif(condition=testLevel < 1, reason="Not doing lvl 1 tests") def test_fabricator_thread_is_running(app, fabricator): thread = app.fabricator_list.get_fabricator_thread(fabricator) diff --git a/server/test.py b/server/test.py index d57d8a8b..8c4835c9 100644 --- a/server/test.py +++ b/server/test.py @@ -1,6 +1,5 @@ -#import re +import time from datetime import datetime -#from Classes.FabricatorList import FabricatorList from Classes.Fabricators.Fabricator import Fabricator from Classes.Jobs import Job from Classes.Vector3 import Vector3 @@ -111,51 +110,58 @@ def runTests(fabricator: Fabricator, isVerbose=False): Vector3(200.0, 150.0, 2.0), Vector3(150.0, 200.0, 2), Vector3(100.0, 200.0, 2.0), Vector3(50.0, 150.0, 2.0)], # octagon ] -# with app.app_context(): -PrusaMK4S = None -PrusaMK4 = None -PrusaMK3 = None -Ender3 = None -MakerBot = None -EnderPro = None - -# FabricatorList.init() -# for printer in FabricatorList.fabricators: -# runTests(printer) - -# ari's MK4S -if Ports.getPortByName("COM5") is not None: - PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S", addToDB=False) - -# nate's Ender 3 Pro -if Ports.getPortByName("COM6") is not None: - EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro", addToDB=False) - -# school prusa mk4 -if Ports.getPortByName("COM3") is not None: - PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4", addToDB=False) - # PrusaMK4.device.logger.setLevel(PrusaMK4.device.logger.DEBUG) - # PrusaMK4.device.sendGcode(b'G29 A\n', isVerbose=True) - -# school Ender 3 -if Ports.getPortByName("COM4") is not None: - Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3", addToDB=False) - # Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) - -# school Makerbot -if Ports.getPortByName("COM7") is not None: - MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot", addToDB=False) - -# school MK3 -if Ports.getPortByName("COM9") is not None: - PrusaMK3 = Fabricator(Ports.getPortByName("COM9"), "PrusaMK3", addToDB=False) - PrusaMK3.device.logger.setLevel(PrusaMK3.device.logger.DEBUG) - PrusaMK3.device.sendGcode(b'G28\n', isVerbose=True) - -# for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: -# if fab is not None: -# runTests(fab) - -for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: - if fab is not None and fab.device.serialConnection.is_open: - fab.device.disconnect() +from app import app +with app.app_context(): + PrusaMK4S = None + PrusaMK4 = None + PrusaMK3 = None + Ender3 = None + MakerBot = None + EnderPro = None + + # FabricatorList.init() + # for printer in FabricatorList.fabricators: + # runTests(printer) + + # ari's MK4S + if Ports.getPortByName("COM5") is not None: + PrusaMK4S = Fabricator(Ports.getPortByName("COM5"), "Prusa MK4S") + + # nate's Ender 3 Pro + if Ports.getPortByName("COM6") is not None: + EnderPro = Fabricator(Ports.getPortByName("COM6"), "Ender 3 Pro") + + # school prusa mk4 + if Ports.getPortByName("COM3") is not None: + PrusaMK4 = Fabricator(Ports.getPortByName("COM3"), "Prusa MK4") + # PrusaMK4.device.logger.setLevel(PrusaMK4.device.logger.DEBUG) + # PrusaMK4.device.sendGcode(b'G29 A\n', isVerbose=True) + + # school Ender 3 + if Ports.getPortByName("COM4") is not None: + Ender3 = Fabricator(Ports.getPortByName("COM4"), "Ender 3") + # Ender3.device.sendGcode(b"M109 S50\n", isVerbose=True) + + # school Makerbot + if Ports.getPortByName("COM7") is not None: + MakerBot = Fabricator(Ports.getPortByName("COM7"), "MakerBot") + + # school MK3 + if Ports.getPortByName("COM9") is not None: + PrusaMK3 = Fabricator(Ports.getPortByName("COM9"), "Prusa MK3") + # PrusaMK3.device.connect() + # PrusaMK3.device.logger.setLevel(PrusaMK3.device.logger.DEBUG) + # PrusaMK3.device.sendGcode(PrusaMK3.device.pauseCMD, isVerbose=True) + # time.sleep(5) + # PrusaMK3.device.sendGcode(PrusaMK3.device.resumeCMD, isVerbose=True) + # time.sleep(5) + # PrusaMK3.device.sendGcode(PrusaMK3.device.cancelCMD, isVerbose=True) + # print(PrusaMK3.device.home(isVerbose=True)) + + # for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: + # if fab is not None: + # runTests(fab) + + for fab in [PrusaMK4S, PrusaMK4, PrusaMK3, Ender3, MakerBot, EnderPro]: + if fab is not None and fab.device is not None and fab.device.serialConnection is not None and fab.device.serialConnection.is_open: + fab.device.disconnect() From 2e9f7a65794f19a5eed9571a52928e6711ecbf89 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 13:47:11 -0500 Subject: [PATCH 143/194] feat: integration (WIP) --- client/src/components/NavBar.vue | 8 +- client/src/model/ports.ts | 3 +- client/src/model/sockets.ts | 2 +- client/src/views/MainView.vue | 96 +++++++++---------- server/Classes/FabricatorList.py | 2 +- server/Classes/Fabricators/Device.py | 13 ++- server/Classes/Fabricators/Fabricator.py | 31 +++--- .../Classes/Fabricators/Printers/Printer.py | 2 +- server/Classes/Jobs.py | 8 +- server/Classes/Queue.py | 4 +- server/app.py | 1 + server/controllers/issues.py | 1 - server/controllers/jobs.py | 10 +- server/routes.py | 2 - 14 files changed, 89 insertions(+), 94 deletions(-) diff --git a/client/src/components/NavBar.vue b/client/src/components/NavBar.vue index 92f211a7..5df09b47 100644 --- a/client/src/components/NavBar.vue +++ b/client/src/components/NavBar.vue @@ -46,9 +46,13 @@ const observer = new MutationObserver(() => { const colorSecondary = getComputedStyle(document.documentElement, null).getPropertyValue('--color-secondary').trim(); const svgElementPrimary = document.getElementById('cls-1'); - svgElementPrimary.style.fill = colorPrimary; + if (svgElementPrimary) { + svgElementPrimary.style.fill = colorPrimary; + } const svgElementSecondary = document.getElementById('cls-2'); - svgElementSecondary.style.stroke = colorSecondary; + if (svgElementSecondary) { + svgElementSecondary.style.stroke = colorSecondary; + } }); observer.observe(document.documentElement, {attributes: true, attributeFilter: ['style']}); diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index 76c2f8fb..a4fc1f12 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -95,8 +95,7 @@ export function useSetStatus() { return { async setStatus(printerid: number | undefined, status: string) { try { - const response = await api('setstatus', { printerid, status }) - return response + return await api('setstatus', { printerid, status }) } catch (error) { console.error(error) } diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 58938066..4f2cb717 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -17,7 +17,7 @@ export function setupTempSocket(printers: any) { export function setupStatusSocket(printers: any) { socket.value.on('status_update', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printer_id) + const printer = printers.value.find((p: Device) => p.id === data.fabricator_id) if (printer) { printer.status = data.status } diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index 25295b1d..56e2f190 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import { nextTick, onMounted, ref, watchEffect } from 'vue'; +import { nextTick, onMounted, ref } from 'vue'; import { printers, useSetStatus, useMovePrinterList, type Device } from '@/model/ports'; import draggable from 'vuedraggable' import GCode3DImageViewer from '@/components/GCode3DImageViewer.vue' @@ -23,23 +23,22 @@ const router = useRouter() const selectedIssue = ref<Issue>() const selectedJob = ref<Job>() -let jobComments = ref('') +const jobComments = ref('') -let currentJob = ref<Job>(); -let currentPrinter = ref<Device>(); +const currentJob = ref<Job>(); +const currentPrinter = ref<Device>(); -let issuelist = ref<Array<Issue>>([]) +const issuelist = ref<Array<Issue>>([]) -let isGcodeImageVisible = ref(false) +const isGcodeImageVisible = ref(false) const isImageVisible = ref(true) -let isGcodeLiveViewVisible = ref(false) +const isGcodeLiveViewVisible = ref(false) let expandedState: (string | undefined)[] = []; onMounted(async () => { - const retrieveissues = await issues() - issuelist.value = retrieveissues + issuelist.value = await issues() const imageModal = document.getElementById('gcodeImageModal') @@ -289,10 +288,10 @@ const handleDragEnd = async () => { @end="restoreExpandedState"> <template #item="{ element: printer }"> <div v-if="printer.isInfoExpanded" class="expanded-info"> - <tr :id="printer.id"> + <tr :id="printer.id" style="vertical-align: middle"> <td v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0].td_id }} + {{ printer.queue?.[0]?.td_id }} </td> <td v-else><i>idle</i></td> @@ -310,7 +309,7 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="printer.status === 'idle' && printer.queue?.[0]?.released === 0" style="color: #ad6060" + <p v-if="(printer.status === 'idle' || printer.status === 'ready') && printer.queue?.[0]?.released === 0" style="color: #ad6060" class="mb-0 me-2"> Waiting release </p> @@ -321,13 +320,13 @@ const handleDragEnd = async () => { </td> <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.name }} </td> <td v-else></td> <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.file_name_original }} </td> <td v-else></td> @@ -348,7 +347,7 @@ const handleDragEnd = async () => { </button> <button class="btn btn-secondary" - v-if="printer.status == 'idle' && printer.queue?.[0].released == 0" + v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> @@ -376,11 +375,11 @@ const handleDragEnd = async () => { </button> <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0].file_pause == 1)" + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" class="mt-2"> Ready for color change. </div> - <div v-else-if="printer.status == 'colorchange' && printer.queue[0].file_pause == 0" class="mt-2"> + <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> Finishing current layer... </div> @@ -394,20 +393,17 @@ const handleDragEnd = async () => { <!-- Display the elapsed time --> <div class="progress" style="position: relative;"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0].progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0].progress" aria-valuemin="0" aria-valuemax="100"> + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> </div> <!-- job progress set to 2 decimal places --> - <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ - printer.queue?.[0].progress - ? - `${printer.queue?.[0].progress.toFixed(2)}%` : '0.00%' }}</p> + <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{printer.queue?.[0]?.progress ? `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> </div> <!-- </div> --> </div> <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> + v-else-if="printer.queue?.[0] && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')"> <div class="buttons-progress"> <div type="button" class="btn btn-secondary" @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> @@ -417,9 +413,7 @@ const handleDragEnd = async () => { <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> Clear/Rerun </div> - <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" - aria-expanded="false"> - </div> + <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"> </div> <div class="dropdown-menu"> <div class="dropdown-item" v-for="printer in printers" :key="printer.id" @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> @@ -427,15 +421,12 @@ const handleDragEnd = async () => { </div> </div> </div> - <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" - @click=setJob(printer.queue[0])> - Fail - </div> + <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" @click=setJob(printer.queue[0])>Fail</div> </div> </div> <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> + v-else-if="printer.queue?.[0] && printer.queue?.[0]?.status == 'printing' && printer.status == 'complete'"> <div style="display: flex; justify-content: center; align-items: center;"> <button class="btn btn-primary w-100" type="button" disabled> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> @@ -472,7 +463,7 @@ const handleDragEnd = async () => { <span class="ms-2">GCode Image</span> </a> </li> - <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> + <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0]?.extruded)"> <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" v-bind:job="printer.queue[0]" @@ -483,8 +474,8 @@ const handleDragEnd = async () => { </li> <li v-if="printer.queue[0]"> <a class="dropdown-item d-flex align-items-center" - @click="getFileDownload(printer.queue[0].id)" - :disabled="printer.queue[0].file_name_original.includes('.gcode:')"> + @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> <i class="fas fa-download"></i> <span class="ms-2">Download</span> </a> @@ -568,8 +559,8 @@ const handleDragEnd = async () => { </div> <tr v-else :id="printer.id"> <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0].td_id }} + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.td_id }} </td> <td v-else><i>idle</i></td> @@ -587,7 +578,7 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="printer.status === 'idle' && printer.queue?.[0]?.released === 0" style="color: #ad6060" + <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" class="mb-0 me-2"> Waiting release </p> @@ -598,13 +589,13 @@ const handleDragEnd = async () => { </td> <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.name }} </td> <td v-else></td> <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.file_name_original }} </td> <td v-else></td> @@ -624,7 +615,7 @@ const handleDragEnd = async () => { Turn Offline </button> - <button class="btn btn-secondary" v-if="printer.status == 'idle' && printer.queue?.[0].released == 0" + <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> @@ -647,16 +638,16 @@ const handleDragEnd = async () => { </button> <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" - v-if="(printer.status == 'printing' || printer.status == 'colorchange')"> + v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> Stop </button> <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0].file_pause == 1)" + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" class="mt-2"> Ready for color change. </div> - <div v-else-if="printer.status == 'colorchange' && printer.queue[0].file_pause == 0" class="mt-2"> + <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> Finishing current layer... </div> @@ -665,25 +656,24 @@ const handleDragEnd = async () => { <td style="width: 250px;"> <div - v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0].released == 1"> + v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> <!-- <div v-for="job in printer.queue" :key="job.id"> --> <!-- Display the elapsed time --> <div class="progress" style="position: relative;"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0].progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0].progress" aria-valuemin="0" aria-valuemax="100"> + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> </div> <!-- job progress set to 2 decimal places --> <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ - printer.queue?.[0].progress + printer.queue?.[0]?.progress ? - `${printer.queue?.[0].progress.toFixed(2)}%` : '0.00%' }}</p> + `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> </div> <!-- </div> --> </div> - <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> + <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> <div class="buttons-progress"> <div type="button" class="btn btn-secondary" @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> @@ -759,8 +749,8 @@ const handleDragEnd = async () => { </a> </li> <li v-if="printer.queue[0]"> - <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0].id)" - :disabled="printer.queue[0].file_name_original.includes('.gcode:')"> + <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> <i class="fas fa-download"></i> <span class="ms-2">Download</span> </a> diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 661c072a..d3140773 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -38,7 +38,7 @@ def run(self): if status == "ready" and queueSize > 0: time.sleep(2) if status != "offline": - self.fabricator.printNextInQueue() + self.fabricator.begin() def stop(self): self.fabricator.terminated = 1 diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 2a4b9eb8..731b6e0a 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -1,3 +1,5 @@ +import io +import os.path import sys from abc import ABC from time import sleep @@ -125,15 +127,16 @@ def parseGcode(self, job: Job, isVerbose: bool = False): """ Parse a G-code file and send the commands to the device. :param job: The Job object, with file name to parse. - :param isVerbose: Whether to log the commands :type job: Job + :param isVerbose: Whether to log the commands :type isVerbose: bool :raises AssertionError: if the file is not a string or if isVerbose is not a bool """ - assert isinstance(job, Job) - file = job.file_name_original - assert isinstance(file, str) - assert isinstance(isVerbose, bool) + assert isinstance(job, Job), f"Expected Job object, got {type(job)}" + file = job.file_path + assert os.path.exists(file), f"File {file} does not exist" + assert isinstance(file, str), f"Expected string, got {type(file)}" + assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" try: with open(file, "r") as f: self.logger.info(f"Printing {file}") diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 1ecb9a18..dbbd2a1d 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,3 +1,6 @@ +import io +from sqlite3 import Blob + import serial from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo @@ -33,7 +36,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog self.dbID = None # Initialize dbID self.job: Job | None = None self.queue: Queue = Queue() - self.status: str = "idle" + self.status: str = "configuring" self.hwid = port.hwid.split(" LOCATION=")[0] self.description = "New Fabricator" self.name: str = name @@ -171,11 +174,8 @@ def begin(self, isVerbose: bool = False): :return: whether the fabrication process was successful :rtype: bool """ - from flask import current_app - if current_app is not None: - current_app.logger.critical(f"current app: {current_app}") try: - assert self.status == "idle", f"Fabricator is not idle, status: {self.status}" + assert self.status == "ready" or "printing", f"Fabricator is not ready or printing, status: {self.status}" assert self.queue is not None, "Queue is None" assert len(self.queue) > 0, "Queue is empty" self.job = self.queue.getNext() @@ -240,9 +240,8 @@ def getStatus(self): def setStatus(self, newStatus): try: - assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint"], f"Invalid status: {newStatus}" + assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint", "ready", "offline"], f"Invalid status: {newStatus}" assert self.device is not None, "Device is None" - if self.status == "error" and newStatus!= "error": self.device.hardReset(newStatus) self.status = newStatus @@ -255,6 +254,8 @@ def setStatus(self, newStatus): current_app.socketio.emit( "status_update", {"fabricator_id": self.dbID, "status": newStatus} ) + else: + print(f"current app is None, status: {newStatus}") return True except Exception as e: from app import handle_errors_and_logging @@ -268,22 +269,17 @@ def handleVerdict(self): assert self.device.verdict in ["complete", "error", "cancelled", "misprint"], f"Invalid verdict: {self.device.verdict}" assert self.job is not None, "Job is None" if self.device.verdict == "complete": - #self.device.disconnect() self.setStatus("complete") self.queue.removeJob() self.job = None - # todo: reset to idle elif self.device.verdict == "error": - #self.device.disconnect() self.setStatus("error") elif self.device.verdict == "cancelled": if isinstance(self.device, hasEndingSequence): self.device.endSequence() else: self.device.home() - #self.device.disconnect() self.setStatus("cancelled") self.queue.removeJob() self.job = None - #todo: reset to idle elif self.device.verdict== "misprint": self.setStatus("misprint") @@ -317,14 +313,17 @@ def getQueue(self): def checkValidJob(self): """checks if the job is valid for the fabricator""" try: - settingsDict = getFileConfig(self.job.file_name_original) + assert self.job is not None, "Job is None" + assert self.device is not None, "Device is None" + self.job.saveToFolder() + settingsDict = getFileConfig(self.job.file_path) from Classes.Fabricators.Printers.Printer import Printer from Classes.Fabricators.CNCMachines.CNCMachine import CNCMachine from Classes.Fabricators.LaserCutters.LaserCutter import LaserCutter if isinstance(self.device, Printer): - assert self.device.filamentType is not None, "Filament type not set" - assert self.device.filamentDiameter is not None, "Filament diameter not set" - assert self.device.nozzleDiameter is not None, "Nozzle diameter not set" + if self.device.filamentType is None: self.device.filamentType = settingsDict["filament_type"] + if self.device.filamentDiameter is None: self.device.filamentDiameter = float(settingsDict["filament_diameter"]) + if self.device.nozzleDiameter is None: self.device.nozzleDiameter = float(settingsDict["nozzle_diameter"]) assert self.device.filamentType == settingsDict["filament_type"], f"Filament type mismatch: {self.device.filamentType} != {settingsDict['filament_type']}" assert self.device.filamentDiameter == float(settingsDict["filament_diameter"]), f"Filament diameter mismatch: {self.device.filamentDiameter} != {float(settingsDict['filament_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0" assert self.device.nozzleDiameter == float(settingsDict["nozzle_diameter"]), f"Nozzle diameter mismatch: {self.device.nozzleDiameter} != {float(settingsDict['nozzle_diameter'])}, subtraction test: {self.device.nozzleDiameter - float(settingsDict['nozzle_diameter'])} != 0.0" diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index c031c354..49a58b88 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -38,7 +38,7 @@ def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None): def parseGcode(self, job: Job, isVerbose: bool = False): assert isinstance(job, Job) - file = job.file_name_original + file = job.file_path assert isinstance(file, str) assert isinstance(isVerbose, bool) assert self.serialConnection.is_open diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index 709a4cec..1eab95f7 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -63,6 +63,7 @@ def __init__(self, file, name, fabricator_id, status, file_name_original, favori self.file_name_original = file_name_original # original file name without PK identifier self.td_id = td_id self.file_name_pk = None + self.file_path = None self.favorite = favorite self.released = 0 self.filePause = 0 @@ -471,13 +472,14 @@ def downloadCSV(cls, alljobs, jobids=None): return jsonify({"error": format_exc()}), 500 def saveToFolder(self): - file_data = self.getFile() + file_data = self.file decompressed_data = gzip.decompress(file_data) - with open(self.generatePath(), 'wb') as f: + self.file_path = self.generatePath() + with open(self.file_path, 'wb') as f: f.write(decompressed_data) def generatePath(self): - return os.path.join('../uploads', self.getFileNamePk()) + return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", self.file_name_original) # getters def getName(self): diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index f9a8571e..0473b784 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -172,6 +172,4 @@ def getSize(self): def removeJob(self): self.pop() if current_app: - current_app.socketio.emit( - "job_removed", {"queue": list(self)}, broadcast=True - ) \ No newline at end of file + current_app.socketio.emit("job_removed", {"queue": list(self)}) \ No newline at end of file diff --git a/server/app.py b/server/app.py index 365e9589..d9be2eb3 100644 --- a/server/app.py +++ b/server/app.py @@ -12,6 +12,7 @@ # Global variables root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +uploads_folder = os.path.abspath(os.path.join(root_path, 'uploads')) # moved this up here so we can pass the app to the PrinterStatusService diff --git a/server/controllers/issues.py b/server/controllers/issues.py index 033759e2..1f877a15 100644 --- a/server/controllers/issues.py +++ b/server/controllers/issues.py @@ -1,5 +1,4 @@ from flask import Blueprint, jsonify, request -from flask_cors import cross_origin from models.issues import Issue issue_bp = Blueprint("issues", __name__) diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 15e6d64a..ffe699d1 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -8,6 +8,7 @@ import serial import serial.tools.list_ports from app import handle_errors_and_logging +from flask import current_app from traceback import format_exc # get data for jobs @@ -405,11 +406,12 @@ def setStatus(): data = request.get_json() # get json data printer_id = data['printerid'] newstatus = data['status'] - - printerobject = findPrinterObject(printer_id) - + from Classes.Fabricators.Fabricator import Fabricator + printerobject: Fabricator | None = findPrinterObject(printer_id) + if printerobject is None: + return jsonify({"error": "Printer not found."}), 404 printerobject.setStatus(newstatus) - + print(printerobject.status) return jsonify({"success": True, "message": "Status updated successfully."}), 200 except Exception as e: diff --git a/server/routes.py b/server/routes.py index 59e446b4..6dc69770 100644 --- a/server/routes.py +++ b/server/routes.py @@ -1,6 +1,4 @@ from flask import Flask - - def defineRoutes(app: Flask): # IMPORTING BLUEPRINTS from controllers.ports import ports_bp From 6924285cf6a44b4adad09400a2c50d769c5cc85a Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 14:28:10 -0500 Subject: [PATCH 144/194] fix: create log folder if it doesnt exist --- server/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/app.py b/server/app.py index d9be2eb3..21ba50d4 100644 --- a/server/app.py +++ b/server/app.py @@ -20,6 +20,7 @@ app = Flask(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) app.config.from_object(__name__) # update application instantly logs = os.path.join(root_path,"server", "logs") +if not os.path.exists(logs): os.makedirs(logs) from Classes.Logger import Logger app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.abspath(os.path.join(logs, "app.log"))) # start database connection From e06fd44c911a6f0d416f4d1bac95213505985266 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 14:48:46 -0500 Subject: [PATCH 145/194] fix: fix bad RegisterModal.vue --- client/src/components/RegisterModal.vue | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/client/src/components/RegisterModal.vue b/client/src/components/RegisterModal.vue index 2e6b1f19..b9be7af2 100644 --- a/client/src/components/RegisterModal.vue +++ b/client/src/components/RegisterModal.vue @@ -75,7 +75,7 @@ const clearSelectedDevice = () => { } const doMove = async (printer: Device) => { - await move(printer.device) + await move(printer.device['serialPort']) } </script> @@ -96,7 +96,7 @@ const doMove = async (printer: Device) => { <label for="ports" class="form-label">Select Device</label> <select class="form-select" id="ports" v-model="selectedDevice" required> <option disabled value="null">Select Device</option> - <option v-for="printer in devices" :value="printer" :key="printer.device"> + <option v-for="printer in devices" :value="printer" :key="printer.device['dbID']"> {{ printer.description }} </option> </select> @@ -104,7 +104,7 @@ const doMove = async (printer: Device) => { <div v-if="selectedDevice"> <div class="mb-3"> <label class="form-label">Device</label> - <p class="form-text p-1 rounded">{{ selectedDevice.device }}</p> + <p class="form-text p-1 rounded">{{ selectedDevice.device['serialPort'] }}</p> </div> <div class="mb-3"> <label class="form-label">Description</label> @@ -144,13 +144,22 @@ const doMove = async (printer: Device) => { } .form-text { + color: var(--color-text); background: var(--color-background-mute); border: 1px solid var(--color-modal-background-inverted); } .form-select { - color: var(--color-background-font); + color: var(--color-text) !important; background-color: var(--color-background-soft) !important; border-color: var(--color-modal-background-inverted) !important; } +input { + color: var(--color-text) !important; + background-color: var(--color-background-soft) !important; + border-color: var(--color-modal-background-inverted) !important; +} +input::placeholder { + color: var(--color-nav-text) !important; +} </style> \ No newline at end of file From 57ef70a8934f6f2711a7ca3ad6ce570b8f6860e4 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 16:31:01 -0500 Subject: [PATCH 146/194] feat: basic serial and socket connection for fabricators --- server/Classes/FabricatorConnection.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 server/Classes/FabricatorConnection.py diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py new file mode 100644 index 00000000..8d08c915 --- /dev/null +++ b/server/Classes/FabricatorConnection.py @@ -0,0 +1,40 @@ +from abc import ABC, abstractmethod + +import serial +from flask_socketio import SocketIO + + +class FabricatorConnection(ABC): + is_open = False + + @abstractmethod + def write(self, data): + pass + + @abstractmethod + def read(self): + pass + +class SerialConnection(FabricatorConnection): + def __init__(self, port: str, baudrate: int, timeout: float): + self.is_open = False + self.serial = serial.Serial(port, baudrate, timeout=timeout) + + def write(self, data): + self.serial.write(data) + + def read(self): + return self.serial.readline() + +class SocketConnection(FabricatorConnection): + def __init__(self, socketio: SocketIO, fabricator_id: str): + self.is_open = False + self.socketio = socketio + self.fabricator_id = fabricator_id + self.response = None + + def write(self, data): + self.socketio.emit("send_gcode", {"printerid": self.fabricator_id, "gcode": data}) + + def read(self): + return self.response or "" From 26c7d9cb793250d6ed91314540cd4fce5851c9a8 Mon Sep 17 00:00:00 2001 From: iron768 <iron768@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:37:34 -0500 Subject: [PATCH 147/194] feat: expand fabricator connection to add functions and properties needed to work --- server/Classes/FabricatorConnection.py | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 8d08c915..20d3be83 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -15,16 +15,41 @@ def write(self, data): def read(self): pass + @abstractmethod + def close(self): + pass + + @abstractmethod + def reset_input_buffer(self): + pass + + @abstractmethod + def readline(self): + pass + class SerialConnection(FabricatorConnection): def __init__(self, port: str, baudrate: int, timeout: float): - self.is_open = False self.serial = serial.Serial(port, baudrate, timeout=timeout) + self.is_open = self.serial.is_open def write(self, data): self.serial.write(data) def read(self): return self.serial.readline() + + def close(self): + self.serial.close() + + def reset_input_buffer(self): + self.serial.reset_input_buffer() + + def readline(self): + return self.serial.readline() + + @property + def is_open(self): + return self.serial.is_open class SocketConnection(FabricatorConnection): def __init__(self, socketio: SocketIO, fabricator_id: str): @@ -38,3 +63,19 @@ def write(self, data): def read(self): return self.response or "" + + def close(self): + # TODO: make this!! + pass + + def reset_input_buffer(self): + # TODO: make this!! + pass + + def readline(self): + # TODO: make this!! + return "" + + @property + def is_open(self): + return self.is_open From 9f24e4d7a7a0fa2483c7669fe184ca1541c9f706 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 19:11:51 -0500 Subject: [PATCH 148/194] feat: sockets cleanup and live gcode render socket --- client/src/App.vue | 30 ++---------------------------- client/src/model/sockets.ts | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index d22ba54d..076ed1e3 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -7,21 +7,7 @@ import NavBar from '@/components/NavBar.vue' import ThemePanel from '@/components/ThemePanel.vue' import SettingsPanel from '@/components/SettingsPanel.vue' import { onMounted } from 'vue'; -import { - setupPortRepairSocket, - setupErrorSocket, - setupJobStatusSocket, - setupPauseFeedbackSocket, - setupProgressSocket, - setupQueueSocket, - setupReleaseSocket, - setupStatusSocket, - setupTempSocket, - setupGCodeViewerSocket, - setupExtrusionSocket, - setupCurrentLayerHeightSocket, - setupMaxLayerHeightSocket -} from '@/model/sockets'; +import {setupSockets} from '@/model/sockets'; import {useRetrievePrintersInfo, printers} from '@/model/ports'; import {setupTimeSocket, isLoading} from '@/model/jobs'; @@ -31,20 +17,8 @@ onMounted(async () => { printers.value = await retrieveInfo() // sockets - setupStatusSocket(printers) - setupQueueSocket(printers) - setupProgressSocket(printers) - setupJobStatusSocket(printers) - setupErrorSocket(printers) + setupSockets(printers) setupTimeSocket(printers) - setupTempSocket(printers) - setupGCodeViewerSocket(printers) - setupPauseFeedbackSocket(printers) //not sure if needed - setupReleaseSocket(printers) - setupPortRepairSocket(printers) - setupExtrusionSocket(printers) - setupMaxLayerHeightSocket(printers) - setupCurrentLayerHeightSocket(printers) }) </script> diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 4f2cb717..6a9aa4a0 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -2,6 +2,26 @@ import { socket } from './myFetch' import type { Device } from './ports' import { jobTime } from './jobs' +export function setupSockets(printers: any) { + setupTempSocket(printers) + setupStatusSocket(printers) + setupQueueSocket(printers) + setupErrorSocket(printers) + setupCanPauseSocket(printers) + setupPauseFeedbackSocket(printers) + setupTimeStartedSocket(printers) + setupProgressSocket(printers) + setupReleaseSocket(printers) + setupJobStatusSocket(printers) + setupPortRepairSocket(printers) + setupGCodeViewerSocket(printers) + setupGCodeLineSocket(printers) + setupExtrusionSocket(printers) + setupColorChangeBuffer(printers) + setupMaxLayerHeightSocket(printers) + setupCurrentLayerHeightSocket(printers) +} + // *** PORTS *** export function setupTempSocket(printers: any) { socket.value.on('temp_update', (data: any) => { @@ -188,6 +208,23 @@ export function setupGCodeViewerSocket(printers: any) { }) } +export function setupGCodeLineSocket(printers: any) { + socket.value.on('gcode_line', (data: any) => { + if (printers) { + const job = printers.value + .flatMap((printer: { queue: any }) => printer.queue) + .find((job: { id: any }) => job?.id === data.job_id) + + if (job) { + //TODO: add gcode_line to wherever it needs to go + job.gcode_line = data.gcode_line + } + } else { + console.error('printers or printers.value is undefined') + } + }) +} + export function setupExtrusionSocket(printers: any) { socket.value.on('extruded_update', (data: any) => { if (printers) { From 3a9c144b059744048d87f899218c6fd0c4866fd3 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 19:11:51 -0500 Subject: [PATCH 149/194] feat: sockets cleanup and live gcode render socket --- client/src/App.vue | 30 +-------------- client/src/model/sockets.ts | 37 +++++++++++++++++++ server/Classes/Fabricators/Device.py | 5 +++ .../Classes/Fabricators/Printers/Printer.py | 8 +++- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index d22ba54d..076ed1e3 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -7,21 +7,7 @@ import NavBar from '@/components/NavBar.vue' import ThemePanel from '@/components/ThemePanel.vue' import SettingsPanel from '@/components/SettingsPanel.vue' import { onMounted } from 'vue'; -import { - setupPortRepairSocket, - setupErrorSocket, - setupJobStatusSocket, - setupPauseFeedbackSocket, - setupProgressSocket, - setupQueueSocket, - setupReleaseSocket, - setupStatusSocket, - setupTempSocket, - setupGCodeViewerSocket, - setupExtrusionSocket, - setupCurrentLayerHeightSocket, - setupMaxLayerHeightSocket -} from '@/model/sockets'; +import {setupSockets} from '@/model/sockets'; import {useRetrievePrintersInfo, printers} from '@/model/ports'; import {setupTimeSocket, isLoading} from '@/model/jobs'; @@ -31,20 +17,8 @@ onMounted(async () => { printers.value = await retrieveInfo() // sockets - setupStatusSocket(printers) - setupQueueSocket(printers) - setupProgressSocket(printers) - setupJobStatusSocket(printers) - setupErrorSocket(printers) + setupSockets(printers) setupTimeSocket(printers) - setupTempSocket(printers) - setupGCodeViewerSocket(printers) - setupPauseFeedbackSocket(printers) //not sure if needed - setupReleaseSocket(printers) - setupPortRepairSocket(printers) - setupExtrusionSocket(printers) - setupMaxLayerHeightSocket(printers) - setupCurrentLayerHeightSocket(printers) }) </script> diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 4f2cb717..6a9aa4a0 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -2,6 +2,26 @@ import { socket } from './myFetch' import type { Device } from './ports' import { jobTime } from './jobs' +export function setupSockets(printers: any) { + setupTempSocket(printers) + setupStatusSocket(printers) + setupQueueSocket(printers) + setupErrorSocket(printers) + setupCanPauseSocket(printers) + setupPauseFeedbackSocket(printers) + setupTimeStartedSocket(printers) + setupProgressSocket(printers) + setupReleaseSocket(printers) + setupJobStatusSocket(printers) + setupPortRepairSocket(printers) + setupGCodeViewerSocket(printers) + setupGCodeLineSocket(printers) + setupExtrusionSocket(printers) + setupColorChangeBuffer(printers) + setupMaxLayerHeightSocket(printers) + setupCurrentLayerHeightSocket(printers) +} + // *** PORTS *** export function setupTempSocket(printers: any) { socket.value.on('temp_update', (data: any) => { @@ -188,6 +208,23 @@ export function setupGCodeViewerSocket(printers: any) { }) } +export function setupGCodeLineSocket(printers: any) { + socket.value.on('gcode_line', (data: any) => { + if (printers) { + const job = printers.value + .flatMap((printer: { queue: any }) => printer.queue) + .find((job: { id: any }) => job?.id === data.job_id) + + if (job) { + //TODO: add gcode_line to wherever it needs to go + job.gcode_line = data.gcode_line + } + } else { + console.error('printers or printers.value is undefined') + } + }) +} + export function setupExtrusionSocket(printers: any) { socket.value.on('extruded_update', (data: any) => { if (printers) { diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 731b6e0a..bf4724e0 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -3,6 +3,8 @@ import sys from abc import ABC from time import sleep + +from flask import current_app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS @@ -143,6 +145,9 @@ def parseGcode(self, job: Job, isVerbose: bool = False): for line in f: if line.startswith(";") or line == "\n": continue + if current_app: + with current_app.app_context(): + current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) if isVerbose: self.logger.debug(line.strip("\n")) if self.status == "paused": self.pause() diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 49a58b88..1c0147ed 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -2,6 +2,9 @@ import re from datetime import datetime from time import sleep + +from flask import current_app + from Classes.Fabricators.Device import Device from Classes.Jobs import Job from Mixins.hasResponseCodes import checkTime, checkExtruderTemp, checkXYZ, checkBedTemp, checkOK @@ -120,7 +123,9 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if len(line) == 0 or line.startswith(";"): continue - + if current_app: + with current_app.app_context(): + current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) if ("M569" in line) and job.getTimeStarted() == 0: job.setTimeStarted(1) job.setTime(job.calculateEta(), 1) @@ -235,7 +240,6 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): decLine = line.decode("utf-8").strip() if "processing" in decLine or "echo" in decLine: continue if "T:" in decLine and "B:" in decLine: - self.logger.warning(f"Temperature line: {decLine}") self.handleTempLine(decLine) if func != checkBedTemp and func != checkExtruderTemp: continue From 204606a6255d08b2fd75db17f59d28f56ba2366d Mon Sep 17 00:00:00 2001 From: iron768 <iron768@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:38:55 -0500 Subject: [PATCH 150/194] feat: beginning of emulator backend connection --- printeremu/data/settings.json | 2 +- printeremu/src/emulator.go | 64 +++++---- requirements.txt | 4 +- server/Classes/FabricatorConnection.py | 158 ++++++++++++++++++++--- server/Classes/Fabricators/Device.py | 3 +- server/Classes/Fabricators/Fabricator.py | 3 - server/app.py | 72 +++++++++-- server/config/config.json | 3 +- server/models/config.py | 6 +- 9 files changed, 253 insertions(+), 62 deletions(-) diff --git a/printeremu/data/settings.json b/printeremu/data/settings.json index 64061958..eddfd86d 100644 --- a/printeremu/data/settings.json +++ b/printeremu/data/settings.json @@ -1,6 +1,6 @@ { "enabledPrinters": [], "defaultAddress": "127.0.0.1", - "defaultPort": 8000, + "defaultPort": 8001, "startup": "default" } \ No newline at end of file diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 68e49188..8c7b94ef 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -63,7 +63,7 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se if settings.DefaultAddress != "" && settings.DefaultPort != 0 { return fmt.Sprintf("%s:%d", settings.DefaultAddress, settings.DefaultPort) } - return "127.0.0.1:8000" // default address + return "127.0.0.1:8001" // default address }() serverURL := "ws://" + loadedAddress @@ -78,8 +78,6 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se fmt.Println("Connected to WebSocket server") - RegisterPrinter(conn, printer) - pingTicker := time.NewTicker(5 * time.Second) defer pingTicker.Stop() @@ -148,37 +146,49 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se log.Println("Error:", data) } - case "gcode": + case "send_gcode": if data != nil { - gcodeCommand, ok := data.(string) - + payload, ok := data.(map[string]interface{}) if ok { - response := CommandHandler(gcodeCommand, printer) - fmt.Println("G-code executed:", response) - - gcodeResponse := map[string]interface{}{ - "event": "gcode_response", - "data": fmt.Sprintf("Response: %s", response), - } - - responseMessage, err := json.Marshal(gcodeResponse) - - if err != nil { - log.Println("Failed to marshal gcode_response:", err) - continue - } - - if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { - log.Println("Error sending gcode_response:", err) - continue + printerID, pidOk := payload["printerid"].(string) + gcode, gcodeOk := payload["gcode"].(string) + + if pidOk && gcodeOk { + response := CommandHandler(gcode, printer) + + fmt.Println("Gcode executed:", response) + + printerResponse := map[string]interface{}{ + "printerid": printerID, + "response": response, + } + + responseMessage, err := json.Marshal(printerResponse) + + if err != nil { + log.Println("Failed to marshal printer_response:", err) + continue + } + + if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { + log.Println("Error sending printer_response:", err) + continue + } + } else { + log.Println("Invalid send_gcode payload") } } else { - log.Println("Received G-code command, but data is not a string:", data) + log.Println("Received G-code command, but data is not a map") } } else { log.Println("Received G-code command, but no data") } + case "printer_connect": + RegisterPrinter(conn, printer) + case "printer_disconnect": + log.Println("Received printer disconnect. Closing connection...") + conn.Close() default: log.Println("Received from server:", string(message)) } @@ -188,7 +198,7 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se func RegisterPrinter(conn *websocket.Conn, printer *Printer) { message := map[string]interface{}{ - "event": "emuprintconnect", + "event": "printer_connect", "data": printer, } @@ -227,4 +237,4 @@ func RunCommand(extruder *Extruder, printer *Printer) { if err := scanner.Err(); err != nil { log.Println("Error reading input:", err) } -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index cb6c6dba..25eff763 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,6 @@ SQLAlchemy~=2.0.34 tzlocal~=2.1 Werkzeug~=3.0.3 eventlet~=0.37.0 -gunicorn~=23.0.0 \ No newline at end of file +gunicorn~=23.0.0 +websockets~=14.1 +uuid~=1.30 \ No newline at end of file diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 20d3be83..8dddc777 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -1,7 +1,11 @@ +import asyncio +import threading from abc import ABC, abstractmethod - +import uuid import serial from flask_socketio import SocketIO +from queue import Queue, Empty +import random class FabricatorConnection(ABC): @@ -51,31 +55,151 @@ def readline(self): def is_open(self): return self.serial.is_open -class SocketConnection(FabricatorConnection): - def __init__(self, socketio: SocketIO, fabricator_id: str): - self.is_open = False - self.socketio = socketio - self.fabricator_id = fabricator_id - self.response = None +class SocketConnection: + def __init__(self, websocket_connections, fabricator_id: str): + """ + Initialize a websocket-based connection for a 3D printer with optional mock response generation. + + :param websocket_connections: Dictionary of websocket connections + :param fabricator_id: Unique identifier for the printer + :param timeout: Maximum time to wait for a response (default: 10.0) + """ + self._fabricator_id = fabricator_id + self._timeout = 10.0 + + # Queue for storing incoming messages + self._receive_queue = Queue() + + # Connection state + self._is_open = True + + # Event for synchronizing responses + self._response_event = threading.Event() + + # WebSocket connection management + self._websocket_connections = websocket_connections + self._websocket = None + + # Setup connection listeners + self._setup_listeners() + + def _setup_listeners(self): + """Configure websocket event listeners for receiving printer responses.""" + pass # Listeners will be managed when sending/receiving messages through websockets. def write(self, data): - self.socketio.emit("send_gcode", {"printerid": self.fabricator_id, "gcode": data}) + """ + Send data to the printer via websocket, with optional mock response. + + :param data: Data to be sent (typically G-code) + """ + if not self._is_open: + raise ConnectionError("WebSocket connection is not open") + + # Clear any previous responses + self._response_event.clear() + while not self._receive_queue.empty(): + try: + self._receive_queue.get_nowait() + except Empty: + break + + # Convert data to string if it's bytes + gcode = data.decode('utf-8') if isinstance(data, bytes) else data + + # Emit the G-code command + self._send_message("send_gcode", { + "printerid": self._fabricator_id, + "gcode": gcode + }) def read(self): - return self.response or "" - + """ + Read response from the printer. + + :return: Response from the printer + """ + if not self._is_open: + raise ConnectionError("WebSocket connection is not open") + + # Wait for response with timeout + if not self._response_event.wait(timeout=self._timeout): + raise TimeoutError(f"No response received within {self._timeout} seconds") + + try: + return self._receive_queue.get(block=False) + except Empty: + return "" + def close(self): - # TODO: make this!! - pass + """ + Close the websocket connection. + """ + if self._is_open: + # Emit a disconnect event if needed + self._send_message("printer_disconnect", {"printerid": self._fabricator_id}) + self._is_open = False + if self._websocket: + asyncio.run(self._websocket.close()) # Ensure closing the websocket. + + def open(self): + """ + Open the websocket connection. + """ + # Generate unique WebSocket connection ID + websocket_id = str(uuid.uuid4()) + + # Find the appropriate websocket based on fabricator_id + self._websocket = self._websocket_connections.get(self._fabricator_id) + + if self._websocket is None: + raise ConnectionError(f"WebSocket not found for printer {self._fabricator_id}") + + # Emit a connection/handshake event via WebSocket + self._send_message("printer_connect", {"printerid": self._fabricator_id}) + + # Wait for connection confirmation + connection_confirmed = self._response_event.wait(timeout=self._timeout) + if connection_confirmed: + self._is_open = True + else: + raise ConnectionError(f"Could not establish websocket connection for printer {self._fabricator_id}") def reset_input_buffer(self): - # TODO: make this!! - pass + """ + Clear the input buffer. + """ + # Clear the receive queue + while not self._receive_queue.empty(): + try: + self._receive_queue.get_nowait() + except Empty: + break + + # Clear any pending response events + self._response_event.clear() def readline(self): - # TODO: make this!! - return "" + """ + Read a line of response from the printer. + + :return: A single line of response + """ + return self.read() @property def is_open(self): - return self.is_open + """ + Check if the connection is open. + + :return: Connection status + """ + return self._is_open + + def _send_message(self, event, data): + """Helper to send messages via the WebSocket connection.""" + if self._websocket and self._is_open: + message = {"event": event, "data": data} + asyncio.run(self._websocket.send(str(message))) + else: + print(f"Cannot send message: WebSocket connection is not open or not available.") \ No newline at end of file diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 731b6e0a..46a8aeaa 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -13,6 +13,7 @@ import serial.tools.list_ports from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkXYZ +from Classes import FabricatorConnection class Device(ABC): @@ -22,7 +23,7 @@ class Device(ABC): PRODUCTID: int | None = None DESCRIPTION: str | None = None MAXFEEDRATE: int | None = None - serialConnection: serial.Serial | None = None + serialConnection: FabricatorConnection.FabricatorConnection | None = None homePosition: Vector3 | None = None homeCMD: bytes | None= b"G28\n" diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index dbbd2a1d..dd868cd8 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,6 +1,3 @@ -import io -from sqlite3 import Blob - import serial from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo diff --git a/server/app.py b/server/app.py index 4b980ce2..3006ba74 100644 --- a/server/app.py +++ b/server/app.py @@ -1,3 +1,6 @@ +import asyncio +import threading +import uuid from flask import Flask, request, Response, send_from_directory from flask_cors import CORS import os @@ -6,10 +9,47 @@ from dotenv import load_dotenv import shutil from flask_socketio import SocketIO +import websockets from models.config import Config from Classes.FabricatorList import FabricatorList from routes import defineRoutes +emulator_connections = {} + +async def websocket_server(): + async def handle_client(websocket): + client_id = str(uuid.uuid4()) + + print(f"New WebSocket client connected: {client_id}") + + emulator_connections[client_id] = websocket + + try: + while True: + message = await websocket.recv() + print(f"Received message from {client_id}: {message}") + await websocket.send(f"Echo from {client_id}: {message}") + except websockets.exceptions.ConnectionClosed as e: + # Handle disconnection gracefully + print(f"Client {client_id} has been disconnected.") + except Exception as e: + # Handle any other exception (unexpected disconnection, etc.) + print(f"Error with client {client_id}: {e}") + finally: + if client_id in emulator_connections: + del emulator_connections[client_id] + + server = await websockets.serve(handle_client, "localhost", 8001) + await server.wait_closed() + +def start_websocket(): + print("Starting WebSocket server...") + asyncio.run(websocket_server()) + +websocket_thread = threading.Thread(target=start_websocket) +websocket_thread.daemon = True # Make it a daemon thread to exit with the main program +websocket_thread.start() + # Global variables root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) uploads_folder = os.path.abspath(os.path.join(root_path, 'uploads')) @@ -54,6 +94,16 @@ socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode, transport=['websocket', 'polling']) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object +async def emulator_ws_handler(websocket, path): + print(f"Emulator connected: {path}") + try: + async for message in websocket: + # Handle messages from the emulator + print(f"Received message from emulator: {message}") + # Add your handling logic here, e.g., update the printer status + except websockets.exceptions.ConnectionClosed as e: + print(f"Emulator connection closed: {e}") + def handle_errors_and_logging(e: Exception | str, fabricator = None): from Classes.Fabricators.Fabricator import Fabricator device = fabricator @@ -71,6 +121,16 @@ def handle_errors_and_logging(e: Exception | str, fabricator = None): app.logger.error(e, stacklevel=3) return False +async def start_emulator_ws(): + print("Starting emulator websocket server...") + try: + # Start the WebSocket server + server = await websockets.serve(emulator_ws_handler, 'localhost', 8001) + print("WebSocket server started on ws://localhost:8001") + await server.wait_closed() # Keeps the server running + except Exception as e: + print(f"Error in WebSocket server: {e}") + app.handle_errors_and_logging = handle_errors_and_logging CORS(app) @@ -110,13 +170,6 @@ def handle_ping(): @app.socketio.on('connect') def handle_connect(): print("Client connected") - -@app.socketio.on('emuprintconnect') -def handle_emuprintconnect(data): - printer_data = json.loads(data) - - print("Received emuprintconnect event with data:", printer_data) - printer_status_service.add_printer(data) # own thread with app.app_context(): @@ -146,10 +199,11 @@ def run_socketio(app): # host=app.config["ip"], port=app.config["port"] socketio.run(app, Debug=True, allow_unsafe_werkzeug=True) -if __name__ == "__main__": +#if __name__ == "__main__": # If hits last line in GCode file: # query for status ("done printing"), update. Use frontend to update status to "ready" once user removes print from plate. # Before sending to printer, query for status. If error, throw error. # since we are using socketio, we need to use socketio.run instead of app.run # which passes the app anyways - run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file + + run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file diff --git a/server/config/config.json b/server/config/config.json index 24db0c10..da248397 100644 --- a/server/config/config.json +++ b/server/config/config.json @@ -1,4 +1,5 @@ { "environment": "development", - "databaseURI": "hvamc" + "databaseURI": "hvamc", + "emulator_port": 8001 } \ No newline at end of file diff --git a/server/models/config.py b/server/models/config.py index 960ae85a..9c4283f9 100644 --- a/server/models/config.py +++ b/server/models/config.py @@ -15,12 +15,14 @@ def load_config(file_path): environment = config.get('environment', 'development') ip = config.get('ip', '127.0.0.1') database_uri = config.get('databaseURI', 'hvamc') + ".db" -port = os.environ.get('FLASK_RUN_PORT', 8000) +port = os.environ.get('FLASK_RUN_PORT', 8000), +emulator_port = os.environ.get('EMULATOR_PORT', 8001) Config = { 'base_url': base_url(), 'environment': environment, 'ip': ip, 'database_uri': database_uri, - 'port': port + 'port': port, + 'emulator_port': emulator_port } \ No newline at end of file From c4ae83c22c911f7516870153292bf3c48c8c1ae0 Mon Sep 17 00:00:00 2001 From: iron768 <iron768@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:16:42 -0500 Subject: [PATCH 151/194] feat: mess around with emulator from front end --- client/src/components/NavBar.vue | 3 +++ client/src/router/index.ts | 9 +++++++-- client/src/views/EmulatorView.vue | 25 +++++++++++++++++++++++++ server/controllers/emulator.py | 27 +++++++++++++++++++++++++++ server/routes.py | 4 +++- 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 client/src/views/EmulatorView.vue create mode 100644 server/controllers/emulator.py diff --git a/client/src/components/NavBar.vue b/client/src/components/NavBar.vue index 5df09b47..8bfb2a1a 100644 --- a/client/src/components/NavBar.vue +++ b/client/src/components/NavBar.vue @@ -120,6 +120,9 @@ watch([API_IP_ADDRESS, API_PORT], async () => { <li class="nav-item"> <router-link to="/error" class="nav-link" active-class="active-tab">ERROR LOG</router-link> </li> + <li class="nav-item"> + <router-link to="/emulator" class="nav-link" active-class="active-tab">EMULATOR</router-link> + </li> </ul> </div> </nav> diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 55426e88..59485177 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -5,7 +5,7 @@ import RegisteredViewVue from '@/views/RegisteredView.vue' import SubmitJobVue from '@/views/SubmitJob.vue' import JobHistoryVue from '@/views/JobHistory.vue' import ErrorView from '@/views/ErrorView.vue' -import { isLoading } from '@/model/jobs' +import EmulatorView from '@/views/EmulatorView.vue' const routes = [ { @@ -37,7 +37,12 @@ const routes = [ path: '/error', name: 'ErrorView', component: ErrorView - }, + }, + { + path: '/emulator', + name: 'EmulatorView', + component: EmulatorView + } ] const router = createRouter({ diff --git a/client/src/views/EmulatorView.vue b/client/src/views/EmulatorView.vue new file mode 100644 index 00000000..3fbe6d71 --- /dev/null +++ b/client/src/views/EmulatorView.vue @@ -0,0 +1,25 @@ +<script setup lang="ts"> +import { api } from '@/model/ports'; +import { ref } from 'vue'; + +const message = ref('') + +const registerPrinter = async () => { + + message.value = await api('registeremulator', { 'data': 'wow' }); + +} + +</script> + +<template> + <div> + <button @click="registerPrinter">Register Emulator</button> + + <p v-if="message">Response:{{ message }}</p> + </div> +</template> + +<style scoped> + +</style> \ No newline at end of file diff --git a/server/controllers/emulator.py b/server/controllers/emulator.py new file mode 100644 index 00000000..4aa6967e --- /dev/null +++ b/server/controllers/emulator.py @@ -0,0 +1,27 @@ +import asyncio +from flask import Blueprint, jsonify, request + +emulator_bp = Blueprint("emulator", __name__) + +@emulator_bp.route('/registeremulator', methods=["POST"]) +def registerEmulator(): + try: + data = request.get_json() + + from app import emulator_connections + + print(emulator_connections) + + socket = next(iter(emulator_connections.values())) + + try: + asyncio.run(socket.send("printer_connect")) + + return jsonify({"message": "Emulator registered successfully"}), 200 + except Exception as e: + print(f"Error registering emulator: {e}") + return jsonify({"error": "Failed to register emulator"}), 500 + + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 \ No newline at end of file diff --git a/server/routes.py b/server/routes.py index 6dc69770..b65ec563 100644 --- a/server/routes.py +++ b/server/routes.py @@ -5,9 +5,11 @@ def defineRoutes(app: Flask): from controllers.jobs import jobs_bp from controllers.statusService import status_bp from controllers.issues import issue_bp + from controllers.emulator import emulator_bp # # Register the display_bp Blueprint app.register_blueprint(ports_bp) app.register_blueprint(jobs_bp) app.register_blueprint(status_bp) - app.register_blueprint(issue_bp) \ No newline at end of file + app.register_blueprint(issue_bp) + app.register_blueprint(emulator_bp) \ No newline at end of file From bc5b2f22e6adc9ceb36fdb9d35c0867ed56b0352 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:48:55 -0500 Subject: [PATCH 152/194] feat: enable renderTravel/also attempt to interrupt --- client/src/components/GCode3DImageViewer.vue | 192 +++++++++++-------- 1 file changed, 117 insertions(+), 75 deletions(-) diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index 84d241f2..0d59affe 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -1,109 +1,151 @@ <script setup lang="ts"> -import { nextTick, onMounted, onActivated, onDeactivated, ref, toRef, onUnmounted } from 'vue'; +import { ref, onMounted, onUnmounted } from 'vue'; import { useGetFile, type Job } from '@/model/jobs'; import * as GCodePreview from 'gcode-preview'; const { getFile } = useGetFile(); const props = defineProps({ - job: Object as () => Job, - file: Object as () => File -}) + job: Object as () => Job, + file: Object as () => File +}); const file = () => { - if (props.file) { - return props.file - } else if (props.job) { - return getFile(props.job) - } else { - return null - } -} + if (props.file) { + return props.file; + } else if (props.job) { + return getFile(props.job); + } else { + return null; + } +}; const modal = document.getElementById('gcodeImageModal'); - -// Create a ref for the canvas const canvas = ref<HTMLCanvasElement | null>(null); let preview: GCodePreview.WebGLPreview | null = null; -onMounted(async () => { - if (!modal) { - console.error('Modal element is not available'); - return; +const renderTravel = ref(true); // Ref to control renderTravel dynamically + +const processGCodeCommand = (gcode: string) => { + try { + // Dynamically process G-code and determine if renderTravel should be enabled + if (gcode.includes('G0') || gcode.includes('G1')) { + renderTravel.value = true; // Enable renderTravel for movement commands + } else { + renderTravel.value = false; // Disable for other commands } + if (preview) { + preview.renderTravel = renderTravel.value; + } + } catch (error) { + console.error('Error processing GCode command. Reverting to default renderTravel:', error); - if (canvas.value) { - preview = GCodePreview.init({ - canvas: canvas.value, - extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--bs-primary-color').trim() || '#7561A9', - backgroundColor: 'black', - buildVolume: { x: 250, y: 210, z: 220 }, - lineWidth: 1, - lineHeight: 1, - extrusionWidth: 1, - renderExtrusion: true, - renderTubes: true, - }); - - preview.camera.position.set(-200, 232, 200); - preview.camera.lookAt(0, 0, 0); - - if (canvas.value) { - // job.file to string - const fileValue = await file(); - if (fileValue) { - const gcode = await fileToString(fileValue); - - try { - preview?.processGCode(gcode); // MAIN LINE - } catch (error) { - console.error('Failed to process GCode:', error); - } - } - } + // Ensure proper fallback + renderTravel.value = true; + if (preview) { + preview.renderTravel = true; } + } +}; - modal.addEventListener('hidden.bs.modal', () => { - // Clean up when the modal is hidden - preview?.processGCode(''); - preview?.clear(); - preview = null; - }); +onMounted(async () => { + if (!modal) { + console.error('Modal element is not available'); + return; + } + + if (canvas.value) { + try { + preview = GCodePreview.init({ + canvas: canvas.value, + extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--bs-primary-color').trim() || '#7561A9', + backgroundColor: 'black', + buildVolume: { x: 250, y: 210, z: 220 }, + travelColor: 'limegreen', + renderExtrusion: true, + renderTravel: renderTravel.value, + }); + + preview.camera.position.set(-200, 232, 200); + preview.camera.lookAt(0, 0, 0); + + const fileValue = await file(); + if (fileValue) { + const gcode = await fileToString(fileValue); + + try { + // Process and render G-code + gcode.split('\n').forEach((command) => { + processGCodeCommand(command); // Update renderTravel dynamically + preview?.processGCode(command); + }); + } catch (error) { + console.error('Failed to process GCode:', error); + + // Reset to original behavior on failure + renderTravel.value = true; + if (preview) { + preview.renderTravel = true; + preview.processGCode(gcode); // Fallback to processing entire file + } + } + } + } catch (error) { + console.error('Error initializing GCodePreview:', error); + + // Fallback logic if initialization fails + renderTravel.value = true; + if (preview) { + preview.renderTravel = true; + } + } + } + + modal.addEventListener('hidden.bs.modal', () => { + // Clean up when the modal is hidden + if (preview) { + preview.processGCode(''); + preview.clear(); + preview = null; + } + }); }); onUnmounted(() => { - preview?.processGCode(''); - preview?.clear(); + if (preview) { + preview.processGCode(''); + preview.clear(); preview = null; + } }); const fileToString = (file: File | undefined) => { - if (!file) { - console.error('File is not available'); - return ''; - } - - const reader = new FileReader(); - reader.readAsText(file); - return new Promise<string>((resolve, reject) => { - reader.onload = () => { - resolve(reader.result as string); - }; - reader.onerror = (error) => { - reject(error); - }; - }); + if (!file) { + console.error('File is not available'); + return ''; + } + + const reader = new FileReader(); + reader.readAsText(file); + return new Promise<string>((resolve, reject) => { + reader.onload = () => { + resolve(reader.result as string); + }; + reader.onerror = (error) => { + reject(error); + }; + }); }; </script> <template> - <canvas ref="canvas"></canvas> + <canvas ref="canvas"></canvas> </template> <style scoped> canvas { - width: 100%; - height: 100%; - display: block; + width: 100%; + height: 100%; + display: block; } -</style> \ No newline at end of file +</style> From 2d82a0ded8b9957794e31bf50361b0f9174f89db Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 1 Dec 2024 22:11:24 -0500 Subject: [PATCH 153/194] feat: PRINTING IS WORKING!!!! --- client/src/model/jobs.ts | 3 +- client/src/model/sockets.ts | 36 ++++++------ client/src/views/MainView.vue | 22 ++++---- client/src/views/QueueView.vue | 4 +- client/src/views/SubmitJob.vue | 4 +- server/Classes/FabricatorList.py | 14 +++-- server/Classes/Fabricators/Fabricator.py | 18 ++++-- server/Classes/Jobs.py | 1 - server/Classes/Queue.py | 10 +--- server/app.py | 6 +- server/controllers/jobs.py | 71 +++++++++++++++--------- server/models/FabricatorStatusService.py | 2 +- 12 files changed, 105 insertions(+), 86 deletions(-) diff --git a/client/src/model/jobs.ts b/client/src/model/jobs.ts index 8fdcf33c..3e14d40e 100644 --- a/client/src/model/jobs.ts +++ b/client/src/model/jobs.ts @@ -553,8 +553,7 @@ export function useStartJob() { return { async start(jobid: number, printerid: number) { try { - const response = await api(`startprint`, { jobid, printerid }) - return response + return await api(`startprint`, { jobid, printerid }) } catch (error) { console.error(error) toast.error('An error occurred while starting the job') diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 6a9aa4a0..9150fc6d 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -47,6 +47,23 @@ export function setupStatusSocket(printers: any) { }) } +export function setupJobStatusSocket(printers: any) { + // Always set up the socket connection and event listener + socket.value.on('job_status_update', (data: any) => { + if (printers) { + const job = printers.value + .flatMap((printer: { queue: any }) => printer.queue) + .find((job: { id: any }) => job?.id === data.job_id) + + if (job) { + job.status = data.status + } + } else { + console.error('printers or printers.value is undefined') + } + }) +} + export function setupQueueSocket(printers: any) { socket.value.on('queue_update', (data: any) => { if (printers) { @@ -64,7 +81,6 @@ export function setupErrorSocket(printers: any) { socket.value.on('error_update', (data: any) => { if (printers) { const printer = printers.value.find((p: Device) => p.id === data.printerid) - console.log(printer) if (printer) { printer.error = data.error } @@ -85,7 +101,6 @@ export function setupCanPauseSocket(printers: any) { console.error('printers or printers.value is undefined') } }) - console.log('queue socket set up') } // *** JOBS *** @@ -162,23 +177,6 @@ export function setupReleaseSocket(printers: any) { }) } -export function setupJobStatusSocket(printers: any) { - // Always set up the socket connection and event listener - socket.value.on('job_status_update', (data: any) => { - if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) - - if (job) { - job.status = data.status - } - } else { - console.error('printers or printers.value is undefined') - } - }) -} - export function setupPortRepairSocket(printers: any) { // Always set up the socket connection and event listener socket.value.on('port_repair', (data: any) => { diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index 56e2f190..c5936a6c 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -166,10 +166,12 @@ const releasePrinter = async (jobToFind: Job | undefined, key: number, printerId let printer = printers.value.find((printer) => printer.id === printerIdToPrintTo) printer!.error = "" - if (printer) { - printer.extruder_temp = 0 - printer.bed_temp = 0; - } + if (printer) { + printer.extruder_temp = 0 + printer.bed_temp = 0; + printer.queue?.shift(); // Remove the first job in the queue + //TODO: marker + } await releaseJob(jobToFind, key, printerIdToPrintTo) await nextTick() @@ -201,7 +203,7 @@ const handleDragEnd = async () => { <label for="issue" class="form-label">Select Issue</label> <select name="issue" id="issue" v-model="selectedIssue" class="form-select" required> <option disabled value="undefined">Select Issue</option> - <option v-for="issue in issuelist" :value="issue"> + <option v-for="issue in issuelist" :value="issue" :key="issue.id"> {{ issue.issue }} </option> </select> @@ -290,7 +292,7 @@ const handleDragEnd = async () => { <div v-if="printer.isInfoExpanded" class="expanded-info"> <tr :id="printer.id" style="vertical-align: middle"> <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status == 'printing' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.td_id }} </td> <td v-else><i>idle</i></td> @@ -309,7 +311,7 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="(printer.status === 'idle' || printer.status === 'ready') && printer.queue?.[0]?.released === 0" style="color: #ad6060" + <p v-if="printer.status === 'ready' && printer.queue?.[0]?.released === 0" style="color: #ad6060" class="mb-0 me-2"> Waiting release </p> @@ -540,11 +542,11 @@ const handleDragEnd = async () => { </td> <td class="borderless-top"> <span - v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> </td> <td class="borderless-top"> <span v-if="printer.queue[0]?.job_client?.remaining_time !== 0" - v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> <span v-else v-html="'00:00:00'"></span> </td> <td class="borderless-top"> @@ -559,7 +561,7 @@ const handleDragEnd = async () => { </div> <tr v-else :id="printer.id"> <td - v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.td_id }} </td> <td v-else><i>idle</i></td> diff --git a/client/src/views/QueueView.vue b/client/src/views/QueueView.vue index a59a568e..f517c37e 100644 --- a/client/src/views/QueueView.vue +++ b/client/src/views/QueueView.vue @@ -299,11 +299,11 @@ const openModal = async (job: Job, printerName: string, num: number, printer: De <td class="truncate" :title="job.file_name_original">{{ job.file_name_original }}</td> <td class="truncate" :title="job.date">{{ job.date }}</td> <td class="truncate" :title="job.status" - v-if="printer.queue && printer.status == 'printing' && printer.queue?.[0].released == 0 && job.status == 'printing'"> + v-if="printer.status == 'ready' && printer.queue?.[0].released == 0 && job.status == 'ready'"> pending release</td> <td v-else>{{ job.status }}</td> - <td style="width:"> + <td style=""> <div class="dropdown"> <div style=" display: flex; diff --git a/client/src/views/SubmitJob.vue b/client/src/views/SubmitJob.vue index f727d366..0f57a717 100644 --- a/client/src/views/SubmitJob.vue +++ b/client/src/views/SubmitJob.vue @@ -1,6 +1,6 @@ <script setup lang="ts"> import { printers } from '../model/ports' -import { selectedPrinters, file, fileName, quantity, priority, favorite, name, tdid, filament, useAddJobToQueue, useGetFile, useAutoQueue, isLoading } from '../model/jobs' +import { selectedPrinters, file, fileName, quantity, priority, favorite, name, tdid, filament, useAddJobToQueue, useGetFile, useAutoQueue, isLoading } from '@/model/jobs' import { ref, onMounted, watchEffect, computed, watch } from 'vue' import { useRoute } from 'vue-router'; import { toast } from '@/model/toast'; @@ -341,7 +341,7 @@ const getFilament = (file: File) => { <label class="form-label" v-else-if="selectedPrinters.length === 1">Selected printer:</label> <label class="form-label" v-else>Selected printers:</label> <ul class="list-group" style="max-height: 200px; overflow-y: auto;"> - <li v-for="printer in selectedPrinters" class="list-group-item"> + <li v-for="printer in selectedPrinters" class="list-group-item" :key="printer.id"> <b>{{ printer.name }}</b> status: {{ printer.status }} </li> </ul> diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index d3140773..10d71efc 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -6,6 +6,7 @@ from Classes.Fabricators.Device import Device from Classes.Ports import Ports from Classes.Fabricators.Fabricator import Fabricator +from Classes.Jobs import Job from Classes.Queue import Queue from threading import Thread import time @@ -13,7 +14,7 @@ class FabricatorThread(Thread): def __init__(self, fabricator, app=None, *args, **kwargs): super().__init__(*args, **kwargs) - self.fabricator = fabricator + self.fabricator: Fabricator = fabricator if app: self.app = app else: @@ -33,9 +34,12 @@ def run(self): while True: time.sleep(2) status = self.fabricator.getStatus() - queueSize = self.fabricator.getQueue().getSize() + queueSize = len(self.fabricator.queue) self.fabricator.responseCount = 0 - if status == "ready" and queueSize > 0: + if status == "printing": + if queueSize > 0: + assert isinstance(self.fabricator.queue[0], Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" + if status == "printing" and queueSize > 0 and self.fabricator.queue[0].released == 1: time.sleep(2) if status != "offline": self.fabricator.begin() @@ -211,7 +215,7 @@ def update_thread(self, fabricator): while True: time.sleep(2) status = fabricator.getStatus() - queueSize = fabricator.getQueue().getSize() + queueSize = len(fabricator) fabricator.responseCount = 0 if status == "ready" and queueSize > 0: time.sleep(2) @@ -253,7 +257,7 @@ def queueRestore(self, fabricator_id, status): "name": fabricator.name, } self.fabricator_threads.remove(thread) - self.queue_restore(status, fabricator.getQueue()) + self.queue_restore(status, fabricator) break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index dd868cd8..1d982bbb 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -56,7 +56,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" def __to_JSON__(self): """ @@ -172,7 +172,7 @@ def begin(self, isVerbose: bool = False): :rtype: bool """ try: - assert self.status == "ready" or "printing", f"Fabricator is not ready or printing, status: {self.status}" + assert self.status == "printing", f"Fabricator is not printing, status: {self.status}" assert self.queue is not None, "Queue is None" assert len(self.queue) > 0, "Queue is empty" self.job = self.queue.getNext() @@ -243,14 +243,22 @@ def setStatus(self, newStatus): self.device.hardReset(newStatus) self.status = newStatus self.device.status = newStatus - if self.job is not None: - self.job.status = newStatus - + if self.job is None and len(self.queue) > 0: + self.job = self.queue[0] + if len(self.queue) > 0: + assert self.job == self.queue[0], "Job is not the first in the queue" + if self.job is not None: + self.job.status = newStatus + self.queue[0].status = newStatus + db.session.commit() from flask import current_app if current_app: current_app.socketio.emit( "status_update", {"fabricator_id": self.dbID, "status": newStatus} ) + if self.job is not None: + current_app.socketio.emit('job_status_update', { + 'job_id': self.job.id, 'status': newStatus}) else: print(f"current app is None, status: {newStatus}") return True diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index 1eab95f7..1ce8efc8 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -648,7 +648,6 @@ def setMaxLayerHeight(self, max_layer_height): current_app.socketio.emit('max_layer_height', {'job_id': self.id, 'max_layer_height': self.max_layer_height}) def setCurrentLayerHeight(self, current_layer_height): - print("Current Layer Height: ", current_layer_height) self.current_layer_height = current_layer_height if current_app: current_app.socketio.emit('current_layer_height', {'job_id': self.id, 'current_layer_height': self.current_layer_height}) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 0473b784..3bf76d8b 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -10,8 +10,6 @@ def setToInQueue(self): def addToBack(self, job: Job, printerid): assert isinstance(job, Job) - print("Adding job to back of queue ", job.id) - print("Adding job to back of queue ", printerid) if self.count(job) > 0: return False @@ -160,14 +158,8 @@ def jobExists(self, jobid): return True return False - def getQueue(self): - return self - def getNext(self): - return self[0] - - def getSize(self): - return len(self) + return self[0] if len(self) > 0 else None def removeJob(self): self.pop() diff --git a/server/app.py b/server/app.py index 3006ba74..9b92eb00 100644 --- a/server/app.py +++ b/server/app.py @@ -184,7 +184,6 @@ def handle_connect(): for folder in [uploads_folder, tempcsv]: if os.path.exists(folder): # Remove the folder and all its contents - import shutil shutil.rmtree(folder) app.logger.info(f"{folder} removed and will be recreated.") # Recreate the folder @@ -199,11 +198,10 @@ def run_socketio(app): # host=app.config["ip"], port=app.config["port"] socketio.run(app, Debug=True, allow_unsafe_werkzeug=True) -#if __name__ == "__main__": +if __name__ == "__main__": # If hits last line in GCode file: # query for status ("done printing"), update. Use frontend to update status to "ready" once user removes print from plate. # Before sending to printer, query for status. If error, throw error. # since we are using socketio, we need to use socketio.run instead of app.run # which passes the app anyways - - run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file + run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index ffe699d1..b254cf89 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -10,6 +10,7 @@ from app import handle_errors_and_logging from flask import current_app from traceback import format_exc +from Classes.Fabricators.Fabricator import Fabricator # get data for jobs jobs_bp = Blueprint("jobs", __name__) @@ -66,7 +67,7 @@ def add_job_to_queue(): favoriteOne = False # for i in range(int(quantity)): - if(favorite == 'true' and not favoriteOne): + if favorite == 'true' and not favoriteOne: favorite = 1 favoriteOne = True else: @@ -142,7 +143,6 @@ def auto_queue(): job.setFilament(filament) # set filament type findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) - return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: @@ -253,36 +253,44 @@ def remove_job_from_queue(): @jobs_bp.route('/releasejob', methods=["POST"]) def releasejob(): + if request.method != "POST": + return try: data = request.get_json() jobpk = data['jobpk'] key = data['key'] job = Job.findJob(jobpk) printerid = job.getPrinterId() - printerobject = findPrinterObject(printerid) - printerobject.error = "" - queue = printerobject.getQueue() - - queue.deleteJob(jobpk, printerid) # remove job from queue + fabricator = findPrinterObject(printerid) + if fabricator is None: + return jsonify({"error": "Printer not found."}), 404 + print(fabricator) + fabricator.error = "" + if len(fabricator.queue) > 0: + assert len(fabricator.queue) > 0, "Queue is empty" + assert fabricator.queue[0].getJobId() == jobpk, "Job not at front of queue" + fabricator.queue.removeJob() + if fabricator.job is not None: + fabricator.job = None printerid = data['printerid'] - currentStatus = printerobject.getStatus() + currentStatus = fabricator.getStatus() if key == 3: Job.update_job_status(jobpk, "error") - printerobject.setError(job.comments) - printerobject.setStatus("error") # printer ready to accept new prints + fabricator.setError(job.comments) + fabricator.setStatus("error") # printer ready to accept new prints elif key == 2: rerunjob(printerid, jobpk, "front") if currentStatus!="offline": - printerobject.setStatus("ready") # printer ready to accept new prints + fabricator.setStatus("ready") # printer ready to accept new prints elif key == 1: if currentStatus!="offline": - printerobject.setStatus("ready") # printer ready to accept new prints + fabricator.setStatus("ready") # printer ready to accept new prints return jsonify({"success": True, "message": "Job released successfully."}), 200 @@ -344,7 +352,7 @@ def updateJobStatus(): printerobject = findPrinterObject(printerid) queue = printerobject.getQueue() - # queue.deleteJob(job_id, printerid) + queue.deleteJob(job_id, printerid) return jsonify(res), 200 except Exception as e: @@ -405,15 +413,13 @@ def setStatus(): try: data = request.get_json() # get json data printer_id = data['printerid'] - newstatus = data['status'] - from Classes.Fabricators.Fabricator import Fabricator - printerobject: Fabricator | None = findPrinterObject(printer_id) - if printerobject is None: + newStatus = data['status'] + fabricator: Fabricator | None = findPrinterObject(printer_id) + if fabricator is not None: + fabricator.setStatus(newStatus) + return jsonify({"success": True, "message": "Status updated successfully."}), 200 + else: return jsonify({"error": "Printer not found."}), 404 - printerobject.setStatus(newstatus) - print(printerobject.status) - return jsonify({"success": True, "message": "Status updated successfully."}), 200 - except Exception as e: handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @@ -508,9 +514,13 @@ def startPrint(): jobid = data['jobid'] printerobject = findPrinterObject(printerid) queue = printerobject.getQueue() - inmemjob = queue.getJobById(jobid) - print(inmemjob) - inmemjob.setReleased(1) + assert queue is not None, "Queue not found." + printerobject.job = queue.getJobById(jobid) + assert printerobject.job is not None, "Job not found." + assert printerobject.job.getStatus() == "ready", f"Job not ready to print. Status: {printerobject.job.getStatus()}" + assert printerobject.job == queue[0], "Job not at front of queue." + print(printerobject.job) + printerobject.job.setReleased(1) printerobject.setStatus("printing") @@ -602,6 +612,8 @@ def refetch_time(): printer = findPrinterObject(printerid) job = printer.getQueue().getNext() + if job is None: + return jsonify({"error": "No job found"}), 404 timearray = job.job_time @@ -612,12 +624,19 @@ def refetch_time(): 'pause': timearray[3].isoformat() } - return jsonify(timejson) + return jsonify(timejson), 200 except Exception as e: handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 + def findPrinterObject(printer_id): + """ + Find the printer object by its ID. + :param printer_id: The ID of the printer. + :type printer_id: int + :rtype: Fabricator | None + """ from app import fabricator_list threads = fabricator_list.getThreadArray() return list(filter(lambda thread: thread.fabricator.dbID == printer_id, threads))[0].fabricator @@ -625,7 +644,7 @@ def findPrinterObject(printer_id): def getSmallestQueue(): from app import fabricator_list threads = fabricator_list.getThreadArray() - smallest_queue_thread = min(threads, key=lambda thread: thread.printer.queue.getSize()) + smallest_queue_thread = min(threads, key=lambda thread: len(thread.printer.queue)) return smallest_queue_thread.printer.id def rerunjob(printerpk, jobpk, position): diff --git a/server/models/FabricatorStatusService.py b/server/models/FabricatorStatusService.py index 8f21dd3b..efe7e8bb 100644 --- a/server/models/FabricatorStatusService.py +++ b/server/models/FabricatorStatusService.py @@ -76,7 +76,7 @@ def update_thread(self, fabricator, app): time.sleep(2) status = fabricator.getStatus() # get fabricator status - queueSize = fabricator.getQueue().getSize() # get size of queue + queueSize = len(fabricator.getQueue()) # get size of queue fabricator.responseCount = 0 if (status == "ready" and queueSize > 0): time.sleep(2) # wait for 2 seconds to allow the fabricator to process the queue From af9bf272c4b55ff07b4cd7a9f9ddbfa064aea25c Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:35:37 -0500 Subject: [PATCH 154/194] fix: live rendering works! --- client/src/components/GCode3DImageViewer.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index 0d59affe..a5457a28 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -74,11 +74,14 @@ onMounted(async () => { const gcode = await fileToString(fileValue); try { - // Process and render G-code - gcode.split('\n').forEach((command) => { - processGCodeCommand(command); // Update renderTravel dynamically - preview?.processGCode(command); - }); + // Process and render G-code with timeout + const commands = gcode.split('\n'); + for (const command of commands) { + setTimeout(() => { + processGCodeCommand(command); // Update renderTravel dynamically + preview?.processGCode(command); + }, 2000); + } } catch (error) { console.error('Failed to process GCode:', error); From a4b5ccdbbd8b3fbc53f936b92e1bf439fa65ce2a Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 2 Dec 2024 13:22:22 -0500 Subject: [PATCH 155/194] feat: finish basic web control panel for emulator --- client/src/views/EmulatorView.vue | 98 ++++++++++++++++++++++++++++--- printeremu/src/emulator.go | 15 ++--- server/app.py | 2 +- server/controllers/emulator.py | 40 ++++++++++++- 4 files changed, 137 insertions(+), 18 deletions(-) diff --git a/client/src/views/EmulatorView.vue b/client/src/views/EmulatorView.vue index 3fbe6d71..e933ca0b 100644 --- a/client/src/views/EmulatorView.vue +++ b/client/src/views/EmulatorView.vue @@ -2,24 +2,104 @@ import { api } from '@/model/ports'; import { ref } from 'vue'; -const message = ref('') +const message = ref(''); +const loading = ref(false); +const isRegistered = ref(false); const registerPrinter = async () => { + loading.value = true; + message.value = ''; - message.value = await api('registeremulator', { 'data': 'wow' }); - + try { + const response = await api('registeremulator', { 'data': 'wow' }, 'POST'); + + if (response && response.message) { + message.value = response.message; + isRegistered.value = true; + } else { + message.value = 'Unknown response structure'; + } + } catch (error) { + message.value = 'Error registering emulator'; + } finally { + loading.value = false; + } } +const disconnectPrinter = async () => { + loading.value = true; + message.value = ''; + + try { + const response = await api('disconnectemulator', { 'data': 'wow' }, 'POST'); + + if (response && response.message) { + message.value = response.message; + isRegistered.value = false; + } else { + message.value = 'Unknown response structure'; + } + } catch (error) { + message.value = 'Error disconnecting emulator'; + } finally { + loading.value = false; + } +} </script> <template> - <div> - <button @click="registerPrinter">Register Emulator</button> - - <p v-if="message">Response:{{ message }}</p> + <div class="container mt-5"> + <!-- Heading Section --> + <h1 class="text-center mb-4">Emulator Registration</h1> + + <!-- Card for the form --> + <div class="card shadow-sm"> + <div class="card-body"> + <h5 class="card-title">Emulator Control</h5> + <p class="card-text"> + Click the button below to either register or disconnect the emulator. + </p> + + <!-- Register Button (only visible if the printer is not registered) --> + <div class="d-grid gap-2"> + <button + class="btn btn-primary" + @click="registerPrinter" + :disabled="loading || isRegistered" + > + Register Emulator + </button> + </div> + + <!-- Disconnect Button (only visible if the printer is registered) --> + <div class="d-grid gap-2 mt-2"> + <button + class="btn btn-danger" + @click="disconnectPrinter" + :disabled="loading || !isRegistered" + > + Disconnect Emulator + </button> + </div> + + <!-- Loading Spinner --> + <div v-if="loading" class="text-center my-3"> + <div class="spinner-border text-primary" role="status"> + <span class="visually-hidden">Loading...</span> + </div> + </div> + + <!-- Message Section --> + <div v-if="message" class="mt-3"> + <div :class="['alert', message.startsWith('Error') ? 'alert-danger' : 'alert-success']" role="alert"> + <strong>{{ message.startsWith('Error') ? 'Oops!' : 'Success!' }}</strong> {{ message }} + </div> + </div> + </div> + </div> </div> </template> <style scoped> - -</style> \ No newline at end of file +/* You can add additional custom styling here if needed */ +</style> diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 8c7b94ef..e90fd3ba 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -120,6 +120,7 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se var parsedMessage map[string]interface{} if err := json.Unmarshal(message, &parsedMessage); err != nil { + fmt.Println("Received message:", string(message)) log.Println("Error parsing received message:", err) continue } @@ -152,24 +153,24 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se if ok { printerID, pidOk := payload["printerid"].(string) gcode, gcodeOk := payload["gcode"].(string) - + if pidOk && gcodeOk { response := CommandHandler(gcode, printer) - + fmt.Println("Gcode executed:", response) - + printerResponse := map[string]interface{}{ "printerid": printerID, "response": response, } - + responseMessage, err := json.Marshal(printerResponse) - + if err != nil { log.Println("Failed to marshal printer_response:", err) continue } - + if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { log.Println("Error sending printer_response:", err) continue @@ -237,4 +238,4 @@ func RunCommand(extruder *Extruder, printer *Printer) { if err := scanner.Err(); err != nil { log.Println("Error reading input:", err) } -} \ No newline at end of file +} diff --git a/server/app.py b/server/app.py index 9b92eb00..c0cdf8d8 100644 --- a/server/app.py +++ b/server/app.py @@ -28,7 +28,7 @@ async def handle_client(websocket): while True: message = await websocket.recv() print(f"Received message from {client_id}: {message}") - await websocket.send(f"Echo from {client_id}: {message}") + #await websocket.send(f"Echo from {client_id}: {message}") except websockets.exceptions.ConnectionClosed as e: # Handle disconnection gracefully print(f"Client {client_id} has been disconnected.") diff --git a/server/controllers/emulator.py b/server/controllers/emulator.py index 4aa6967e..3ce0752c 100644 --- a/server/controllers/emulator.py +++ b/server/controllers/emulator.py @@ -1,4 +1,5 @@ import asyncio +import json from flask import Blueprint, jsonify, request emulator_bp = Blueprint("emulator", __name__) @@ -15,13 +16,50 @@ def registerEmulator(): socket = next(iter(emulator_connections.values())) try: - asyncio.run(socket.send("printer_connect")) + message = { + 'event': 'printer_connect', + 'data': 'register_from_front' + } + + json_message = json.dumps(message) + + asyncio.run(socket.send(json_message)) return jsonify({"message": "Emulator registered successfully"}), 200 except Exception as e: print(f"Error registering emulator: {e}") return jsonify({"error": "Failed to register emulator"}), 500 + except Exception as e: + print(f"Unexpected error: {e}") + return jsonify({"error": "Unexpected error occurred"}), 500 + +@emulator_bp.route('/disconnectemulator', methods=["POST"]) +def disconnectEmulator(): + try: + data = request.get_json() + + from app import emulator_connections + + print(emulator_connections) + + socket = next(iter(emulator_connections.values())) + + try: + message = { + 'event': 'printer_disconnect', + 'data': 'disconnect_from_front' + } + + json_message = json.dumps(message) + + asyncio.run(socket.send(json_message)) + + return jsonify({"message": "Emulator disconnected successfully"}), 200 + except Exception as e: + print(f"Error disconnecting emulator: {e}") + return jsonify({"error": "Failed to disconnect emulator"}), 500 + except Exception as e: print(f"Unexpected error: {e}") return jsonify({"error": "Unexpected error occurred"}), 500 \ No newline at end of file From ac27209747c2e780c49858da0f45cd3dfb945cc7 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Mon, 2 Dec 2024 14:17:46 -0500 Subject: [PATCH 156/194] fix: register printers works --- client/src/components/GCode3DImageViewer.vue | 4 + client/src/model/ports.ts | 6 +- client/src/views/RegisteredView.vue | 12 +- server/Classes/FabricatorConnection.py | 55 +--- server/Classes/Fabricators/Device.py | 11 +- server/Classes/Fabricators/Fabricator.py | 50 ++- server/Classes/Jobs.py | 2 +- server/Classes/Logger.py | 7 +- server/Classes/Ports.py | 304 ++++++++++--------- server/app.py | 5 +- server/controllers/jobs.py | 2 + server/controllers/ports.py | 40 ++- server/models/PrinterStatusService.py | 2 +- 13 files changed, 278 insertions(+), 222 deletions(-) diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index a5457a28..506ed8b0 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -62,8 +62,12 @@ onMounted(async () => { backgroundColor: 'black', buildVolume: { x: 250, y: 210, z: 220 }, travelColor: 'limegreen', + lineWidth: 1, + lineHeight: 1, + extrusionWidth: 1, renderExtrusion: true, renderTravel: renderTravel.value, + renderTubes: true, }); preview.camera.position.set(-200, 232, 200); diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index a4fc1f12..268e2731 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -32,7 +32,9 @@ export function useGetPorts() { return { async ports() { try { - return await api('getports') + const response = await api('getports') + console.log('response:', response) + return response } catch (error) { console.error(error) } @@ -159,7 +161,7 @@ export function useDeletePrinter() { return { async deletePrinter(printerid: number | undefined) { try { - const response = await api('deleteprinter', { printerid }) + const response = await api('deletefabricator', { printerid }) // if (response) { // if (response.success == false) { // toast.error(response.message) diff --git a/client/src/views/RegisteredView.vue b/client/src/views/RegisteredView.vue index 317beee2..9a0f8d4b 100644 --- a/client/src/views/RegisteredView.vue +++ b/client/src/views/RegisteredView.vue @@ -1,10 +1,9 @@ <script setup lang="ts"> -import { printers, useGetPorts, useRetrievePrintersInfo, useHardReset, useDeletePrinter, useNullifyJobs, useEditName, useRemoveThread, useEditThread, useDiagnosePrinter, useRepair, type Device, useRetrievePrinters, useMoveHead } from '../model/ports' -import { isLoading } from '../model/jobs' -import { useRouter } from 'vue-router' +import { printers, useRetrievePrintersInfo, useHardReset, useDeletePrinter, useNullifyJobs, useEditName, useRemoveThread, useEditThread, useDiagnosePrinter, useRepair, type Device, useRetrievePrinters, useMoveHead } from '@/model/ports' +import { isLoading } from '@/model/jobs' import { ref, onMounted } from 'vue'; -import { toast } from '../model/toast' -import RegisterModal from '../components/RegisterModal.vue' +import { toast } from '@/model/toast' +import RegisterModal from '@/components/RegisterModal.vue' import router from '@/router'; const { retrieve } = useRetrievePrinters(); @@ -35,8 +34,7 @@ const selectedPrinter = ref<Device | null>(null); // fetch list of connected ports from backend and automatically load them into the form dropdown onMounted(async () => { isLoading.value = true - const allPrinters = await retrieve(); // load all registered printers - registered.value = allPrinters + registered.value = await retrieve(); // load all registered printers isLoading.value = false }); diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 8dddc777..c117aab1 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -9,53 +9,22 @@ class FabricatorConnection(ABC): - is_open = False - - @abstractmethod - def write(self, data): - pass - - @abstractmethod - def read(self): - pass - - @abstractmethod - def close(self): - pass - - @abstractmethod - def reset_input_buffer(self): - pass - - @abstractmethod - def readline(self): - pass + @staticmethod + def staticCreateConnection(port: str = None, baudrate: int = None, timeout: float = 10.0, websocket_connections: dict = None, fabricator_id: str = None): + """Create a new connection to a 3D printer.""" + if websocket_connections is not None and fabricator_id is not None: + return SocketConnection(websocket_connections, fabricator_id) + elif port is not None and baudrate is not None: + return SerialConnection(port, baudrate, timeout=timeout) + else: + raise ValueError("Invalid connection parameters") -class SerialConnection(FabricatorConnection): +class SerialConnection(FabricatorConnection, serial.Serial): def __init__(self, port: str, baudrate: int, timeout: float): - self.serial = serial.Serial(port, baudrate, timeout=timeout) - self.is_open = self.serial.is_open - - def write(self, data): - self.serial.write(data) + super().__init__(port, baudrate, timeout=timeout) - def read(self): - return self.serial.readline() - - def close(self): - self.serial.close() - - def reset_input_buffer(self): - self.serial.reset_input_buffer() - - def readline(self): - return self.serial.readline() - - @property - def is_open(self): - return self.serial.is_open -class SocketConnection: +class SocketConnection(FabricatorConnection): def __init__(self, websocket_connections, fabricator_id: str): """ Initialize a websocket-based connection for a 3D printer with optional mock response generation. diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 03e18b3c..edeae83c 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -15,7 +15,7 @@ import serial.tools.list_ports from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkXYZ -from Classes import FabricatorConnection +from Classes.FabricatorConnection import FabricatorConnection class Device(ABC): @@ -25,7 +25,7 @@ class Device(ABC): PRODUCTID: int | None = None DESCRIPTION: str | None = None MAXFEEDRATE: int | None = None - serialConnection: FabricatorConnection.FabricatorConnection | None = None + serialConnection: FabricatorConnection | None = None homePosition: Vector3 | None = None homeCMD: bytes | None= b"G28\n" @@ -46,7 +46,7 @@ def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sy self.dbID: int = dbID self.serialPort: ListPortInfo | SysFS | None = serialPort self.serialID: str | None = serialPort.serial_number - self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG) + self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG, consoleLevel=Logger.ERROR) self.status = "idle" self.verdict = "" @@ -78,7 +78,7 @@ def __to_JSON__(self): def connect(self): """Connect to the hardware using the serial port.""" try: - self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) + self.serialConnection = FabricatorConnection.staticCreateConnection(port=self.serialPort.device, baudrate=115200, timeout=60) self.serialConnection.reset_input_buffer() return True except Exception as e: @@ -101,8 +101,11 @@ def home(self, isVerbose: bool = True): try: assert isinstance(isVerbose, bool) assert isinstance(self, Device) + print("passed asserts") self.sendGcode(self.homeCMD, isVerbose=isVerbose) + print("sent gcode") assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" + print("asserted") return True except Exception as e: from app import handle_errors_and_logging diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 1d982bbb..9507e3c2 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -90,6 +90,54 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: response = response.decode("utf-8") return response + @staticmethod + def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None): + """ + creates the correct printer object based on the serial port info + :param serialPort: + :type serialPort: ListPortInfo | SysFS | None + :param consoleLogger: + :type consoleLogger: TextIO | None + :param fileLogger: + :type fileLogger: str | None + :return: device without a fabricator object + :rtype: Device | None + """ + if serialPort is None: + return None + from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter + from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter + from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter + if serialPort.vid == PrusaPrinter.VENDORID: + from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 + from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 + from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S + if serialPort.pid == PrusaMK4.PRODUCTID: + return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + elif serialPort.pid == PrusaMK4S.PRODUCTID: + return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + elif serialPort.pid == PrusaMK3.PRODUCTID: + return PrusaMK3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + else: + return None + elif serialPort.vid == EnderPrinter.VENDORID: + from Classes.Fabricators.Printers.Ender.Ender3 import Ender3 + from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro + model = Fabricator.getModelFromGcodeCommand(serialPort) + if "Ender-3 Pro" in model: + return Ender3Pro(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + elif "Ender-3" in model: + return Ender3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + else: + return None + elif serialPort.vid == MakerBotPrinter.VENDORID: + from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 + if serialPort.pid == Replicator2.PRODUCTID: + return Replicator2(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + else: + #TODO: assume generic printer, do stuff + return None + def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None): """ creates the correct printer object based on the serial port info @@ -275,8 +323,6 @@ def handleVerdict(self): assert self.job is not None, "Job is None" if self.device.verdict == "complete": self.setStatus("complete") - self.queue.removeJob() - self.job = None elif self.device.verdict == "error": self.setStatus("error") elif self.device.verdict == "cancelled": diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index 1ce8efc8..a88468c2 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -317,7 +317,7 @@ def getPathForDelete(cls, file_name): @classmethod def nullifyPrinterId(cls, printer_id): try: - jobs = cls.query.filter_by(printer_id=printer_id).all() + jobs = cls.query.filter_by(fabricator_id=printer_id).all() for job in jobs: job.fabricator_id = 0 db.session.commit() diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index fa133f0d..a2d8693b 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -14,7 +14,7 @@ class Logger(logging.Logger): ERROR = logging.ERROR CRITICAL = logging.CRITICAL - def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True): + def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True, consoleLevel=None): title = [] if port: title.append(port) @@ -32,7 +32,10 @@ def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=N formatString = " - ".join(info + ["%(message)s"]) if consoleLogger is not None: consoleLogger = logging.StreamHandler(consoleLogger) - consoleLogger.setLevel(loggingLevel) + if consoleLevel is not None: + consoleLogger.setLevel(consoleLevel) + else: + consoleLogger.setLevel(loggingLevel) consoleLogger.setFormatter(CustomFormatter(formatString)) self.consoleLogger = consoleLogger self.addHandler(consoleLogger) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 137216ce..089790aa 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -2,32 +2,47 @@ import serial.tools.list_ports from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS -from flask import Blueprint, jsonify, request -from sqlalchemy.exc import SQLAlchemyError from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode +from flask import current_app as app class Ports: @staticmethod - def getPorts() -> list[ListPortInfo | SysFS]: + def getPorts(): """Get a list of all connected serial ports.""" + ports = serial.tools.list_ports.comports() + + try: + with app.app_context(): + full_devices = [Fabricator.staticCreateDevice(port) for port in ports if app.fabricator_list.getFabricatorByPort(port) is None] + except Exception as e: + full_devices = [Fabricator.staticCreateDevice(port) for port in ports] + devices = [{ + "device": device.__to_JSON__(), + "hwid": device.getHWID(), + "description": device.DESCRIPTION + } for device in full_devices if device is not None] + return devices + + @staticmethod + def getListPorts(): return serial.tools.list_ports.comports() @staticmethod - def getPortByName(name: str) -> ListPortInfo | SysFS | None: + def getPortByName(name: str): """Get a specific port by its device name.""" assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" - ports = Ports.getPorts() + ports = Ports.getListPorts() for port in ports: if port.device == name: return port return None @staticmethod - def getPortByHwid(hwid: str) -> ListPortInfo | SysFS | None: + def getPortByHwid(hwid: str): """Get a specific port by its hardware ID.""" assert isinstance(hwid, str), f"HWID must be a string: {hwid} : {type(hwid)}" - ports = Ports.getPorts() + ports = Ports.getListPorts() for port in ports: if hwid in port.hwid: return port @@ -48,7 +63,10 @@ def getRegisteredFabricators() -> list[Fabricator]: def diagnosePort(port: ListPortInfo | SysFS) -> str: """Diagnose a port to check if it is functional by sending basic G-code commands.""" try: - device = Fabricator.createDevice(port) + if app: + device = app.fabricator_list.getFabricatorByPort(port).device + else: + device = Fabricator(port).device if not device: return "Device creation failed." @@ -61,137 +79,139 @@ def diagnosePort(port: ListPortInfo | SysFS) -> str: except Exception as e: return f"Error diagnosing port {port.device}: {e}" -# Blueprint for ports routes -ports_bp = Blueprint("ports", __name__) - -@ports_bp.route("/getports", methods=["GET"]) -def getPorts(): - """Get a list of all connected ports.""" - try: - ports = Ports.getPorts() - return jsonify([port.device for port in ports]) - except Exception as e: - print(f"Error getting ports: {e}") - return jsonify({"error": "Failed to retrieve ports"}), 500 - -@ports_bp.route("/getfabricators", methods=["GET"]) -def getRegisteredFabricators(): - """Get a list of all registered fabricators.""" - try: - fabricators = Ports.getRegisteredFabricators() - return jsonify([{ - "name": fab.name, - "description": fab.description, - "hwid": fab.hwid, - "devicePort": fab.devicePort, - "status": fab.getStatus() - } for fab in fabricators]) - except Exception as e: - print(f"Error getting registered fabricators: {e}") - return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 - -@ports_bp.route("/register", methods=["POST"]) -def registerFabricator(): - """Register a new fabricator with the system.""" - try: - data = request.get_json() - device = data['fabricator']['device'] - description = data['fabricator']['description'] - hwid = data['fabricator']['hwid'] - name = data['fabricator']['name'] - - new_fabricator = Fabricator(Ports.getPortByName(device), name) - new_fabricator.description = description - new_fabricator.hwid = hwid - - return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) - except SQLAlchemyError as db_err: - print(f"Database error during registration: {db_err}") - return jsonify({"error": "Database error occurred"}), 500 - except Exception as e: - print(f"Error registering fabricator: {e}") - return jsonify({"error": "Failed to register fabricator"}), 500 - -@ports_bp.route("/deletefabricator", methods=["POST"]) -def deleteFabricator(): - """Delete a fabricator from the system.""" - try: - data = request.get_json() - fabricator_id = data['fabricator_id'] - fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() - - if fabricator: - Fabricator.query.filter_by(dbID=fabricator_id).delete() - Fabricator.updateDB() - return jsonify({"success": True, "message": "Fabricator deleted successfully"}) - else: - return jsonify({"error": "Fabricator not found"}), 404 - except Exception as e: - print(f"Error deleting fabricator: {e}") - return jsonify({"error": "Failed to delete fabricator"}), 500 - -@ports_bp.route("/editname", methods=["POST"]) -def editName(): - """Edit the name of a registered fabricator.""" - try: - data = request.get_json() - fabricator_id = data['fabricator_id'] - new_name = data['name'] - - fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() - if fabricator: - fabricator.setName(new_name) - return jsonify({"success": True, "message": "Fabricator name updated successfully"}) - else: - return jsonify({"error": "Fabricator not found"}), 404 - except Exception as e: - print(f"Error editing fabricator name: {e}") - return jsonify({"error": "Failed to edit fabricator name"}), 500 - -@ports_bp.route("/diagnose", methods=["POST"]) -def diagnoseFabricator(): - """Diagnose a fabricator based on its port.""" - try: - data = request.get_json() - device_name = data['device'] - port = Ports.getPortByName(device_name) - - if port: - diagnosis_result = Ports.diagnosePort(port) - return jsonify({"success": True, "message": diagnosis_result}) - else: - return jsonify({"error": "Device not found"}), 404 - except Exception as e: - print(f"Error diagnosing fabricator: {e}") - return jsonify({"error": "Failed to diagnose fabricator"}), 500 - -@ports_bp.route("/movehead", methods=["POST"]) -def moveHead(): - """Move the head of a fabricator. Deprecated??????""" - try: - data = request.get_json() - device_name = data['port'] - port = Ports.getPortByName(device_name) - - if port: - fabricator = Fabricator(port) - result = fabricator.device.home() # Ensuring `home()` method from Device is used - return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) - else: - return jsonify({"error": "Device not found"}), 404 - except Exception as e: - print(f"Error moving head: {e}") - return jsonify({"error": "Failed to move fabricator head"}), 500 - -@ports_bp.route("/movefabricatorlist", methods=["POST"]) -def moveFabricatorList(): - """Change the order of fabricators.""" - try: - data = request.get_json() - fabricator_ids = data['fabricator_ids'] - from app import fabricator_list - result = fabricator_list.moveFabricatorList(fabricator_ids) - return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) - except Exception as e: - print(f"Error moving fabricator list: {e}") - return jsonify({"error": "Failed to move fabricator list"}), 500 \ No newline at end of file +# # Blueprint for ports routes +# ports_bp = Blueprint("ports", __name__) +# +# @ports_bp.route("/getports", methods=["GET"]) +# def getPorts(): +# """Get a list of all connected ports.""" +# try: +# ports = Ports.getPorts() +# print("getting ports from route") +# print(ports if ports else "No ports found") +# return jsonify([port.device for port in ports]) +# except Exception as e: +# print(f"Error getting ports: {e}") +# return jsonify({"error": "Failed to retrieve ports"}), 500 +# +# @ports_bp.route("/getfabricators", methods=["GET"]) +# def getRegisteredFabricators(): +# """Get a list of all registered fabricators.""" +# try: +# fabricators = Ports.getRegisteredFabricators() +# return jsonify([{ +# "name": fab.name, +# "description": fab.description, +# "hwid": fab.hwid, +# "devicePort": fab.devicePort, +# "status": fab.getStatus() +# } for fab in fabricators]) +# except Exception as e: +# print(f"Error getting registered fabricators: {e}") +# return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 +# +# @ports_bp.route("/register", methods=["POST"]) +# def registerFabricator(): +# """Register a new fabricator with the system.""" +# try: +# data = request.get_json() +# device = data['fabricator']['device'] +# description = data['fabricator']['description'] +# hwid = data['fabricator']['hwid'] +# name = data['fabricator']['name'] +# +# new_fabricator = Fabricator(Ports.getPortByName(device), name) +# new_fabricator.description = description +# new_fabricator.hwid = hwid +# +# return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) +# except SQLAlchemyError as db_err: +# print(f"Database error during registration: {db_err}") +# return jsonify({"error": "Database error occurred"}), 500 +# except Exception as e: +# print(f"Error registering fabricator: {e}") +# return jsonify({"error": "Failed to register fabricator"}), 500 +# +# @ports_bp.route("/deletefabricator", methods=["POST"]) +# def deleteFabricator(): +# """Delete a fabricator from the system.""" +# try: +# data = request.get_json() +# fabricator_id = data['fabricator_id'] +# fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() +# +# if fabricator: +# Fabricator.query.filter_by(dbID=fabricator_id).delete() +# Fabricator.updateDB() +# return jsonify({"success": True, "message": "Fabricator deleted successfully"}) +# else: +# return jsonify({"error": "Fabricator not found"}), 404 +# except Exception as e: +# print(f"Error deleting fabricator: {e}") +# return jsonify({"error": "Failed to delete fabricator"}), 500 +# +# @ports_bp.route("/editname", methods=["POST"]) +# def editName(): +# """Edit the name of a registered fabricator.""" +# try: +# data = request.get_json() +# fabricator_id = data['fabricator_id'] +# new_name = data['name'] +# +# fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() +# if fabricator: +# fabricator.setName(new_name) +# return jsonify({"success": True, "message": "Fabricator name updated successfully"}) +# else: +# return jsonify({"error": "Fabricator not found"}), 404 +# except Exception as e: +# print(f"Error editing fabricator name: {e}") +# return jsonify({"error": "Failed to edit fabricator name"}), 500 +# +# @ports_bp.route("/diagnose", methods=["POST"]) +# def diagnoseFabricator(): +# """Diagnose a fabricator based on its port.""" +# try: +# data = request.get_json() +# device_name = data['device'] +# port = Ports.getPortByName(device_name) +# +# if port: +# diagnosis_result = Ports.diagnosePort(port) +# return jsonify({"success": True, "message": diagnosis_result}) +# else: +# return jsonify({"error": "Device not found"}), 404 +# except Exception as e: +# print(f"Error diagnosing fabricator: {e}") +# return jsonify({"error": "Failed to diagnose fabricator"}), 500 +# +# @ports_bp.route("/movehead", methods=["POST"]) +# def moveHead(): +# """Move the head of a fabricator. Deprecated??????""" +# try: +# data = request.get_json() +# device_name = data['port'] +# port = Ports.getPortByName(device_name) +# +# if port: +# fabricator = Fabricator(port) +# result = fabricator.device.home() # Ensuring `home()` method from Device is used +# return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) +# else: +# return jsonify({"error": "Device not found"}), 404 +# except Exception as e: +# print(f"Error moving head: {e}") +# return jsonify({"error": "Failed to move fabricator head"}), 500 +# +# @ports_bp.route("/movefabricatorlist", methods=["POST"]) +# def moveFabricatorList(): +# """Change the order of fabricators.""" +# try: +# data = request.get_json() +# fabricator_ids = data['fabricator_ids'] +# from app import fabricator_list +# result = fabricator_list.moveFabricatorList(fabricator_ids) +# return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) +# except Exception as e: +# print(f"Error moving fabricator list: {e}") +# return jsonify({"error": "Failed to move fabricator list"}), 500 \ No newline at end of file diff --git a/server/app.py b/server/app.py index c0cdf8d8..05eab9bb 100644 --- a/server/app.py +++ b/server/app.py @@ -1,4 +1,6 @@ import asyncio +import logging +import sys import threading import uuid from flask import Flask, request, Response, send_from_directory @@ -62,7 +64,7 @@ def start_websocket(): logs = os.path.join(root_path,"server", "logs") if not os.path.exists(logs): os.makedirs(logs) from Classes.Logger import Logger -app.logger = Logger("App", consoleLogger=None, fileLogger=os.path.abspath(os.path.join(logs, "app.log"))) +app.logger = Logger("App", consoleLogger=sys.stdout, fileLogger=os.path.abspath(os.path.join(logs, "app.log")), consoleLevel=logging.ERROR) # start database connection app.config["environment"] = Config.get('environment') app.config["ip"] = Config.get('ip') @@ -179,7 +181,6 @@ def handle_connect(): tempcsv = os.path.abspath('../tempcsv') fabricator_list = FabricatorList(app) app.fabricator_list = fabricator_list - # Check if directories exist and handle them accordingly for folder in [uploads_folder, tempcsv]: if os.path.exists(folder): diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index b254cf89..4cbd39b6 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -517,6 +517,8 @@ def startPrint(): assert queue is not None, "Queue not found." printerobject.job = queue.getJobById(jobid) assert printerobject.job is not None, "Job not found." + assert printerobject.job.getStatus() == "inqueue", f"Job status not inqueue. Status: {printerobject.job.getStatus()}" + printerobject.job.setStatus("ready") assert printerobject.job.getStatus() == "ready", f"Job not ready to print. Status: {printerobject.job.getStatus()}" assert printerobject.job == queue[0], "Job not at front of queue." print(printerobject.job) diff --git a/server/controllers/ports.py b/server/controllers/ports.py index b4d95237..4fe61cfc 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -9,6 +9,15 @@ ports_bp = Blueprint("ports", __name__) @ports_bp.route("/getports", methods=["GET"]) +def getPorts(): + """Get a list of all connected ports.""" + try: + ports = Ports.getPorts() + return jsonify([port for port in ports]) + except Exception as e: + handle_errors_and_logging(e) + return jsonify({"error": format_exc()}), 500 + @ports_bp.route("/getfabricators", methods=["GET"]) def getRegisteredFabricators(): """Get a list of all registered fabricators.""" @@ -24,11 +33,12 @@ def registerFabricator(): """Register a new fabricator with the system.""" try: data = request.get_json() - device = data['fabricator']['device'] - name = data['fabricator']['name'] + printer = data['printer'] + device = printer['device']['serialPort'] + name = printer['name'] # Create a new fabricator instance using the Fabricator class - new_fabricator = Fabricator(Ports.getPortByName(device), name) + new_fabricator = Fabricator(Ports.getPortByName(device), name=name) return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") @@ -123,17 +133,16 @@ def moveHead(): port = data['port'] if port: if current_app: - with current_app.app_context(): - fabricator = current_app.fabricator_list.getFabricatorByPort(port) - if fabricator: - result = fabricator.device.home() # Use home() method from Device - return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) - else: - return jsonify({"error": "Fabricator not found", "line number": 136}), 404 - else: - fabricator = Fabricator(port) - result = fabricator.device.home() # Use home() method from Device - return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + fab = current_app.fabricator_list.getFabricatorByPort(port) + if fab: device = current_app.fabricator_list.getFabricatorByPort(port).device + else: device = Fabricator.staticCreateDevice(Ports.getPortByName(port)) + else: device = Fabricator(port).device + device.connect() + result = device.home() # Use home() method from Device + device.disconnect() + res = jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) + print(res) + return res else: return jsonify({"error": "Device not found"}), 404 except Exception as e: @@ -146,8 +155,7 @@ def moveFabricatorList(): try: data = request.get_json() fabricator_ids = data['fabricator_ids'] - from app import app - result = app.fabricator_list.moveFabricatorList(fabricator_ids) + result = current_app.fabricator_list.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: handle_errors_and_logging(e) diff --git a/server/models/PrinterStatusService.py b/server/models/PrinterStatusService.py index 2b8f2414..4fef66a7 100644 --- a/server/models/PrinterStatusService.py +++ b/server/models/PrinterStatusService.py @@ -75,7 +75,7 @@ def update_thread(self, printer, app): time.sleep(2) status = printer.getStatus() # get printer status - queueSize = printer.getQueue().getSize() # get size of queue + queueSize = len(printer.getQueue()) # get size of queue printer.responseCount = 0 if (status == "ready" and queueSize > 0): time.sleep(2) # wait for 2 seconds to allow the printer to process the queue From 12cec6f05d6a10cc020d8d833c27d4def9b0699c Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 2 Dec 2024 14:22:04 -0500 Subject: [PATCH 157/194] feat: add fake serial port to emulator --- printeremu/data/printers.json | 12 +++++--- printeremu/src/emulator.go | 51 +++++++++++++++++++++++++++++++ printeremu/src/printer.go | 20 ++++++++---- printeremu/src/printerregistry.go | 31 +++++++++++++++++++ 4 files changed, 104 insertions(+), 10 deletions(-) diff --git a/printeremu/data/printers.json b/printeremu/data/printers.json index b8ef1e1c..c0dad123 100644 --- a/printeremu/data/printers.json +++ b/printeremu/data/printers.json @@ -4,13 +4,15 @@ "name": "Prusa MK4", "brand": { "printerName": "Prusa", - "printerModel": "MK4" + "printerModel": "MK4", + "vendorId": "2C99" }, "data": { "length": 250, "width": 210, "height": 220, - "startTemp": 0 + "startTemp": 0, + "productId": "000D" }, "attributes": { "marlin": true @@ -21,13 +23,15 @@ "name": "Ender 3", "brand": { "printerName": "Creality", - "printerModel": "Ender 3" + "printerModel": "Ender 3", + "vendorId": "1A86" }, "data": { "length": 220, "width": 220, "height": 250, - "startTemp": 0 + "startTemp": 0, + "productId": "7523" }, "attributes": { "marlin": true diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index e90fd3ba..a1229b20 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -190,6 +190,57 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se log.Println("Received printer disconnect. Closing connection...") conn.Close() + case "fake_serial_port": + pid := printer.GetData("productId") + + if pid == nil { + log.Println("PID not found for this printer!") + return + } + + vid := printer.GetAttribute("vendorId") + + if vid == nil { + log.Println("VID not found for this printer!") + return + } + + port := printer.GetData("port") + + if port == nil { + log.Println("Port not found for this printer!") + return + } + + hwid := printer.GetHwid() + + if hwid == "" { + log.Println("HWID not found for this printer!") + return + } + + dataMap := map[string]interface{}{ + "productId": pid, + "vendorId": vid, + "port": port, + "hwid": hwid, + } + + message := map[string]interface{}{ + "event": "printer_connect", + "data": dataMap, + } + + jsonMessage, err := json.Marshal(message) + + if err != nil { + log.Println("Failed to marshal printer object:", err) + return + } + + log.Println("Sending fake serial port message...") + + conn.WriteMessage(websocket.TextMessage, []byte(jsonMessage)) default: log.Println("Received from server:", string(message)) } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index e855bebb..173a97be 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -178,12 +178,12 @@ func (printer *Printer) Resume() { // UpdateProgress updates the progress of the print job func (printer *Printer) UpdateProgress(progress int) error { - if progress >= 0 && progress <= 100 { - printer.Progress = progress - } else { - return fmt.Errorf("invalid progress: %d. Valid range: 0 to 100", progress) - } - return nil + if progress >= 0 && progress <= 100 { + printer.Progress = progress + } else { + return fmt.Errorf("invalid progress: %d. Valid range: 0 to 100", progress) + } + return nil } // MoveExtruder moves the extruder to the specified position. @@ -225,6 +225,14 @@ func (printer *Printer) GetData(key string) interface{} { return printer.Data[key] } +func (printer *Printer) GetHwid() string { + return printer.Hwid +} + +func (printer *Printer) SetHwid(hwid string) { + printer.Hwid = hwid +} + func (printer *Printer) WriteSerial(event string, data interface{}) error { if printer.WSConnection == nil { return fmt.Errorf("WebSocket connection not established") diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index 8b82fede..d342e36a 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strconv" "time" "golang.org/x/exp/rand" @@ -21,6 +22,7 @@ type PrinterConfig struct { type PrinterBrand struct { PrinterName string `json:"printerName"` PrinterModel string `json:"printerModel"` + VendorId string `json:"vendorId"` } // LoadPrinters loads a list of printers from the specified JSON file @@ -53,6 +55,7 @@ func LoadPrinters(filePath string) ([]Printer, error) { // always add the model as an attribute so we always know what the model is printer.AddAttribute("model", printerConfig.Brand.PrinterName) + printer.AddAttribute("vendorId", printerConfig.Brand.VendorId) for key, value := range printerConfig.Attributes { printer.AddAttribute(key, value) @@ -66,6 +69,30 @@ func LoadPrinters(filePath string) ([]Printer, error) { return nil, fmt.Errorf("failed to initialize printer %d: %v", printerConfig.Id, err) } + // handle HWID generation + pid := printer.GetData("productId") + + if pid == nil { + return nil, fmt.Errorf("pid not found for printer %d: %v", printerConfig.Id, err) + } + + vid := printer.GetAttribute("vendorId") + + if vid == nil { + return nil, fmt.Errorf("vid not found for printer %d: %v", printerConfig.Id, err) + } + + hwid := fmt.Sprintf("USB VID:PID=%s:%s SER=2024-QView3DEmulator", vid, pid) + + if hwid == "" { + return nil, fmt.Errorf("hwid could not be generated for this printer %d: %v", printerConfig.Id, err) + } + + printer.SetHwid(hwid) + + printer.AddData("port", "EMU-"+strconv.Itoa(RandomRange(0, 9))) + + // handle stuff like the bed size and such PostRegistry(printer) printers = append(printers, *printer) @@ -104,3 +131,7 @@ func RandomString(length int) string { return string(result) } + +func RandomRange(min, max int) int { + return rand.Intn(max-min+1) + min +} From 10a789bc9ae4a0f50ba5010a39e307949327c142 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 2 Dec 2024 15:28:07 -0500 Subject: [PATCH 158/194] feat: clean up old websocket server and add event emitter --- server/Classes/EventEmitter.py | 18 ++++++++++++++++++ server/app.py | 20 -------------------- 2 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 server/Classes/EventEmitter.py diff --git a/server/Classes/EventEmitter.py b/server/Classes/EventEmitter.py new file mode 100644 index 00000000..9b33fa4f --- /dev/null +++ b/server/Classes/EventEmitter.py @@ -0,0 +1,18 @@ +import asyncio + +class EventEmitter: + def __init__(self): + self._events = {} + + def on(self, event_name, callback): + """Register a handler for an event.""" + if event_name not in self._events: + self._events[event_name] = [] + self._events[event_name].append(callback) + + def emit(self, event_name, *args, **kwargs): + """Emit an event, calling all registered callbacks with the event data.""" + if event_name in self._events: + for callback in self._events[event_name]: + # Trigger the callback with the provided data + asyncio.create_task(callback(*args, **kwargs)) diff --git a/server/app.py b/server/app.py index 05eab9bb..06aba2cf 100644 --- a/server/app.py +++ b/server/app.py @@ -96,16 +96,6 @@ def start_websocket(): socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode, transport=['websocket', 'polling']) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object -async def emulator_ws_handler(websocket, path): - print(f"Emulator connected: {path}") - try: - async for message in websocket: - # Handle messages from the emulator - print(f"Received message from emulator: {message}") - # Add your handling logic here, e.g., update the printer status - except websockets.exceptions.ConnectionClosed as e: - print(f"Emulator connection closed: {e}") - def handle_errors_and_logging(e: Exception | str, fabricator = None): from Classes.Fabricators.Fabricator import Fabricator device = fabricator @@ -123,16 +113,6 @@ def handle_errors_and_logging(e: Exception | str, fabricator = None): app.logger.error(e, stacklevel=3) return False -async def start_emulator_ws(): - print("Starting emulator websocket server...") - try: - # Start the WebSocket server - server = await websockets.serve(emulator_ws_handler, 'localhost', 8001) - print("WebSocket server started on ws://localhost:8001") - await server.wait_closed() # Keeps the server running - except Exception as e: - print(f"Error in WebSocket server: {e}") - app.handle_errors_and_logging = handle_errors_and_logging CORS(app) From 4fb8772c618cc53925916b137992a9d5d75922c0 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Tue, 3 Dec 2024 12:41:05 -0500 Subject: [PATCH 159/194] feat: allow start up command to chose printer and connection type --- printeremu/cmd/test_printer.go | 48 +++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 2f88c726..1812ee0c 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -16,21 +16,63 @@ import ( func main() { settings, err := src.LoadSettings("data/settings.json") - if err != nil { log.Fatalf("Error loading settings: %v", err) } printers, err := src.LoadPrinters("data/printers.json") - if err != nil { log.Fatalf("Error loading printers: %v", err) } fmt.Println("Loaded printers...") - var printer *src.Printer + var printer *src.Printer // Declare this once + + if len(os.Args) == 3 { // Check if both arguments are provided + printerID, err := strconv.Atoi(os.Args[1]) + if err != nil { + log.Fatalf("Invalid printer ID: %v", err) + } + + // Find the printer by ID + for _, p := range printers { + if p.Id == printerID { + printer = &p + break + } + } + + if printer == nil { + log.Fatalf("Printer with ID %d not found.", printerID) + } + + // Now we can safely access the printer.Extruder + extruder := printer.Extruder + + // Parse the command + command := os.Args[2] + + if command == "-conn" { + handleConnection(extruder, printer, &settings) + return + } + + if command == "-comm" { + handleCommand(extruder, printer) + return + } + + if command == "-reg" { + src.PrintPrinters(printers) + return + } + + log.Fatalf("Unknown command: %s", command) + return + } + // If no arguments, let the user choose a printer interactively for printer == nil { printer = askForPrinterID(printers) } From 8f98c2675092aef3eb61dfc2ce631a0f7874e27e Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Tue, 3 Dec 2024 13:19:38 -0500 Subject: [PATCH 160/194] fix: home printer works --- client/src/model/ports.ts | 13 +- printeremu/src/printerregistry.go | 2 +- server/Classes/FabricatorConnection.py | 73 ++++++-- server/Classes/FabricatorList.py | 125 ++++++++----- server/Classes/Fabricators/Device.py | 16 +- server/Classes/Fabricators/Fabricator.py | 46 +++-- .../Classes/Fabricators/Printers/Printer.py | 11 +- .../Printers/Prusa/PrusaPrinter.py | 9 +- server/Classes/Logger.py | 3 +- server/Classes/Ports.py | 172 +++--------------- server/app.py | 32 +++- server/controllers/emulator.py | 11 +- server/controllers/jobs.py | 2 - server/controllers/ports.py | 24 +-- server/controllers/statusService.py | 6 +- 15 files changed, 263 insertions(+), 282 deletions(-) diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index 268e2731..32794996 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -159,9 +159,9 @@ export function useNullifyJobs() { export function useDeletePrinter() { return { - async deletePrinter(printerid: number | undefined) { + async deletePrinter(fabricator_id: number | undefined) { try { - const response = await api('deletefabricator', { printerid }) + const response = await api('deletefabricator', { fabricator_id }) // if (response) { // if (response.success == false) { // toast.error(response.message) @@ -211,9 +211,9 @@ export function useRemoveThread() { export function useEditName() { return { - async editName(printerid: number | undefined, name: string) { + async editName(fabricator_id: number | undefined, name: string) { try { - const response = await api('editname', { printerid, name }) + const response = await api('editname', { fabricator_id, name }) if (response) { if (response.success == false) { toast.error(response.message) @@ -237,10 +237,9 @@ export function useEditName() { export function useEditThread() { return { - async editThread(printerid: number | undefined, newname: string) { + async editThread(fabricator_id: number | undefined, newname: string) { try { - const response = await api('editNameInThread', { printerid, newname }) - return response + return await api('editNameInThread', { fabricator_id, newname }) } catch (error) { console.error(error) } diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index d342e36a..52bcfb72 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -90,7 +90,7 @@ func LoadPrinters(filePath string) ([]Printer, error) { printer.SetHwid(hwid) - printer.AddData("port", "EMU-"+strconv.Itoa(RandomRange(0, 9))) + printer.AddData("port", "EMU"+strconv.Itoa(RandomRange(0, 9))) // handle stuff like the bed size and such PostRegistry(printer) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index c117aab1..4d7fccaf 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -1,11 +1,11 @@ import asyncio import threading -from abc import ABC, abstractmethod +from abc import ABC import uuid import serial -from flask_socketio import SocketIO from queue import Queue, Empty -import random + +from serial.tools.list_ports_common import ListPortInfo class FabricatorConnection(ABC): @@ -25,40 +25,45 @@ def __init__(self, port: str, baudrate: int, timeout: float): class SocketConnection(FabricatorConnection): - def __init__(self, websocket_connections, fabricator_id: str): + def __init__(self, port: str, baudrate: int, timeout: float, websocket_connection, fabricator_id: str): """ Initialize a websocket-based connection for a 3D printer with optional mock response generation. - :param websocket_connections: Dictionary of websocket connections + :param websocket_connection: Dictionary of websocket connections :param fabricator_id: Unique identifier for the printer :param timeout: Maximum time to wait for a response (default: 10.0) """ self._fabricator_id = fabricator_id - self._timeout = 10.0 - + self._timeout = timeout + # Queue for storing incoming messages self._receive_queue = Queue() - + # Connection state self._is_open = True - + # Event for synchronizing responses self._response_event = threading.Event() - + # WebSocket connection management - self._websocket_connections = websocket_connections - self._websocket = None - + self._websocket_connection = websocket_connection + # Setup connection listeners self._setup_listeners() + self.emuListPortInfo = EmuListPortInfo(device=port, description="Emulator", hwid="") + + self.port = self.emuListPortInfo.device + self.baudrate = baudrate + self.timeout = timeout + def _setup_listeners(self): """Configure websocket event listeners for receiving printer responses.""" pass # Listeners will be managed when sending/receiving messages through websockets. def write(self, data): """ - Send data to the printer via websocket, with optional mock response. + Send data to the printer via websocket. :param data: Data to be sent (typically G-code) """ @@ -119,9 +124,7 @@ def open(self): websocket_id = str(uuid.uuid4()) # Find the appropriate websocket based on fabricator_id - self._websocket = self._websocket_connections.get(self._fabricator_id) - - if self._websocket is None: + if self._websocket_connection is None: raise ConnectionError(f"WebSocket not found for printer {self._fabricator_id}") # Emit a connection/handshake event via WebSocket @@ -171,4 +174,38 @@ def _send_message(self, event, data): message = {"event": event, "data": data} asyncio.run(self._websocket.send(str(message))) else: - print(f"Cannot send message: WebSocket connection is not open or not available.") \ No newline at end of file + print(f"Cannot send message: WebSocket connection is not open or not available.") + +class EmuListPortInfo(ListPortInfo): + def __init__(self, device: str, description: str = None, hwid: str = None): + super().__init__(device) + self._device = device + self._description = description + self._hwid = hwid + + def __repr__(self): + return f"EmuListPortInfo(device={self.device}, description={self.description}, hwid={self.hwid})" + + @property + def device(self): + return self._device + + @device.setter + def device(self, value): + self._device = value + + @property + def description(self): + return self._description + + @description.setter + def description(self, value): + self._description = value + + @property + def hwid(self): + return self._hwid + + @hwid.setter + def hwid(self, value): + self._hwid = value diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 10d71efc..2f351ad9 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -10,42 +10,8 @@ from Classes.Queue import Queue from threading import Thread import time +from flask import current_app as app -class FabricatorThread(Thread): - def __init__(self, fabricator, app=None, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fabricator: Fabricator = fabricator - if app: - self.app = app - else: - from app import app - self.app = app - - def __to_JSON__(self): - return { - "fabricator": self.fabricator, - "app": self.app, - "running": self.is_alive(), - "daemon": self.daemon, - } - - def run(self): - with self.app.app_context(): - while True: - time.sleep(2) - status = self.fabricator.getStatus() - queueSize = len(self.fabricator.queue) - self.fabricator.responseCount = 0 - if status == "printing": - if queueSize > 0: - assert isinstance(self.fabricator.queue[0], Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" - if status == "printing" and queueSize > 0 and self.fabricator.queue[0].released == 1: - time.sleep(2) - if status != "offline": - self.fabricator.begin() - - def stop(self): - self.fabricator.terminated = 1 class FabricatorList: def __init__(self, app=None): @@ -100,8 +66,7 @@ def addFabricator(self, serialPortName: str, name: str = ""): newFab: Fabricator | None = None if dbFab is not None: # means that the fabricator is in the db if listFab is not None: # means that the fabricator is in the list and the db - from app import handle_errors_and_logging - handle_errors_and_logging(Exception(f"Fabricator {dbFab.getname()} already exists in the list"), listFab) + app.handle_errors_and_logging(Exception(f"Fabricator {dbFab.getname()} already exists in the list"), listFab) else: # means that the fabricator is in the db but not in the list newFab = Fabricator(serialPort, name=dbFab.getname()) self.fabricators.append(newFab) @@ -114,15 +79,27 @@ def addFabricator(self, serialPortName: str, name: str = ""): self.fabricators.append(newFab) dbFabricators = Fabricator.queryAll() assert(len(self) == len(dbFabricators)), f"len(self)={len(self)}, len(dbFabricators)={len(dbFabricators)}" - assert all(fabricator in self for fabricator in dbFabricators), f"self={self}, dbFabricators={dbFabricators}" - if newFab: self.start_fabricator_thread(newFab) - + # TODO: figure out how to check if the fabricator is in the db + # assert all(fabricator in self.fabricators for fabricator in dbFabricators), f"self={self.fabricators}, dbFabricators={dbFabricators}" + if newFab: + print("starting new fabricator thread") + self.start_fabricator_thread(newFab) def deleteFabricator(self, fabricatorid): """delete a fabricator from the list, and from the database""" - # TODO: Implement deleteFabricator - raise NotImplementedError("deleteFabricator is not implemented") - + fabricator = self.getFabricatorById(fabricatorid) + if fabricator: + try: + self.fabricators.remove(fabricator) + Fabricator.query.filter_by(dbID=fabricatorid).delete() + Fabricator.updateDB() + except ValueError as e: + app.handle_errors_and_logging(e) + return e + except Exception as e: + app.handle_errors_and_logging(e) + return False + return True def getFabricatorByName(self, name) -> Fabricator | None: """find the first fabricator with the given name""" @@ -139,11 +116,15 @@ def getFabricatorById(self, id) -> Fabricator | None: def getFabricatorByPort(self, port) -> Fabricator | None: - """find the first fabricator with the given port""" + """ + find the first fabricator with the given port + :param port: the port to search for + :type port: str | ListPortInfo | SysFS + """ + if isinstance(port, ListPortInfo or SysFS): port = port.device assert isinstance(port, str), f"port={port}, type(port)={type(port)}" for fabricator in self: assert isinstance(fabricator.devicePort, str), f"fabricator.devicePort={fabricator.devicePort}, type(fabricator.devicePort)={type(fabricator.devicePort)}" - print(fabricator.devicePort + " ?= " + port) if fabricator.devicePort == port: return fabricator return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) @@ -257,7 +238,7 @@ def queueRestore(self, fabricator_id, status): "name": fabricator.name, } self.fabricator_threads.remove(thread) - self.queue_restore(status, fabricator) + self.queue_restore(status, fabricator.queue) break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: @@ -268,7 +249,7 @@ def queueRestore(self, fabricator_id, status): def deleteThread(self, fabricator_id): try: for thread in self.fabricator_threads: - if thread.fabricator.id == fabricator_id: + if thread.fabricator.dbID == fabricator_id: fabricator = thread.fabricator if fabricator.getStatus() == "ready": fabricator.terminated = 1 @@ -296,4 +277,52 @@ def moveFabricatorList(self, fabricator_ids): new_thread_list.append(thread) break self.fabricator_threads = new_thread_list - return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) \ No newline at end of file + return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) + + def editName(self, fabricator_id, name): + fabricator = self.getFabricatorById(fabricator_id) + if fabricator: + fabricator.setName(name) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + else: + return jsonify({"error": "Fabricator not found"}), 404 + +class FabricatorThread(Thread): + def __init__(self, fabricator, app=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fabricator: Fabricator = fabricator + if app: + self.app = app + else: + from app import app + self.app = app + + def __repr__(self): + return f"FabricatorThread(fabricator={self.fabricator}, daemon={self.daemon}, running={self.is_alive()})" + + def __to_JSON__(self): + return { + "fabricator": self.fabricator, + "app": self.app, + "running": self.is_alive(), + "daemon": self.daemon, + } + + def run(self): + with self.app.app_context(): + while True: + time.sleep(2) + status = self.fabricator.getStatus() + queueSize = len(self.fabricator.queue) + self.fabricator.responseCount = 0 + if status == "printing": + if queueSize > 0: + assert isinstance(self.fabricator.queue[0], + Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" + if status == "printing" and queueSize > 0 and self.fabricator.queue[0].released == 1: + time.sleep(2) + if status != "offline": + self.fabricator.begin() + + def stop(self): + self.fabricator.terminated = 1 diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index edeae83c..da04e7a3 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -42,13 +42,15 @@ class Device(ABC): "G28": [checkXYZ], # Home } - def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sys.stdout, fileLogger=None): + def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sys.stdout, fileLogger=None, websocket_connection=None, addLogger: bool =False): self.dbID: int = dbID self.serialPort: ListPortInfo | SysFS | None = serialPort self.serialID: str | None = serialPort.serial_number - self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG, consoleLevel=Logger.ERROR) + if addLogger: + self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG, consoleLevel=Logger.ERROR) self.status = "idle" self.verdict = "" + self.websocket_connection = websocket_connection def __repr__(self): return f"{self.getModel()} on {self.getSerialPort().device}" @@ -70,7 +72,6 @@ def __to_JSON__(self): "dbID": self.dbID, "serialPort": self.serialPort.name if self.serialPort else None, "serialID": self.serialID, - "logger": self.logger.name, "status": self.status, "verdict": self.verdict } @@ -78,7 +79,11 @@ def __to_JSON__(self): def connect(self): """Connect to the hardware using the serial port.""" try: - self.serialConnection = FabricatorConnection.staticCreateConnection(port=self.serialPort.device, baudrate=115200, timeout=60) + assert self.serialPort is not None, "Serial port is not set" + assert self.serialPort.device is not None, "Serial port device is not set" + assert self.serialPort.device != "", "Serial port device is empty" + if self.serialConnection is None: + self.serialConnection = FabricatorConnection.staticCreateConnection(port=self.serialPort.device, baudrate=115200, timeout=60, websocket_connections=self.websocket_connection, fabricator_id=str(self.dbID)) self.serialConnection.reset_input_buffer() return True except Exception as e: @@ -101,11 +106,8 @@ def home(self, isVerbose: bool = True): try: assert isinstance(isVerbose, bool) assert isinstance(self, Device) - print("passed asserts") self.sendGcode(self.homeCMD, isVerbose=isVerbose) - print("sent gcode") assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" - print("asserted") return True except Exception as e: from app import handle_errors_and_logging diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 9507e3c2..304fa2fc 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,8 +1,10 @@ -import serial from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError + +import app +from Classes.FabricatorConnection import FabricatorConnection from Classes.Fabricators.Device import Device from typing_extensions import TextIO from Mixins.hasEndingSequence import hasEndingSequence @@ -50,13 +52,12 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog self.devicePort = dbFab.devicePort self.date = dbFab.date self.dbID = dbFab.dbID - - self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger) + self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True) if self.description == "New Fabricator": self.description = self.device.getDescription() db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" def __to_JSON__(self): """ @@ -79,7 +80,7 @@ def __to_JSON__(self): @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: """returns the model of the printer based on the response to M997""" - testName = serial.Serial(serialPort.device, 115200, timeout=10) + testName = FabricatorConnection.staticCreateConnection(port=serialPort.device, baudrate=115200, timeout=60) testName.write(b"M997\n") while True: response = testName.readline() @@ -113,11 +114,11 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: - return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) else: return None elif serialPort.vid == EnderPrinter.VENDORID: @@ -125,20 +126,20 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3Pro(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) elif "Ender-3" in model: - return Ender3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Replicator2(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) else: #TODO: assume generic printer, do stuff return None - def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None): + def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, addLogger = False): """ creates the correct printer object based on the serial port info :param serialPort: the serial port info @@ -147,6 +148,8 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No :type consoleLogger: TextIO | None :param fileLogger: the file path to output file logs to :type fileLogger: str | None + :param addLogger: whether to add a logger to the device + :type addLogger: bool :return: the printer object :rtype: Device | None """ @@ -162,11 +165,11 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: - return PrusaMK4(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK4S(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return PrusaMK3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) else: return None elif serialPort.vid == EnderPrinter.VENDORID: @@ -174,15 +177,15 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3Pro(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) elif "Ender-3" in model: - return Ender3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Ender3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + return Replicator2(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) else: #TODO: assume generic printer, do stuff return None @@ -199,6 +202,9 @@ def queryAll(cls): for fab in cls.query.all(): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) + fake_port, fake_name, fake_hwid = app.get_emu_ports() + if fake_port and fake_name: + fabList.append(cls(fake_port, fake_name)) return fabList @classmethod @@ -231,9 +237,9 @@ def begin(self, isVerbose: bool = False): # self.device.startupSequence() assert self.setStatus("printing"), "Failed to set status to printing" assert self.device.parseGcode(self.job, isVerbose=isVerbose), f"Failed to parse Gcode, status: {self.status}, verdict: {self.device.verdict}, file: {self.job.file_name_original}" # this is the actual command to read the file and fabricate. - if isVerbose: self.device.logger.debug(f"Job complete, verdict: {self.device.verdict}") + if isVerbose and hasattr(self.device,"logger"): self.device.logger.debug(f"Job complete, verdict: {self.device.verdict}") self.handleVerdict() - if isVerbose: self.device.logger.debug(f"Verdict handled, status: {self.status}") + if isVerbose and hasattr(self.device,"logger"): self.device.logger.debug(f"Verdict handled, status: {self.status}") return True except Exception as e: from app import handle_errors_and_logging diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 1c0147ed..d244d38b 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -33,8 +33,8 @@ class Printer(Device, metaclass=ABCMeta): bedTemperature: int | float | None = None nozzleTemperature: int | float | None = None - def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None): - super().__init__(dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger) + def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None, addLogger: bool =False): + super().__init__(dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) self.filamentType = None self.filamentDiameter = None self.nozzleDiameter = None @@ -243,14 +243,15 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): self.handleTempLine(decLine) if func != checkBedTemp and func != checkExtruderTemp: continue - self.logger.debug(f"{gcode.decode().strip()}: {decLine}") + if hasattr(self, "logger") and self.logger: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") if func(line): break except UnicodeDecodeError: - if isVerbose: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") + if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") continue except Exception as e: - self.logger.error(e) + if current_app: return current_app.handle_errors_and_logging(e, self) + elif hasattr(self, "logger") and self.logger: self.logger.error(e) return False if not callables: self.logger.info(f"{gcode.decode().strip()}: Always True") diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 0a9688ba..4182b2f0 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -33,8 +33,9 @@ def extractIndex(self, gcode: bytes) -> str: hashIndex += ".01" if g29addon == "P1" else ".02" except IndexError as e: hashIndex += ".01" - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") + if hasattr(self, "logger"): + if hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") + elif hashIndex == "G28": + self.logger.info("Homing...") return hashIndex diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index a2d8693b..936ff420 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -46,8 +46,7 @@ def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=N os.makedirs(log_folder, exist_ok=True) subfolder = os.path.join(log_folder, deviceName) os.makedirs(subfolder, exist_ok=True) - from datetime import datetime - fileLogger = CustomFileHandler(os.path.join(subfolder, f"{datetime.now().strftime('%m-%d-%Y__%H-%M-%S')}.log")) + fileLogger = CustomFileHandler(os.path.join(subfolder, "fabricator.log")) else: if not os.path.exists(fileLogger): fileLogger = CustomFileHandler(fileLogger, mode='w') diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 089790aa..db76f290 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -5,18 +5,35 @@ from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode from flask import current_app as app +from Classes.FabricatorConnection import EmuListPortInfo class Ports: @staticmethod def getPorts(): """Get a list of all connected serial ports.""" ports = serial.tools.list_ports.comports() - - try: - with app.app_context(): - full_devices = [Fabricator.staticCreateDevice(port) for port in ports if app.fabricator_list.getFabricatorByPort(port) is None] - except Exception as e: - full_devices = [Fabricator.staticCreateDevice(port) for port in ports] + emu_port, emu_name, emu_hwid = app.get_emu_ports() + if emu_port and emu_name and emu_hwid: + ports.append(EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid)) + # if app: + # if app.fabricator_list is not None: + # print(1) + # [print(fab.device) for fab in app.fabricator_list.fabricators] + # else: + # print("No fabricator list") + full_devices = [] + for port in ports: + if app: + # print("app exists") + if app.fabricator_list.getFabricatorByPort(port) is None: + # print(2) + # print(port) + device = Fabricator.staticCreateDevice(port) + # print(3) + # print(device) + full_devices.append(device) + else: + full_devices.append(Fabricator(port).device) devices = [{ "device": device.__to_JSON__(), "hwid": device.getHWID(), @@ -33,7 +50,11 @@ def getPortByName(name: str): """Get a specific port by its device name.""" assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" ports = Ports.getListPorts() + emu_port, emu_name, emu_hwid = app.get_emu_ports() + if emu_port and emu_name and emu_hwid: + ports.append(EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid)) for port in ports: + if not port: continue if port.device == name: return port return None @@ -77,141 +98,4 @@ def diagnosePort(port: ListPortInfo | SysFS) -> str: return f"Diagnosis result for {port.device}: {response}" except Exception as e: - return f"Error diagnosing port {port.device}: {e}" - -# # Blueprint for ports routes -# ports_bp = Blueprint("ports", __name__) -# -# @ports_bp.route("/getports", methods=["GET"]) -# def getPorts(): -# """Get a list of all connected ports.""" -# try: -# ports = Ports.getPorts() -# print("getting ports from route") -# print(ports if ports else "No ports found") -# return jsonify([port.device for port in ports]) -# except Exception as e: -# print(f"Error getting ports: {e}") -# return jsonify({"error": "Failed to retrieve ports"}), 500 -# -# @ports_bp.route("/getfabricators", methods=["GET"]) -# def getRegisteredFabricators(): -# """Get a list of all registered fabricators.""" -# try: -# fabricators = Ports.getRegisteredFabricators() -# return jsonify([{ -# "name": fab.name, -# "description": fab.description, -# "hwid": fab.hwid, -# "devicePort": fab.devicePort, -# "status": fab.getStatus() -# } for fab in fabricators]) -# except Exception as e: -# print(f"Error getting registered fabricators: {e}") -# return jsonify({"error": "Failed to retrieve registered fabricators"}), 500 -# -# @ports_bp.route("/register", methods=["POST"]) -# def registerFabricator(): -# """Register a new fabricator with the system.""" -# try: -# data = request.get_json() -# device = data['fabricator']['device'] -# description = data['fabricator']['description'] -# hwid = data['fabricator']['hwid'] -# name = data['fabricator']['name'] -# -# new_fabricator = Fabricator(Ports.getPortByName(device), name) -# new_fabricator.description = description -# new_fabricator.hwid = hwid -# -# return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) -# except SQLAlchemyError as db_err: -# print(f"Database error during registration: {db_err}") -# return jsonify({"error": "Database error occurred"}), 500 -# except Exception as e: -# print(f"Error registering fabricator: {e}") -# return jsonify({"error": "Failed to register fabricator"}), 500 -# -# @ports_bp.route("/deletefabricator", methods=["POST"]) -# def deleteFabricator(): -# """Delete a fabricator from the system.""" -# try: -# data = request.get_json() -# fabricator_id = data['fabricator_id'] -# fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() -# -# if fabricator: -# Fabricator.query.filter_by(dbID=fabricator_id).delete() -# Fabricator.updateDB() -# return jsonify({"success": True, "message": "Fabricator deleted successfully"}) -# else: -# return jsonify({"error": "Fabricator not found"}), 404 -# except Exception as e: -# print(f"Error deleting fabricator: {e}") -# return jsonify({"error": "Failed to delete fabricator"}), 500 -# -# @ports_bp.route("/editname", methods=["POST"]) -# def editName(): -# """Edit the name of a registered fabricator.""" -# try: -# data = request.get_json() -# fabricator_id = data['fabricator_id'] -# new_name = data['name'] -# -# fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() -# if fabricator: -# fabricator.setName(new_name) -# return jsonify({"success": True, "message": "Fabricator name updated successfully"}) -# else: -# return jsonify({"error": "Fabricator not found"}), 404 -# except Exception as e: -# print(f"Error editing fabricator name: {e}") -# return jsonify({"error": "Failed to edit fabricator name"}), 500 -# -# @ports_bp.route("/diagnose", methods=["POST"]) -# def diagnoseFabricator(): -# """Diagnose a fabricator based on its port.""" -# try: -# data = request.get_json() -# device_name = data['device'] -# port = Ports.getPortByName(device_name) -# -# if port: -# diagnosis_result = Ports.diagnosePort(port) -# return jsonify({"success": True, "message": diagnosis_result}) -# else: -# return jsonify({"error": "Device not found"}), 404 -# except Exception as e: -# print(f"Error diagnosing fabricator: {e}") -# return jsonify({"error": "Failed to diagnose fabricator"}), 500 -# -# @ports_bp.route("/movehead", methods=["POST"]) -# def moveHead(): -# """Move the head of a fabricator. Deprecated??????""" -# try: -# data = request.get_json() -# device_name = data['port'] -# port = Ports.getPortByName(device_name) -# -# if port: -# fabricator = Fabricator(port) -# result = fabricator.device.home() # Ensuring `home()` method from Device is used -# return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) -# else: -# return jsonify({"error": "Device not found"}), 404 -# except Exception as e: -# print(f"Error moving head: {e}") -# return jsonify({"error": "Failed to move fabricator head"}), 500 -# -# @ports_bp.route("/movefabricatorlist", methods=["POST"]) -# def moveFabricatorList(): -# """Change the order of fabricators.""" -# try: -# data = request.get_json() -# fabricator_ids = data['fabricator_ids'] -# from app import fabricator_list -# result = fabricator_list.moveFabricatorList(fabricator_ids) -# return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) -# except Exception as e: -# print(f"Error moving fabricator list: {e}") -# return jsonify({"error": "Failed to move fabricator list"}), 500 \ No newline at end of file + return f"Error diagnosing port {port.device}: {e}" \ No newline at end of file diff --git a/server/app.py b/server/app.py index 06aba2cf..3d41f480 100644 --- a/server/app.py +++ b/server/app.py @@ -1,5 +1,6 @@ import asyncio import logging +import re import sys import threading import uuid @@ -29,7 +30,25 @@ async def handle_client(websocket): try: while True: message = await websocket.recv() + assert isinstance(message, str), f"Received non-string message: {message}, Type: ({type(message)})" print(f"Received message from {client_id}: {message}") + if not hasattr(emulator_connections[client_id],"fake_port"): + fake_port = message.split('port":"')[-1].split('",')[0] + print(f"Fake port: {fake_port}" if fake_port else "No fake port found") + if fake_port: + emulator_connections[client_id].fake_port = fake_port + fake_name = message.split('Name":"')[-1].split('",')[0] + print(f"Fake name: {fake_name}" if fake_name else "No fake name found") + if fake_name: + emulator_connections[client_id].fake_name = fake_name + fake_hwid = message.split('Hwid":"')[-1].split('",')[0] + print(f"Fake Hwid: {fake_hwid}" if fake_hwid else "No fake Hwid found") + if fake_hwid: + emulator_connections[client_id].fake_hwid = fake_hwid + else: + print(f"Fake port: {emulator_connections[client_id].fake_port}") + print(f"Fake name: {emulator_connections[client_id].fake_name}") + print(f"Fake Hwid: {emulator_connections[client_id].fake_hwid}") #await websocket.send(f"Echo from {client_id}: {message}") except websockets.exceptions.ConnectionClosed as e: # Handle disconnection gracefully @@ -96,12 +115,21 @@ def start_websocket(): socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode, transport=['websocket', 'polling']) # make it eventlet on production! app.socketio = socketio # Add the SocketIO object to the app object +def get_emu_ports(): + fake_device = next(iter(emulator_connections.values()), None) + if fake_device: + fake_port = fake_device.fake_port + fake_name = fake_device.fake_name + fake_hwid = fake_device.fake_hwid + return [fake_port, fake_name, fake_hwid] + return [None, None, None] + def handle_errors_and_logging(e: Exception | str, fabricator = None): from Classes.Fabricators.Fabricator import Fabricator device = fabricator if isinstance(fabricator, Fabricator): device = fabricator.device - if device is not None and device.logger is not None: + if device is not None and hasattr(device,"logger") and device.logger is not None: device.logger.error(e, stacklevel=3) elif app.logger is None: if isinstance(e, str): @@ -159,6 +187,8 @@ def handle_connect(): # Define directory paths for uploads and tempcsv uploads_folder = os.path.abspath('../uploads') tempcsv = os.path.abspath('../tempcsv') + app.get_emu_ports = get_emu_ports + app.emulator_connections = emulator_connections fabricator_list = FabricatorList(app) app.fabricator_list = fabricator_list # Check if directories exist and handle them accordingly diff --git a/server/controllers/emulator.py b/server/controllers/emulator.py index 3ce0752c..d15f8dae 100644 --- a/server/controllers/emulator.py +++ b/server/controllers/emulator.py @@ -1,6 +1,6 @@ import asyncio import json -from flask import Blueprint, jsonify, request +from flask import Blueprint, jsonify, request, current_app emulator_bp = Blueprint("emulator", __name__) @@ -8,13 +8,10 @@ def registerEmulator(): try: data = request.get_json() - - from app import emulator_connections - - print(emulator_connections) - - socket = next(iter(emulator_connections.values())) + socket = next(iter(current_app.emulator_connections.values()), None) + if not socket: + return jsonify({"error": "No emulator connection found"}), 404 try: message = { 'event': 'printer_connect', diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 4cbd39b6..c0f53889 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -559,8 +559,6 @@ def downloadCSV(): else: # Call the model method to get the CSV content res = Job.downloadCSV(0, jobids) - - print(res) return res except Exception as e: diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 4fe61cfc..90a80070 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,5 +1,7 @@ from sqlalchemy.exc import SQLAlchemyError from flask import Blueprint, jsonify, request, current_app + +import app from Classes.Fabricators.Fabricator import Fabricator from Classes.Ports import Ports from app import handle_errors_and_logging @@ -38,7 +40,8 @@ def registerFabricator(): name = printer['name'] # Create a new fabricator instance using the Fabricator class - new_fabricator = Fabricator(Ports.getPortByName(device), name=name) + app.fabricator_list.addFabricator(device, name) + new_fabricator = Fabricator.query.filter_by(devicePort=device).first() return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") @@ -52,15 +55,13 @@ def deleteFabricator(): """Delete a fabricator from the system.""" try: data = request.get_json() + print(data) fabricator_id = data['fabricator_id'] - fabricator = Fabricator.query.filter_by(dbID=fabricator_id).first() - - if fabricator: - Fabricator.query.filter_by(dbID=fabricator_id).delete() - Fabricator.updateDB() - return jsonify({"success": True, "message": "Fabricator deleted successfully"}) - else: + res = app.fabricator_list.deleteFabricator(fabricator_id) + if isinstance(res, ValueError): return jsonify({"error": "Fabricator not found"}), 404 + if res: return jsonify({"success": True, "message": "Fabricator deleted successfully"}) + return jsonify({"error": "Failed to delete fabricator"}), 500 except Exception as e: handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @@ -134,15 +135,14 @@ def moveHead(): if port: if current_app: fab = current_app.fabricator_list.getFabricatorByPort(port) - if fab: device = current_app.fabricator_list.getFabricatorByPort(port).device + print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {current_app.fabricator_list.fabricators}, threads: {current_app.fabricator_list.fabricator_threads}") + if fab: device = fab.device else: device = Fabricator.staticCreateDevice(Ports.getPortByName(port)) else: device = Fabricator(port).device device.connect() result = device.home() # Use home() method from Device device.disconnect() - res = jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) - print(res) - return res + return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) else: return jsonify({"error": "Device not found"}), 404 except Exception as e: diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index 22d3766f..789aef8e 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -77,11 +77,9 @@ def removeThread(): def editName(): try: data = request.get_json() - printerid = data['printerid'] + fabricator_id = data['fabricator_id'] name = data['newname'] - app.fabricator_list.getFabricatorByHwid() - res = app.fabricator_list.editName(printerid, name) - return res + return app.fabricator_list.editName(fabricator_id, name) except Exception as e: handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 From 3017e2759f81a6ab57370b89b3780f73a1a58903 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Tue, 3 Dec 2024 16:22:40 -0500 Subject: [PATCH 161/194] fix: diagnose and repair ports works --- client/src/views/RegisteredView.vue | 4 +- server/Classes/FabricatorConnection.py | 18 ++++++- server/Classes/FabricatorList.py | 12 ++--- server/Classes/Fabricators/Device.py | 70 +++++++++++++------------- server/controllers/ports.py | 19 ++++--- 5 files changed, 71 insertions(+), 52 deletions(-) diff --git a/client/src/views/RegisteredView.vue b/client/src/views/RegisteredView.vue index 9a0f8d4b..13d33870 100644 --- a/client/src/views/RegisteredView.vue +++ b/client/src/views/RegisteredView.vue @@ -259,8 +259,8 @@ const doCloseRegisterModal = async () => { <h6 class="card-text mt-0"> <b>HWID:</b> {{ printer.hwid }}</h6> <div v-if="messageId == printer.id && showMessage" - class="alert alert-danger d-flex flex-column align-items-center justify-content-center"> - <h6 v-html="message"></h6> + class="alert alert-light d-flex flex-column align-items-center justify-content-center" style="overflow: auto; word-wrap: break-word;"> + <p v-html="message" style="width: 100%; word-break: break-word"></p> <div class="d-flex justify-content-between"> <button class="btn btn-secondary me-3" @click="clearMessage()">Clear</button> <button class="btn btn-primary w-100" @click="doRepair()">Repair Ports</button> diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 4d7fccaf..f41a2cf0 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -10,8 +10,22 @@ class FabricatorConnection(ABC): @staticmethod - def staticCreateConnection(port: str = None, baudrate: int = None, timeout: float = 10.0, websocket_connections: dict = None, fabricator_id: str = None): - """Create a new connection to a 3D printer.""" + def staticCreateConnection(port = None, baudrate = None, timeout = 10.0, websocket_connections = None, fabricator_id = None): + """ + Create a new connection to a 3D printer. + :param port: Serial port to connect to + :type port: str + :param baudrate: Baudrate for the serial connection + :type baudrate: int + :param timeout: Maximum time to wait for a response (default: 10.0) + :type timeout: float + :param websocket_connections: Dictionary of websocket connections + :type websocket_connections: dict + :param fabricator_id: Unique identifier for the printer + :type fabricator_id: str + :return: FabricatorConnection instance + :rtype: SerialConnection | SocketConnection + """ if websocket_connections is not None and fabricator_id is not None: return SocketConnection(websocket_connections, fabricator_id) elif port is not None and baudrate is not None: diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 2f351ad9..b38262bb 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -1,5 +1,5 @@ import serial.tools.list_ports -from flask import jsonify +from flask import jsonify, current_app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS @@ -166,7 +166,7 @@ def start_fabricator_thread(self, fabricator: Fabricator): def create_fabricator_threads(self): for fabricator in self: - fabricator.setQueue(Queue()) # Ensure each fabricator has its own queue + fabricator.queue = Queue() # Ensure each fabricator has its own queue fabricator_thread = self.start_fabricator_thread(fabricator) self.fabricator_threads.append(fabricator_thread) self.ping_thread = Thread(target=self.pingForStatus) @@ -206,22 +206,22 @@ def update_thread(self, fabricator): def resetThread(self, fabricator_id): try: for thread in self.fabricator_threads: - if thread.fabricator.id == fabricator_id: + if thread.fabricator.dbID == fabricator_id: fabricator = thread.fabricator fabricator.terminated = 1 thread_data = { - "id": fabricator.id, + "id": fabricator.dbID, "device": fabricator.device, "description": fabricator.description, "hwid": fabricator.hwid, "name": fabricator.name, } self.fabricator_threads.remove(thread) - self.create_fabricator_threads() + self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: - print(f"Unexpected error: {e}") + current_app.handle_errors_and_logging(e) return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 def queueRestore(self, fabricator_id, status): diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index da04e7a3..154dd1a1 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -1,21 +1,16 @@ -import io import os.path import sys from abc import ABC from time import sleep - from flask import current_app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS - from Classes.Jobs import Job from Classes.Vector3 import Vector3 from Classes.Logger import Logger -import serial -import serial.tools.list_ports from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkXYZ -from Classes.FabricatorConnection import FabricatorConnection +from Classes.FabricatorConnection import SerialConnection, SocketConnection, FabricatorConnection class Device(ABC): @@ -25,7 +20,7 @@ class Device(ABC): PRODUCTID: int | None = None DESCRIPTION: str | None = None MAXFEEDRATE: int | None = None - serialConnection: FabricatorConnection | None = None + serialConnection: SocketConnection | SerialConnection | None = None homePosition: Vector3 | None = None homeCMD: bytes | None= b"G28\n" @@ -87,8 +82,7 @@ def connect(self): self.serialConnection.reset_input_buffer() return True except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e, self) + current_app.handle_errors_and_logging(e, self) return False def disconnect(self): @@ -102,6 +96,8 @@ def home(self, isVerbose: bool = True): Home the device. :param isVerbose: Whether to log the command. :type isVerbose: bool + :return: True if the device is homed, else False + :rtype: bool """ try: assert isinstance(isVerbose, bool) @@ -120,11 +116,13 @@ def goTo(self, loc: Vector3, isVerbose: bool = False): :type loc: Vector3 :param isVerbose: whether to log the command :type isVerbose: bool + :return: True if the tool head is at the location, else False + :rtype: bool :raises AssertionError: if the location is not a Vector3, if isVerbose is not a bool, or if self is not a Device """ - assert isinstance(loc, Vector3) - assert isinstance(isVerbose, bool) - assert isinstance(self, Device) + assert isinstance(loc, Vector3), f"Expected Vector3, got {type(loc)}" + assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" + assert isinstance(self, Device), f"Expected Device, got {type(self)}" self.sendGcode(f"G0 X{loc.x} Y{loc.y} Z{loc.z} F{str(self.MAXFEEDRATE)}\n".encode("utf-8"), isVerbose=isVerbose) self.sendGcode(f'M114\n'.encode("utf-8"), isVerbose=isVerbose) if hasattr(self, "getLocationCMD"): @@ -138,6 +136,8 @@ def parseGcode(self, job: Job, isVerbose: bool = False): :type job: Job :param isVerbose: Whether to log the commands :type isVerbose: bool + :return: True if the job is complete, else False + :rtype: bool :raises AssertionError: if the file is not a string or if isVerbose is not a bool """ assert isinstance(job, Job), f"Expected Job object, got {type(job)}" @@ -147,14 +147,14 @@ def parseGcode(self, job: Job, isVerbose: bool = False): assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" try: with open(file, "r") as f: - self.logger.info(f"Printing {file}") + if hasattr(self, "logger"): self.logger.info(f"Printing {file}") for line in f: if line.startswith(";") or line == "\n": continue if current_app: with current_app.app_context(): current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) - if isVerbose: self.logger.debug(line.strip("\n")) + if isVerbose and hasattr(self, "logger"): self.logger.debug(line.strip("\n")) if self.status == "paused": self.pause() while self.status == "paused": @@ -163,7 +163,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - self.logger.info("Job cancelled") + if hasattr(self, "logger"): self.logger.info("Job cancelled") return True elif self.status == "printing": self.resume() @@ -171,20 +171,20 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - self.logger.info("Job cancelled") + if hasattr(self, "logger"): self.logger.info("Job cancelled") return True if ";" in line: line = line.split(";")[0].strip() + "\n" self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" - self.logger.info("Job complete") + if hasattr(self, "logger"): self.logger.info("Job complete") return True except Exception as e: - if self.logger is None: + if not hasattr(self, "logger") or self.logger is None: print(e) else: - self.logger.error("Error cancelling job:") - self.logger.error(e) + if hasattr(self, "logger"): self.logger.error("Error cancelling job:") + if hasattr(self, "logger"): self.logger.error(e) self.verdict = "error" return True @@ -212,13 +212,14 @@ def sendGcode(self, gcode: bytes, isVerbose: bool = False): assert isinstance(gcode, bytes) assert isinstance(isVerbose, bool) self.serialConnection.write(gcode) - if isVerbose: self.logger.debug(gcode.decode("utf-8")) + if isVerbose and hasattr(self, "logger"): self.logger.debug(gcode.decode("utf-8")) return True - def getToolHeadLocation(self, isVerbose: bool = False) -> Vector3: + def getToolHeadLocation(self, isVerbose = False): """ Get the current location of the tool head. :param isVerbose: Whether to log the command + :type isVerbose: bool :return: the current location of the tool head :rtype: Vector3 """ @@ -227,7 +228,7 @@ def getToolHeadLocation(self, isVerbose: bool = False) -> Vector3: response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") - if isVerbose: self.logger.info(response) + if isVerbose and hasattr(self, "logger"): self.logger.info(response) loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) @@ -236,25 +237,25 @@ def repair(self): try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip specific repair commands - if self.logger: + if hasattr(self, "logger") and self.logger: self.logger.info(f"Repair skipped for {self.MODEL}") return "Repair not necessary for Ender devices." if self.serialConnection: - self.logger.info("Closing existing connection for repair.") + if hasattr(self, "logger"): self.logger.info("Closing existing connection for repair.") self.serialConnection.close() # Attempt to reconnect - self.logger.info("Attempting to reconnect for repair.") + if hasattr(self, "logger"): self.logger.info("Attempting to reconnect for repair.") self.connect() if self.serialConnection and self.serialConnection.is_open: - self.logger.info("Repair successful: connection reopened.") + if hasattr(self, "logger"): self.logger.info("Repair successful: connection reopened.") return "Repair successful." else: return "Repair failed: unable to reopen connection." except Exception as e: - self.logger.error(f"Error during repair: {e}") + if hasattr(self, "logger"): self.logger.error(f"Error during repair: {e}") return f"Repair failed with error: {e}" def diagnose(self): @@ -262,26 +263,25 @@ def diagnose(self): try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip the diagnosis - self.logger.info(f"Diagnosis skipped for {self.MODEL}") + if hasattr(self, "logger"): self.logger.info(f"Diagnosis skipped for {self.MODEL}") return "Diagnosis not necessary for Ender devices." - self.logger.info("Starting device diagnosis.") + if hasattr(self, "logger"): self.logger.info("Starting device diagnosis.") if not self.connect(): return "Diagnosis failed: unable to connect." - self.logger.info("Sending diagnostic G-code command (e.g., M115).") + if hasattr(self, "logger"): self.logger.info("Sending diagnostic G-code command (e.g., M115).") self.sendGcode(b"M115\n") response = self.serialConnection.readline().decode("utf-8").strip() - self.disconnect() if response: - self.logger.info(f"Diagnosis response: {response}") - return f"Diagnosis successful: {response}" + if hasattr(self, "logger"): self.logger.info(f"Diagnosis response: {response}") + return response else: return "Diagnosis failed: no response from device." except Exception as e: - self.logger.error(f"Error during diagnosis: {e}") + if hasattr(self, "logger"): self.logger.error(f"Error during diagnosis: {e}") return f"Diagnosis failed with error: {e}" def hardReset(self, newStatus: str): diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 90a80070..05a65cba 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -2,6 +2,7 @@ from flask import Blueprint, jsonify, request, current_app import app +from Classes.Fabricators.Device import Device from Classes.Fabricators.Fabricator import Fabricator from Classes.Ports import Ports from app import handle_errors_and_logging @@ -91,14 +92,18 @@ def diagnoseFabricator(): data = request.get_json() device_name = data['device'] port = Ports.getPortByName(device_name) - - if port: - fabricator = Fabricator(port) # Ensure the Fabricator has this method - if fabricator: - diagnosis_result = fabricator.diagnose() - return jsonify({"success": True, "message": diagnosis_result}) + fabricator = current_app.fabricator_list.getFabricatorByPort(port) + if fabricator: + device = fabricator.device + if device is None: + device = Fabricator.staticCreateDevice(port) # Ensure the Fabricator has this method + if device is not None: + assert isinstance(device, Device), f"Device must be an instance of Device: {device} : {type(device)}" + diagnosis_result = device.diagnose() + return jsonify({"success": True, "message": "Diagnosis successful", "diagnoseString": diagnosis_result}) else: - return jsonify({"error": "Failed to create fabricator for diagnosis"}), 500 + print("no device?") + return jsonify({"error": "Failed to create device for diagnosis"}), 500 else: return jsonify({"error": "Device not found"}), 404 except Exception as e: From 2dd386630be212b0e921422089dac3f603e9615d Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Tue, 3 Dec 2024 19:31:34 -0500 Subject: [PATCH 162/194] feat: current app and app custom implementation --- client/src/model/jobs.ts | 3 +- client/src/model/sockets.ts | 6 +- server/Classes/FabricatorConnection.py | 3 + server/Classes/FabricatorList.py | 2 +- server/Classes/Fabricators/Device.py | 2 +- server/Classes/Fabricators/Fabricator.py | 23 +- .../Classes/Fabricators/Printers/Printer.py | 8 +- server/Classes/Jobs.py | 11 +- server/Classes/Ports.py | 12 +- server/Classes/Queue.py | 2 +- server/app.py | 241 ++++++++++-------- server/controllers/jobs.py | 137 +++++----- server/controllers/ports.py | 23 +- server/controllers/statusService.py | 31 +-- 14 files changed, 257 insertions(+), 247 deletions(-) diff --git a/client/src/model/jobs.ts b/client/src/model/jobs.ts index 3e14d40e..02195494 100644 --- a/client/src/model/jobs.ts +++ b/client/src/model/jobs.ts @@ -226,10 +226,9 @@ export function useGetJobs() { countOnly?: number ) { try { - const response = await api( + return await api( `getjobs?page=${page}&pageSize=${pageSize}&printerIds=${JSON.stringify(printerIds)}&oldestFirst=${oldestFirst}&searchJob=${encodeURIComponent(searchJob)}&searchCriteria=${encodeURIComponent(searchCriteria)}&searchTicketId=${encodeURIComponent(searchTicketId)}&favoriteOnly=${favoriteOnly}&issueIds=${JSON.stringify(issues)}&startdate=${startdate}&enddate=${enddate}&fromError=${fromError}&countOnly=${countOnly}` ) - return response } catch (error) { console.error(error) toast.error('An error occurred while retrieving the jobs') diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 9150fc6d..cc954099 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -1,6 +1,7 @@ import { socket } from './myFetch' import type { Device } from './ports' import { jobTime } from './jobs' +import { ref } from 'vue' export function setupSockets(printers: any) { setupTempSocket(printers) @@ -205,7 +206,8 @@ export function setupGCodeViewerSocket(printers: any) { } }) } - +import * as GCodePreview from 'gcode-preview'; +export const preview = ref<GCodePreview.WebGLPreview>(); export function setupGCodeLineSocket(printers: any) { socket.value.on('gcode_line', (data: any) => { if (printers) { @@ -215,7 +217,7 @@ export function setupGCodeLineSocket(printers: any) { if (job) { //TODO: add gcode_line to wherever it needs to go - job.gcode_line = data.gcode_line + preview?.value?.processGCode(data.gcode_line) } } else { console.error('printers or printers.value is undefined') diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index f41a2cf0..eff80737 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -26,6 +26,7 @@ def staticCreateConnection(port = None, baudrate = None, timeout = 10.0, websock :return: FabricatorConnection instance :rtype: SerialConnection | SocketConnection """ + print(f"Creating connection with port: {port}, baudrate: {baudrate}, timeout: {timeout}, websocket_connections: {websocket_connections}, fabricator_id: {fabricator_id}") if websocket_connections is not None and fabricator_id is not None: return SocketConnection(websocket_connections, fabricator_id) elif port is not None and baudrate is not None: @@ -196,6 +197,8 @@ def __init__(self, device: str, description: str = None, hwid: str = None): self._device = device self._description = description self._hwid = hwid + self.vid = int(hwid.split("PID=")[1].split(":")[0], 16) if hwid else None + self.pid = int(hwid.split(":")[2].split(" ")[0], 16) if hwid else None def __repr__(self): return f"EmuListPortInfo(device={self.device}, description={self.description}, hwid={self.hwid})" diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index b38262bb..39c5342c 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -10,7 +10,7 @@ from Classes.Queue import Queue from threading import Thread import time -from flask import current_app as app +from app import current_app as app class FabricatorList: diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 154dd1a1..8a1a6baa 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -2,7 +2,7 @@ import sys from abc import ABC from time import sleep -from flask import current_app +from app import current_app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from Classes.Jobs import Job diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 304fa2fc..2b181a67 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,12 +1,11 @@ -from flask import jsonify, Response +from flask import jsonify, Response, current_app as app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError - -import app -from Classes.FabricatorConnection import FabricatorConnection +from Classes.FabricatorConnection import FabricatorConnection, EmuListPortInfo from Classes.Fabricators.Device import Device from typing_extensions import TextIO +from Classes.Jobs import Job from Mixins.hasEndingSequence import hasEndingSequence from models.db import db from datetime import datetime, timezone @@ -92,7 +91,7 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: return response @staticmethod - def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None): + def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, websocket_connection=None): """ creates the correct printer object based on the serial port info :param serialPort: @@ -104,16 +103,17 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No :return: device without a fabricator object :rtype: Device | None """ - if serialPort is None: - return None + assert serialPort is not None, "Serial port is None" from Classes.Fabricators.Printers.Ender.EnderPrinter import EnderPrinter from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter if serialPort.vid == PrusaPrinter.VENDORID: + print("Creating Prusa Printer") from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: + print("Creating Prusa MK4") return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) elif serialPort.pid == PrusaMK4S.PRODUCTID: return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) @@ -203,8 +203,8 @@ def queryAll(cls): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) fake_port, fake_name, fake_hwid = app.get_emu_ports() - if fake_port and fake_name: - fabList.append(cls(fake_port, fake_name)) + if fake_port and fake_name and fake_hwid: + fabList.append(cls(EmuListPortInfo(fake_port, "Emulator", fake_hwid), fake_name)) return fabList @classmethod @@ -305,14 +305,13 @@ def setStatus(self, newStatus): self.job.status = newStatus self.queue[0].status = newStatus db.session.commit() - from flask import current_app + from app import current_app if current_app: current_app.socketio.emit( "status_update", {"fabricator_id": self.dbID, "status": newStatus} ) if self.job is not None: - current_app.socketio.emit('job_status_update', { - 'job_id': self.job.id, 'status': newStatus}) + Job.update_job_status(self.job.id, newStatus) else: print(f"current app is None, status: {newStatus}") return True diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index d244d38b..30e86192 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -3,7 +3,7 @@ from datetime import datetime from time import sleep -from flask import current_app +from app import current_app from Classes.Fabricators.Device import Device from Classes.Jobs import Job @@ -33,8 +33,8 @@ class Printer(Device, metaclass=ABCMeta): bedTemperature: int | float | None = None nozzleTemperature: int | float | None = None - def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None, addLogger: bool =False): - super().__init__(dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None, addLogger: bool =False, websocket_connection=None): + super().__init__(dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) self.filamentType = None self.filamentDiameter = None self.nozzleDiameter = None @@ -311,7 +311,7 @@ def handleTempLine(self, line: str): self.nozzleTemperature = float(temp_t.group(1)) if temp_b: self.bedTemperature = float(temp_b.group(1)) - from flask import current_app + from app import current_app if current_app: current_app.socketio.emit('temp_update', {'printerid': self.dbID, 'extruder_temp': self.nozzleTemperature, 'bed_temp': self.bedTemperature}) diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index a88468c2..49cec2a4 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -5,7 +5,8 @@ from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory from datetime import timezone, timedelta -from flask import jsonify, current_app +from flask import jsonify +from app import current_app from traceback import format_exc from sqlalchemy.exc import SQLAlchemyError from datetime import datetime @@ -126,10 +127,7 @@ def get_job_history( ): try: query = cls.query - - print("fromError: ", fromError) if (fromError == 1): - print("here") query = cls.query.filter_by(status="error") if printerIds: @@ -295,11 +293,6 @@ def findJob(cls, job_id): print(f"Database error: {e}") return jsonify({"error": format_exc()}), 500 - # @classmethod - # def findPrinterObject(self, printer_id): - # threads = printer_status_service.getThreadArray() - # return list(filter(lambda thread: thread.printer.id == printer_id, threads))[0].printer - @classmethod def removeFileFromPath(cls, file_path): # file_path = self.generatePath() # Get the file path diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index db76f290..9b5dd9b7 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -4,7 +4,7 @@ from serial.tools.list_ports_linux import SysFS from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode -from flask import current_app as app +from app import current_app as app from Classes.FabricatorConnection import EmuListPortInfo class Ports: @@ -24,13 +24,11 @@ def getPorts(): full_devices = [] for port in ports: if app: - # print("app exists") if app.fabricator_list.getFabricatorByPort(port) is None: - # print(2) - # print(port) - device = Fabricator.staticCreateDevice(port) - # print(3) - # print(device) + if port.device == emu_port: + device = Fabricator.staticCreateDevice(port, websocket_connection=next(iter(app.emulator_connections.values()))) + else: + device = Fabricator.staticCreateDevice(port) full_devices.append(device) else: full_devices.append(Fabricator(port).device) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 3bf76d8b..9e4d807a 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,5 +1,5 @@ from collections import deque -from flask import current_app +from app import current_app from Classes.Jobs import Job diff --git a/server/app.py b/server/app.py index 3d41f480..5f1e0b4a 100644 --- a/server/app.py +++ b/server/app.py @@ -1,21 +1,19 @@ import asyncio import logging -import re import sys import threading import uuid -from flask import Flask, request, Response, send_from_directory + +from flask import request, Response, send_from_directory, Flask, current_app as flask_current_app from flask_cors import CORS import os -from models.db import db from flask_migrate import Migrate from dotenv import load_dotenv import shutil from flask_socketio import SocketIO import websockets -from models.config import Config -from Classes.FabricatorList import FabricatorList from routes import defineRoutes +from werkzeug.local import LocalProxy emulator_connections = {} @@ -78,108 +76,131 @@ def start_websocket(): # moved this up here so we can pass the app to the PrinterStatusService # Basic app setup -app = Flask(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) -app.config.from_object(__name__) # update application instantly -logs = os.path.join(root_path,"server", "logs") -if not os.path.exists(logs): os.makedirs(logs) -from Classes.Logger import Logger -app.logger = Logger("App", consoleLogger=sys.stdout, fileLogger=os.path.abspath(os.path.join(logs, "app.log")), consoleLevel=logging.ERROR) -# start database connection -app.config["environment"] = Config.get('environment') -app.config["ip"] = Config.get('ip') -app.config["port"] = Config.get('port') -app.config["base_url"] = Config.get('base_url') - -load_dotenv() -basedir = os.path.abspath(os.path.join(root_path, "server")) -database_file = os.path.abspath(os.path.join(basedir, Config.get('database_uri'))) -if isinstance(database_file, bytes): - database_file = database_file.decode('utf-8') -databaseuri = 'sqlite:///' + database_file -app.config['SQLALCHEMY_DATABASE_URI'] = databaseuri -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -db.init_app(app) - -migrate = Migrate(app, db) -# moved this before importing the blueprints so that it can be accessed by the PrinterStatusService -#printer_status_service = PrinterStatusService(app) - -# Initialize SocketIO, which will be used to send printer status updates to the frontend -# and this specific socket it will be used throughout the backend - -if app.config["environment"] == 'production': - async_mode = 'eventlet' # Use 'eventlet' for production -else: - async_mode = 'threading' # Use 'threading' for development - -socketio = SocketIO(app, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, async_mode=async_mode, transport=['websocket', 'polling']) # make it eventlet on production! -app.socketio = socketio # Add the SocketIO object to the app object - -def get_emu_ports(): - fake_device = next(iter(emulator_connections.values()), None) - if fake_device: - fake_port = fake_device.fake_port - fake_name = fake_device.fake_name - fake_hwid = fake_device.fake_hwid - return [fake_port, fake_name, fake_hwid] - return [None, None, None] - -def handle_errors_and_logging(e: Exception | str, fabricator = None): - from Classes.Fabricators.Fabricator import Fabricator - device = fabricator - if isinstance(fabricator, Fabricator): - device = fabricator.device - if device is not None and hasattr(device,"logger") and device.logger is not None: - device.logger.error(e, stacklevel=3) - elif app.logger is None: - if isinstance(e, str): - print(e.strip()) +class MyFlaskApp(Flask): + def __init__(self): + from models.config import Config + from Classes.FabricatorList import FabricatorList + from models.db import db + + super().__init__(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) + self._logger = None + from Classes.Logger import Logger + logs = os.path.join(root_path, "server", "logs") + os.makedirs(logs, exist_ok=True) + self.logger = Logger("App", consoleLogger=sys.stdout, fileLogger=os.path.abspath(os.path.join(logs, f"{__name__}.log")), + consoleLevel=logging.ERROR) + self.config.from_object(__name__) # update application instantly + # start database connection + self.config["environment"] = Config.get('environment') + self.config["ip"] = Config.get('ip') + self.config["port"] = Config.get('port') + self.config["base_url"] = Config.get('base_url') + + load_dotenv() + basedir = os.path.abspath(os.path.join(root_path, "server")) + database_file = os.path.abspath(os.path.join(basedir, Config.get('database_uri'))) + if isinstance(database_file, bytes): + database_file = database_file.decode('utf-8') + databaseuri = 'sqlite:///' + database_file + self.config['SQLALCHEMY_DATABASE_URI'] = databaseuri + self.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + db.init_app(self) + + Migrate(self, db) + + self.socketio = SocketIO(self, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, + async_mode='eventlet' if self.config["environment"] == 'production' else 'threading', + transport=['websocket', 'polling']) # make it eventlet on production! + + self.emulator_connections = emulator_connections + + CORS(self) + + # Register all routes + defineRoutes(self) + + self.fabricator_list = FabricatorList(self) + + @self.cli.command("test") + def run_tests(): + """Run all tests.""" + import subprocess + subprocess.run(["python", "../Tests/parallel_test_runner.py"]) + + @self.before_request + def handle_preflight(): + if request.method == "OPTIONS": + res = Response() + res.headers['X-Content-Type-Options'] = '*' + res.headers['Access-Control-Allow-Origin'] = '*' + res.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' + res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' + return res + + # Serve static files + @self.route('/') + def serve_static(path='index.html'): + return send_from_directory(self.static_folder, path) + + @self.route('/assets/<path:filename>') + def serve_assets(filename): + return send_from_directory(os.path.join(self.static_folder, 'assets'), filename) + + @self.socketio.on('ping') + def handle_ping(): + self.socketio.emit('pong') + + @self.socketio.on('connect') + def handle_connect(): + print("Client connected") + + + @property + def logger(self): + return self._logger + + @logger.setter + def logger(self, logger): + self._logger = logger + + @logger.getter + def logger(self): + return self._logger + + def handle_errors_and_logging(self, e: Exception | str, fabricator=None): + from Classes.Fabricators.Fabricator import Fabricator + device = fabricator + if isinstance(fabricator, Fabricator): + device = fabricator.device + if device is not None and hasattr(device, "logger") and device.logger is not None: + device.logger.error(e, stacklevel=3) + elif self.logger is None: + if isinstance(e, str): + print(e.strip()) + else: + import traceback + print(traceback.format_exception(None, e, e.__traceback__)) else: - import traceback - print(traceback.format_exception(None, e, e.__traceback__)) - else: - app.logger.error(e, stacklevel=3) - return False - -app.handle_errors_and_logging = handle_errors_and_logging - -CORS(app) - -# Register all routes -defineRoutes(app) - -@app.cli.command("test") -def run_tests(): - """Run all tests.""" - import subprocess - subprocess.run(["python", "../Tests/parallel_test_runner.py"]) - -@app.before_request -def handle_preflight(): - if request.method == "OPTIONS": - res = Response() - res.headers['X-Content-Type-Options'] = '*' - res.headers['Access-Control-Allow-Origin'] = '*' - res.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' - res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' - return res - -# Serve static files -@app.route('/') -def serve_static(path='index.html'): - return send_from_directory(app.static_folder, path) - -@app.route('/assets/<path:filename>') -def serve_assets(filename): - return send_from_directory(os.path.join(app.static_folder, 'assets'), filename) - -@app.socketio.on('ping') -def handle_ping(): - app.socketio.emit('pong') - -@app.socketio.on('connect') -def handle_connect(): - print("Client connected") + self.logger.error(e, stacklevel=3) + return False + + def get_emu_ports(self): + fake_device = next(iter(self.emulator_connections.values()), None) + if fake_device: + fake_port = fake_device.fake_port + fake_name = fake_device.fake_name + fake_hwid = fake_device.fake_hwid + + return [fake_port, fake_name, fake_hwid] + return [None, None, None] + +def _find_custom_app(): + app = flask_current_app._get_current_object() + return app if isinstance(app, MyFlaskApp) else None + +current_app = LocalProxy(_find_custom_app) + +app = MyFlaskApp() # own thread with app.app_context(): @@ -187,10 +208,6 @@ def handle_connect(): # Define directory paths for uploads and tempcsv uploads_folder = os.path.abspath('../uploads') tempcsv = os.path.abspath('../tempcsv') - app.get_emu_ports = get_emu_ports - app.emulator_connections = emulator_connections - fabricator_list = FabricatorList(app) - app.fabricator_list = fabricator_list # Check if directories exist and handle them accordingly for folder in [uploads_folder, tempcsv]: if os.path.exists(folder): @@ -207,7 +224,7 @@ def handle_connect(): def run_socketio(app): # host=app.config["ip"], port=app.config["port"] - socketio.run(app, Debug=True, allow_unsafe_werkzeug=True) + app.socketio.run(app, allow_unsafe_werkzeug=True) if __name__ == "__main__": # If hits last line in GCode file: @@ -215,4 +232,4 @@ def run_socketio(app): # Before sending to printer, query for status. If error, throw error. # since we are using socketio, we need to use socketio.run instead of app.run # which passes the app anyways - run_socketio(app) # Replace app.run with socketio.run \ No newline at end of file + run_socketio(app) # Replace app.run with socketio.run diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index c0f53889..e1f767f4 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -1,14 +1,14 @@ import shutil from flask import Blueprint, jsonify, request from Classes.Jobs import Job +from models.db import db from models.printers import Printer import json import os import gzip import serial import serial.tools.list_ports -from app import handle_errors_and_logging -from flask import current_app +from app import current_app from traceback import format_exc from Classes.Fabricators.Fabricator import Fabricator @@ -40,14 +40,12 @@ def getJobs(): fromError = request.args.get('fromError', default=0, type=int) countOnly = request.args.get('countOnly', default=0, type=int) - - print(fromError) try: res = Job.get_job_history(page, pageSize, printerIds, oldestFirst, searchJob, searchCriteria, searchTicketId, favoriteOnly, issueIds, startdate, enddate, fromError, countOnly) return jsonify(res) except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 # add job to queue @@ -92,15 +90,18 @@ def add_job_to_queue(): priority = request.form['priority'] # if priotiry is '1' then add to front of queue, else add to back + fabricator = findPrinterObject(printer_id) + if fabricator is None: + return jsonify({"error": "Fabricator not found."}), 404 if priority == 'true': - findPrinterObject(printer_id).getQueue().addToFront(job, printer_id) + fabricator.queue.addToFront(job, printer_id) else: - findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) + fabricator.queue.addToBack(job, printer_id) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/autoqueue', methods=["POST"]) @@ -141,12 +142,14 @@ def auto_queue(): job.setFileName(file_name_pk) # set unique in-memory file name job.setFilament(filament) # set filament type - - findPrinterObject(printer_id).getQueue().addToBack(job, printer_id) + fabricator = findPrinterObject(printer_id) + if fabricator is None: + return jsonify({"error": "Fabricator not found."}), 404 + fabricator.queue.addToBack(job, printer_id) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/rerunjob', methods=["POST"]) @@ -156,10 +159,9 @@ def rerun_job(): printerpk = data['printerpk'] # printer to rerun job on jobpk = data['jobpk'] - rerunjob(printerpk, jobpk, "back") - return jsonify({"success": True, "message": "Job added to printer queue."}), 200 + return rerunjob(printerpk, jobpk, "back") except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 # route to insert job into database @@ -182,7 +184,7 @@ def job_db_insert(): return "success" except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 # cancel queued job @@ -200,6 +202,8 @@ def remove_job(): jobstatus = job.getStatus() # retrieve printer object & corresponding queue printerobject = findPrinterObject(printerid) + if printerobject is None: + return jsonify({"error": "Fabricator not found."}), 404 # printerobject.setStatus("complete") queue = printerobject.getQueue() inmemjob = queue.getJob(job) @@ -213,7 +217,7 @@ def remove_job(): return jsonify({"success": True, "message": "Job removed from printer queue."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @@ -234,6 +238,8 @@ def remove_job_from_queue(): jobstatus = job.getStatus() # retrieve printer object & corresponding queue printerobject = findPrinterObject(printerid) + if printerobject is None: + return jsonify({"error": "Fabricator not found."}), 404 # printerobject.setStatus("complete") queue = printerobject.getQueue() inmemjob = queue.getJob(job) @@ -247,7 +253,7 @@ def remove_job_from_queue(): return jsonify({"success": True, "message": "Job removed from printer queue."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @@ -283,19 +289,23 @@ def releasejob(): fabricator.setStatus("error") # printer ready to accept new prints elif key == 2: - rerunjob(printerid, jobpk, "front") if currentStatus!="offline": fabricator.setStatus("ready") # printer ready to accept new prints + return rerunjob(printerid, jobpk, "front") + elif key == 1: if currentStatus!="offline": fabricator.setStatus("ready") # printer ready to accept new prints + + if current_app: + db.session.commit() return jsonify({"success": True, "message": "Job released successfully."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/bumpjob', methods=["POST"]) @@ -307,6 +317,8 @@ def bumpjob(): choice = data['choice'] printerobject = findPrinterObject(printer_id) + if printerobject is None: + return jsonify({"error": "Fabricator not found"}), 404 if choice == 1: printerobject.queue.bump(True, job_id) @@ -321,7 +333,7 @@ def bumpjob(): return jsonify({"success": True, "message": "Job bumped up in printer queue."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/movejob', methods=["POST"]) @@ -332,10 +344,12 @@ def moveJob(): arr = data['arr'] printerobject = findPrinterObject(printer_id) + if printerobject is None: + return jsonify({"error": "Fabricator not found"}), 404 printerobject.queue.reorder(arr) return jsonify({"success": True, "message": "Queue updated successfully."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/updatejobstatus', methods=["POST"]) @@ -356,7 +370,7 @@ def updateJobStatus(): return jsonify(res), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/assigntoerror', methods=["POST"]) @@ -371,13 +385,13 @@ def assignToError(): job = Job.findJob(job_id) printerid = job.getPrinterId() printerobject = findPrinterObject(printerid) - queue = printerobject.getQueue() - - queue.deleteJob(job_id, printerid) + if printerobject is not None: + queue = printerobject.getQueue() + queue.deleteJob(job_id, printerid) return jsonify(res), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/deletejob', methods=["POST"]) @@ -389,23 +403,23 @@ def delete_job(): # Retrieve job to delete & printer id job = Job.findJob(job_id) printer_id = job.getPrinterId() - print("ID: ", printer_id) if printer_id != 0: # Retrieve printer object & corresponding queue printer_object = findPrinterObject(printer_id) - queue = printer_object.getQueue() + if printer_object is not None: + queue = printer_object.getQueue() - # Delete job from the queue - queue.deleteJob(job_id, printer_id) + # Delete job from the queue + queue.deleteJob(job_id, printer_id) - # Delete job from the database + # Delete job from the database Job.delete_job(job_id) return jsonify({"success": True, "message": f"Job with ID {job_id} deleted successfully."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/setstatus", methods=["POST"]) @@ -421,7 +435,7 @@ def setStatus(): else: return jsonify({"error": "Printer not found."}), 404 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/getfile', methods=["GET"]) @@ -434,7 +448,7 @@ def getFile(): return jsonify({"file": decompressed_file, "file_name": job.getFileNameOriginal()}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/nullifyjobs', methods=["POST"]) @@ -445,7 +459,7 @@ def nullifyJobs(): res = Job.nullifyPrinterId(printerid) return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/clearspace', methods=["GET"]) @@ -454,7 +468,7 @@ def clearSpace(): res = Job.clearSpace() return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/getfavoritejobs', methods=["GET"]) @@ -463,7 +477,7 @@ def getFavoriteJobs(): res = Job.getFavoriteJobs() return jsonify(res) except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/favoritejob', methods=["POST"]) @@ -476,7 +490,7 @@ def favoriteJob(): res = job.setFileFavorite(favorite) return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/assignissue', methods=["POST"]) @@ -490,7 +504,7 @@ def assignIssue(): res = job.setIssue(jobid, issueid) return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/removeissue', methods=["POST"]) @@ -503,7 +517,7 @@ def removeIssue(): res = job.unsetIssue(jobid) return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/startprint', methods=["POST"]) @@ -517,18 +531,16 @@ def startPrint(): assert queue is not None, "Queue not found." printerobject.job = queue.getJobById(jobid) assert printerobject.job is not None, "Job not found." - assert printerobject.job.getStatus() == "inqueue", f"Job status not inqueue. Status: {printerobject.job.getStatus()}" - printerobject.job.setStatus("ready") + if printerobject.job.getStatus() == "inqueue": printerobject.job.setStatus("ready") assert printerobject.job.getStatus() == "ready", f"Job not ready to print. Status: {printerobject.job.getStatus()}" assert printerobject.job == queue[0], "Job not at front of queue." - print(printerobject.job) printerobject.job.setReleased(1) printerobject.setStatus("printing") return jsonify({"success": True, "message": "Job started successfully."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/savecomment', methods=["POST"]) @@ -543,7 +555,7 @@ def saveComment(): return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/downloadcsv', methods=["GET", "POST"]) @@ -562,7 +574,7 @@ def downloadCSV(): return res except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route('/removeCSV', methods=["GET", "POST"]) @@ -582,7 +594,7 @@ def removeCSV(): return jsonify({"success": True, "message": "CSV file removed successfully."}), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/repairports", methods=["POST", "GET"]) @@ -600,7 +612,7 @@ def repair_ports(): printerthread.setDevice(port.device) return {"success": True, "message": "Printer port(s) successfully updated."} except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @jobs_bp.route("/refetchtimedata", methods=['POST', 'GET']) @@ -611,6 +623,8 @@ def refetch_time(): printerid = data['printerid'] printer = findPrinterObject(printerid) + if printer is None: + return jsonify({"error": "Fabricator not found"}), 404 job = printer.getQueue().getNext() if job is None: return jsonify({"error": "No job found"}), 404 @@ -627,25 +641,24 @@ def refetch_time(): return jsonify(timejson), 200 except Exception as e: - handle_errors_and_logging(e) + current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 -def findPrinterObject(printer_id): +def findPrinterObject(fabricator_id): """ Find the printer object by its ID. - :param printer_id: The ID of the printer. - :type printer_id: int + :param fabricator_id: The ID of the printer. + :type fabricator_id: int :rtype: Fabricator | None """ - from app import fabricator_list - threads = fabricator_list.getThreadArray() - return list(filter(lambda thread: thread.fabricator.dbID == printer_id, threads))[0].fabricator + threads = current_app.fabricator_list.getThreadArray() + fabricatorThread = list(filter(lambda thread: thread.fabricator.dbID == fabricator_id, threads)) + return fabricatorThread[0].fabricator if len(fabricatorThread) > 0 else None def getSmallestQueue(): - from app import fabricator_list - threads = fabricator_list.getThreadArray() - smallest_queue_thread = min(threads, key=lambda thread: len(thread.printer.queue)) - return smallest_queue_thread.printer.id + threads = current_app.fabricator_list.getThreadArray() + smallest_queue_thread = min(threads, key=lambda thread: len(thread.fabricator.queue)) + return smallest_queue_thread.fabricator.dbID def rerunjob(printerpk, jobpk, position): job = Job.findJob(jobpk) # retrieve Job to rerun @@ -668,8 +681,12 @@ def rerunjob(printerpk, jobpk, position): file_name_pk = f"{base_name}_{id}{extension}" rjob.setFileName(file_name_pk) # set unique file name - + fabricator = findPrinterObject(printerpk) + if fabricator is None: + return jsonify({"error": "Fabricator not found."}), 404 if position == "back": findPrinterObject(printerpk).getQueue().addToBack(rjob, rjob.fabricator_id) else: findPrinterObject(printerpk).getQueue().addToFront(rjob, rjob.fabricator_id) + + return jsonify({"success": True, "message": "Job added to printer queue."}), 200 diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 05a65cba..0dcc11e6 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,11 +1,10 @@ from sqlalchemy.exc import SQLAlchemyError from flask import Blueprint, jsonify, request, current_app -import app +from app import current_app as app from Classes.Fabricators.Device import Device from Classes.Fabricators.Fabricator import Fabricator from Classes.Ports import Ports -from app import handle_errors_and_logging from traceback import format_exc # Blueprint for ports routes @@ -18,7 +17,7 @@ def getPorts(): ports = Ports.getPorts() return jsonify([port for port in ports]) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/getfabricators", methods=["GET"]) @@ -28,7 +27,7 @@ def getRegisteredFabricators(): fabricators = Fabricator.queryAll() return jsonify([fab.__to_JSON__() for fab in fabricators]) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/register", methods=["POST"]) @@ -48,7 +47,7 @@ def registerFabricator(): print(f"Database error during registration: {db_err}") return jsonify({"error": "Database error occurred"}), 500 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/deletefabricator", methods=["POST"]) @@ -64,7 +63,7 @@ def deleteFabricator(): if res: return jsonify({"success": True, "message": "Fabricator deleted successfully"}) return jsonify({"error": "Failed to delete fabricator"}), 500 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/editname", methods=["POST"]) @@ -82,7 +81,7 @@ def editName(): else: return jsonify({"error": "Fabricator not found"}), 404 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/diagnose", methods=["POST"]) @@ -107,7 +106,7 @@ def diagnoseFabricator(): else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/repair", methods=["POST"]) @@ -119,7 +118,7 @@ def repairFabricator(): port = Ports.getPortByName(device_name) if port: - fabricator = Fabricator.createDevice(port) # Ensure the Fabricator has this method + fabricator = Fabricator.staticCreateDevice(port) # Ensure the Fabricator has this method if fabricator: repair_result = fabricator.repair() return jsonify({"success": True, "message": repair_result}) @@ -128,7 +127,7 @@ def repairFabricator(): else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/movehead", methods=["POST"]) @@ -151,7 +150,7 @@ def moveHead(): else: return jsonify({"error": "Device not found"}), 404 except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @ports_bp.route("/movefabricatorlist", methods=["POST"]) @@ -163,5 +162,5 @@ def moveFabricatorList(): result = current_app.fabricator_list.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index 789aef8e..b8599680 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -1,33 +1,16 @@ from flask import Blueprint, jsonify, request import os -from app import app, handle_errors_and_logging +from app import current_app as app from traceback import format_exc status_bp = Blueprint("status", __name__) -@status_bp.route('/ping', methods=["GET"]) -def getStatus(): - try: - return jsonify({"status": "pong"}), 200 - except Exception as e: - handle_errors_and_logging(e) - return jsonify({"error": format_exc()}), 500 - -@status_bp.route('/getopenthreads', methods=["GET"]) -def getOpenThreads(): - try: - open_threads = app.fabricator_list.getOpenThreads() - return jsonify(open_threads), 200 - except Exception as e: - handle_errors_and_logging(e) - return jsonify({"error": format_exc()}), 500 - @status_bp.route('/getprinters', methods=["GET"]) def getPrinters(): try: printers = app.fabricator_list.fabricators # call the method on the instance return jsonify({"printers": printers}) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 # this is the route that will be called by the UI to get the printers that have threads information @@ -36,7 +19,7 @@ def getPrinterInfo(): try: return jsonify([fab.__to_JSON__() for fab in app.fabricator_list.fabricators]) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @status_bp.route('/hardreset', methods=["POST"]) @@ -47,7 +30,7 @@ def hardreset(): res = app.fabricator_list.resetThread(id) return res except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @status_bp.route('/queuerestore', methods=["POST"]) @@ -59,7 +42,7 @@ def queueRestore(): res = app.fabricator_list.queueRestore(id, status) return res except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @status_bp.route("/removethread", methods=["POST"]) @@ -70,7 +53,7 @@ def removeThread(): res = app.fabricator_list.deleteThread(printerid) return res except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @status_bp.route("/editNameInThread", methods=["POST"]) @@ -81,7 +64,7 @@ def editName(): name = data['newname'] return app.fabricator_list.editName(fabricator_id, name) except Exception as e: - handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 @status_bp.route("/serverVersion", methods=["GET"]) From 4a3dbd4566b12725a19ea5843cf19e57de9aa990 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Tue, 3 Dec 2024 20:12:04 -0500 Subject: [PATCH 163/194] feat: websocket integration done --- server/Classes/FabricatorConnection.py | 10 +++++----- server/Classes/Fabricators/Device.py | 5 ++--- server/Classes/Fabricators/Fabricator.py | 14 ++++++-------- server/Classes/Fabricators/Printers/Printer.py | 3 +-- server/Classes/Ports.py | 8 +++++++- server/app.py | 6 +----- server/controllers/ports.py | 13 +++++++------ 7 files changed, 29 insertions(+), 30 deletions(-) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index eff80737..ced28ce8 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -28,7 +28,7 @@ def staticCreateConnection(port = None, baudrate = None, timeout = 10.0, websock """ print(f"Creating connection with port: {port}, baudrate: {baudrate}, timeout: {timeout}, websocket_connections: {websocket_connections}, fabricator_id: {fabricator_id}") if websocket_connections is not None and fabricator_id is not None: - return SocketConnection(websocket_connections, fabricator_id) + return SocketConnection(port, baudrate, timeout, websocket_connections, fabricator_id) elif port is not None and baudrate is not None: return SerialConnection(port, baudrate, timeout=timeout) else: @@ -128,8 +128,8 @@ def close(self): # Emit a disconnect event if needed self._send_message("printer_disconnect", {"printerid": self._fabricator_id}) self._is_open = False - if self._websocket: - asyncio.run(self._websocket.close()) # Ensure closing the websocket. + if self._websocket_connection: + asyncio.run(self._websocket_connection.close()) # Ensure closing the websocket. def open(self): """ @@ -185,9 +185,9 @@ def is_open(self): def _send_message(self, event, data): """Helper to send messages via the WebSocket connection.""" - if self._websocket and self._is_open: + if self._websocket_connection and self._is_open: message = {"event": event, "data": data} - asyncio.run(self._websocket.send(str(message))) + asyncio.run(self._websocket_connection.send(str(message))) else: print(f"Cannot send message: WebSocket connection is not open or not available.") diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 8a1a6baa..77ff633e 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -48,7 +48,7 @@ def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sy self.websocket_connection = websocket_connection def __repr__(self): - return f"{self.getModel()} on {self.getSerialPort().device}" + return f"port: {self.serialPort.device}, status: {self.status}, websocket_connection: {self.websocket_connection if self.websocket_connection else 'None'}" def __to_JSON__(self): return { @@ -106,8 +106,7 @@ def home(self, isVerbose: bool = True): assert self.getHomePosition() == self.getToolHeadLocation(), f"Failed to home, expected {self.getHomePosition()} but got {self.getToolHeadLocation()}" return True except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + return current_app.handle_errors_and_logging(e, self) def goTo(self, loc: Vector3, isVerbose: bool = False): """ diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 2b181a67..a9eed122 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -108,17 +108,15 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.MakerBot.MakerBotPrinter import MakerBotPrinter from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter if serialPort.vid == PrusaPrinter.VENDORID: - print("Creating Prusa Printer") from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: - print("Creating Prusa MK4") - return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return PrusaMK4(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return PrusaMK4S(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return PrusaMK3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) else: return None elif serialPort.vid == EnderPrinter.VENDORID: @@ -126,15 +124,15 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return Ender3Pro(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) elif "Ender-3" in model: - return Ender3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return Ender3(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False) + return Replicator2(100000, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=False, websocket_connection=websocket_connection) else: #TODO: assume generic printer, do stuff return None diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 30e86192..e560d91b 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -390,8 +390,7 @@ def connect(self): self.serialConnection.write(b"M155 S1\n") return True except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + return current_app.handle_errors_and_logging(e, self) def disconnect(self: Device): if self.serialConnection and self.serialConnection.is_open: diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 9b5dd9b7..e0656c43 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -26,7 +26,13 @@ def getPorts(): if app: if app.fabricator_list.getFabricatorByPort(port) is None: if port.device == emu_port: - device = Fabricator.staticCreateDevice(port, websocket_connection=next(iter(app.emulator_connections.values()))) + print(f"emulator_connections: {app.emulator_connections}" if app.emulator_connections else "No emulator connections") + values = app.emulator_connections.values() + print(values) + ws = next(iter(values), None) + print(ws if ws else "No websocket connection") + device = Fabricator.staticCreateDevice(port, websocket_connection=ws) + print(device) else: device = Fabricator.staticCreateDevice(port) full_devices.append(device) diff --git a/server/app.py b/server/app.py index 5f1e0b4a..0250b697 100644 --- a/server/app.py +++ b/server/app.py @@ -187,11 +187,7 @@ def handle_errors_and_logging(self, e: Exception | str, fabricator=None): def get_emu_ports(self): fake_device = next(iter(self.emulator_connections.values()), None) if fake_device: - fake_port = fake_device.fake_port - fake_name = fake_device.fake_name - fake_hwid = fake_device.fake_hwid - - return [fake_port, fake_name, fake_hwid] + return [fake_device.fake_port, fake_device.fake_name, fake_device.fake_hwid] return [None, None, None] def _find_custom_app(): diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 0dcc11e6..a0186240 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,5 +1,5 @@ from sqlalchemy.exc import SQLAlchemyError -from flask import Blueprint, jsonify, request, current_app +from flask import Blueprint, jsonify, request from app import current_app as app from Classes.Fabricators.Device import Device @@ -91,7 +91,7 @@ def diagnoseFabricator(): data = request.get_json() device_name = data['device'] port = Ports.getPortByName(device_name) - fabricator = current_app.fabricator_list.getFabricatorByPort(port) + fabricator = app.fabricator_list.getFabricatorByPort(port) if fabricator: device = fabricator.device if device is None: @@ -137,10 +137,11 @@ def moveHead(): data = request.get_json() port = data['port'] if port: - if current_app: - fab = current_app.fabricator_list.getFabricatorByPort(port) - print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {current_app.fabricator_list.fabricators}, threads: {current_app.fabricator_list.fabricator_threads}") + if app: + fab = app.fabricator_list.getFabricatorByPort(port) + print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {app.fabricator_list.fabricators}, threads: {app.fabricator_list.fabricator_threads}") if fab: device = fab.device + elif port.startswith("EMU"): device = Fabricator.staticCreateDevice(Ports.getPortByName(port), websocket_connection=next(iter(app.emulator_connections.values()))) else: device = Fabricator.staticCreateDevice(Ports.getPortByName(port)) else: device = Fabricator(port).device device.connect() @@ -159,7 +160,7 @@ def moveFabricatorList(): try: data = request.get_json() fabricator_ids = data['fabricator_ids'] - result = current_app.fabricator_list.moveFabricatorList(fabricator_ids) + result = app.fabricator_list.moveFabricatorList(fabricator_ids) return jsonify({"success": True, "message": "Fabricator list successfully updated"}) if result != "none" else jsonify({"success": False, "message": "Fabricator list not updated"}) except Exception as e: app.handle_errors_and_logging(e) From e91b58611363729dcbcd104aec5ac1c49f18faf2 Mon Sep 17 00:00:00 2001 From: iron768 <iron768@users.noreply.github.com> Date: Tue, 3 Dec 2024 23:16:21 -0500 Subject: [PATCH 164/194] fix: improve emulator connection --- printeremu/src/emulator.go | 86 ++++++++++++++++---------- printeremu/src/gcode_commands.go | 14 ++--- server/Classes/EventEmitter.py | 9 +++ server/Classes/FabricatorConnection.py | 62 +++++++++++++++---- server/Classes/FabricatorList.py | 4 +- server/app.py | 12 +++- 6 files changed, 131 insertions(+), 56 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index a1229b20..fc607c62 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -112,6 +112,8 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se return } + message = []byte(strings.ReplaceAll(string(message), "'", "\"")) + if messageType == websocket.TextMessage && string(message) == "close" { fmt.Println("Received 'close' signal from server, ending connection.") return @@ -148,41 +150,61 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se } case "send_gcode": - if data != nil { - payload, ok := data.(map[string]interface{}) - if ok { - printerID, pidOk := payload["printerid"].(string) - gcode, gcodeOk := payload["gcode"].(string) - - if pidOk && gcodeOk { - response := CommandHandler(gcode, printer) - - fmt.Println("Gcode executed:", response) - - printerResponse := map[string]interface{}{ - "printerid": printerID, - "response": response, - } - - responseMessage, err := json.Marshal(printerResponse) - - if err != nil { - log.Println("Failed to marshal printer_response:", err) - continue - } - - if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { - log.Println("Error sending printer_response:", err) - continue - } - } else { - log.Println("Invalid send_gcode payload") - } + messageStr := strings.ReplaceAll(string(message), "'", "\"") + + var parsedMessage map[string]interface{} + if err := json.Unmarshal([]byte(messageStr), &parsedMessage); err != nil { + log.Println("Error parsing received message:", err) + log.Println("Original message:", string(message)) + continue + } + + data := parsedMessage["data"] + if data == nil { + log.Println("Received G-code command, but no data.") + continue + } + + // Convert data to map + dataMap, ok := data.(map[string]interface{}) + if !ok { + log.Println("Data is not a map") + continue + } + + printerID, pidOk := dataMap["printerid"].(string) + gcode, gcodeOk := dataMap["gcode"].(string) + + fmt.Print("Received G-code command for printer ", printerID, ": ", gcode, "\n") + + if pidOk && gcodeOk { + response := CommandHandler(gcode, printer) + + parsedResponse := "" + if response != "Unknown command" { + parsedResponse = "ok" } else { - log.Println("Received G-code command, but data is not a map") + parsedResponse = "Unknown command" + } + + printerResponse := map[string]interface{}{ + "printerid": printerID, + "response": parsedResponse, + } + + responseMessage, err := json.Marshal(printerResponse) + + if err != nil { + log.Println("Failed to marshal printer_response:", err) + continue + } + + if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { + log.Println("Error sending printer_response:", err) + continue } } else { - log.Println("Received G-code command, but no data") + log.Println("Invalid send_gcode payload") } case "printer_connect": RegisterPrinter(conn, printer) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 477e6ea6..96aeb3ba 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -1,12 +1,12 @@ package src import ( - "fmt" - "math" - "regexp" - "strconv" - "strings" - "time" + "fmt" + "math" + "regexp" + "strconv" + "strings" + "time" ) type Command interface { @@ -358,7 +358,7 @@ type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) string { position := printer.Extruder.Position - return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\n", position.X, position.Y, position.Z) + return fmt.Sprintf("ok\n X:%.2f Y:%.2f Z:%.2f\n ok\n", position.X, position.Y, position.Z) } type M115Command struct{} diff --git a/server/Classes/EventEmitter.py b/server/Classes/EventEmitter.py index 9b33fa4f..dfe2e4da 100644 --- a/server/Classes/EventEmitter.py +++ b/server/Classes/EventEmitter.py @@ -16,3 +16,12 @@ def emit(self, event_name, *args, **kwargs): for callback in self._events[event_name]: # Trigger the callback with the provided data asyncio.create_task(callback(*args, **kwargs)) + + def remove_listener(self, event_name, listener): + """ + Remove a specific listener for an event + """ + if event_name in self._listeners: + self._listeners[event_name] = [ + l for l in self._listeners[event_name] if l != listener + ] diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index ced28ce8..0f8ed772 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -4,9 +4,12 @@ import uuid import serial from queue import Queue, Empty +import json from serial.tools.list_ports_common import ListPortInfo +from app import current_app as app + class FabricatorConnection(ABC): @staticmethod @@ -94,8 +97,10 @@ def write(self, data): break # Convert data to string if it's bytes - gcode = data.decode('utf-8') if isinstance(data, bytes) else data + gcode = data.decode('utf-8').strip() if isinstance(data, bytes) else str(data).strip() + print(f"Sending G-code command: {gcode}") + # Emit the G-code command self._send_message("send_gcode", { "printerid": self._fabricator_id, @@ -105,20 +110,48 @@ def write(self, data): def read(self): """ Read response from the printer. - - :return: Response from the printer + + :return: Response from the printer as bytes """ if not self._is_open: raise ConnectionError("WebSocket connection is not open") + + # Use a local event and queue for this specific read operation + local_response_event = threading.Event() + local_receive_queue = Queue() + + def on_message_received(client_id, message): + try: + data = json.loads(message) + + if data.get("printerid") == self._fabricator_id: + response = data.get("response", "") + local_receive_queue.put(response) + local_response_event.set() + else: + print(f"Received message from unknown printer {data.get('printerid')}") + except json.JSONDecodeError as e: + print(f"Failed to decode message: {message} - {e}") + except Exception as e: + print(f"Error in message handling: {e}") + + #try: + # Register the temporary listener + app.event_emitter.on("message_received", on_message_received) + + # Wait for response with timeout + if not local_response_event.wait(timeout=self._timeout): + return b'' + #raise TimeoutError(f"No response received within {self._timeout} seconds") - # Wait for response with timeout - if not self._response_event.wait(timeout=self._timeout): - raise TimeoutError(f"No response received within {self._timeout} seconds") - - try: - return self._receive_queue.get(block=False) - except Empty: - return "" + try: + response = local_receive_queue.get(block=False) + return response.encode('utf-8') if response else b'' # Convert string to bytes + except Empty: + return b'' + #finally: + # Unregister the listener to prevent memory leaks + # app.event_emitter.remove_listener("message_received", on_message_received) def close(self): """ @@ -172,7 +205,12 @@ def readline(self): :return: A single line of response """ - return self.read() + response = self.read() + + if response is None: + response = b'' + + return response @property def is_open(self): diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 39c5342c..4c840baa 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -66,9 +66,9 @@ def addFabricator(self, serialPortName: str, name: str = ""): newFab: Fabricator | None = None if dbFab is not None: # means that the fabricator is in the db if listFab is not None: # means that the fabricator is in the list and the db - app.handle_errors_and_logging(Exception(f"Fabricator {dbFab.getname()} already exists in the list"), listFab) + app.handle_errors_and_logging(Exception(f"Fabricator {dbFab.getName()} already exists in the list"), listFab) else: # means that the fabricator is in the db but not in the list - newFab = Fabricator(serialPort, name=dbFab.getname()) + newFab = Fabricator(serialPort, name=dbFab.getName()) self.fabricators.append(newFab) else: # means that the fabricator is not in the db if listFab is not None: # means that the fabricator is in the list but not in the db diff --git a/server/app.py b/server/app.py index 0250b697..67db0adb 100644 --- a/server/app.py +++ b/server/app.py @@ -14,14 +14,16 @@ import websockets from routes import defineRoutes from werkzeug.local import LocalProxy +from Classes.EventEmitter import EventEmitter emulator_connections = {} +event_emitter = EventEmitter() async def websocket_server(): async def handle_client(websocket): client_id = str(uuid.uuid4()) - print(f"New WebSocket client connected: {client_id}") + print(f"Emulator websocket connected: {client_id}") emulator_connections[client_id] = websocket @@ -29,7 +31,10 @@ async def handle_client(websocket): while True: message = await websocket.recv() assert isinstance(message, str), f"Received non-string message: {message}, Type: ({type(message)})" - print(f"Received message from {client_id}: {message}") + #print(f"Received message from {client_id}: {message}") + + event_emitter.emit("message_received", client_id, message) + if not hasattr(emulator_connections[client_id],"fake_port"): fake_port = message.split('port":"')[-1].split('",')[0] print(f"Fake port: {fake_port}" if fake_port else "No fake port found") @@ -50,7 +55,7 @@ async def handle_client(websocket): #await websocket.send(f"Echo from {client_id}: {message}") except websockets.exceptions.ConnectionClosed as e: # Handle disconnection gracefully - print(f"Client {client_id} has been disconnected.") + print(f"Emulator '{client_id}' has been disconnected.") except Exception as e: # Handle any other exception (unexpected disconnection, etc.) print(f"Error with client {client_id}: {e}") @@ -113,6 +118,7 @@ def __init__(self): transport=['websocket', 'polling']) # make it eventlet on production! self.emulator_connections = emulator_connections + self.event_emitter = event_emitter CORS(self) From 93156ff0db90a30af99f37a5f1f78631bbd9f961 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Tue, 3 Dec 2024 23:22:20 -0500 Subject: [PATCH 165/194] fix: emu gcode commands --- printeremu/src/gcode_commands.go | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 96aeb3ba..02e942f0 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -52,7 +52,7 @@ func (cmd *G28Command) Execute(printer *Printer) string { return fmt.Sprintf("Error: %s\n", err.Error()) } - return "Homing completed\n" + return fmt.Sprintf("ok\nX:%.2f Y:%.2f Z:%.2f\nok\n", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z) } type G0G1Command struct { @@ -191,7 +191,7 @@ func (cmd *M104Command) Execute(printer *Printer) string { return fmt.Sprintf("Error: %s\n", err.Error()) } - return fmt.Sprintf("Extruder temperature set to %.2f°C\n", cmd.temperature) + return "ok\n" } // M106Command sets the fan speed @@ -234,14 +234,12 @@ func NewM140Command(command string) *M140Command { } func (cmd *M140Command) Execute(printer *Printer) string { - // Set the target temperature of the bed, but do not block if err := printer.SetBedTemperature(cmd.temperature); err != nil { return fmt.Sprintf("Error: %s\n", err.Error()) } - printer.Heatbed.Heating = true // Start heating - - return fmt.Sprintf("Bed temperature set to %.2f, heating in progress\n", cmd.temperature) + printer.Heatbed.Heating = true + return "ok\n" } // M190Command waits until the bed reaches the target temperature before allowing further commands @@ -256,20 +254,17 @@ func NewM190Command(command string) *M190Command { } func (cmd *M190Command) Execute(printer *Printer) string { - // Set the target temperature and check if the bed is heated - // TODO: use error handling! printer.Heatbed.TargetTemp = cmd.temperature - // Check if the current temperature is below the target if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { - return "Waiting for bed to reach target temperature...\n" + return fmt.Sprintf("B:%.2f / %.2f\n", printer.Heatbed.Temp, printer.Heatbed.TargetTemp) } - - // If target temperature reached, continue +//TODO: Error handling printer.Heatbed.Heating = false - return fmt.Sprintf("Bed temperature reached %.2f\n", cmd.temperature) + return "ok\n" } + type M109Command struct { temperature float64 } @@ -281,16 +276,13 @@ func NewM109Command(command string) *M109Command { } func (cmd *M109Command) Execute(printer *Printer) string { - // Set the target temperature and check if the extruder is heated printer.Extruder.TargetTemp = cmd.temperature - // Check if the current temperature is below the target if printer.Extruder.ExtruderTemp < printer.Extruder.TargetTemp { - return "Waiting for extruder to reach target temperature...\n" + return fmt.Sprintf("T:%.2f / %.2f\n", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp) } - // If target temperature reached, continue - return fmt.Sprintf("Extruder temperature reached %.2f\n", cmd.temperature) + return "ok\n" } type M113Command struct{} @@ -331,7 +323,7 @@ func NewM73Command(command string) *M73Command { func (cmd *M73Command) Execute(printer *Printer) string { printer.UpdateProgress(cmd.progress) - return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\n", cmd.progress, cmd.remaining) + return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\nok\n", cmd.progress, cmd.remaining) } // Parsing helpers @@ -358,7 +350,7 @@ type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) string { position := printer.Extruder.Position - return fmt.Sprintf("ok\n X:%.2f Y:%.2f Z:%.2f\n ok\n", position.X, position.Y, position.Z) + return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\nok\n", position.X, position.Y, position.Z) } type M115Command struct{} From 831899729220505eb9b68068b940e9c9167180ec Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Tue, 3 Dec 2024 23:29:05 -0500 Subject: [PATCH 166/194] fix: create emulator even if in list --- server/Classes/Fabricators/Fabricator.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index a9eed122..a4259f17 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -51,7 +51,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog self.devicePort = dbFab.devicePort self.date = dbFab.date self.dbID = dbFab.dbID - self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True) + self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True, websocket_connection=next(iter(app.emulator_connections.values(), None)) if port.device == app.get_emu_ports()[0] else None) if self.description == "New Fabricator": self.description = self.device.getDescription() db.session.commit() @@ -94,6 +94,7 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, websocket_connection=None): """ creates the correct printer object based on the serial port info + :param websocket_connection: the websocket connection to the emulator, if it exists :param serialPort: :type serialPort: ListPortInfo | SysFS | None :param consoleLogger: @@ -137,9 +138,11 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No #TODO: assume generic printer, do stuff return None - def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, addLogger = False): + def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, addLogger = False, websocket_connection=None): """ creates the correct printer object based on the serial port info + :param websocket_connection: the websocket connection to the emulator, if it exists + :type websocket_connection: WebSocket | None :param serialPort: the serial port info :type serialPort: ListPortInfo | SysFS | None :param consoleLogger: the console stream to output to @@ -163,11 +166,11 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Prusa.PrusaMK4 import PrusaMK4 from Classes.Fabricators.Printers.Prusa.PrusaMK4S import PrusaMK4S if serialPort.pid == PrusaMK4.PRODUCTID: - return PrusaMK4(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return PrusaMK4(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) elif serialPort.pid == PrusaMK4S.PRODUCTID: - return PrusaMK4S(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return PrusaMK4S(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) elif serialPort.pid == PrusaMK3.PRODUCTID: - return PrusaMK3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return PrusaMK3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) else: return None elif serialPort.vid == EnderPrinter.VENDORID: @@ -175,15 +178,15 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No from Classes.Fabricators.Printers.Ender.Ender3Pro import Ender3Pro model = Fabricator.getModelFromGcodeCommand(serialPort) if "Ender-3 Pro" in model: - return Ender3Pro(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return Ender3Pro(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) elif "Ender-3" in model: - return Ender3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return Ender3(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) else: return None elif serialPort.vid == MakerBotPrinter.VENDORID: from Classes.Fabricators.Printers.MakerBot.Replicator2 import Replicator2 if serialPort.pid == Replicator2.PRODUCTID: - return Replicator2(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger) + return Replicator2(self.dbID, serialPort, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=addLogger, websocket_connection=websocket_connection) else: #TODO: assume generic printer, do stuff return None From 353aae7b6a6073d9d10c5226c5f9f648b4c12bf5 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:20:49 -0500 Subject: [PATCH 167/194] fix: MainView errors --- client/src/views/MainView.vue | 473 +++++++++++++++------------------- 1 file changed, 207 insertions(+), 266 deletions(-) diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index c5936a6c..512c0495 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -273,24 +273,28 @@ const handleDragEnd = async () => { <div class="container"> <table ref="table"> - <tr> - <!-- NEED TO FIX THIS FOR EVERY DISPLAYS --> - <th style="width: 64px">ID</th> - <th style="width: 130px">Printer name</th> - <th style="width: 142px">Printer Status</th> - <th style="width: 110px">Job Name</th> - <th style="width: 110px">File</th> - <th style="width: 314px">Printer Options</th> - <th style="width: 315px">Progress</th> - <th style="width: 75px;">Actions</th> - <th style="width: 58px">Move</th> - </tr> - <draggable v-model="printers" tag="tbody" :animation="300" item-key="printer.id" handle=".handle" - dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" - @end="restoreExpandedState"> - <template #item="{ element: printer }"> - <div v-if="printer.isInfoExpanded" class="expanded-info"> - <tr :id="printer.id" style="vertical-align: middle"> + <!-- Table header --> + <thead> + <tr> + <th>ID</th> + <th>Printer Name</th> + <!-- Add other headers here --> + <th style="width: 142px">Printer Status</th> + <th style="width: 110px">Job Name</th> + <th style="width: 110px">File</th> + <th style="width: 314px">Printer Options</th> + <th style="width: 315px">Progress</th> + <th style="width: 75px;">Actions</th> + <th style="width: 58px">Move</th> + </tr> + </thead> + <!-- Table body --> + <tbody> + <draggable v-model="printers" tag="tbody" :animation="300" item-key="printer.id" handle=".handle" + dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" + @end="restoreExpandedState"> + <template #item="{ element: printer }"> + <tr v-if="printer.isInfoExpanded"> <td v-if="(printer.status == 'printing' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.td_id }} @@ -495,282 +499,219 @@ const handleDragEnd = async () => { :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> </td> </tr> - <tr> - <td class="borderless-bottom"> - <b>Layer:</b> - </td> - <td class="borderless-bottom"> - <b>Filament:</b> - </td> - <td class="borderless-bottom"> - <b>Nozzle:</b> - </td> - <td class="borderless-bottom"> - <b>Bed:</b> - </td> - <td class="borderless-bottom"> - <b>Elapsed:</b> - </td> - <td class="borderless-bottom"> - <b>Remaining:</b> - </td> - <td class="borderless-bottom"> - <b>Total:</b> - </td> - <td class="borderless-bottom" colspan="2"> - <b>ETA:</b> - </td> - </tr> - <tr> - <td class="borderless-top"> - <span - v-if="printer.queue[0] && printer.queue[0]?.current_layer_height != null && printer.queue[0]?.max_layer_height != null && printer.queue[0]?.max_layer_height !== 0"> - {{ printer.queue[0]?.current_layer_height + '/' + printer.queue[0]?.max_layer_height }} - </span> - <span v-else> - <i>idle</i> - </span> - </td> - <td class="borderless-top"> - <span v-html="printer.queue[0]?.filament ? printer.queue[0]?.filament : '<i>idle</i>'"></span> - </td> - <td class="borderless-top"> - <span v-html="printer?.extruder_temp ? printer.extruder_temp + '°C' : '<i>idle</i>'"></span> - </td> - <td class="borderless-top"> - <span v-html="printer?.bed_temp ? printer.bed_temp + '°C' : '<i>idle</i>'"></span> + <tr v-else> + <td + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.td_id }} </td> - <td class="borderless-top"> - <span - v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> + <td v-else><i>idle</i></td> + + <td class="truncate" :title="printer.name"> + <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" + style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> + <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> + {{ printer.name }} + </div> + </button> </td> - <td class="borderless-top"> - <span v-if="printer.queue[0]?.job_client?.remaining_time !== 0" - v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> - <span v-else v-html="'00:00:00'"></span> + + <td> + <div class="d-flex align-items-center justify-content-center"> + <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> + Change filament + </p> --> + <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" + class="mb-0 me-2"> + Waiting release + </p> + <p v-else class="mb-0 me-2"> + {{ printer.status }} + </p> + </div> </td> - <td class="borderless-top"> - <span - v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.total_time)"></span> + + <td class="truncate" :title="printer.queue?.[0]?.name" + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.name }} </td> - <td class="borderless-top" colspan="2"> - <span - v-html="printer?.status === 'colorchange' ? 'Waiting...' : (printer.queue[0]?.extruded ? formatETA(printer.queue[0]?.job_client?.eta) : '<i>Waiting...</i>')"></span> + <td v-else></td> + + <td class="truncate" :title="printer.queue?.[0]?.file_name_original" + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.file_name_original }} </td> - </tr> - </div> - <tr v-else :id="printer.id"> - <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.td_id }} - </td> - <td v-else><i>idle</i></td> - - <td class="truncate" :title="printer.name"> - <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" - style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> - <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> - {{ printer.name }} - </div> - </button> - </td> + <td v-else></td> - <td> - <div class="d-flex align-items-center justify-content-center"> - <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> - Change filament - </p> --> - <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" - class="mb-0 me-2"> - Waiting release - </p> - <p v-else class="mb-0 me-2"> - {{ printer.status }} - </p> - </div> - </td> - - <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.name }} - </td> - <td v-else></td> - - <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.file_name_original }} - </td> - <td v-else></td> - - <td> - <div class="buttons"> - - <button class="btn btn-primary" - v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" - @click="setPrinterStatus(printer, 'ready')"> - Set to Ready - </button> + <td> + <div class="buttons"> - <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" - @click="setPrinterStatus(printer, 'offline')"> - Turn Offline - </button> + <button class="btn btn-primary" + v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" + @click="setPrinterStatus(printer, 'ready')"> + Set to Ready + </button> - <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" - @click="startPrint(printer.id, printer.queue[0].id)"> - Start Print - </button> + <button class="btn btn-danger" + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" + @click="setPrinterStatus(printer, 'offline')"> + Turn Offline + </button> - <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'paused')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> - Pause - </button> + <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" + @click="startPrint(printer.id, printer.queue[0].id)"> + Start Print + </button> - <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'colorchange')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> - Color Change - </button> + <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" + @click="setPrinterStatus(printer, 'paused')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + Pause + </button> - <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" - v-if="printer.status == 'paused'"> - Unpause - </button> + <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" + @click="setPrinterStatus(printer, 'colorchange')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + Color Change + </button> - <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" - v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> - Stop - </button> + <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" + v-if="printer.status == 'paused'"> + Unpause + </button> - <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" - class="mt-2"> - Ready for color change. - </div> - <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> - Finishing current layer... - </div> + <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" + v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> + Stop + </button> - </div> - </td> - - <td style="width: 250px;"> - <div - v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> - <!-- <div v-for="job in printer.queue" :key="job.id"> --> - <!-- Display the elapsed time --> - <div class="progress" style="position: relative;"> - <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> + <div + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" + class="mt-2"> + Ready for color change. </div> - <!-- job progress set to 2 decimal places --> - <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ + <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> + Finishing current layer... + </div> + + </div> + </td> + + <td style="width: 250px;"> + <div + v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> + <!-- <div v-for="job in printer.queue" :key="job.id"> --> + <!-- Display the elapsed time --> + <div class="progress" style="position: relative;"> + <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> + </div> + <!-- job progress set to 2 decimal places --> + <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ printer.queue?.[0]?.progress ? `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> - </div> - <!-- </div> --> - </div> - - <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> - <div class="buttons-progress"> - <div type="button" class="btn btn-secondary" - @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> - Clear </div> - <div class="btn-group"> - <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> - Clear/Rerun - </div> - <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" - aria-expanded="false"> + <!-- </div> --> + </div> + + <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> + <div class="buttons-progress"> + <div type="button" class="btn btn-secondary" + @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> + Clear </div> - <div class="dropdown-menu"> - <div class="dropdown-item" v-for="printer in printers" :key="printer.id" - @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> - {{ printer.name }} + <div class="btn-group"> + <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> + Clear/Rerun + </div> + <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" + aria-expanded="false"> + </div> + <div class="dropdown-menu"> + <div class="dropdown-item" v-for="printer in printers" :key="printer.id" + @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> + {{ printer.name }} + </div> </div> </div> + <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" + @click=setJob(printer.queue[0])> + Fail + </div> </div> - <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" - @click=setJob(printer.queue[0])> - Fail + </div> + + <div + v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> + <div style="display: flex; justify-content: center; align-items: center;"> + <button class="btn btn-primary w-100" type="button" disabled> + <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> + <span class="sr-only">Finishing print...</span> + </button> </div> </div> - </div> - - <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> - <div style="display: flex; justify-content: center; align-items: center;"> - <button class="btn btn-primary w-100" type="button" disabled> - <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> - <span class="sr-only">Finishing print...</span> - </button> + <div v-else-if="printer.status == 'error'" class="alert alert-danger truncate" role="alert" + :title="printer?.error"> + {{ printer?.error }} </div> - </div> - <div v-else-if="printer.status == 'error'" class="alert alert-danger truncate" role="alert" - :title="printer?.error"> - {{ printer?.error }} - </div> - <div v-else></div> - - </td> - - <td style="width: 1%; white-space: nowrap;"> - <div style="display: flex; justify-content: space-between; align-items: center;"> - <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" - @click="openPrinterInfo(printer)"> - </i> - <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> - <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> - <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" - style="background: none; border: none;"> - <i class="fa-solid fa-bars" - :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> - </button> - <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> - <li> - <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> - <i class="fa-solid fa-image"></i> - <span class="ms-2">GCode Image</span> - </a> - </li> - <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> - <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> - <i class="fas fa-code"></i> - <span class="ms-2">GCode Live</span> - </a> - </li> - <li v-if="printer.queue[0]"> - <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" - :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> - <i class="fas fa-download"></i> - <span class="ms-2">Download</span> - </a> - </li> - </ul> + <div v-else></div> + + </td> + + <td style="width: 1%; white-space: nowrap;"> + <div style="display: flex; justify-content: space-between; align-items: center;"> + <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" + @click="openPrinterInfo(printer)"> + </i> + <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> + <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> + <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" + style="background: none; border: none;"> + <i class="fa-solid fa-bars" + :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> + </button> + <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> + <li> + <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" + data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> + <i class="fa-solid fa-image"></i> + <span class="ms-2">GCode Image</span> + </a> + </li> + <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> + <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" + data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> + <i class="fas fa-code"></i> + <span class="ms-2">GCode Live</span> + </a> + </li> + <li v-if="printer.queue[0]"> + <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> + <i class="fas fa-download"></i> + <span class="ms-2">Download</span> + </a> + </li> + </ul> + </div> </div> </div> - </div> - </td> - - <td class="text-center handle" :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" - :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> - <i class="fas fa-grip-vertical" - :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> - </td> - </tr> - </template> - </draggable> + </td> + + <td class="text-center handle" :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" + :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> + <i class="fas fa-grip-vertical" + :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> + </td> + </tr> + </template> + </draggable> + </tbody> </table> <div v-if="printers.length === 0" style="margin-top: 1rem;"> No printers available. Either register a printer <RouterLink class="routerLink" to="/registration">here From d937d1862a6b557ce24942d20df3fee92b485959 Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 4 Dec 2024 04:47:33 -0500 Subject: [PATCH 168/194] fix: break some stuff and fix it, why did we make our own flask again? --- server/app.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/server/app.py b/server/app.py index 67db0adb..c762c910 100644 --- a/server/app.py +++ b/server/app.py @@ -15,6 +15,7 @@ from routes import defineRoutes from werkzeug.local import LocalProxy from Classes.EventEmitter import EventEmitter +from controllers.emulator import emulator_bp # import the blueprint emulator_connections = {} event_emitter = EventEmitter() @@ -63,8 +64,11 @@ async def handle_client(websocket): if client_id in emulator_connections: del emulator_connections[client_id] - server = await websockets.serve(handle_client, "localhost", 8001) - await server.wait_closed() + try: + server = await websockets.serve(handle_client, "localhost", 8001) + await server.wait_closed() + except Exception as e: + print(f"WebSocket server error: {e}") def start_websocket(): print("Starting WebSocket server...") @@ -124,6 +128,7 @@ def __init__(self): # Register all routes defineRoutes(self) + self.register_blueprint(emulator_bp, url_prefix='/api', name='emulator_bp') # Register the emulator blueprint with a unique name self.fabricator_list = FabricatorList(self) @@ -225,8 +230,10 @@ def _find_custom_app(): app.handle_errors_and_logging(e) def run_socketio(app): - # host=app.config["ip"], port=app.config["port"] - app.socketio.run(app, allow_unsafe_werkzeug=True) + try: + app.socketio.run(app, allow_unsafe_werkzeug=True) + except Exception as e: + app.handle_errors_and_logging(e) if __name__ == "__main__": # If hits last line in GCode file: From bca42499a4fd1c0396b41877eebe4943921574ee Mon Sep 17 00:00:00 2001 From: Nathan G <73437724+ndg8743@users.noreply.github.com> Date: Wed, 4 Dec 2024 04:47:54 -0500 Subject: [PATCH 169/194] fix: break some stuff and fix it, why did we make our own flask again? --- client/src/components/GCode3DImageViewer.vue | 2 +- client/src/views/EmulatorView.vue | 8 +++++-- server/controllers/emulator.py | 22 +++++++++++++------- server/controllers/ports.py | 6 +++++- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index 506ed8b0..c40d13e3 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -64,7 +64,7 @@ onMounted(async () => { travelColor: 'limegreen', lineWidth: 1, lineHeight: 1, - extrusionWidth: 1, + extrusionWidth: 0.4, renderExtrusion: true, renderTravel: renderTravel.value, renderTubes: true, diff --git a/client/src/views/EmulatorView.vue b/client/src/views/EmulatorView.vue index e933ca0b..9e25b4a6 100644 --- a/client/src/views/EmulatorView.vue +++ b/client/src/views/EmulatorView.vue @@ -11,11 +11,13 @@ const registerPrinter = async () => { message.value = ''; try { - const response = await api('registeremulator', { 'data': 'wow' }, 'POST'); + const response = await api('api/registeremulator', { 'data': 'wow' }, 'POST'); if (response && response.message) { message.value = response.message; isRegistered.value = true; + } else if (response && response.error) { + message.value = response.error; } else { message.value = 'Unknown response structure'; } @@ -31,11 +33,13 @@ const disconnectPrinter = async () => { message.value = ''; try { - const response = await api('disconnectemulator', { 'data': 'wow' }, 'POST'); + const response = await api('api/disconnectemulator', { 'data': 'wow' }, 'POST'); if (response && response.message) { message.value = response.message; isRegistered.value = false; + } else if (response && response.error) { + message.value = response.error; } else { message.value = 'Unknown response structure'; } diff --git a/server/controllers/emulator.py b/server/controllers/emulator.py index d15f8dae..73d55a3f 100644 --- a/server/controllers/emulator.py +++ b/server/controllers/emulator.py @@ -8,20 +8,24 @@ def registerEmulator(): try: data = request.get_json() + print(f"Received data: {data}") # Log received data + print(f"Current app emulator connections: {current_app.emulator_connections}") # Log connections socket = next(iter(current_app.emulator_connections.values()), None) if not socket: + print("No emulator connection found") # More detailed logging return jsonify({"error": "No emulator connection found"}), 404 + try: message = { 'event': 'printer_connect', 'data': 'register_from_front' } - + json_message = json.dumps(message) - + asyncio.run(socket.send(json_message)) - + return jsonify({"message": "Emulator registered successfully"}), 200 except Exception as e: print(f"Error registering emulator: {e}") @@ -35,7 +39,7 @@ def registerEmulator(): def disconnectEmulator(): try: data = request.get_json() - + from app import emulator_connections print(emulator_connections) @@ -47,11 +51,11 @@ def disconnectEmulator(): 'event': 'printer_disconnect', 'data': 'disconnect_from_front' } - + json_message = json.dumps(message) - + asyncio.run(socket.send(json_message)) - + return jsonify({"message": "Emulator disconnected successfully"}), 200 except Exception as e: print(f"Error disconnecting emulator: {e}") @@ -59,4 +63,6 @@ def disconnectEmulator(): except Exception as e: print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 \ No newline at end of file + return jsonify({"error": "Unexpected error occurred"}), 500 + + diff --git a/server/controllers/ports.py b/server/controllers/ports.py index a0186240..283d783a 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -40,7 +40,11 @@ def registerFabricator(): name = printer['name'] # Create a new fabricator instance using the Fabricator class - app.fabricator_list.addFabricator(device, name) + try: + app.fabricator_list.addFabricator(device, name) + except AssertionError as ae: + return jsonify({"error": f"Failed to add fabricator: {ae}"}), 500 + new_fabricator = Fabricator.query.filter_by(devicePort=device).first() return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: From 2b7d588134211ea6c9f6a582f36b78c11ec3aa25 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Wed, 4 Dec 2024 09:10:21 -0500 Subject: [PATCH 170/194] fix: api for emulator --- .gitignore | 1 + client/src/views/EmulatorView.vue | 4 ++-- server/app.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index dedfdede..2e0f19b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.vscode +.DS_Store *.pyc __pycache__/ diff --git a/client/src/views/EmulatorView.vue b/client/src/views/EmulatorView.vue index 9e25b4a6..5378575d 100644 --- a/client/src/views/EmulatorView.vue +++ b/client/src/views/EmulatorView.vue @@ -11,7 +11,7 @@ const registerPrinter = async () => { message.value = ''; try { - const response = await api('api/registeremulator', { 'data': 'wow' }, 'POST'); + const response = await api('registeremulator', { 'data': 'wow' }, 'POST'); if (response && response.message) { message.value = response.message; @@ -33,7 +33,7 @@ const disconnectPrinter = async () => { message.value = ''; try { - const response = await api('api/disconnectemulator', { 'data': 'wow' }, 'POST'); + const response = await api('disconnectemulator', { 'data': 'wow' }, 'POST'); if (response && response.message) { message.value = response.message; diff --git a/server/app.py b/server/app.py index c762c910..29dc82c3 100644 --- a/server/app.py +++ b/server/app.py @@ -128,7 +128,6 @@ def __init__(self): # Register all routes defineRoutes(self) - self.register_blueprint(emulator_bp, url_prefix='/api', name='emulator_bp') # Register the emulator blueprint with a unique name self.fabricator_list = FabricatorList(self) From f48d15313b32fdda13cbcc57a7246eab44e47aa3 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Wed, 4 Dec 2024 09:39:57 -0500 Subject: [PATCH 171/194] fix: fix broken serial for mac and possibly linux --- server/Classes/Ports.py | 9 +++++---- server/controllers/ports.py | 12 ++++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index e0656c43..e9782849 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -54,12 +54,13 @@ def getPortByName(name: str): """Get a specific port by its device name.""" assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" ports = Ports.getListPorts() - emu_port, emu_name, emu_hwid = app.get_emu_ports() - if emu_port and emu_name and emu_hwid: - ports.append(EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid)) + if app.emulator_connections: + emu_port, emu_name, emu_hwid = app.get_emu_ports() + if emu_port and emu_name and emu_hwid and emu_name == name: + return EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid) for port in ports: if not port: continue - if port.device == name: + if port.device.lstrip("/dev/") == name: return port return None diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 283d783a..04fb0d0d 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -144,10 +144,14 @@ def moveHead(): if app: fab = app.fabricator_list.getFabricatorByPort(port) print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {app.fabricator_list.fabricators}, threads: {app.fabricator_list.fabricator_threads}") - if fab: device = fab.device - elif port.startswith("EMU"): device = Fabricator.staticCreateDevice(Ports.getPortByName(port), websocket_connection=next(iter(app.emulator_connections.values()))) - else: device = Fabricator.staticCreateDevice(Ports.getPortByName(port)) - else: device = Fabricator(port).device + if fab: + device = fab.device + elif port.startswith("EMU"): + device = Fabricator.staticCreateDevice(Ports.getPortByName(port), websocket_connection=next(iter(app.emulator_connections.values()))) + else: + device = Fabricator.staticCreateDevice(Ports.getPortByName(port)) + else: + device = Fabricator(port).device device.connect() result = device.home() # Use home() method from Device device.disconnect() From 02e707a3dd873a9fe01e5fb7134b95a58a25c5b9 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 4 Dec 2024 09:50:15 -0500 Subject: [PATCH 172/194] fix: check that logger exists. --- server/Classes/Fabricators/Device.py | 36 ++++++------ .../Classes/Fabricators/Printers/Printer.py | 58 +++++++++---------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 77ff633e..5613152c 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -146,14 +146,14 @@ def parseGcode(self, job: Job, isVerbose: bool = False): assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" try: with open(file, "r") as f: - if hasattr(self, "logger"): self.logger.info(f"Printing {file}") + if hasattr(self, "logger") and self.logger: self.logger.info(f"Printing {file}") for line in f: if line.startswith(";") or line == "\n": continue if current_app: with current_app.app_context(): current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) - if isVerbose and hasattr(self, "logger"): self.logger.debug(line.strip("\n")) + if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(line.strip("\n")) if self.status == "paused": self.pause() while self.status == "paused": @@ -162,7 +162,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - if hasattr(self, "logger"): self.logger.info("Job cancelled") + if hasattr(self, "logger") and self.logger: self.logger.info("Job cancelled") return True elif self.status == "printing": self.resume() @@ -170,20 +170,20 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - if hasattr(self, "logger"): self.logger.info("Job cancelled") + if hasattr(self, "logger") and self.logger: self.logger.info("Job cancelled") return True if ";" in line: line = line.split(";")[0].strip() + "\n" self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" - if hasattr(self, "logger"): self.logger.info("Job complete") + if hasattr(self, "logger") and self.logger: self.logger.info("Job complete") return True except Exception as e: if not hasattr(self, "logger") or self.logger is None: print(e) else: - if hasattr(self, "logger"): self.logger.error("Error cancelling job:") - if hasattr(self, "logger"): self.logger.error(e) + self.logger.error("Error cancelling job:") + self.logger.error(e) self.verdict = "error" return True @@ -211,7 +211,7 @@ def sendGcode(self, gcode: bytes, isVerbose: bool = False): assert isinstance(gcode, bytes) assert isinstance(isVerbose, bool) self.serialConnection.write(gcode) - if isVerbose and hasattr(self, "logger"): self.logger.debug(gcode.decode("utf-8")) + if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(gcode.decode("utf-8")) return True def getToolHeadLocation(self, isVerbose = False): @@ -227,7 +227,7 @@ def getToolHeadLocation(self, isVerbose = False): response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") - if isVerbose and hasattr(self, "logger"): self.logger.info(response) + if isVerbose and hasattr(self, "logger") and self.logger: self.logger.info(response) loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) @@ -241,20 +241,20 @@ def repair(self): return "Repair not necessary for Ender devices." if self.serialConnection: - if hasattr(self, "logger"): self.logger.info("Closing existing connection for repair.") + if hasattr(self, "logger") and self.logger: self.logger.info("Closing existing connection for repair.") self.serialConnection.close() # Attempt to reconnect - if hasattr(self, "logger"): self.logger.info("Attempting to reconnect for repair.") + if hasattr(self, "logger") and self.logger: self.logger.info("Attempting to reconnect for repair.") self.connect() if self.serialConnection and self.serialConnection.is_open: - if hasattr(self, "logger"): self.logger.info("Repair successful: connection reopened.") + if hasattr(self, "logger") and self.logger: self.logger.info("Repair successful: connection reopened.") return "Repair successful." else: return "Repair failed: unable to reopen connection." except Exception as e: - if hasattr(self, "logger"): self.logger.error(f"Error during repair: {e}") + if hasattr(self, "logger") and self.logger: self.logger.error(f"Error during repair: {e}") return f"Repair failed with error: {e}" def diagnose(self): @@ -262,25 +262,25 @@ def diagnose(self): try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip the diagnosis - if hasattr(self, "logger"): self.logger.info(f"Diagnosis skipped for {self.MODEL}") + if hasattr(self, "logger") and self.logger: self.logger.info(f"Diagnosis skipped for {self.MODEL}") return "Diagnosis not necessary for Ender devices." - if hasattr(self, "logger"): self.logger.info("Starting device diagnosis.") + if hasattr(self, "logger") and self.logger: self.logger.info("Starting device diagnosis.") if not self.connect(): return "Diagnosis failed: unable to connect." - if hasattr(self, "logger"): self.logger.info("Sending diagnostic G-code command (e.g., M115).") + if hasattr(self, "logger") and self.logger: self.logger.info("Sending diagnostic G-code command (e.g., M115).") self.sendGcode(b"M115\n") response = self.serialConnection.readline().decode("utf-8").strip() if response: - if hasattr(self, "logger"): self.logger.info(f"Diagnosis response: {response}") + if hasattr(self, "logger") and self.logger: self.logger.info(f"Diagnosis response: {response}") return response else: return "Diagnosis failed: no response from device." except Exception as e: - if hasattr(self, "logger"): self.logger.error(f"Error during diagnosis: {e}") + if hasattr(self, "logger") and self.logger: self.logger.error(f"Error during diagnosis: {e}") return f"Diagnosis failed with error: {e}" def hardReset(self, newStatus: str): diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index e560d91b..f67bdee2 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -52,7 +52,8 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - self.logger.debug("Job cancelled") + if hasattr(self, "logger") and self.logger: + self.logger.debug("Job cancelled") return True lines = g.readlines() @@ -97,7 +98,8 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - self.logger.debug("Job cancelled") + if hasattr(self, "logger") and self.logger: + self.logger.debug("Job cancelled") return True # print("LINE: ", line, " STATUS: ", self.status, " FILE PAUSE: ", job.getFilePause()) @@ -142,7 +144,8 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - self.logger.debug("Job cancelled") + if hasattr(self, "logger") and self.logger: + self.logger.debug("Job cancelled") return True self.status = "printing" @@ -166,14 +169,14 @@ def parseGcode(self, job: Job, isVerbose: bool = False): sleep(.5) readline = self.serialConnection.readline().decode("utf-8").strip() if readline: - self.logger.debug(readline) + if hasattr(self, "logger") and self.logger: self.logger.debug(readline) if "T:" in readline and "B:" in readline: - self.logger.warning(f"Temperature line: {readline}") + if hasattr(self, "logger") and self.logger: self.logger.warning(f"Temperature line: {readline}") self.handleTempLine(readline) if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - self.logger.debug("Job cancelled") + if hasattr(self, "logger") and self.logger: self.logger.debug("Job cancelled") return True elif self.status == "printing": self.resume() @@ -206,16 +209,16 @@ def parseGcode(self, job: Job, isVerbose: bool = False): # if self.status == "complete" and job.extruded != 0: if self.status == "complete": self.verdict = "complete" - self.logger.debug("Job complete") + if hasattr(self, "logger") and self.logger: self.logger.debug("Job complete") return True if self.status == "error": self.verdict = "error" - self.logger.debug("Job error") + if hasattr(self, "logger") and self.logger: self.logger.debug("Job error") return False self.verdict = "complete" self.status = "complete" - self.logger.debug("Job complete") + if hasattr(self, "logger") and self.logger: self.logger.debug("Job complete") return True except Exception as e: # self.setStatus("error") @@ -254,9 +257,9 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): elif hasattr(self, "logger") and self.logger: self.logger.error(e) return False if not callables: - self.logger.info(f"{gcode.decode().strip()}: Always True") + if hasattr(self, "logger") and self.logger: self.logger.info(f"{gcode.decode().strip()}: Always True") else: - self.logger.info( + if hasattr(self, "logger") and self.logger: self.logger.info( gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) return True @@ -329,11 +332,12 @@ def extractIndex(self, gcode: bytes) -> str: :rtype: str """ hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") - return hashIndex + if hasattr(self, "logger") and self.logger: + if hashIndex == "M109" or hashIndex == "M190": + self.logger.info("Waiting for temperature to stabilize...") + elif hashIndex == "G28": + self.logger.info("Homing...") + return hashIndex def pause(self): """ @@ -342,7 +346,7 @@ def pause(self): :rtype: bool """ if not self.pauseCMD: - self.logger.error("Pause command not implemented.") + if hasattr(self, "logger") and self.logger: self.logger.error("Pause command not implemented.") return True try: assert self.pauseCMD is not None @@ -352,20 +356,15 @@ def pause(self): if hasattr(self, "keepAliveCMD") and self.keepAliveCMD: self.sendGcode(self.keepAliveCMD) self.sendGcode(self.pauseCMD) - self.logger.info("Job Paused") + if hasattr(self, "logger") and self.logger: self.logger.info("Job Paused") return True except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error pausing job:") - self.logger.error(e) - return False + return current_app.handle_errors_and_logging(e, self) def resume(self): """Resume the device, if the resume command is implemented.""" if self.resumeCMD is None: - self.logger.error("Resume command not implemented.") + if hasattr(self, "logger") and self.logger: self.logger.error("Resume command not implemented.") return False try: assert isinstance(self, Device), "self is not an instance of Device" @@ -373,15 +372,10 @@ def resume(self): assert self.serialConnection.is_open, "Serial connection is not open" if hasattr(self, "doNotKeepAliveCMD") and self.doNotKeepAliveCMD: self.sendGcode(self.doNotKeepAliveCMD) self.sendGcode(self.resumeCMD) - self.logger.info("Job Resumed") + if hasattr(self, "logger") and self.logger: self.logger.info("Job Resumed") return True except Exception as e: - if self.logger is None: - print(e) - else: - self.logger.error("Error resuming job:") - self.logger.error(e) - return False + return current_app.handle_errors_and_logging(e, self) def connect(self): super().connect() From 707d2d9c9b0972da3f26f9d67dc23555638f14df Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 4 Dec 2024 14:38:36 -0500 Subject: [PATCH 173/194] fix: undo the breaking of MainView.vue --- client/src/views/MainView.vue | 473 +++++++++++++++++++--------------- 1 file changed, 266 insertions(+), 207 deletions(-) diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index 512c0495..7d921b6e 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -273,28 +273,24 @@ const handleDragEnd = async () => { <div class="container"> <table ref="table"> - <!-- Table header --> - <thead> - <tr> - <th>ID</th> - <th>Printer Name</th> - <!-- Add other headers here --> - <th style="width: 142px">Printer Status</th> - <th style="width: 110px">Job Name</th> - <th style="width: 110px">File</th> - <th style="width: 314px">Printer Options</th> - <th style="width: 315px">Progress</th> - <th style="width: 75px;">Actions</th> - <th style="width: 58px">Move</th> - </tr> - </thead> - <!-- Table body --> - <tbody> - <draggable v-model="printers" tag="tbody" :animation="300" item-key="printer.id" handle=".handle" - dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" - @end="restoreExpandedState"> - <template #item="{ element: printer }"> - <tr v-if="printer.isInfoExpanded"> + <tr> + <!-- NEED TO FIX THIS FOR EVERY DISPLAYS --> + <th style="width: 64px">ID</th> + <th style="width: 130px">Printer Name</th> + <th style="width: 142px">Printer Status</th> + <th style="width: 110px">Job Name</th> + <th style="width: 110px">File</th> + <th style="width: 314px">Printer Options</th> + <th style="width: 315px">Progress</th> + <th style="width: 75px;">Actions</th> + <th style="width: 58px">Move</th> + </tr> + <draggable v-model="printers" tag="tbody" :animation="300" item-key="printer.id" handle=".handle" + dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" + @end="restoreExpandedState"> + <template #item="{ element: printer }"> + <div v-if="printer.isInfoExpanded" class="expanded-info"> + <tr :id="printer.id" style="vertical-align: middle"> <td v-if="(printer.status == 'printing' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.td_id }} @@ -499,219 +495,282 @@ const handleDragEnd = async () => { :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> </td> </tr> - <tr v-else> - <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.td_id }} + <tr> + <td class="borderless-bottom"> + <b>Layer:</b> </td> - <td v-else><i>idle</i></td> - - <td class="truncate" :title="printer.name"> - <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" - style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> - <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> - {{ printer.name }} - </div> - </button> + <td class="borderless-bottom"> + <b>Filament:</b> </td> - - <td> - <div class="d-flex align-items-center justify-content-center"> - <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> - Change filament - </p> --> - <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" - class="mb-0 me-2"> - Waiting release - </p> - <p v-else class="mb-0 me-2"> - {{ printer.status }} - </p> - </div> + <td class="borderless-bottom"> + <b>Nozzle:</b> </td> - - <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.name }} + <td class="borderless-bottom"> + <b>Bed:</b> </td> - <td v-else></td> - - <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> - {{ printer.queue?.[0]?.file_name_original }} + <td class="borderless-bottom"> + <b>Elapsed:</b> </td> - <td v-else></td> - - <td> - <div class="buttons"> - - <button class="btn btn-primary" - v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" - @click="setPrinterStatus(printer, 'ready')"> - Set to Ready - </button> + <td class="borderless-bottom"> + <b>Remaining:</b> + </td> + <td class="borderless-bottom"> + <b>Total:</b> + </td> + <td class="borderless-bottom" colspan="2"> + <b>ETA:</b> + </td> + </tr> + <tr> + <td class="borderless-top"> + <span + v-if="printer.queue[0] && printer.queue[0]?.current_layer_height != null && printer.queue[0]?.max_layer_height != null && printer.queue[0]?.max_layer_height !== 0"> + {{ printer.queue[0]?.current_layer_height + '/' + printer.queue[0]?.max_layer_height }} + </span> + <span v-else> + <i>idle</i> + </span> + </td> + <td class="borderless-top"> + <span v-html="printer.queue[0]?.filament ? printer.queue[0]?.filament : '<i>idle</i>'"></span> + </td> + <td class="borderless-top"> + <span v-html="printer?.extruder_temp ? printer.extruder_temp + '°C' : '<i>idle</i>'"></span> + </td> + <td class="borderless-top"> + <span v-html="printer?.bed_temp ? printer.bed_temp + '°C' : '<i>idle</i>'"></span> + </td> + <td class="borderless-top"> + <span + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> + </td> + <td class="borderless-top"> + <span v-if="printer.queue[0]?.job_client?.remaining_time !== 0" + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> + <span v-else v-html="'00:00:00'"></span> + </td> + <td class="borderless-top"> + <span + v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.total_time)"></span> + </td> + <td class="borderless-top" colspan="2"> + <span + v-html="printer?.status === 'colorchange' ? 'Waiting...' : (printer.queue[0]?.extruded ? formatETA(printer.queue[0]?.job_client?.eta) : '<i>Waiting...</i>')"></span> + </td> + </tr> + </div> + <tr v-else :id="printer.id"> + <td + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.td_id }} + </td> + <td v-else><i>idle</i></td> + + <td class="truncate" :title="printer.name"> + <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" + style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> + <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> + {{ printer.name }} + </div> + </button> + </td> - <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" - @click="setPrinterStatus(printer, 'offline')"> - Turn Offline - </button> + <td> + <div class="d-flex align-items-center justify-content-center"> + <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> + Change filament + </p> --> + <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" + class="mb-0 me-2"> + Waiting release + </p> + <p v-else class="mb-0 me-2"> + {{ printer.status }} + </p> + </div> + </td> + + <td class="truncate" :title="printer.queue?.[0]?.name" + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.name }} + </td> + <td v-else></td> + + <td class="truncate" :title="printer.queue?.[0]?.file_name_original" + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + {{ printer.queue?.[0]?.file_name_original }} + </td> + <td v-else></td> + + <td> + <div class="buttons"> + + <button class="btn btn-primary" + v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" + @click="setPrinterStatus(printer, 'ready')"> + Set to Ready + </button> - <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" - @click="startPrint(printer.id, printer.queue[0].id)"> - Start Print - </button> + <button class="btn btn-danger" + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" + @click="setPrinterStatus(printer, 'offline')"> + Turn Offline + </button> - <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'paused')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> - Pause - </button> + <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" + @click="startPrint(printer.id, printer.queue[0].id)"> + Start Print + </button> - <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'colorchange')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> - Color Change - </button> + <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" + @click="setPrinterStatus(printer, 'paused')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + Pause + </button> - <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" - v-if="printer.status == 'paused'"> - Unpause - </button> + <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" + @click="setPrinterStatus(printer, 'colorchange')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + Color Change + </button> - <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" - v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> - Stop - </button> + <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" + v-if="printer.status == 'paused'"> + Unpause + </button> - <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" - class="mt-2"> - Ready for color change. - </div> - <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> - Finishing current layer... - </div> + <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" + v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> + Stop + </button> + <div + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" + class="mt-2"> + Ready for color change. + </div> + <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> + Finishing current layer... </div> - </td> - <td style="width: 250px;"> - <div - v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> - <!-- <div v-for="job in printer.queue" :key="job.id"> --> - <!-- Display the elapsed time --> - <div class="progress" style="position: relative;"> - <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> - </div> - <!-- job progress set to 2 decimal places --> - <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ + </div> + </td> + + <td style="width: 250px;"> + <div + v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> + <!-- <div v-for="job in printer.queue" :key="job.id"> --> + <!-- Display the elapsed time --> + <div class="progress" style="position: relative;"> + <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> + </div> + <!-- job progress set to 2 decimal places --> + <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ printer.queue?.[0]?.progress ? `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> - </div> - <!-- </div> --> </div> - - <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> - <div class="buttons-progress"> - <div type="button" class="btn btn-secondary" - @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> - Clear + <!-- </div> --> + </div> + + <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> + <div class="buttons-progress"> + <div type="button" class="btn btn-secondary" + @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> + Clear + </div> + <div class="btn-group"> + <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> + Clear/Rerun </div> - <div class="btn-group"> - <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> - Clear/Rerun - </div> - <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" - aria-expanded="false"> - </div> - <div class="dropdown-menu"> - <div class="dropdown-item" v-for="printer in printers" :key="printer.id" - @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> - {{ printer.name }} - </div> - </div> + <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" + aria-expanded="false"> </div> - <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" - @click=setJob(printer.queue[0])> - Fail + <div class="dropdown-menu"> + <div class="dropdown-item" v-for="printer in printers" :key="printer.id" + @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> + {{ printer.name }} + </div> </div> </div> - </div> - - <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> - <div style="display: flex; justify-content: center; align-items: center;"> - <button class="btn btn-primary w-100" type="button" disabled> - <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> - <span class="sr-only">Finishing print...</span> - </button> + <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" + @click=setJob(printer.queue[0])> + Fail </div> </div> - <div v-else-if="printer.status == 'error'" class="alert alert-danger truncate" role="alert" - :title="printer?.error"> - {{ printer?.error }} + </div> + + <div + v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> + <div style="display: flex; justify-content: center; align-items: center;"> + <button class="btn btn-primary w-100" type="button" disabled> + <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> + <span class="sr-only">Finishing print...</span> + </button> </div> - <div v-else></div> - - </td> - - <td style="width: 1%; white-space: nowrap;"> - <div style="display: flex; justify-content: space-between; align-items: center;"> - <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" - @click="openPrinterInfo(printer)"> - </i> - <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> - <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> - <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" - style="background: none; border: none;"> - <i class="fa-solid fa-bars" - :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> - </button> - <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> - <li> - <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> - <i class="fa-solid fa-image"></i> - <span class="ms-2">GCode Image</span> - </a> - </li> - <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> - <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> - <i class="fas fa-code"></i> - <span class="ms-2">GCode Live</span> - </a> - </li> - <li v-if="printer.queue[0]"> - <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" - :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> - <i class="fas fa-download"></i> - <span class="ms-2">Download</span> - </a> - </li> - </ul> - </div> + </div> + <div v-else-if="printer.status == 'error'" class="alert alert-danger truncate" role="alert" + :title="printer?.error"> + {{ printer?.error }} + </div> + <div v-else></div> + + </td> + + <td style="width: 1%; white-space: nowrap;"> + <div style="display: flex; justify-content: space-between; align-items: center;"> + <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" + @click="openPrinterInfo(printer)"> + </i> + <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> + <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> + <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" + style="background: none; border: none;"> + <i class="fa-solid fa-bars" + :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> + </button> + <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> + <li> + <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" + data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> + <i class="fa-solid fa-image"></i> + <span class="ms-2">GCode Image</span> + </a> + </li> + <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> + <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" + data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> + <i class="fas fa-code"></i> + <span class="ms-2">GCode Live</span> + </a> + </li> + <li v-if="printer.queue[0]"> + <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> + <i class="fas fa-download"></i> + <span class="ms-2">Download</span> + </a> + </li> + </ul> </div> </div> - </td> - - <td class="text-center handle" :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" - :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> - <i class="fas fa-grip-vertical" - :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> - </td> - </tr> - </template> - </draggable> - </tbody> + </div> + </td> + + <td class="text-center handle" :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" + :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> + <i class="fas fa-grip-vertical" + :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> + </td> + </tr> + </template> + </draggable> </table> <div v-if="printers.length === 0" style="margin-top: 1rem;"> No printers available. Either register a printer <RouterLink class="routerLink" to="/registration">here From 3a48a91ecde43e88cf1afab9b69ea42c00fd06ff Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 4 Dec 2024 16:19:05 -0500 Subject: [PATCH 174/194] fix: make flask db init work again. --- Tests/conftest.py | 7 +- Tests/parallel_test_runner.py | 7 +- Tests/test_fabricator.py | 10 +- server/Classes/FabricatorConnection.py | 4 +- server/Classes/FabricatorList.py | 68 ++++----- server/Classes/Fabricators/Device.py | 2 +- server/Classes/Fabricators/Fabricator.py | 41 +++-- .../Classes/Fabricators/Printers/Printer.py | 4 +- server/Classes/Jobs.py | 4 +- server/Classes/Logger.py | 2 +- server/Classes/Ports.py | 4 +- server/Classes/Queue.py | 2 +- server/MyFlaskApp.py | 136 +++++++++++++++++ server/app.py | 142 +----------------- server/controllers/jobs.py | 2 +- server/controllers/ports.py | 2 +- server/controllers/statusService.py | 2 +- server/globals.py | 18 +++ 18 files changed, 231 insertions(+), 226 deletions(-) create mode 100644 server/MyFlaskApp.py create mode 100644 server/globals.py diff --git a/Tests/conftest.py b/Tests/conftest.py index d6cf9751..b78f6d03 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -3,7 +3,6 @@ import re import sys import pytest -from app import fabricator_list from _pytest.terminal import TerminalWriter, TerminalReporter @@ -101,10 +100,10 @@ def fabricator(request, app): @pytest.fixture(scope="session", autouse=True) def app(): - from app import app + from globals import current_app as app with app.app_context(): yield app - fabricator_list.teardown() + app.fabricator_list.teardown() from Classes.Logger import Logger @@ -147,7 +146,7 @@ def line_separator(interrupter: str, symbol: str = "-", length: int = 136, color def setup_logger(port): # set up fie location for output logs - from app import root_path + from globals import root_path log_folder = os.path.join(root_path,"Tests", "logs") os.makedirs(log_folder, exist_ok=True) from datetime import datetime diff --git a/Tests/parallel_test_runner.py b/Tests/parallel_test_runner.py index 79566917..9c1eb35e 100644 --- a/Tests/parallel_test_runner.py +++ b/Tests/parallel_test_runner.py @@ -5,7 +5,7 @@ import platform # Add test root to sys.path if needed -from app import root_path +from globals import root_path if root_path not in sys.path: sys.path.append(root_path) serverpath = os.path.join(root_path, "server") @@ -16,7 +16,6 @@ sys.path.append(testpath) from server.Classes.Ports import Ports -from app import app PORTS = [] # List of available ports for testing @@ -67,5 +66,5 @@ def run_tests_for_port(comm_port): try: future.result() except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e) \ No newline at end of file + from globals import current_app + current_app.handle_errors_and_logging(e) \ No newline at end of file diff --git a/Tests/test_fabricator.py b/Tests/test_fabricator.py index ae2f9843..29df6d66 100644 --- a/Tests/test_fabricator.py +++ b/Tests/test_fabricator.py @@ -2,7 +2,7 @@ from datetime import datetime import re import pytest -from app import fabricator_list +from globals import current_app as app from Classes.Jobs import Job from parallel_test_runner import testLevel @@ -14,7 +14,7 @@ def __repr__(): return f"test_fabricator.py running on port {os.getenv('PORT')}" def cali_cube_setup(fabricator=None): if fabricator is None: - fabricator = fabricator_list.getFabricatorByPort(os.getenv("PORT")) + fabricator = app.fabricator_list.getFabricatorByPort(os.getenv("PORT")) file = "../server/xyz-cali-cube" if shortTest: file = file + "-mini" file = file + f"_{fabricator.device.MODEL}.gcode" @@ -34,10 +34,10 @@ def test_add_job(app, fabricator): file = cali_cube_setup(fabricator=fabricator) with open(file, "r") as f: assert fabricator.queue.addToFront(Job(f.read(), "xyz cali cube", fabricator.dbID, "ready", file, False, 1, fabricator.name)), f"Failed to add job on {fabricator.getDescription()}" - for job in fabricator.queue.getQueue(): + for job in fabricator.queue: assert job.status == "ready", f"Job status incorrect on {fabricator.getDescription()}" fabricator.queue.removeJob() - assert len(fabricator.queue.getQueue()) == 0, f"Failed to remove job on {fabricator.getDescription()}" + assert len(fabricator.queue) == 0, f"Failed to remove job on {fabricator.getDescription()}" @pytest.mark.dependency(depends=["test_device.py::test_home", "test_fabricator.py::test_add_job"], scope="session") @pytest.mark.skipif(condition=testLevelToRun < 7, reason="Not doing lvl 7 tests") @@ -88,7 +88,7 @@ def pause_and_resume_fabricator(): fabricator.resetToIdle() assert fabricator.getStatus() == "idle", f"Failed to reset to idle on {fabricator.getDescription()}, fab status: {fabricator.getStatus()}, dev status: {fabricator.device.status}" - from flask import current_app + from globals import current_app fabricator.device.logger.critical(f"app: ,{current_app}") fabricator.device.logger.critical(f"app.socketio: ,{current_app.socketio}") from Classes.Fabricators.Printers.Prusa.PrusaMK3 import PrusaMK3 diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 0f8ed772..1ecf0933 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -5,10 +5,8 @@ import serial from queue import Queue, Empty import json - from serial.tools.list_ports_common import ListPortInfo - -from app import current_app as app +from globals import current_app as app class FabricatorConnection(ABC): diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 4c840baa..f4feb3da 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -1,23 +1,23 @@ -import serial.tools.list_ports -from flask import jsonify, current_app +from flask import jsonify from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS - -from Classes.Fabricators.Device import Device +from sqlalchemy import inspect from Classes.Ports import Ports from Classes.Fabricators.Fabricator import Fabricator from Classes.Jobs import Job from Classes.Queue import Queue from threading import Thread import time -from app import current_app as app - +from globals import current_app as app +from models.db import db class FabricatorList: def __init__(self, app=None): self.app = app assert self.app is not None, "app is None" with self.app.app_context(): + if not inspect(db.engine).has_table('fabricator') or not Fabricator.metadata.tables: + Fabricator.metadata.create_all(db.engine) self.fabricators = Fabricator.queryAll() self.fabricator_threads = [] self.ping_thread = None @@ -130,31 +130,31 @@ def getFabricatorByPort(self, port) -> Fabricator | None: return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) - def diagnose(self, device: Device | Fabricator): - """diagnose a fabricator""" - if isinstance(device, Fabricator): - device = device.device - try: - diagnoseString = "" - for port in serial.tools.list_ports.comports(): - if port.device == device.getSerialPort().device: - diagnoseString += f"The system has found a <b>matching port</b> with the following details: <br><br> <b>Device:</b> {port.device}, <br> <b>Description:</b> {port.description}, <br> <b>HWID:</b> {port.hwid}" - hwid = device.getHWID() - fabricatorExists = self.getFabricatorByHwid(hwid) - if fabricatorExists: - fabricator = self.getFabricatorByHwid(hwid) - diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {fabricator.name} <br> <b>Device:</b> {fabricator.device}, <br> <b>Description:</b> {fabricator.description}, <br><b> HWID:</b> {fabricator.hwid}" - if diagnoseString == "": - diagnoseString = "The port this fabricator is registered under is <b>not found</b>. Please check the connection and try again." - return { - "success": True, - "message": "fabricator successfully diagnosed.", - "diagnoseString": diagnoseString, - } - - except Exception as e: - print(f"Unexpected error: {e}") - return jsonify({"error": "Unexpected error occurred"}), 500 + # def diagnose(self, device: Device | Fabricator): + # """diagnose a fabricator""" + # if isinstance(device, Fabricator): + # device = device.device + # try: + # diagnoseString = "" + # for port in serial.tools.list_ports.comports(): + # if port.device == device.getSerialPort().device: + # diagnoseString += f"The system has found a <b>matching port</b> with the following details: <br><br> <b>Device:</b> {port.device}, <br> <b>Description:</b> {port.description}, <br> <b>HWID:</b> {port.hwid}" + # hwid = device.getHWID() + # fabricatorExists = self.getFabricatorByHwid(hwid) + # if fabricatorExists: + # fabricator = self.getFabricatorByHwid(hwid) + # diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {fabricator.name} <br> <b>Device:</b> {fabricator.device}, <br> <b>Description:</b> {fabricator.description}, <br><b> HWID:</b> {fabricator.hwid}" + # if diagnoseString == "": + # diagnoseString = "The port this fabricator is registered under is <b>not found</b>. Please check the connection and try again." + # return { + # "success": True, + # "message": "fabricator successfully diagnosed.", + # "diagnoseString": diagnoseString, + # } + # + # except Exception as e: + # print(f"Unexpected error: {e}") + # return jsonify({"error": "Unexpected error occurred"}), 500 def start_fabricator_thread(self, fabricator: Fabricator): @@ -221,7 +221,7 @@ def resetThread(self, fabricator_id): break return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) except Exception as e: - current_app.handle_errors_and_logging(e) + app.handle_errors_and_logging(e) return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 def queueRestore(self, fabricator_id, status): @@ -294,8 +294,8 @@ def __init__(self, fabricator, app=None, *args, **kwargs): if app: self.app = app else: - from app import app - self.app = app + from globals import current_app + self.app = current_app def __repr__(self): return f"FabricatorThread(fabricator={self.fabricator}, daemon={self.daemon}, running={self.is_alive()})" diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 5613152c..9c6250b8 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -2,7 +2,7 @@ import sys from abc import ABC from time import sleep -from app import current_app +from globals import current_app from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from Classes.Jobs import Job diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index a4259f17..de067efa 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -1,4 +1,4 @@ -from flask import jsonify, Response, current_app as app +from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError @@ -9,6 +9,7 @@ from Mixins.hasEndingSequence import hasEndingSequence from models.db import db from datetime import datetime, timezone +from globals import current_app class Fabricator(db.Model): dbID = db.Column(db.Integer, primary_key=True) @@ -22,7 +23,6 @@ class Fabricator(db.Model): ) devicePort = db.Column(db.String(50), nullable=False) - def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLogger=None, fileLogger=None): if port is None: return @@ -51,7 +51,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog self.devicePort = dbFab.devicePort self.date = dbFab.date self.dbID = dbFab.dbID - self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True, websocket_connection=next(iter(app.emulator_connections.values(), None)) if port.device == app.get_emu_ports()[0] else None) + self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True, websocket_connection=next(iter(current_app.emulator_connections.values(), None)) if port.device == current_app.get_emu_ports()[0] else None) if self.description == "New Fabricator": self.description = self.device.getDescription() db.session.commit() @@ -203,7 +203,7 @@ def queryAll(cls): for fab in cls.query.all(): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) - fake_port, fake_name, fake_hwid = app.get_emu_ports() + fake_port, fake_name, fake_hwid = current_app.get_emu_ports() if fake_port and fake_name and fake_hwid: fabList.append(cls(EmuListPortInfo(fake_port, "Emulator", fake_hwid), fake_name)) return fabList @@ -243,34 +243,31 @@ def begin(self, isVerbose: bool = False): if isVerbose and hasattr(self.device,"logger"): self.device.logger.debug(f"Verdict handled, status: {self.status}") return True except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + + return current_app.handle_errors_and_logging(e, self) def pause(self): """pauses the fabrication process if the fabricator supports it""" + assert isinstance(self.device, + Device), f"Device is not a Device object or subclass: {self.device}, type: {type(self.device)}" if not self.device.pauseCMD: - from app import handle_errors_and_logging - return handle_errors_and_logging("Fabricator doesn't support pausing", self) - assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" + return current_app.handle_errors_and_logging("Fabricator doesn't support pausing", self) if self.status != "printing": - from app import handle_errors_and_logging - return handle_errors_and_logging("Nothing to pause, Fabricator isn't printing", self) - assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" + return current_app.handle_errors_and_logging("Nothing to pause, Fabricator isn't printing", self) assert self.device.pause(), "Failed to pause" self.setStatus("paused") return self.status == self.device.status == "paused" def resume(self): """resumes the fabrication process if the fabricator supports it""" + assert isinstance(self.device, + Device), f"Device is not a Device object or subclass: {self.device}, type: {type(self.device)}" if not self.device.resumeCMD: - from app import handle_errors_and_logging - return handle_errors_and_logging("Fabricator doesn't support pausing", self) + return current_app.handle_errors_and_logging("Fabricator doesn't support pausing", self) if self.status != "paused": - from app import handle_errors_and_logging - return handle_errors_and_logging("Nothing to resume, Fabricator isn't paused", self) + return current_app.handle_errors_and_logging("Nothing to resume, Fabricator isn't paused", self) self.setStatus("printing") - assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}" return self.status == self.device.status == "printing" def cancel(self): @@ -279,13 +276,11 @@ def cancel(self): assert self.job is not None, "Job is None" assert self.device is not None, "Device is None" if self.status != "printing" and self.status != "paused": - from app import handle_errors_and_logging - return handle_errors_and_logging("Nothing to cancel, Fabricator isn't printing", self) + return current_app.handle_errors_and_logging("Nothing to cancel, Fabricator isn't printing", self) self.setStatus("cancelled") return self.status == self.device.status == "cancelled" except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + return current_app.handle_errors_and_logging(e, self) def getStatus(self): return self.status @@ -306,7 +301,6 @@ def setStatus(self, newStatus): self.job.status = newStatus self.queue[0].status = newStatus db.session.commit() - from app import current_app if current_app: current_app.socketio.emit( "status_update", {"fabricator_id": self.dbID, "status": newStatus} @@ -317,8 +311,7 @@ def setStatus(self, newStatus): print(f"current app is None, status: {newStatus}") return True except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + return current_app.handle_errors_and_logging(e, self) def resetToIdle(self): #TODO: send message to front end insuring that the print bed is clear and that the job is done diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index f67bdee2..74082c70 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -3,7 +3,7 @@ from datetime import datetime from time import sleep -from app import current_app +from globals import current_app from Classes.Fabricators.Device import Device from Classes.Jobs import Job @@ -314,7 +314,7 @@ def handleTempLine(self, line: str): self.nozzleTemperature = float(temp_t.group(1)) if temp_b: self.bedTemperature = float(temp_b.group(1)) - from app import current_app + from globals import current_app if current_app: current_app.socketio.emit('temp_update', {'printerid': self.dbID, 'extruder_temp': self.nozzleTemperature, 'bed_temp': self.bedTemperature}) diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index 49cec2a4..c999e07d 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -6,15 +6,13 @@ from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory from datetime import timezone, timedelta from flask import jsonify -from app import current_app +from globals import current_app from traceback import format_exc from sqlalchemy.exc import SQLAlchemyError from datetime import datetime -from tzlocal import get_localzone import gzip import csv from flask import send_file -# model for job history table class Job(db.Model): id = db.Column(db.Integer, primary_key=True) diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 936ff420..41c10d90 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -41,7 +41,7 @@ def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=N self.addHandler(consoleLogger) else: self.consoleLogger = None if fileLogger is None: - from app import root_path + from globals import root_path log_folder = os.path.abspath(os.path.join(root_path, "server","logs")) os.makedirs(log_folder, exist_ok=True) subfolder = os.path.join(log_folder, deviceName) diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index e9782849..040f4a33 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -4,7 +4,7 @@ from serial.tools.list_ports_linux import SysFS from Classes.Fabricators.Fabricator import Fabricator from Classes.serialCommunication import sendGcode -from app import current_app as app +from globals import current_app as app from Classes.FabricatorConnection import EmuListPortInfo class Ports: @@ -54,7 +54,7 @@ def getPortByName(name: str): """Get a specific port by its device name.""" assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" ports = Ports.getListPorts() - if app.emulator_connections: + if len(app.emulator_connections) > 0: emu_port, emu_name, emu_hwid = app.get_emu_ports() if emu_port and emu_name and emu_hwid and emu_name == name: return EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 9e4d807a..7b898373 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,5 +1,5 @@ from collections import deque -from app import current_app +from globals import current_app from Classes.Jobs import Job diff --git a/server/MyFlaskApp.py b/server/MyFlaskApp.py new file mode 100644 index 00000000..16f2edfe --- /dev/null +++ b/server/MyFlaskApp.py @@ -0,0 +1,136 @@ +import logging +import os +import sys + +from flask import request, Response, send_from_directory, Flask +from flask_cors import CORS +from flask_migrate import Migrate +from dotenv import load_dotenv +from flask_socketio import SocketIO +from globals import root_path, emulator_connections, event_emitter +from routes import defineRoutes +from models.config import Config +from Classes.FabricatorList import FabricatorList +from models.db import db + +class MyFlaskApp(Flask): + def __init__(self): + super().__init__(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) + load_dotenv() + basedir = os.path.abspath(os.path.join(root_path, "server")) + database_file = os.path.abspath(os.path.join(basedir, Config.get('database_uri'))) + if isinstance(database_file, bytes): + database_file = database_file.decode('utf-8') + databaseuri = 'sqlite:///' + database_file + self.config['SQLALCHEMY_DATABASE_URI'] = databaseuri + self.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + db.init_app(self) + Migrate(self, db) + self._fabricator_list = None + self._logger = None + from Classes.Logger import Logger + logs = os.path.join(root_path, "server", "logs") + os.makedirs(logs, exist_ok=True) + self.logger = Logger("App", consoleLogger=sys.stdout, fileLogger=os.path.abspath(os.path.join(logs, f"{__name__}.log")), + consoleLevel=logging.ERROR) + self.config.from_object(__name__) # update application instantly + # start database connection + self.config["environment"] = Config.get('environment') + self.config["ip"] = Config.get('ip') + self.config["port"] = Config.get('port') + self.config["base_url"] = Config.get('base_url') + + self.socketio = SocketIO(self, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, + async_mode='eventlet' if self.config["environment"] == 'production' else 'threading', + transport=['websocket', 'polling']) # make it eventlet on production! + + self.emulator_connections = emulator_connections + self.event_emitter = event_emitter + + CORS(self) + + # Register all routes + defineRoutes(self) + + self.fabricator_list = FabricatorList(self) + + @self.cli.command("test") + def run_tests(): + """Run all tests.""" + import subprocess + subprocess.run(["python", "../Tests/parallel_test_runner.py"]) + + @self.before_request + def handle_preflight(): + if request.method == "OPTIONS": + res = Response() + res.headers['X-Content-Type-Options'] = '*' + res.headers['Access-Control-Allow-Origin'] = '*' + res.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' + res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' + return res + + # Serve static files + @self.route('/') + def serve_static(path='index.html'): + return send_from_directory(self.static_folder, path) + + @self.route('/assets/<path:filename>') + def serve_assets(filename): + return send_from_directory(os.path.join(self.static_folder, 'assets'), filename) + + @self.socketio.on('ping') + def handle_ping(): + self.socketio.emit('pong') + + @self.socketio.on('connect') + def handle_connect(): + print("Client connected") + + + @property + def logger(self): + return self._logger + + @logger.setter + def logger(self, logger): + self._logger = logger + + @logger.getter + def logger(self): + return self._logger + + @property + def fabricator_list(self): + return self._fabricator_list + + @fabricator_list.setter + def fabricator_list(self, fabricator_list): + self._fabricator_list = fabricator_list + + @fabricator_list.getter + def fabricator_list(self): + return self._fabricator_list + + def handle_errors_and_logging(self, e: Exception | str, fabricator=None): + from Classes.Fabricators.Fabricator import Fabricator + device = fabricator + if isinstance(fabricator, Fabricator): + device = fabricator.device + if device is not None and hasattr(device, "logger") and device.logger is not None: + device.logger.error(e, stacklevel=3) + elif self.logger is None: + if isinstance(e, str): + print(e.strip()) + else: + import traceback + print(traceback.format_exception(None, e, e.__traceback__)) + else: + self.logger.error(e, stacklevel=3) + return False + + def get_emu_ports(self): + fake_device = next(iter(self.emulator_connections.values()), None) + if fake_device: + return [fake_device.fake_port, fake_device.fake_name, fake_device.fake_hwid] + return [None, None, None] \ No newline at end of file diff --git a/server/app.py b/server/app.py index 29dc82c3..d7439470 100644 --- a/server/app.py +++ b/server/app.py @@ -1,24 +1,12 @@ import asyncio -import logging -import sys import threading import uuid - -from flask import request, Response, send_from_directory, Flask, current_app as flask_current_app -from flask_cors import CORS import os -from flask_migrate import Migrate -from dotenv import load_dotenv import shutil -from flask_socketio import SocketIO import websockets -from routes import defineRoutes -from werkzeug.local import LocalProxy -from Classes.EventEmitter import EventEmitter -from controllers.emulator import emulator_bp # import the blueprint +from MyFlaskApp import MyFlaskApp +from globals import emulator_connections, event_emitter -emulator_connections = {} -event_emitter = EventEmitter() async def websocket_server(): async def handle_client(websocket): @@ -78,133 +66,9 @@ def start_websocket(): websocket_thread.daemon = True # Make it a daemon thread to exit with the main program websocket_thread.start() -# Global variables -root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -uploads_folder = os.path.abspath(os.path.join(root_path, 'uploads')) - # moved this up here so we can pass the app to the PrinterStatusService -# Basic app setup -class MyFlaskApp(Flask): - def __init__(self): - from models.config import Config - from Classes.FabricatorList import FabricatorList - from models.db import db - - super().__init__(__name__, static_folder=os.path.abspath(os.path.join(root_path, "client", "dist"))) - self._logger = None - from Classes.Logger import Logger - logs = os.path.join(root_path, "server", "logs") - os.makedirs(logs, exist_ok=True) - self.logger = Logger("App", consoleLogger=sys.stdout, fileLogger=os.path.abspath(os.path.join(logs, f"{__name__}.log")), - consoleLevel=logging.ERROR) - self.config.from_object(__name__) # update application instantly - # start database connection - self.config["environment"] = Config.get('environment') - self.config["ip"] = Config.get('ip') - self.config["port"] = Config.get('port') - self.config["base_url"] = Config.get('base_url') - - load_dotenv() - basedir = os.path.abspath(os.path.join(root_path, "server")) - database_file = os.path.abspath(os.path.join(basedir, Config.get('database_uri'))) - if isinstance(database_file, bytes): - database_file = database_file.decode('utf-8') - databaseuri = 'sqlite:///' + database_file - self.config['SQLALCHEMY_DATABASE_URI'] = databaseuri - self.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - db.init_app(self) - - Migrate(self, db) - - self.socketio = SocketIO(self, cors_allowed_origins="*", engineio_logger=False, socketio_logger=False, - async_mode='eventlet' if self.config["environment"] == 'production' else 'threading', - transport=['websocket', 'polling']) # make it eventlet on production! - - self.emulator_connections = emulator_connections - self.event_emitter = event_emitter - - CORS(self) - - # Register all routes - defineRoutes(self) - - self.fabricator_list = FabricatorList(self) - - @self.cli.command("test") - def run_tests(): - """Run all tests.""" - import subprocess - subprocess.run(["python", "../Tests/parallel_test_runner.py"]) - - @self.before_request - def handle_preflight(): - if request.method == "OPTIONS": - res = Response() - res.headers['X-Content-Type-Options'] = '*' - res.headers['Access-Control-Allow-Origin'] = '*' - res.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' - res.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' - return res - - # Serve static files - @self.route('/') - def serve_static(path='index.html'): - return send_from_directory(self.static_folder, path) - - @self.route('/assets/<path:filename>') - def serve_assets(filename): - return send_from_directory(os.path.join(self.static_folder, 'assets'), filename) - - @self.socketio.on('ping') - def handle_ping(): - self.socketio.emit('pong') - - @self.socketio.on('connect') - def handle_connect(): - print("Client connected") - - - @property - def logger(self): - return self._logger - - @logger.setter - def logger(self, logger): - self._logger = logger - - @logger.getter - def logger(self): - return self._logger - - def handle_errors_and_logging(self, e: Exception | str, fabricator=None): - from Classes.Fabricators.Fabricator import Fabricator - device = fabricator - if isinstance(fabricator, Fabricator): - device = fabricator.device - if device is not None and hasattr(device, "logger") and device.logger is not None: - device.logger.error(e, stacklevel=3) - elif self.logger is None: - if isinstance(e, str): - print(e.strip()) - else: - import traceback - print(traceback.format_exception(None, e, e.__traceback__)) - else: - self.logger.error(e, stacklevel=3) - return False - - def get_emu_ports(self): - fake_device = next(iter(self.emulator_connections.values()), None) - if fake_device: - return [fake_device.fake_port, fake_device.fake_name, fake_device.fake_hwid] - return [None, None, None] - -def _find_custom_app(): - app = flask_current_app._get_current_object() - return app if isinstance(app, MyFlaskApp) else None - -current_app = LocalProxy(_find_custom_app) +# Basic app setup app = MyFlaskApp() diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index e1f767f4..a6d3c6d1 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -8,7 +8,7 @@ import gzip import serial import serial.tools.list_ports -from app import current_app +from globals import current_app from traceback import format_exc from Classes.Fabricators.Fabricator import Fabricator diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 04fb0d0d..01ff7f7e 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -1,7 +1,7 @@ from sqlalchemy.exc import SQLAlchemyError from flask import Blueprint, jsonify, request -from app import current_app as app +from globals import current_app as app from Classes.Fabricators.Device import Device from Classes.Fabricators.Fabricator import Fabricator from Classes.Ports import Ports diff --git a/server/controllers/statusService.py b/server/controllers/statusService.py index b8599680..1bac5cdc 100644 --- a/server/controllers/statusService.py +++ b/server/controllers/statusService.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, request import os -from app import current_app as app +from globals import current_app as app from traceback import format_exc status_bp = Blueprint("status", __name__) diff --git a/server/globals.py b/server/globals.py new file mode 100644 index 00000000..a7d09c07 --- /dev/null +++ b/server/globals.py @@ -0,0 +1,18 @@ +import os +from werkzeug.local import LocalProxy +from Classes.EventEmitter import EventEmitter + +# Global variables +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +uploads_folder = os.path.abspath(os.path.join(root_path, 'uploads')) + +emulator_connections = {} +event_emitter = EventEmitter() + +def _find_custom_app(): + from flask import current_app as flask_current_app + from MyFlaskApp import MyFlaskApp + app = flask_current_app._get_current_object() + return app if isinstance(app, MyFlaskApp) else None + +current_app = LocalProxy(_find_custom_app) \ No newline at end of file From 9051f86640566c6a2fb967163506a92a13176fa8 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 12:50:11 -0500 Subject: [PATCH 175/194] fix: go directory pathing --- printeremu/cmd/test_printer.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index 1812ee0c..d9feb090 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -7,6 +7,7 @@ import ( "log" "os" "os/signal" + "path/filepath" "strconv" "strings" "syscall" @@ -14,13 +15,32 @@ import ( "printeremu/src" ) +func getDataFilePath(filename string) string { + // Get the current working directory + wd, err := os.Getwd() + if err != nil { + log.Fatalf("Error getting working directory: %v", err) + } + + if filepath.Base(wd) == "cmd" { + // Go up one level to the root project directory + wd = filepath.Dir(wd) + } + + // Construct the absolute path to the file + return filepath.Join(wd, "data", filename) +} + func main() { - settings, err := src.LoadSettings("data/settings.json") + settingsFilePath := getDataFilePath("settings.json") + printersFilePath := getDataFilePath("printers.json") + + settings, err := src.LoadSettings(settingsFilePath) if err != nil { log.Fatalf("Error loading settings: %v", err) } - printers, err := src.LoadPrinters("data/printers.json") + printers, err := src.LoadPrinters(printersFilePath) if err != nil { log.Fatalf("Error loading printers: %v", err) } From bd88fe8e5bda4f65c6b02688635fe473345d116d Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 12:50:59 -0500 Subject: [PATCH 176/194] feat: allow user to quit in printer id ask --- printeremu/cmd/test_printer.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/printeremu/cmd/test_printer.go b/printeremu/cmd/test_printer.go index d9feb090..7c86f6dd 100644 --- a/printeremu/cmd/test_printer.go +++ b/printeremu/cmd/test_printer.go @@ -180,6 +180,11 @@ func askForPrinterID(printers []src.Printer) *src.Printer { scanner.Scan() idInput := scanner.Text() + if idInput == "exit" || idInput == "quit" { + fmt.Println("Exiting printer emulator...") + os.Exit(0) + } + id, err := strconv.Atoi(idInput) if err != nil { From 1e4d61e9e247d9e1c444d18ad027aa4b8bce5fcb Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 14:51:48 -0500 Subject: [PATCH 177/194] feat: work on M155 and begin to make it handle updates better --- printeremu/src/emulator.go | 65 ++- printeremu/src/gcode_commands.go | 720 +++++++++++++++++------------ printeremu/src/printer.go | 24 + printeremu/testserver/src/index.ts | 22 +- 4 files changed, 488 insertions(+), 343 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index fc607c62..c11b645b 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -78,6 +78,8 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se fmt.Println("Connected to WebSocket server") + printer.WSConnection = conn + pingTicker := time.NewTicker(5 * time.Second) defer pingTicker.Stop() @@ -87,19 +89,9 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se fmt.Println("Context canceled, closing connection.") return case <-pingTicker.C: - pingMessage := map[string]interface{}{ - "event": "ping", - "data": "alive", - } - - jsonPingMessage, err := json.Marshal(pingMessage) + err := printer.WriteSerial("ping", "alive") if err != nil { - log.Println("Failed to marshal ping message:", err) - return - } - - if err := conn.WriteMessage(websocket.TextMessage, jsonPingMessage); err != nil { log.Println("Error sending ping message:", err) return } @@ -151,32 +143,40 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se case "send_gcode": messageStr := strings.ReplaceAll(string(message), "'", "\"") - + var parsedMessage map[string]interface{} if err := json.Unmarshal([]byte(messageStr), &parsedMessage); err != nil { log.Println("Error parsing received message:", err) log.Println("Original message:", string(message)) continue } - + data := parsedMessage["data"] if data == nil { log.Println("Received G-code command, but no data.") continue } - + // Convert data to map dataMap, ok := data.(map[string]interface{}) if !ok { log.Println("Data is not a map") continue } - + printerID, pidOk := dataMap["printerid"].(string) gcode, gcodeOk := dataMap["gcode"].(string) - + fmt.Print("Received G-code command for printer ", printerID, ": ", gcode, "\n") - + + if !pidOk { + log.Println("Missing or invalid 'printerid' field in 'send_gcode' event data") + } + + if !gcodeOk { + log.Println("Missing or invalid 'gcode' field in 'send_gcode' event data") + } + if pidOk && gcodeOk { response := CommandHandler(gcode, printer) @@ -186,21 +186,23 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se } else { parsedResponse = "Unknown command" } - + printerResponse := map[string]interface{}{ "printerid": printerID, "response": parsedResponse, } - - responseMessage, err := json.Marshal(printerResponse) - + + jsonResponse, err := json.Marshal(printerResponse) + if err != nil { - log.Println("Failed to marshal printer_response:", err) + log.Println("Error marshaling printer response:", err) continue } - - if err := conn.WriteMessage(websocket.TextMessage, responseMessage); err != nil { - log.Println("Error sending printer_response:", err) + + err = printer.WriteSerial("gcode_response", string(jsonResponse)) + + if err != nil { + log.Println("Error sending gcode_response:", err) continue } } else { @@ -248,21 +250,12 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se "hwid": hwid, } - message := map[string]interface{}{ - "event": "printer_connect", - "data": dataMap, - } - - jsonMessage, err := json.Marshal(message) + err := printer.WriteSerial("printer_connect", dataMap) if err != nil { - log.Println("Failed to marshal printer object:", err) + log.Println("Failed to send printer_connect:", err) return } - - log.Println("Sending fake serial port message...") - - conn.WriteMessage(websocket.TextMessage, []byte(jsonMessage)) default: log.Println("Received from server:", string(message)) } diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 02e942f0..7bc3ada3 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -1,7 +1,9 @@ package src import ( + "encoding/json" "fmt" + "log" "math" "regexp" "strconv" @@ -10,37 +12,76 @@ import ( ) type Command interface { - Execute(printer *Printer) string + Execute(printer *Printer) string } // CommandHandler parses and executes commands func CommandHandler(command string, printer *Printer) string { - if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { - command = command[:semicolonIndex] - } + if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { + command = command[:semicolonIndex] + } - if command == "" { - return "" - } + if command == "" { + return "" + } - command = strings.TrimSpace(command) - cmd := NewCommand(command, printer) + command = strings.TrimSpace(command) + cmd := NewCommand(command, printer) - if cmd == nil { - return "Unknown command\n" - } + if cmd == nil { + return "Unknown command\n" + } - return cmd.Execute(printer) + if m155Cmd, ok := cmd.(*M155Command); ok { + if m155Cmd.interval == 0 { + m155Cmd.Stop() + return "ok" + } + + go func() { + for result := range m155Cmd.resultChan { + if printer.WSConnection != nil { + + printerResponse := map[string]interface{}{ + "printerid": printer.Id, + "response": result, + } + + jsonResponse, err := json.Marshal(printerResponse) + + if err != nil { + log.Println("Error marshaling printer response:", err) + continue + } + + err = printer.WriteSerial("gcode_response", string(jsonResponse)) + + if err != nil { + log.Println("Error sending gcode_response:", err) + continue + } + } + fmt.Println(result) + } + }() + } + + return cmd.Execute(printer) } // NewCommand parses a command string and returns the appropriate Command func NewCommand(command string, printer *Printer) Command { - for key, factory := range commandRegistry { - if strings.HasPrefix(command, key) { - return factory(command, printer) - } - } - return nil + command = strings.TrimSpace(command) + + for key, factory := range commandRegistry { + if len(command) >= len(key) && command[:len(key)] == key { + if len(command) == len(key) || command[len(key)] == ' ' || command[len(key)] == ';' { + return factory(command, printer) + } + } + } + + return nil } // ==================== Movement and Positioning Commands ==================== @@ -48,114 +89,114 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { - if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("ok\nX:%.2f Y:%.2f Z:%.2f\nok\n", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z) + return fmt.Sprintf("ok\nX:%.2f Y:%.2f Z:%.2f\nok\n", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z) } type G0G1Command struct { - target Vector3 - feedRate float64 + target Vector3 + feedRate float64 } func NewG0G1Command(command string, printer *Printer) *G0G1Command { - target, feedRate := parseMoveCommand(command, printer.Extruder.Position) + target, feedRate := parseMoveCommand(command, printer.Extruder.Position) - return &G0G1Command{target: target, feedRate: feedRate} + return &G0G1Command{target: target, feedRate: feedRate} } func (cmd *G0G1Command) Execute(printer *Printer) string { - // TODO: Implement feed rate - if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + // TODO: Implement feed rate + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } type G2G3Command struct { - target Vector3 - radius float64 - clockwise bool + target Vector3 + radius float64 + clockwise bool } func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Command { - target, radius := parseArcCommand(command, printer.Extruder.Position) + target, radius := parseArcCommand(command, printer.Extruder.Position) - return &G2G3Command{target: target, radius: radius, clockwise: clockwise} + return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } func (cmd *G2G3Command) Execute(printer *Printer) string { - if printer.Paused { - return "Printer is paused\n" - } + if printer.Paused { + return "Printer is paused\n" + } - currentPos := printer.Extruder.Position - arcAngle := 180.0 - angleRad := arcAngle * math.Pi / 180.0 + currentPos := printer.Extruder.Position + arcAngle := 180.0 + angleRad := arcAngle * math.Pi / 180.0 - if cmd.clockwise { - cmd.target.X = currentPos.X + cmd.radius*math.Cos(angleRad) - cmd.target.Y = currentPos.Y - cmd.radius*math.Sin(angleRad) - } else { - cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) - cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) - } + if cmd.clockwise { + cmd.target.X = currentPos.X + cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y - cmd.radius*math.Sin(angleRad) + } else { + cmd.target.X = currentPos.X - cmd.radius*math.Cos(angleRad) + cmd.target.Y = currentPos.Y + cmd.radius*math.Sin(angleRad) + } - if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(cmd.target); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) } type G4Command struct { - duration int + duration int } func NewG4Command(command string) *G4Command { - duration := parseDuration(command) - return &G4Command{duration: duration} + duration := parseDuration(command) + return &G4Command{duration: duration} } func (cmd *G4Command) Execute(printer *Printer) string { - return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) + return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) } type G90Command struct{} func (cmd *G90Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = true + printer.Extruder.AbsolutePositioning = true - return "Set to Absolute Positioning\n" + return "Set to Absolute Positioning\n" } type G91Command struct{} func (cmd *G91Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = false + printer.Extruder.AbsolutePositioning = false - return "Set to Relative Positioning\n" + return "Set to Relative Positioning\n" } type G92Command struct { - position Vector3 + position Vector3 } func NewG92Command(command string) *G92Command { - position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) + position, _ := parseMoveCommand(command, Vector3{0, 0, 0}) - return &G92Command{position: position} + return &G92Command{position: position} } func (cmd *G92Command) Execute(printer *Printer) string { - if err := printer.MoveExtruder(cmd.position); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.MoveExtruder(cmd.position); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) + return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) } // ==================== Unit Conversion Commands ==================== @@ -163,177 +204,175 @@ func (cmd *G92Command) Execute(printer *Printer) string { type G20Command struct{} func (cmd *G20Command) Execute(printer *Printer) string { - printer.Units = "inches" - return "Units set to inches\n" + printer.Units = "inches" + return "Units set to inches\n" } type G21Command struct{} func (cmd *G21Command) Execute(printer *Printer) string { - printer.Units = "mm" - return "Units set to millimeters\n" + printer.Units = "mm" + return "Units set to millimeters\n" } // ==================== Temperature and Fan Control Commands ==================== type M104Command struct { - temperature float64 + temperature float64 } func NewM104Command(command string) *M104Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M104Command{temperature: temperature} + return &M104Command{temperature: temperature} } func (cmd *M104Command) Execute(printer *Printer) string { - if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - return "ok\n" + return "ok\n" } // M106Command sets the fan speed type M106Command struct { - fanSpeed float64 + fanSpeed float64 } func NewM106Command(command string) *M106Command { - fanSpeed := parseFanSpeed(command) - return &M106Command{fanSpeed: fanSpeed} + fanSpeed := parseFanSpeed(command) + return &M106Command{fanSpeed: fanSpeed} } func (cmd *M106Command) Execute(printer *Printer) string { - if cmd.fanSpeed > 255 { - return "Error: invalid fan speed. Valid range: 0 to 255\n" - } - if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } - return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) + if cmd.fanSpeed > 255 { + return "Error: invalid fan speed. Valid range: 0 to 255\n" + } + if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } + return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) } - type M107Command struct{} func (cmd *M107Command) Execute(printer *Printer) string { - printer.SetFanSpeed(0) + printer.SetFanSpeed(0) - return "Fan turned off\n" + return "Fan turned off\n" } type M140Command struct { - temperature float64 + temperature float64 } func NewM140Command(command string) *M140Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M140Command{temperature: temperature} + return &M140Command{temperature: temperature} } func (cmd *M140Command) Execute(printer *Printer) string { - if err := printer.SetBedTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) - } + if err := printer.SetBedTemperature(cmd.temperature); err != nil { + return fmt.Sprintf("Error: %s\n", err.Error()) + } - printer.Heatbed.Heating = true - return "ok\n" + printer.Heatbed.Heating = true + return "ok\n" } // M190Command waits until the bed reaches the target temperature before allowing further commands type M190Command struct { - temperature float64 + temperature float64 } func NewM190Command(command string) *M190Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M190Command{temperature: temperature} + return &M190Command{temperature: temperature} } func (cmd *M190Command) Execute(printer *Printer) string { - printer.Heatbed.TargetTemp = cmd.temperature + printer.Heatbed.TargetTemp = cmd.temperature - if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { - return fmt.Sprintf("B:%.2f / %.2f\n", printer.Heatbed.Temp, printer.Heatbed.TargetTemp) - } -//TODO: Error handling - printer.Heatbed.Heating = false - return "ok\n" + if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { + return fmt.Sprintf("B:%.2f / %.2f\n", printer.Heatbed.Temp, printer.Heatbed.TargetTemp) + } + //TODO: Error handling + printer.Heatbed.Heating = false + return "ok\n" } - type M109Command struct { - temperature float64 + temperature float64 } func NewM109Command(command string) *M109Command { - temperature := parseTemperature(command) + temperature := parseTemperature(command) - return &M109Command{temperature: temperature} + return &M109Command{temperature: temperature} } func (cmd *M109Command) Execute(printer *Printer) string { - printer.Extruder.TargetTemp = cmd.temperature + printer.Extruder.TargetTemp = cmd.temperature - if printer.Extruder.ExtruderTemp < printer.Extruder.TargetTemp { - return fmt.Sprintf("T:%.2f / %.2f\n", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp) - } + if printer.Extruder.ExtruderTemp < printer.Extruder.TargetTemp { + return fmt.Sprintf("T:%.2f / %.2f\n", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp) + } - return "ok\n" + return "ok\n" } type M113Command struct{} func NewM113Command(command string) *M113Command { - return &M113Command{} + return &M113Command{} } func (cmd *M113Command) Execute(printer *Printer) string { - printer.KeepAliveTime = time.Now() - return "Keepalive signal sent\n" + printer.KeepAliveTime = time.Now() + return "Keepalive signal sent\n" } type M204Command struct { - acceleration float64 + acceleration float64 } func NewM204Command(command string) *M204Command { - acceleration := parseAcceleration(command) - return &M204Command{acceleration: acceleration} + acceleration := parseAcceleration(command) + return &M204Command{acceleration: acceleration} } func (cmd *M204Command) Execute(printer *Printer) string { - printer.Acceleration = cmd.acceleration - return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) + printer.Acceleration = cmd.acceleration + return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) } type M73Command struct { - progress int - remaining int + progress int + remaining int } func NewM73Command(command string) *M73Command { - progress := parseProgress(command) - remaining := parseRemainingTime(command) - return &M73Command{progress: progress, remaining: remaining} + progress := parseProgress(command) + remaining := parseRemainingTime(command) + return &M73Command{progress: progress, remaining: remaining} } func (cmd *M73Command) Execute(printer *Printer) string { - printer.UpdateProgress(cmd.progress) - return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\nok\n", cmd.progress, cmd.remaining) + printer.UpdateProgress(cmd.progress) + return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\nok\n", cmd.progress, cmd.remaining) } // Parsing helpers func parseRemainingTime(command string) int { - reR := regexp.MustCompile(`R([0-9]+)`) - remaining := 0 - if rMatch := reR.FindStringSubmatch(command); rMatch != nil { - remaining, _ = strconv.Atoi(rMatch[1]) - } - return remaining + reR := regexp.MustCompile(`R([0-9]+)`) + remaining := 0 + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + remaining, _ = strconv.Atoi(rMatch[1]) + } + return remaining } // ==================== Control and Status Commands ==================== @@ -341,310 +380,403 @@ func parseRemainingTime(command string) int { type CancelCommand struct{} func (cmd *CancelCommand) Execute(printer *Printer) string { - printer.Pause() + printer.Pause() - return "Emergency stop activated, printer paused\n" + return "Emergency stop activated, printer paused\n" } type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) string { - position := printer.Extruder.Position - return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\nok\n", position.X, position.Y, position.Z) + position := printer.Extruder.Position + return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\nok\n", position.X, position.Y, position.Z) } type M115Command struct{} func (cmd *M115Command) Execute(printer *Printer) string { - return "Firmware: TotallyRealMarlin 2.1.2.5\n" + return "Firmware: TotallyRealMarlin 2.1.2.5\n" } type M17Command struct{} func (cmd *M17Command) Execute(printer *Printer) string { - printer.EnableMotor("x") - printer.EnableMotor("y") - printer.EnableMotor("z") - return "Motors enabled\n" + printer.EnableMotor("x") + printer.EnableMotor("y") + printer.EnableMotor("z") + return "Motors enabled\n" } type M18Command struct{} func (cmd *M18Command) Execute(printer *Printer) string { - printer.DisableMotor("x") - printer.DisableMotor("y") - printer.DisableMotor("z") - return "Motors disabled\n" + printer.DisableMotor("x") + printer.DisableMotor("y") + printer.DisableMotor("z") + return "Motors disabled\n" } type M82Command struct{} func (cmd *M82Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = true - return "Extruder set to absolute positioning\n" + printer.Extruder.AbsolutePositioning = true + return "Extruder set to absolute positioning\n" } type M83Command struct{} func (cmd *M83Command) Execute(printer *Printer) string { - printer.Extruder.AbsolutePositioning = false - return "Extruder set to relative positioning\n" + printer.Extruder.AbsolutePositioning = false + return "Extruder set to relative positioning\n" } type M302Command struct { - allowColdExtrusion bool + allowColdExtrusion bool } func NewM302Command(command string) *M302Command { - allowColdExtrusion := parseAllowColdExtrusion(command) - return &M302Command{allowColdExtrusion: allowColdExtrusion} + allowColdExtrusion := parseAllowColdExtrusion(command) + return &M302Command{allowColdExtrusion: allowColdExtrusion} } func (cmd *M302Command) Execute(printer *Printer) string { - if cmd.allowColdExtrusion { - return "Cold extrusion allowed\n" - } - return "Cold extrusion not allowed\n" + if cmd.allowColdExtrusion { + return "Cold extrusion allowed\n" + } + return "Cold extrusion not allowed\n" } type M503Command struct{} func (cmd *M503Command) Execute(printer *Printer) string { - return printer.String() + return printer.String() } type M997Command struct{} func (cmd *M997Command) Execute(printer *Printer) string { - return fmt.Sprintf("Machine name: %s", printer.Device) + return fmt.Sprintf("Machine name: %s", printer.Device) } type M601Command struct{} func (cmd *M601Command) Execute(printer *Printer) string { - printer.Pause() + printer.Pause() - return "Printer paused\n" + return "Printer paused\n" } type M602Command struct{} func (cmd *M602Command) Execute(printer *Printer) string { - printer.Resume() + printer.Resume() - return "Printer not paused\n" + return "Printer not paused\n" } type M900Command struct { - kFactor float64 + kFactor float64 } func NewM900Command(command string) *M900Command { - kFactor := parseKFactor(command) // Extracts the K value from the command + kFactor := parseKFactor(command) // Extracts the K value from the command - return &M900Command{kFactor: kFactor} + return &M900Command{kFactor: kFactor} } func (cmd *M900Command) Execute(printer *Printer) string { - printer.LinearAdvanceFactor = cmd.kFactor + printer.LinearAdvanceFactor = cmd.kFactor - return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) + return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) } type M142Command struct { - temperature float64 + temperature float64 } func NewM142Command(command string) *M142Command { - temperature := parseTemperature(command) // Uses parseTemperature to get S value + temperature := parseTemperature(command) // Uses parseTemperature to get S value - return &M142Command{temperature: temperature} + return &M142Command{temperature: temperature} } func (cmd *M142Command) Execute(printer *Printer) string { - printer.HeatbreakTemp = cmd.temperature + printer.HeatbreakTemp = cmd.temperature - return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) + return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) } type M84Command struct { - axes []string + axes []string } func NewM84Command(command string) *M84Command { - axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command + axes := parseAxes(command) // Extracts axes (X, Y, Z, E) from the command - return &M84Command{axes: axes} + return &M84Command{axes: axes} } func (cmd *M84Command) Execute(printer *Printer) string { - for _, axis := range cmd.axes { - printer.DisableMotor(axis) - } - - return fmt.Sprintf("Motors %v disabled\n", cmd.axes) + for _, axis := range cmd.axes { + printer.DisableMotor(axis) + } + + return fmt.Sprintf("Motors %v disabled\n", cmd.axes) +} + +type M155Command struct { + interval time.Duration + stopChan chan struct{} + resultChan chan string + once bool +} + +func NewM155Command(command string) *M155Command { + interval := parseM155Interval(command) + stopChan := make(chan struct{}) + resultChan := make(chan string) + + return &M155Command{ + interval: time.Duration(interval) * time.Second, + stopChan: stopChan, + resultChan: resultChan, + once: false, + } +} + +func (cmd *M155Command) Execute(printer *Printer) string { + if cmd.interval == 0 { + cmd.Stop() + return "ok" + } + + if !cmd.once { + cmd.once = true + result := fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.Heatbed.Temp, + printer.Heatbed.TargetTemp, + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.HeatbreakTemp) + + go func() { + ticker := time.NewTicker(cmd.interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + result := fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.Heatbed.Temp, + printer.Heatbed.TargetTemp, + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.HeatbreakTemp) + + select { + case cmd.resultChan <- result: + //log.Println("Sent result to channel") + default: + //log.Println("Channel is blocked, unable to send result") + } + + case <-cmd.stopChan: + close(cmd.resultChan) + return + } + } + }() + + return result + } + + return "ok" +} + +func (cmd *M155Command) Stop() { + close(cmd.stopChan) } // ==================== Parsing Helpers ==================== func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { - reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) - reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) - reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) - reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) + reX := regexp.MustCompile(`X([-+]?[0-9]*\.?[0-9]+)`) + reY := regexp.MustCompile(`Y([-+]?[0-9]*\.?[0-9]+)`) + reZ := regexp.MustCompile(`Z([-+]?[0-9]*\.?[0-9]+)`) + reF := regexp.MustCompile(`F([-+]?[0-9]*\.?[0-9]+)`) - target := currentPos - feedRate := 3600.0 + target := currentPos + feedRate := 3600.0 - if xMatch := reX.FindStringSubmatch(command); xMatch != nil { - target.X, _ = strconv.ParseFloat(xMatch[1], 64) - } + if xMatch := reX.FindStringSubmatch(command); xMatch != nil { + target.X, _ = strconv.ParseFloat(xMatch[1], 64) + } - if yMatch := reY.FindStringSubmatch(command); yMatch != nil { - target.Y, _ = strconv.ParseFloat(yMatch[1], 64) - } + if yMatch := reY.FindStringSubmatch(command); yMatch != nil { + target.Y, _ = strconv.ParseFloat(yMatch[1], 64) + } - if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { - target.Z, _ = strconv.ParseFloat(zMatch[1], 64) - } + if zMatch := reZ.FindStringSubmatch(command); zMatch != nil { + target.Z, _ = strconv.ParseFloat(zMatch[1], 64) + } - if fMatch := reF.FindStringSubmatch(command); fMatch != nil { - feedRate, _ = strconv.ParseFloat(fMatch[1], 64) - } + if fMatch := reF.FindStringSubmatch(command); fMatch != nil { + feedRate, _ = strconv.ParseFloat(fMatch[1], 64) + } - return target, feedRate + return target, feedRate } func parseArcCommand(command string, currentPos Vector3) (Vector3, float64) { - reR := regexp.MustCompile(`R([-+]?[0-9]*\.?[0-9]+)`) - radius := 0.0 - target := currentPos + reR := regexp.MustCompile(`R([-+]?[0-9]*\.?[0-9]+)`) + radius := 0.0 + target := currentPos - if rMatch := reR.FindStringSubmatch(command); rMatch != nil { - radius, _ = strconv.ParseFloat(rMatch[1], 64) - } + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + radius, _ = strconv.ParseFloat(rMatch[1], 64) + } - return target, radius + return target, radius } func parseDuration(command string) int { - reP := regexp.MustCompile(`P([0-9]+)`) - duration := 0 + reP := regexp.MustCompile(`P([0-9]+)`) + duration := 0 - if pMatch := reP.FindStringSubmatch(command); pMatch != nil { - duration, _ = strconv.Atoi(pMatch[1]) - } - return duration + if pMatch := reP.FindStringSubmatch(command); pMatch != nil { + duration, _ = strconv.Atoi(pMatch[1]) + } + return duration } func parseTemperature(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - temperature := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + temperature := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - temperature, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + temperature, _ = strconv.ParseFloat(sMatch[1], 64) + } - return temperature + return temperature } func parseFanSpeed(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - fanSpeed := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + fanSpeed := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + fanSpeed, _ = strconv.ParseFloat(sMatch[1], 64) + } - return fanSpeed + return fanSpeed } func parseAcceleration(command string) float64 { - reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) - acceleration := 0.0 + reS := regexp.MustCompile(`S([-+]?[0-9]*\.?[0-9]+)`) + acceleration := 0.0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - acceleration, _ = strconv.ParseFloat(sMatch[1], 64) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + acceleration, _ = strconv.ParseFloat(sMatch[1], 64) + } - return acceleration + return acceleration } func parseProgress(command string) int { - reS := regexp.MustCompile(`S([0-9]+)`) - progress := 0 + reS := regexp.MustCompile(`S([0-9]+)`) + progress := 0 - if sMatch := reS.FindStringSubmatch(command); sMatch != nil { - progress, _ = strconv.Atoi(sMatch[1]) - } + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + progress, _ = strconv.Atoi(sMatch[1]) + } - return progress + return progress } func parseKFactor(command string) float64 { - reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) - kFactor := 0.0 + reK := regexp.MustCompile(`K([-+]?[0-9]*\.?[0-9]+)`) + kFactor := 0.0 - if kMatch := reK.FindStringSubmatch(command); kMatch != nil { - kFactor, _ = strconv.ParseFloat(kMatch[1], 64) - } + if kMatch := reK.FindStringSubmatch(command); kMatch != nil { + kFactor, _ = strconv.ParseFloat(kMatch[1], 64) + } - return kFactor + return kFactor } func parseAllowColdExtrusion(command string) bool { - reP := regexp.MustCompile(`P([01])`) - allowColdExtrusion := false + reP := regexp.MustCompile(`P([01])`) + allowColdExtrusion := false - if pMatch := reP.FindStringSubmatch(command); pMatch != nil { - allowColdExtrusion, _ = strconv.ParseBool(pMatch[1]) - } + if pMatch := reP.FindStringSubmatch(command); pMatch != nil { + allowColdExtrusion, _ = strconv.ParseBool(pMatch[1]) + } - return allowColdExtrusion + return allowColdExtrusion } func parseAxes(command string) []string { - reAxes := regexp.MustCompile(`[XYZE]`) + reAxes := regexp.MustCompile(`[XYZE]`) + + return reAxes.FindAllString(command, -1) +} + +func parseM155Interval(command string) int { + reS := regexp.MustCompile(`S([0-9]+)`) + interval := 1 // Default interval is 1 second + + if sMatch := reS.FindStringSubmatch(command); sMatch != nil { + if intervalValue, err := strconv.Atoi(sMatch[1]); err == nil { + interval = intervalValue + } else { + log.Printf("Warning: Invalid interval value '%s' in M155 command. Using default interval: 1 second.", sMatch[1]) + } + } - return reAxes.FindAllString(command, -1) + return interval } // ==================== Command Registry ==================== var commandRegistry = map[string]func(string, *Printer) Command{ - "G28": func(cmd string, p *Printer) Command { return &G28Command{} }, - "G0": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, - "G1": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, - "G2": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, true, p) }, - "G3": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, false, p) }, - "G4": func(cmd string, p *Printer) Command { return NewG4Command(cmd) }, - "G20": func(cmd string, p *Printer) Command { return &G20Command{} }, - "G21": func(cmd string, p *Printer) Command { return &G21Command{} }, - "G90": func(cmd string, p *Printer) Command { return &G90Command{} }, - "G91": func(cmd string, p *Printer) Command { return &G91Command{} }, - "G92": func(cmd string, p *Printer) Command { return NewG92Command(cmd) }, - "M104": func(cmd string, p *Printer) Command { return NewM104Command(cmd) }, - "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, - "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, - "M109": func(cmd string, p *Printer) Command { return NewM109Command(cmd) }, - "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, - "M113": func(cmd string, p *Printer) Command { return &M113Command{} }, - "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, - "M115": func(cmd string, p *Printer) Command { return &M115Command{} }, - "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, - "M190": func(cmd string, p *Printer) Command { return NewM190Command(cmd) }, - "M204": func(cmd string, p *Printer) Command { return NewM204Command(cmd) }, - "M73": func(cmd string, p *Printer) Command { return NewM73Command(cmd) }, - "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, - "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, - "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, - "M900": func(cmd string, p *Printer) Command { return NewM900Command(cmd) }, - "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, - "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, - "M17": func(cmd string, p *Printer) Command { return &M17Command{} }, - "M18": func(cmd string, p *Printer) Command { return &M18Command{} }, - "M302": func(cmd string, p *Printer) Command { return NewM302Command(cmd) }, - "M503": func(cmd string, p *Printer) Command { return &M503Command{} }, - "M82": func(cmd string, p *Printer) Command { return &M82Command{} }, - "M83": func(cmd string, p *Printer) Command { return &M83Command{} }, + "G28": func(cmd string, p *Printer) Command { return &G28Command{} }, + "G0": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G1": func(cmd string, p *Printer) Command { return NewG0G1Command(cmd, p) }, + "G2": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, true, p) }, + "G3": func(cmd string, p *Printer) Command { return NewG2G3Command(cmd, false, p) }, + "G4": func(cmd string, p *Printer) Command { return NewG4Command(cmd) }, + "G20": func(cmd string, p *Printer) Command { return &G20Command{} }, + "G21": func(cmd string, p *Printer) Command { return &G21Command{} }, + "G90": func(cmd string, p *Printer) Command { return &G90Command{} }, + "G91": func(cmd string, p *Printer) Command { return &G91Command{} }, + "G92": func(cmd string, p *Printer) Command { return NewG92Command(cmd) }, + "M104": func(cmd string, p *Printer) Command { return NewM104Command(cmd) }, + "M106": func(cmd string, p *Printer) Command { return NewM106Command(cmd) }, + "M107": func(cmd string, p *Printer) Command { return &M107Command{} }, + "M109": func(cmd string, p *Printer) Command { return NewM109Command(cmd) }, + "M112": func(cmd string, p *Printer) Command { return &CancelCommand{} }, + "M113": func(cmd string, p *Printer) Command { return &M113Command{} }, + "M114": func(cmd string, p *Printer) Command { return &M114Command{} }, + "M115": func(cmd string, p *Printer) Command { return &M115Command{} }, + "M140": func(cmd string, p *Printer) Command { return NewM140Command(cmd) }, + "M190": func(cmd string, p *Printer) Command { return NewM190Command(cmd) }, + "M204": func(cmd string, p *Printer) Command { return NewM204Command(cmd) }, + "M73": func(cmd string, p *Printer) Command { return NewM73Command(cmd) }, + "M601": func(cmd string, p *Printer) Command { return &M601Command{} }, + "M602": func(cmd string, p *Printer) Command { return &M602Command{} }, + "M997": func(cmd string, p *Printer) Command { return &M997Command{} }, + "M900": func(cmd string, p *Printer) Command { return NewM900Command(cmd) }, + "M142": func(cmd string, p *Printer) Command { return NewM142Command(cmd) }, + "M84": func(cmd string, p *Printer) Command { return NewM84Command(cmd) }, + "M17": func(cmd string, p *Printer) Command { return &M17Command{} }, + "M18": func(cmd string, p *Printer) Command { return &M18Command{} }, + "M302": func(cmd string, p *Printer) Command { return NewM302Command(cmd) }, + "M503": func(cmd string, p *Printer) Command { return &M503Command{} }, + "M82": func(cmd string, p *Printer) Command { return &M82Command{} }, + "M83": func(cmd string, p *Printer) Command { return &M83Command{} }, + "M155": func(cmd string, p *Printer) Command { return NewM155Command(cmd) }, } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 173a97be..4bb4297a 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -3,6 +3,7 @@ package src import ( "encoding/json" "fmt" + "sync" "time" "github.com/gorilla/websocket" @@ -30,6 +31,20 @@ type Printer struct { Attributes map[string]interface{} Data map[string]interface{} WSConnection *websocket.Conn + + CommandStatus *PrinterCommandStatus +} + +type PrinterCommandStatus struct { + m115Status M115Status +} + +type M115Status struct { + Interval time.Duration + StopChan chan struct{} + ResultChan chan string + Once bool + Mutex sync.Mutex } // DisableMotor disables a motor for a specific axis @@ -94,6 +109,15 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex Attributes: make(map[string]interface{}), Data: make(map[string]interface{}), WSConnection: nil, // Default will be offline emulator + + CommandStatus: &PrinterCommandStatus{ + m115Status: M115Status{ + Interval: 0, + StopChan: make(chan struct{}), + ResultChan: make(chan string), + Once: false, + }, + }, } return printer, nil diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts index a93924a3..9cc3a9a6 100644 --- a/printeremu/testserver/src/index.ts +++ b/printeremu/testserver/src/index.ts @@ -15,7 +15,6 @@ const eventHandlers = { ws.send(JSON.stringify({ event: 'error', message: 'No printerId provided' })); return; } - printersMap.set(printerId, { ws, printerInfo }); console.log(`Printer connected with ID: ${printerId}`); @@ -29,7 +28,7 @@ const eventHandlers = { ws.send(JSON.stringify({ event: 'error', message: 'Invalid ping data' })); } }, - gcode: (ws, data) => { + send_gcode: (ws, data) => { // Handle G-code command from the client here console.log(`Sent G-code: ${data}`); }, @@ -40,21 +39,18 @@ const eventHandlers = { function sendGCodeCommands(ws) { const gcodeCommands = [ - "G28", // Home all axes - "G1 X10 Y10 Z10 F1500", // Move to X:10 Y:10 Z:10 - "G1 X20 Y20 Z20 F1500", // Move to X:20 Y:20 Z:20 - "G1 Z0", // Move to Z:0 - "M104 S200", // Set extruder temperature - "M140 S60", // Set bed temperature - "M107", // Turn off fan + "M155 S1" ]; gcodeCommands.forEach((gcode, index) => { setTimeout(() => { console.log(`Sending G-code command: ${gcode}`); ws.send(JSON.stringify({ - event: 'gcode', - data: gcode + event: 'send_gcode', + data: { + printerid: '10000', + gcode: gcode + } })); }, index * 2000); // Send every 2 seconds }); @@ -113,6 +109,6 @@ wss.on('connection', (ws) => { }); }); -httpServer.listen(8000, () => { - console.log("WebSocket server is running on http://127.0.0.1:8000"); +httpServer.listen(8001, () => { + console.log("WebSocket server is running on http://127.0.0.1:8001"); }); \ No newline at end of file From 94d8cc687ed8e81464f33a35cfa0cd1365c65cdf Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 15:01:25 -0500 Subject: [PATCH 178/194] fix: finish properly handle M155 --- printeremu/src/gcode_commands.go | 53 ++++------------------------ printeremu/src/printer.go | 59 ++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 7bc3ada3..84bd5431 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -33,15 +33,9 @@ func CommandHandler(command string, printer *Printer) string { } if m155Cmd, ok := cmd.(*M155Command); ok { - if m155Cmd.interval == 0 { - m155Cmd.Stop() - return "ok" - } - go func() { for result := range m155Cmd.resultChan { if printer.WSConnection != nil { - printerResponse := map[string]interface{}{ "printerid": printer.Id, "response": result, @@ -546,61 +540,26 @@ func NewM155Command(command string) *M155Command { func (cmd *M155Command) Execute(printer *Printer) string { if cmd.interval == 0 { - cmd.Stop() + printer.CommandStatus.m155Status.Stop() return "ok" } - if !cmd.once { - cmd.once = true - result := fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + if !printer.CommandStatus.m155Status.IsRunning { + printer.CommandStatus.m155Status.Start(cmd.interval, cmd.resultChan) + + return (fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp, printer.Heatbed.Temp, printer.Heatbed.TargetTemp, printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp, - printer.HeatbreakTemp) - - go func() { - ticker := time.NewTicker(cmd.interval) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - result := fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", - printer.Extruder.ExtruderTemp, - printer.Extruder.TargetTemp, - printer.Heatbed.Temp, - printer.Heatbed.TargetTemp, - printer.Extruder.ExtruderTemp, - printer.Extruder.TargetTemp, - printer.HeatbreakTemp) - - select { - case cmd.resultChan <- result: - //log.Println("Sent result to channel") - default: - //log.Println("Channel is blocked, unable to send result") - } - - case <-cmd.stopChan: - close(cmd.resultChan) - return - } - } - }() - - return result + printer.HeatbreakTemp)) } return "ok" } -func (cmd *M155Command) Stop() { - close(cmd.stopChan) -} - // ==================== Parsing Helpers ==================== func parseMoveCommand(command string, currentPos Vector3) (Vector3, float64) { diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 4bb4297a..d43bff61 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -36,15 +36,16 @@ type Printer struct { } type PrinterCommandStatus struct { - m115Status M115Status + m155Status M155Status } -type M115Status struct { +type M155Status struct { Interval time.Duration StopChan chan struct{} ResultChan chan string Once bool Mutex sync.Mutex + IsRunning bool } // DisableMotor disables a motor for a specific axis @@ -111,11 +112,12 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex WSConnection: nil, // Default will be offline emulator CommandStatus: &PrinterCommandStatus{ - m115Status: M115Status{ + m155Status: M155Status{ Interval: 0, StopChan: make(chan struct{}), ResultChan: make(chan string), Once: false, + IsRunning: false, }, }, } @@ -257,6 +259,57 @@ func (printer *Printer) SetHwid(hwid string) { printer.Hwid = hwid } +// Start method to start the M155 status update +func (m *M155Status) Start(interval time.Duration, resultChan chan string) { + m.Mutex.Lock() + defer m.Mutex.Unlock() + + if m.IsRunning { + return // Avoid starting a new update if it's already running + } + + m.Interval = interval + m.ResultChan = resultChan + m.StopChan = make(chan struct{}) + m.IsRunning = true + + go func() { + ticker := time.NewTicker(m.Interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + // Send result via the result channel + result := fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) // Replace with actual values + select { + case m.ResultChan <- result: + default: + } + + case <-m.StopChan: + m.IsRunning = false + close(m.ResultChan) + return + } + } + }() +} + +// Stop method to stop the M155 status update +func (m *M155Status) Stop() { + m.Mutex.Lock() + defer m.Mutex.Unlock() + + if !m.IsRunning { + return // If it's not running, do nothing + } + + close(m.StopChan) // Signal the goroutine to stop + m.IsRunning = false +} + func (printer *Printer) WriteSerial(event string, data interface{}) error { if printer.WSConnection == nil { return fmt.Errorf("WebSocket connection not established") From 4e5ab59fd5e93eceb3695b221c6210d34c98751e Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 15:16:47 -0500 Subject: [PATCH 179/194] fix: handle first ok properly for auto temp --- printeremu/src/gcode_commands.go | 11 +------- printeremu/src/printer.go | 44 ++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 84bd5431..fb6cd483 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -545,16 +545,7 @@ func (cmd *M155Command) Execute(printer *Printer) string { } if !printer.CommandStatus.m155Status.IsRunning { - printer.CommandStatus.m155Status.Start(cmd.interval, cmd.resultChan) - - return (fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", - printer.Extruder.ExtruderTemp, - printer.Extruder.TargetTemp, - printer.Heatbed.Temp, - printer.Heatbed.TargetTemp, - printer.Extruder.ExtruderTemp, - printer.Extruder.TargetTemp, - printer.HeatbreakTemp)) + return printer.CommandStatus.m155Status.Start(cmd.interval, cmd.resultChan, printer) } return "ok" diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index d43bff61..318c3c4d 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -3,6 +3,7 @@ package src import ( "encoding/json" "fmt" + "log" "sync" "time" @@ -260,12 +261,42 @@ func (printer *Printer) SetHwid(hwid string) { } // Start method to start the M155 status update -func (m *M155Status) Start(interval time.Duration, resultChan chan string) { +func (m *M155Status) Start(interval time.Duration, resultChan chan string, printer *Printer) string { m.Mutex.Lock() defer m.Mutex.Unlock() if m.IsRunning { - return // Avoid starting a new update if it's already running + return "" // Avoid starting a new update if it's already running + } + + if !m.IsRunning && printer.WSConnection != nil { + result := fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.Heatbed.Temp, + printer.Heatbed.TargetTemp, + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.HeatbreakTemp) + + printerResponse := map[string]interface{}{ + "printerid": printer.Id, + "response": result, + } + + jsonResponse, err := json.Marshal(printerResponse) + + if err != nil { + log.Println("Error marshaling printer response:", err) + return "" + } + + err = printer.WriteSerial("gcode_response", string(jsonResponse)) + + if err != nil { + log.Println("Error sending gcode_response:", err) + return "" + } } m.Interval = interval @@ -295,6 +326,15 @@ func (m *M155Status) Start(interval time.Duration, resultChan chan string) { } } }() + + return fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.Heatbed.Temp, + printer.Heatbed.TargetTemp, + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.HeatbreakTemp) } // Stop method to stop the M155 status update From cf346554e8962635daa15b6032efb0611dad7ba4 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 15:53:47 -0500 Subject: [PATCH 180/194] fix: auto temp sends actual values --- printeremu/src/printer.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index 318c3c4d..b355891b 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -313,7 +313,13 @@ func (m *M155Status) Start(interval time.Duration, resultChan chan string, print case <-ticker.C: // Send result via the result channel result := fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) // Replace with actual values + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.Heatbed.Temp, + printer.Heatbed.TargetTemp, + printer.Extruder.ExtruderTemp, + printer.Extruder.TargetTemp, + printer.HeatbreakTemp) select { case m.ResultChan <- result: default: From 779419b323b8bf000ebdd71961b69b4751e3fd66 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 16:30:58 -0500 Subject: [PATCH 181/194] fix: remove weird printer response for M155 --- printeremu/src/emulator.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index c11b645b..085aef34 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -199,11 +199,13 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se continue } - err = printer.WriteSerial("gcode_response", string(jsonResponse)) + if gcode == "M155" { + err = printer.WriteSerial("gcode_response", string(jsonResponse)) - if err != nil { - log.Println("Error sending gcode_response:", err) - continue + if err != nil { + log.Println("Error sending gcode_response:", err) + continue + } } } else { log.Println("Invalid send_gcode payload") From 5a2bc6ba1c065c4340aa8a222d4b38ae6c8c73c3 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 17:10:22 -0500 Subject: [PATCH 182/194] fix: assorted command print fixes --- printeremu/src/emulator.go | 4 +++- printeremu/src/gcode_commands.go | 2 +- printeremu/testserver/src/index.ts | 10 +++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 085aef34..9c6e3abe 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -180,6 +180,8 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se if pidOk && gcodeOk { response := CommandHandler(gcode, printer) + fmt.Println("Response:", response) + parsedResponse := "" if response != "Unknown command" { parsedResponse = "ok" @@ -199,7 +201,7 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se continue } - if gcode == "M155" { + if gcode != "M155" { err = printer.WriteSerial("gcode_response", string(jsonResponse)) if err != nil { diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index fb6cd483..b1d2cd01 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -55,7 +55,7 @@ func CommandHandler(command string, printer *Printer) string { continue } } - fmt.Println(result) + //fmt.Println(result) } }() } diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts index 9cc3a9a6..9b559012 100644 --- a/printeremu/testserver/src/index.ts +++ b/printeremu/testserver/src/index.ts @@ -39,7 +39,15 @@ const eventHandlers = { function sendGCodeCommands(ws) { const gcodeCommands = [ - "M155 S1" + "G28", // Home all axes + "G1 X10 Y10 Z10 F1500", // Move to X:10 Y:10 Z:10 + "G1 X20 Y20 Z20 F1500", // Move to X:20 Y:20 Z:20 + "G1 Z0", // Move to Z:0 + "M104 S200", // Set extruder temperature + "M140 S60", // Set bed temperature + "M107", // Turn off fan + "M155 S1", // get temps + "M155 S0", // turn off ]; gcodeCommands.forEach((gcode, index) => { From 49a4c535375ec8c4be006aa5f0ab3221d75e97d2 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 5 Dec 2024 17:50:08 -0500 Subject: [PATCH 183/194] fix: working home pos command G28 --- printeremu/data/printers.json | 6 +++-- printeremu/src/emulator.go | 38 +++++++++++++++++++++++++++++++- printeremu/src/gcode_commands.go | 5 ++++- printeremu/src/printer.go | 11 +++++++-- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/printeremu/data/printers.json b/printeremu/data/printers.json index c0dad123..b2c9e938 100644 --- a/printeremu/data/printers.json +++ b/printeremu/data/printers.json @@ -12,7 +12,8 @@ "width": 210, "height": 220, "startTemp": 0, - "productId": "000D" + "productId": "000D", + "homePos": [14.0, -4.0, 2.0] }, "attributes": { "marlin": true @@ -31,7 +32,8 @@ "width": 220, "height": 250, "startTemp": 0, - "productId": "7523" + "productId": "7523", + "homePos": [-3.0, -10.0, 0.0] }, "attributes": { "marlin": true diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 9c6e3abe..2bd12981 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -56,6 +56,12 @@ func PostRegistry(printer *Printer) { printer.Heatbed.Width = printer.GetData("width").(float64) printer.Extruder.MaxZHeight = printer.GetData("height").(float64) printer.Heatbed.Temp = printer.GetData("startTemp").(float64) + + printer.HomePos = Vector3{ + X: printer.GetData("homePos").([]interface{})[0].(float64), + Y: printer.GetData("homePos").([]interface{})[1].(float64), + Z: printer.GetData("homePos").([]interface{})[2].(float64), + } } func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, settings *EmulatorSettings) { @@ -201,7 +207,37 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se continue } - if gcode != "M155" { + if gcode == "G28" { + okResponse := map[string]interface{}{ + "printerid": printerID, + "response": "ok", + } + + okJsonResponse, err := json.Marshal(okResponse) + + if err != nil { + log.Println("Error marshaling printer response:", err) + continue + } + + bodyResponse := map[string]interface{}{ + "printerid": printerID, + "response": fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z), + } + + okBodyResponse, err := json.Marshal(bodyResponse) + + if err != nil { + log.Println("Error marshaling printer response:", err) + continue + } + + err = printer.WriteSerial("gcode_response", string(okJsonResponse)) + + err = printer.WriteSerial("gcode_response", string(okBodyResponse)) + + err = printer.WriteSerial("gcode_response", string(okJsonResponse)) + } else if gcode != "M155" { err = printer.WriteSerial("gcode_response", string(jsonResponse)) if err != nil { diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index b1d2cd01..0a75f7b9 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -83,10 +83,13 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} func (cmd *G28Command) Execute(printer *Printer) string { - if err := printer.MoveExtruder(Vector3{X: 0, Y: 0, Z: 0}); err != nil { + homePos := printer.HomePos + + if err := printer.MoveExtruder(homePos); err != nil { return fmt.Sprintf("Error: %s\n", err.Error()) } + // TODO: send as seperate messages for socket return fmt.Sprintf("ok\nX:%.2f Y:%.2f Z:%.2f\nok\n", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z) } diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index b355891b..a2a2bfde 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -32,6 +32,7 @@ type Printer struct { Attributes map[string]interface{} Data map[string]interface{} WSConnection *websocket.Conn + HomePos Vector3 CommandStatus *PrinterCommandStatus } @@ -112,6 +113,12 @@ func NewPrinter(id int, device, description, hwid, name, status, date string, ex Data: make(map[string]interface{}), WSConnection: nil, // Default will be offline emulator + HomePos: Vector3{ + X: 0.0, + Y: 0.0, + Z: 0.0, + }, + CommandStatus: &PrinterCommandStatus{ m155Status: M155Status{ Interval: 0, @@ -222,9 +229,9 @@ func (printer *Printer) MoveExtruder(targetPos Vector3) error { } // Optionally, check if the position is within the allowed print area - if targetPos.X < 0 || targetPos.Y < 0 || targetPos.Z < 0 { + /* if targetPos.X < 0 || targetPos.Y < 0 || targetPos.Z < 0 { return fmt.Errorf("invalid position: coordinates cannot be negative") - } + } */ // Check for the printable area bounds (example) if targetPos.X > printer.Heatbed.Width || targetPos.Y > printer.Heatbed.Length || targetPos.Z > printer.Extruder.MaxZHeight { From 285a64c3012bd3c1742f97812669e9eae023c8a7 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Sun, 8 Dec 2024 12:17:05 -0500 Subject: [PATCH 184/194] fix: push wip --- client/package.json | 4 +- client/src/App.vue | 7 +- client/src/components/ConsoleTerminal.vue | 81 +++++++++++++++++++ client/src/components/SettingsPanel.vue | 6 +- client/src/model/ports.ts | 2 + client/src/views/MainView.vue | 26 ++++-- server/Classes/EventEmitter.py | 9 +-- server/Classes/FabricatorConnection.py | 50 ++++++------ server/Classes/Fabricators/Device.py | 43 +++++----- server/Classes/Fabricators/Fabricator.py | 3 +- .../Classes/Fabricators/Printers/Printer.py | 77 ++++++++++-------- .../Fabricators/Printers/Prusa/PrusaMK3.py | 7 +- .../Printers/Prusa/PrusaPrinter.py | 2 +- server/Classes/Ports.py | 20 ++--- server/Mixins/hasResponseCodes.py | 43 +++++----- server/MyFlaskApp.py | 21 ++++- server/app.py | 32 ++++---- server/controllers/jobs.py | 2 + server/controllers/ports.py | 8 +- 19 files changed, 284 insertions(+), 159 deletions(-) create mode 100644 client/src/components/ConsoleTerminal.vue diff --git a/client/package.json b/client/package.json index dc2bc39e..60b624f0 100644 --- a/client/package.json +++ b/client/package.json @@ -17,7 +17,8 @@ "type-check": "vue-tsc --build --force", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "format": "prettier --write src/", - "start-flask": "npm run build-only && cd ../server && flask run", + "start-flask": "cd ../server && flask run", + "run-prod": "npm run build-only && start-flask", "start-vue": "npm run dev", "start": "npm-run-all --parallel start-flask start-vue", "start-electron": "electron-forge start", @@ -32,6 +33,7 @@ "@vuepic/vue-datepicker": "^8.5.0", "@vueuse/components": "^11.1.0", "@vueuse/core": "^11.1.0", + "ansi-to-html": "^0.7.2", "axios": "^1.6.5", "bootstrap": "^5.3.3", "bootstrap-daterangepicker": "^3.1.0", diff --git a/client/src/App.vue b/client/src/App.vue index 076ed1e3..f709e5c7 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -9,16 +9,17 @@ import SettingsPanel from '@/components/SettingsPanel.vue' import { onMounted } from 'vue'; import {setupSockets} from '@/model/sockets'; import {useRetrievePrintersInfo, printers} from '@/model/ports'; -import {setupTimeSocket, isLoading} from '@/model/jobs'; +import {isLoading} from '@/model/jobs'; const { retrieveInfo } = useRetrievePrintersInfo(); onMounted(async () => { printers.value = await retrieveInfo() + console.log(printers.value) // sockets - setupSockets(printers) - setupTimeSocket(printers) + setupSockets(printers.value) + }) </script> diff --git a/client/src/components/ConsoleTerminal.vue b/client/src/components/ConsoleTerminal.vue new file mode 100644 index 00000000..f1d77209 --- /dev/null +++ b/client/src/components/ConsoleTerminal.vue @@ -0,0 +1,81 @@ +<script setup lang="ts"> +import { ref, computed, watch } from "vue"; +import AnsiToHtml from "ansi-to-html"; + +// Props +const props = defineProps({lines: Array<string>}) + +const lines = ref(props.lines); + +watch(() => props.lines, (newLines) => { + lines.value = newLines; +}); + +// Watch for changes in the lines prop and trigger scrolling +watch( + () => lines, + () => { + scrollToBottom(); + } +); + +// Ref for the terminal container to enable automatic scrolling +const terminalOutput = ref<HTMLDivElement | null>(null); + +// Instance of ANSI-to-HTML converter +const ansiConverter = new AnsiToHtml(); + +// Convert the lines into HTML using the ANSI-to-HTML converter +const processedLines = computed(() => + lines.value!.map((line) => ansiConverter.toHtml(line)) +); + +// Scroll to the bottom whenever lines update +const scrollToBottom = () => { + if (terminalOutput.value) { + terminalOutput.value.scrollTop = terminalOutput.value.scrollHeight; + } +}; + +</script> + +<template> + <div class="terminal-container"> + <div class="terminal-output" ref="terminalOutput"> + <div + v-for="(line, index) in processedLines" + :key="index" + v-html="line" + class="terminal-line" + ></div> + </div> + </div> +</template> + +<style scoped> +.terminal-container { + width: 100%; + height: 400px; + background-color: #1e1e1e; + color: #ffffff; + font-family: "Courier New", Courier, monospace; + font-size: 14px; + border: 1px solid #333; + border-radius: 4px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.terminal-output { + flex: 1; + overflow-y: auto; + padding: 10px; +} + +.terminal-line { + white-space: pre-wrap; +} +</style> + + diff --git a/client/src/components/SettingsPanel.vue b/client/src/components/SettingsPanel.vue index 52cbb897..3a82286b 100644 --- a/client/src/components/SettingsPanel.vue +++ b/client/src/components/SettingsPanel.vue @@ -2,14 +2,14 @@ import {ref} from 'vue'; import {API_IP_ADDRESS, API_PORT, setServerIP, setServerPort} from '@/model/myFetch'; -const serverIP = ref<string>(API_IP_ADDRESS.value); -const serverPort = ref<number>(API_PORT.value); +const serverIP = ref<string>(API_IP_ADDRESS.value!); +const serverPort = ref<number>(parseInt(API_PORT.value!)); const saveSettings = () => { if (serverIP.value !== API_IP_ADDRESS.value) { setServerIP(serverIP.value); } - if (serverPort.value !== API_PORT.value) { + if (serverPort.value !== parseInt(API_PORT.value!)) { setServerPort(serverPort.value); } console.log(`Server IP: ${serverIP.value}, Server Port: ${serverPort.value}`); diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index 32794996..1ea8e3c0 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -24,6 +24,8 @@ export interface Device { extruder_temp?: number bed_temp?: number colorChangeBuffer?: number + colorbuff?: number, + consoles?: [string[], string[], string[], string[], string[]] // array of debug, info, and error console messages } export const printers = ref<Device[]>([]) diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index 7d921b6e..f3a491a8 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -8,6 +8,7 @@ import GCode3DLiveViewer from '@/components/GCode3DLiveViewer.vue'; import { useAssignIssue, useGetIssues, type Issue } from '@/model/issues'; import { jobTime, useAssignComment, useGetFile, useGetJobFile, useReleaseJob, useStartJob, type Job } from '@/model/jobs'; import { useRouter } from 'vue-router'; +import ConsoleTerminal from "@/components/ConsoleTerminal.vue"; const { assign } = useAssignIssue() const { assignComment } = useAssignComment() @@ -52,6 +53,13 @@ onMounted(async () => { liveModal?.addEventListener('hidden.bs.modal', () => { isGcodeLiveViewVisible.value = false; }); + if (printers.value.length > 0) { + for (const printer of printers.value as Device[]) { + if (printer.consoles === undefined) { + printer.consoles = [[],[],[],[],[]] + } + } + } }); function formatTime(milliseconds: number): string { @@ -166,12 +174,13 @@ const releasePrinter = async (jobToFind: Job | undefined, key: number, printerId let printer = printers.value.find((printer) => printer.id === printerIdToPrintTo) printer!.error = "" - if (printer) { - printer.extruder_temp = 0 - printer.bed_temp = 0; - printer.queue?.shift(); // Remove the first job in the queue - //TODO: marker - } + if (printer) { + printer.extruder_temp = 0 + printer.bed_temp = 0; + printer.queue?.shift(); // Remove the first job in the queue + printer.consoles = [[],[],[],[],[]] + //TODO: marker + } await releaseJob(jobToFind, key, printerIdToPrintTo) await nextTick() @@ -558,6 +567,11 @@ const handleDragEnd = async () => { v-html="printer?.status === 'colorchange' ? 'Waiting...' : (printer.queue[0]?.extruded ? formatETA(printer.queue[0]?.job_client?.eta) : '<i>Waiting...</i>')"></span> </td> </tr> + <tr> + <td colspan="9"> + <ConsoleTerminal :lines="printer.consoles![4] || []"/> + </td> + </tr> </div> <tr v-else :id="printer.id"> <td diff --git a/server/Classes/EventEmitter.py b/server/Classes/EventEmitter.py index dfe2e4da..7a46d511 100644 --- a/server/Classes/EventEmitter.py +++ b/server/Classes/EventEmitter.py @@ -2,6 +2,7 @@ class EventEmitter: def __init__(self): + self._listeners = None self._events = {} def on(self, event_name, callback): @@ -17,11 +18,9 @@ def emit(self, event_name, *args, **kwargs): # Trigger the callback with the provided data asyncio.create_task(callback(*args, **kwargs)) - def remove_listener(self, event_name, listener): + def remove_event(self, event_name): """ Remove a specific listener for an event """ - if event_name in self._listeners: - self._listeners[event_name] = [ - l for l in self._listeners[event_name] if l != listener - ] + if event_name in self._events: + self._events.pop(event_name) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index 1ecf0933..af76fd3f 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -108,26 +108,30 @@ def write(self, data): def read(self): """ Read response from the printer. - + :return: Response from the printer as bytes """ if not self._is_open: raise ConnectionError("WebSocket connection is not open") - + # Use a local event and queue for this specific read operation - local_response_event = threading.Event() - local_receive_queue = Queue() - def on_message_received(client_id, message): + async def on_message_received(client_id, message): try: data = json.loads(message) + print(data) + + if data.get("event") != ("gcode_response"): + return - if data.get("printerid") == self._fabricator_id: - response = data.get("response", "") - local_receive_queue.put(response) - local_response_event.set() - else: - print(f"Received message from unknown printer {data.get('printerid')}") + info: dict = json.loads(data.get("data")) + + # if data.get("printerid") == self._fabricator_id: + response = info.get("response", "") + self._receive_queue.put(response) + self._response_event.set() + # else: + # print(f"Received message from unknown printer {data.get('printerid')}") except json.JSONDecodeError as e: print(f"Failed to decode message: {message} - {e}") except Exception as e: @@ -135,21 +139,16 @@ def on_message_received(client_id, message): #try: # Register the temporary listener - app.event_emitter.on("message_received", on_message_received) - - # Wait for response with timeout - if not local_response_event.wait(timeout=self._timeout): - return b'' - #raise TimeoutError(f"No response received within {self._timeout} seconds") - - try: - response = local_receive_queue.get(block=False) - return response.encode('utf-8') if response else b'' # Convert string to bytes - except Empty: - return b'' - #finally: + app.event_emitter.on("message_received", on_message_received) + + try: + response = self._receive_queue.get(timeout=self._timeout) + return response.encode('utf-8') if response else b'' # Convert string to bytes + except Empty: + return b'' + finally: # Unregister the listener to prevent memory leaks - # app.event_emitter.remove_listener("message_received", on_message_received) + app.event_emitter.remove_event("message_received") def close(self): """ @@ -204,7 +203,6 @@ def readline(self): :return: A single line of response """ response = self.read() - if response is None: response = b'' diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 9c6250b8..5f63b00c 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -41,8 +41,7 @@ def __init__(self, dbID: int, serialPort: ListPortInfo | SysFS, consoleLogger=sy self.dbID: int = dbID self.serialPort: ListPortInfo | SysFS | None = serialPort self.serialID: str | None = serialPort.serial_number - if addLogger: - self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG, consoleLevel=Logger.ERROR) + self.logger = Logger(self.DESCRIPTION, port=self.serialPort.device, consoleLogger=consoleLogger, fileLogger=fileLogger, loggingLevel=Logger.DEBUG, consoleLevel=Logger.ERROR) if addLogger else None self.status = "idle" self.verdict = "" self.websocket_connection = websocket_connection @@ -91,7 +90,7 @@ def disconnect(self): self.serialConnection.close() self.serialConnection = None - def home(self, isVerbose: bool = True): + def home(self, isVerbose: bool = False): """ Home the device. :param isVerbose: Whether to log the command. @@ -146,14 +145,14 @@ def parseGcode(self, job: Job, isVerbose: bool = False): assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" try: with open(file, "r") as f: - if hasattr(self, "logger") and self.logger: self.logger.info(f"Printing {file}") + if self.logger is not None: self.logger.info(f"Printing {file}") for line in f: if line.startswith(";") or line == "\n": continue if current_app: with current_app.app_context(): current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) - if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(line.strip("\n")) + if isVerbose and self.logger: self.logger.debug(line.strip("\n")) if self.status == "paused": self.pause() while self.status == "paused": @@ -162,7 +161,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: self.logger.info("Job cancelled") + if self.logger is not None: self.logger.info("Job cancelled") return True elif self.status == "printing": self.resume() @@ -170,16 +169,16 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if isinstance(self, hasEndingSequence): self.endSequence() self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: self.logger.info("Job cancelled") + if self.logger is not None: self.logger.info("Job cancelled") return True if ";" in line: line = line.split(";")[0].strip() + "\n" self.sendGcode(line.encode("utf-8"), isVerbose=isVerbose) self.verdict = "complete" - if hasattr(self, "logger") and self.logger: self.logger.info("Job complete") + if self.logger is not None: self.logger.info("Job complete") return True except Exception as e: - if not hasattr(self, "logger") or self.logger is None: + if self.logger is None: print(e) else: self.logger.error("Error cancelling job:") @@ -211,7 +210,9 @@ def sendGcode(self, gcode: bytes, isVerbose: bool = False): assert isinstance(gcode, bytes) assert isinstance(isVerbose, bool) self.serialConnection.write(gcode) - if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(gcode.decode("utf-8")) + if isVerbose: + if self.logger is not None: self.logger.debug(gcode.decode("utf-8")) + else: print(gcode.decode("utf-8")) return True def getToolHeadLocation(self, isVerbose = False): @@ -227,7 +228,7 @@ def getToolHeadLocation(self, isVerbose = False): response = "" while not (("X:" in response) and ("Y:" in response) and ("Z:" in response)): response = self.serialConnection.readline().decode("utf-8") - if isVerbose and hasattr(self, "logger") and self.logger: self.logger.info(response) + if isVerbose and self.logger: self.logger.info(response) loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) @@ -236,25 +237,25 @@ def repair(self): try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip specific repair commands - if hasattr(self, "logger") and self.logger: + if self.logger is not None: self.logger.info(f"Repair skipped for {self.MODEL}") return "Repair not necessary for Ender devices." if self.serialConnection: - if hasattr(self, "logger") and self.logger: self.logger.info("Closing existing connection for repair.") + if self.logger is not None: self.logger.info("Closing existing connection for repair.") self.serialConnection.close() # Attempt to reconnect - if hasattr(self, "logger") and self.logger: self.logger.info("Attempting to reconnect for repair.") + if self.logger is not None: self.logger.info("Attempting to reconnect for repair.") self.connect() if self.serialConnection and self.serialConnection.is_open: - if hasattr(self, "logger") and self.logger: self.logger.info("Repair successful: connection reopened.") + if self.logger is not None: self.logger.info("Repair successful: connection reopened.") return "Repair successful." else: return "Repair failed: unable to reopen connection." except Exception as e: - if hasattr(self, "logger") and self.logger: self.logger.error(f"Error during repair: {e}") + if self.logger is not None: self.logger.error(f"Error during repair: {e}") return f"Repair failed with error: {e}" def diagnose(self): @@ -262,25 +263,25 @@ def diagnose(self): try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip the diagnosis - if hasattr(self, "logger") and self.logger: self.logger.info(f"Diagnosis skipped for {self.MODEL}") + if self.logger is not None: self.logger.info(f"Diagnosis skipped for {self.MODEL}") return "Diagnosis not necessary for Ender devices." - if hasattr(self, "logger") and self.logger: self.logger.info("Starting device diagnosis.") + if self.logger is not None: self.logger.info("Starting device diagnosis.") if not self.connect(): return "Diagnosis failed: unable to connect." - if hasattr(self, "logger") and self.logger: self.logger.info("Sending diagnostic G-code command (e.g., M115).") + if self.logger is not None: self.logger.info("Sending diagnostic G-code command (e.g., M115).") self.sendGcode(b"M115\n") response = self.serialConnection.readline().decode("utf-8").strip() if response: - if hasattr(self, "logger") and self.logger: self.logger.info(f"Diagnosis response: {response}") + if self.logger is not None: self.logger.info(f"Diagnosis response: {response}") return response else: return "Diagnosis failed: no response from device." except Exception as e: - if hasattr(self, "logger") and self.logger: self.logger.error(f"Error during diagnosis: {e}") + if self.logger is not None: self.logger.error(f"Error during diagnosis: {e}") return f"Diagnosis failed with error: {e}" def hardReset(self, newStatus: str): diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index de067efa..9e0fb12e 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -51,7 +51,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog self.devicePort = dbFab.devicePort self.date = dbFab.date self.dbID = dbFab.dbID - self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True, websocket_connection=next(iter(current_app.emulator_connections.values(), None)) if port.device == current_app.get_emu_ports()[0] else None) + self.device = self.createDevice(port, consoleLogger=consoleLogger, fileLogger=fileLogger, addLogger=True, websocket_connection=next(iter(current_app.emulator_connections.values())) if port.device == current_app.get_emu_ports()[0] else None) if self.description == "New Fabricator": self.description = self.device.getDescription() db.session.commit() @@ -74,6 +74,7 @@ def __to_JSON__(self): "queue": self.queue.convertQueueToJson(), "job": self.job.__to_JSON__() if self.job is not None else None, "device": self.device.__to_JSON__(), + "consoles": [[],[],[],[],[]], } @staticmethod diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 74082c70..3f8b6b0e 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -1,10 +1,9 @@ +import traceback from abc import ABCMeta import re from datetime import datetime from time import sleep - from globals import current_app - from Classes.Fabricators.Device import Device from Classes.Jobs import Job from Mixins.hasResponseCodes import checkTime, checkExtruderTemp, checkXYZ, checkBedTemp, checkOK @@ -52,7 +51,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: + if self.logger is not None: self.logger.debug("Job cancelled") return True @@ -98,7 +97,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: + if self.logger is not None: self.logger.debug("Job cancelled") return True @@ -144,7 +143,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: + if self.logger is not None: self.logger.debug("Job cancelled") return True self.status = "printing" @@ -169,14 +168,14 @@ def parseGcode(self, job: Job, isVerbose: bool = False): sleep(.5) readline = self.serialConnection.readline().decode("utf-8").strip() if readline: - if hasattr(self, "logger") and self.logger: self.logger.debug(readline) + if self.logger is not None: self.logger.debug(readline) if "T:" in readline and "B:" in readline: - if hasattr(self, "logger") and self.logger: self.logger.warning(f"Temperature line: {readline}") + if self.logger is not None: self.logger.warning(f"Temperature line: {readline}") self.handleTempLine(readline) if self.status == "cancelled": self.sendGcode(self.cancelCMD) self.verdict = "cancelled" - if hasattr(self, "logger") and self.logger: self.logger.debug("Job cancelled") + if self.logger is not None: self.logger.debug("Job cancelled") return True elif self.status == "printing": self.resume() @@ -209,31 +208,40 @@ def parseGcode(self, job: Job, isVerbose: bool = False): # if self.status == "complete" and job.extruded != 0: if self.status == "complete": self.verdict = "complete" - if hasattr(self, "logger") and self.logger: self.logger.debug("Job complete") + if self.logger is not None: self.logger.debug("Job complete") return True if self.status == "error": self.verdict = "error" - if hasattr(self, "logger") and self.logger: self.logger.debug("Job error") + if self.logger is not None: self.logger.debug("Job error") return False self.verdict = "complete" self.status = "complete" - if hasattr(self, "logger") and self.logger: self.logger.debug("Job complete") + if self.logger is not None: self.logger.debug("Job complete") return True except Exception as e: # self.setStatus("error") - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) + return current_app.handle_errors_and_logging(e, self) def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): + """ + Method to send gcode to the printer + :param gcode: the line of gcode to send to the printer + :type gcode: bytes | str | LiteralString + :param isVerbose: whether to log or not + :type isVerbose: bool + :return: True if the gcode was successfully sent, else False + :rtype: bool + """ assert self.serialConnection is not None, "Serial connection is None" assert self.serialConnection.is_open, "Serial connection is not open" if isinstance(gcode, str): if gcode[-1] != "\n": gcode += "\n" gcode = gcode.encode("utf-8") assert isinstance(gcode, bytes), f"Expected bytes, got {type(gcode)}" - self.serialConnection.write(gcode) + assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" callables = self.callablesHashtable.get(self.extractIndex(gcode), [checkOK]) + self.serialConnection.write(gcode) line = '' for func in callables: while True: @@ -241,25 +249,31 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): try: line = self.serialConnection.readline() decLine = line.decode("utf-8").strip() + # print(decLine if decLine != "" else "No line") + print("send gcode ok" if "ok" in decLine.lower() else "send gcode no ok") if "processing" in decLine or "echo" in decLine: continue if "T:" in decLine and "B:" in decLine: self.handleTempLine(decLine) - if func != checkBedTemp and func != checkExtruderTemp: + if func != checkBedTemp and func != checkExtruderTemp and func != checkOK: continue - if hasattr(self, "logger") and self.logger: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") - if func(line): + if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") + current_app.socketio.emit("console_update", {"message": decLine, "level": "debug", "printerid": self.dbID}) + if func(line, self): break except UnicodeDecodeError: - if isVerbose and hasattr(self, "logger") and self.logger: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") + if isVerbose: + if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") + else: print(f"{gcode.decode().strip()}: {line.strip()}") continue except Exception as e: if current_app: return current_app.handle_errors_and_logging(e, self) - elif hasattr(self, "logger") and self.logger: self.logger.error(e) + elif self.logger is not None: self.logger.error(e) + else: print(traceback.format_exc()) return False if not callables: - if hasattr(self, "logger") and self.logger: self.logger.info(f"{gcode.decode().strip()}: Always True") + if self.logger is not None: self.logger.info(f"{gcode.decode().strip()}: Always True") else: - if hasattr(self, "logger") and self.logger: self.logger.info( + if self.logger is not None: self.logger.info( gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) return True @@ -279,8 +293,7 @@ def changeFilament(self, filamentType: str, filamentDiameter: float): self.filamentType = filamentType self.filamentDiameter = filamentDiameter except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e, self) + current_app.handle_errors_and_logging(e, self) def changeNozzle(self, nozzleDiameter: float): """ @@ -294,8 +307,7 @@ def changeNozzle(self, nozzleDiameter: float): assert self.status == "idle", "Printer is not idle" self.nozzleDiameter = nozzleDiameter except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e, self) + current_app.handle_errors_and_logging(e, self) def handleTempLine(self, line: str): """ @@ -321,8 +333,7 @@ def handleTempLine(self, line: str): except ValueError: pass except Exception as e: - from app import handle_errors_and_logging - handle_errors_and_logging(e, self) + current_app.handle_errors_and_logging(e, self) def extractIndex(self, gcode: bytes) -> str: """ Method to extract the index of the gcode for use in the callablesHashtable @@ -332,7 +343,7 @@ def extractIndex(self, gcode: bytes) -> str: :rtype: str """ hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if hasattr(self, "logger") and self.logger: + if self.logger is not None: if hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") elif hashIndex == "G28": @@ -346,7 +357,7 @@ def pause(self): :rtype: bool """ if not self.pauseCMD: - if hasattr(self, "logger") and self.logger: self.logger.error("Pause command not implemented.") + if self.logger is not None: self.logger.error("Pause command not implemented.") return True try: assert self.pauseCMD is not None @@ -356,7 +367,7 @@ def pause(self): if hasattr(self, "keepAliveCMD") and self.keepAliveCMD: self.sendGcode(self.keepAliveCMD) self.sendGcode(self.pauseCMD) - if hasattr(self, "logger") and self.logger: self.logger.info("Job Paused") + if self.logger is not None: self.logger.info("Job Paused") return True except Exception as e: return current_app.handle_errors_and_logging(e, self) @@ -364,7 +375,7 @@ def pause(self): def resume(self): """Resume the device, if the resume command is implemented.""" if self.resumeCMD is None: - if hasattr(self, "logger") and self.logger: self.logger.error("Resume command not implemented.") + if self.logger is not None: self.logger.error("Resume command not implemented.") return False try: assert isinstance(self, Device), "self is not an instance of Device" @@ -372,7 +383,7 @@ def resume(self): assert self.serialConnection.is_open, "Serial connection is not open" if hasattr(self, "doNotKeepAliveCMD") and self.doNotKeepAliveCMD: self.sendGcode(self.doNotKeepAliveCMD) self.sendGcode(self.resumeCMD) - if hasattr(self, "logger") and self.logger: self.logger.info("Job Resumed") + if self.logger is not None: self.logger.info("Job Resumed") return True except Exception as e: return current_app.handle_errors_and_logging(e, self) @@ -381,7 +392,7 @@ def connect(self): super().connect() try: if self.serialConnection and self.serialConnection.is_open: - self.serialConnection.write(b"M155 S1\n") + self.sendGcode(b"M155 S1\n", isVerbose=True) return True except Exception as e: return current_app.handle_errors_and_logging(e, self) diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index 2493b69d..b9541612 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -1,6 +1,8 @@ +from Classes.FabricatorConnection import FabricatorConnection from Classes.Fabricators.Printers.Prusa.PrusaPrinter import PrusaPrinter from Classes.Vector3 import Vector3 from Mixins.hasResponseCodes import checkOK, checkTime, checkXYZ +from globals import current_app class PrusaMK3(PrusaPrinter): @@ -37,7 +39,7 @@ def getPrintTime(self): def connect(self): try: import serial - self.serialConnection = serial.Serial(self.serialPort.device, 115200, timeout=60) + self.serialConnection = FabricatorConnection.staticCreateConnection(self.serialPort.device, 115200, timeout=60) self.serialConnection.reset_input_buffer() from time import sleep sleep(4) @@ -45,5 +47,4 @@ def connect(self): self.sendGcode(b"M155 S1\n") return True except Exception as e: - from app import handle_errors_and_logging - return handle_errors_and_logging(e, self) \ No newline at end of file + return current_app.handle_errors_and_logging(e, self) \ No newline at end of file diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 4182b2f0..acc98b9e 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -33,7 +33,7 @@ def extractIndex(self, gcode: bytes) -> str: hashIndex += ".01" if g29addon == "P1" else ".02" except IndexError as e: hashIndex += ".01" - if hasattr(self, "logger"): + if self.logger is not None: if hashIndex == "M109" or hashIndex == "M190": self.logger.info("Waiting for temperature to stabilize...") elif hashIndex == "G28": diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 040f4a33..503b809b 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -15,24 +15,14 @@ def getPorts(): emu_port, emu_name, emu_hwid = app.get_emu_ports() if emu_port and emu_name and emu_hwid: ports.append(EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid)) - # if app: - # if app.fabricator_list is not None: - # print(1) - # [print(fab.device) for fab in app.fabricator_list.fabricators] - # else: - # print("No fabricator list") full_devices = [] for port in ports: if app: if app.fabricator_list.getFabricatorByPort(port) is None: if port.device == emu_port: - print(f"emulator_connections: {app.emulator_connections}" if app.emulator_connections else "No emulator connections") values = app.emulator_connections.values() - print(values) ws = next(iter(values), None) - print(ws if ws else "No websocket connection") device = Fabricator.staticCreateDevice(port, websocket_connection=ws) - print(device) else: device = Fabricator.staticCreateDevice(port) full_devices.append(device) @@ -51,12 +41,18 @@ def getListPorts(): @staticmethod def getPortByName(name: str): - """Get a specific port by its device name.""" + """ + Get a specific port by its device name. + :param name: The name of the device. + :type name: str + :return: The port object + :rtype: ListPortInfo | SysFS + """ assert isinstance(name, str), f"Name must be a string: {name} : {type(name)}" ports = Ports.getListPorts() if len(app.emulator_connections) > 0: emu_port, emu_name, emu_hwid = app.get_emu_ports() - if emu_port and emu_name and emu_hwid and emu_name == name: + if emu_port and emu_name and emu_hwid and emu_port == name: return EmuListPortInfo(emu_port, description="Emulator", hwid=emu_hwid) for port in ports: if not port: continue diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index 23e85908..f7235505 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -1,7 +1,7 @@ import re -import traceback from abc import ABCMeta, abstractmethod from Classes.Vector3 import Vector3 +from globals import current_app class hasResponsecodes(metaclass=ABCMeta): headPosition: Vector3 = None @@ -14,48 +14,45 @@ def getPrintTime(self): def getToolHeadLocation(self) -> Vector3: pass -def checkOK(line): - line = (line.decode() if isinstance(line, bytes) else line).strip() - return line == "ok" +def checkOK(line, dev): + line = (line.decode() if isinstance(line, bytes) else line).strip().lower() + return "ok" in line -def checkXYZ(line): - line = (line.decode() if isinstance(line, bytes) else line).strip() - return ("X:" in line) and ("Y:" in line) and ("Z:" in line) +def checkXYZ(line, dev): + line = (line.decode() if isinstance(line, bytes) else line).strip().lower() + return ("x:" in line) and ("y:" in line) and ("z:" in line) -def checkEcho(line): - line = (line.decode() if isinstance(line, bytes) else line).strip() +def checkEcho(line, dev): + line = (line.decode() if isinstance(line, bytes) else line).strip().lower() return line.startswith("echo") -def checkBedTemp(line): - line = (line.decode() if isinstance(line, bytes) else line).strip() +def checkBedTemp(line, dev): + line = (line.decode() if isinstance(line, bytes) else line).strip().lower() try: - return checkTemp([temp.strip() for temp in line.split("B:")[1].split("T0:")[0].split("X:")[0].split("/")]) - except IndexError as e: + return checkTemp([temp.strip() for temp in line.split("b:")[1].split("t0:")[0].split("x:")[0].split("/")]) + except IndexError: return False except Exception as e: - traceback.print_exc() - return False + return current_app.handle_errors_and_logging(e, dev) -def checkExtruderTemp(line): +def checkExtruderTemp(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip() try: return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")]) - except IndexError as e: + except IndexError: return False except Exception as e: - traceback.print_exc() - return False + return current_app.handle_errors_and_logging(e, dev) -def checkTemp(temps): +def checkTemp(temps, dev): try: if len(temps) == 2: if float(temps[1]) == 0.0: return True return float(temps[1]) - float(temps[0]) < 0.25 return False except Exception as e: - traceback.print_exc() - return False + return current_app.handle_errors_and_logging(e, dev) -def checkTime(line): +def checkTime(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip() return re.search(r"\d+m \d+s", line) or re.search(r"\d+ min, \d+ sec", line) \ No newline at end of file diff --git a/server/MyFlaskApp.py b/server/MyFlaskApp.py index 16f2edfe..3165aba0 100644 --- a/server/MyFlaskApp.py +++ b/server/MyFlaskApp.py @@ -1,7 +1,7 @@ import logging import os +import subprocess import sys - from flask import request, Response, send_from_directory, Flask from flask_cors import CORS from flask_migrate import Migrate @@ -53,6 +53,9 @@ def __init__(self): defineRoutes(self) self.fabricator_list = FabricatorList(self) + # TODO: figure out how to run the emu from here + # emu_path = os.path.abspath(os.path.join(".", root_path, "printeremu", "cmd", "test_printer.go")) + # self.run_go_command(f"go run .\\{emu_path} 1 -conn") @self.cli.command("test") def run_tests(): @@ -133,4 +136,18 @@ def get_emu_ports(self): fake_device = next(iter(self.emulator_connections.values()), None) if fake_device: return [fake_device.fake_port, fake_device.fake_name, fake_device.fake_hwid] - return [None, None, None] \ No newline at end of file + return [None, None, None] + + def run_go_command(self, command): + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + return result.stdout.decode('utf-8') + except subprocess.CalledProcessError as e: + self.handle_errors_and_logging(e) + return None \ No newline at end of file diff --git a/server/app.py b/server/app.py index d7439470..60b7c738 100644 --- a/server/app.py +++ b/server/app.py @@ -1,5 +1,6 @@ import asyncio import threading +import traceback import uuid import os import shutil @@ -20,34 +21,33 @@ async def handle_client(websocket): while True: message = await websocket.recv() assert isinstance(message, str), f"Received non-string message: {message}, Type: ({type(message)})" - #print(f"Received message from {client_id}: {message}") - event_emitter.emit("message_received", client_id, message) - + fake_port = None + fake_name = None + fake_hwid = None if not hasattr(emulator_connections[client_id],"fake_port"): fake_port = message.split('port":"')[-1].split('",')[0] - print(f"Fake port: {fake_port}" if fake_port else "No fake port found") if fake_port: emulator_connections[client_id].fake_port = fake_port fake_name = message.split('Name":"')[-1].split('",')[0] - print(f"Fake name: {fake_name}" if fake_name else "No fake name found") if fake_name: emulator_connections[client_id].fake_name = fake_name fake_hwid = message.split('Hwid":"')[-1].split('",')[0] - print(f"Fake Hwid: {fake_hwid}" if fake_hwid else "No fake Hwid found") if fake_hwid: emulator_connections[client_id].fake_hwid = fake_hwid - else: - print(f"Fake port: {emulator_connections[client_id].fake_port}") - print(f"Fake name: {emulator_connections[client_id].fake_name}") - print(f"Fake Hwid: {emulator_connections[client_id].fake_hwid}") - #await websocket.send(f"Echo from {client_id}: {message}") - except websockets.exceptions.ConnectionClosed as e: + if fake_hwid is not None and fake_name is not None and fake_port is not None: + break + while True: + message = await websocket.recv() + print(f"Received message: {message}") + assert isinstance(message, str), f"Received non-string message: {message}, Type: ({type(message)})" + event_emitter.emit("message_received", client_id, message) + except websockets.exceptions.ConnectionClosed: # Handle disconnection gracefully print(f"Emulator '{client_id}' has been disconnected.") - except Exception as e: + except Exception: # Handle any other exception (unexpected disconnection, etc.) - print(f"Error with client {client_id}: {e}") + print(f"Error with client {client_id}: {traceback.format_exc()}") finally: if client_id in emulator_connections: del emulator_connections[client_id] @@ -55,8 +55,8 @@ async def handle_client(websocket): try: server = await websockets.serve(handle_client, "localhost", 8001) await server.wait_closed() - except Exception as e: - print(f"WebSocket server error: {e}") + except Exception: + print(f"WebSocket server error: {traceback.format_exc()}") def start_websocket(): print("Starting WebSocket server...") diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index a6d3c6d1..8267e404 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -433,6 +433,7 @@ def setStatus(): fabricator.setStatus(newStatus) return jsonify({"success": True, "message": "Status updated successfully."}), 200 else: + print(f"Fabricator not found: {printer_id}, fabricator: {fabricator}") return jsonify({"error": "Printer not found."}), 404 except Exception as e: current_app.handle_errors_and_logging(e) @@ -652,6 +653,7 @@ def findPrinterObject(fabricator_id): :rtype: Fabricator | None """ threads = current_app.fabricator_list.getThreadArray() + print(threads) fabricatorThread = list(filter(lambda thread: thread.fabricator.dbID == fabricator_id, threads)) return fabricatorThread[0].fabricator if len(fabricatorThread) > 0 else None diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 01ff7f7e..569120b1 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -40,12 +40,14 @@ def registerFabricator(): name = printer['name'] # Create a new fabricator instance using the Fabricator class + print(f"Registering fabricator: device: {device}, name: {name}, printer: {printer}") try: app.fabricator_list.addFabricator(device, name) except AssertionError as ae: return jsonify({"error": f"Failed to add fabricator: {ae}"}), 500 - new_fabricator = Fabricator.query.filter_by(devicePort=device).first() + print(f"New fabricator: {new_fabricator}") + return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: print(f"Database error during registration: {db_err}") @@ -143,7 +145,7 @@ def moveHead(): if port: if app: fab = app.fabricator_list.getFabricatorByPort(port) - print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {app.fabricator_list.fabricators}, threads: {app.fabricator_list.fabricator_threads}") + # print(fab if fab else f"No fabricator found in fabricator list, fabricator_list: {app.fabricator_list.fabricators}, threads: {app.fabricator_list.fabricator_threads}") if fab: device = fab.device elif port.startswith("EMU"): @@ -153,7 +155,7 @@ def moveHead(): else: device = Fabricator(port).device device.connect() - result = device.home() # Use home() method from Device + result = device.home(isVerbose=False) # Use home() method from Device device.disconnect() return jsonify({"success": True, "message": "Head move successful"}) if result else jsonify({"success": False, "message": "Head move unsuccessful"}) else: From 20f5ffac0b075605f8ff2755ef9ef2980ec6d69f Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 9 Dec 2024 12:31:10 -0500 Subject: [PATCH 185/194] fix/feat: multiline responses for emulator --- printeremu/src/emulator.go | 51 +----- printeremu/src/gcode_commands.go | 242 ++++++++++++++--------------- printeremu/testserver/src/index.ts | 1 + 3 files changed, 126 insertions(+), 168 deletions(-) diff --git a/printeremu/src/emulator.go b/printeremu/src/emulator.go index 2bd12981..d249cc02 100644 --- a/printeremu/src/emulator.go +++ b/printeremu/src/emulator.go @@ -184,64 +184,29 @@ func RunConnection(ctx context.Context, extruder *Extruder, printer *Printer, se } if pidOk && gcodeOk { - response := CommandHandler(gcode, printer) + responses := CommandHandler(gcode, printer) - fmt.Println("Response:", response) + for _, response := range responses { + response := string(response) - parsedResponse := "" - if response != "Unknown command" { - parsedResponse = "ok" - } else { - parsedResponse = "Unknown command" - } - - printerResponse := map[string]interface{}{ - "printerid": printerID, - "response": parsedResponse, - } + fmt.Println("GCode Handler Response:", response) - jsonResponse, err := json.Marshal(printerResponse) - - if err != nil { - log.Println("Error marshaling printer response:", err) - continue - } - - if gcode == "G28" { - okResponse := map[string]interface{}{ + printerResponse := map[string]interface{}{ "printerid": printerID, - "response": "ok", + "response": response, } - okJsonResponse, err := json.Marshal(okResponse) + jsonResponse, err := json.Marshal(printerResponse) if err != nil { log.Println("Error marshaling printer response:", err) continue } - bodyResponse := map[string]interface{}{ - "printerid": printerID, - "response": fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z), - } - - okBodyResponse, err := json.Marshal(bodyResponse) - - if err != nil { - log.Println("Error marshaling printer response:", err) - continue - } - - err = printer.WriteSerial("gcode_response", string(okJsonResponse)) - - err = printer.WriteSerial("gcode_response", string(okBodyResponse)) - - err = printer.WriteSerial("gcode_response", string(okJsonResponse)) - } else if gcode != "M155" { err = printer.WriteSerial("gcode_response", string(jsonResponse)) if err != nil { - log.Println("Error sending gcode_response:", err) + log.Println("Error sending printer response:", err) continue } } diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 0a75f7b9..146409f5 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -1,7 +1,6 @@ package src import ( - "encoding/json" "fmt" "log" "math" @@ -12,52 +11,24 @@ import ( ) type Command interface { - Execute(printer *Printer) string + Execute(printer *Printer) []string } // CommandHandler parses and executes commands -func CommandHandler(command string, printer *Printer) string { +func CommandHandler(command string, printer *Printer) []string { if semicolonIndex := strings.Index(command, ";"); semicolonIndex != -1 { command = command[:semicolonIndex] } if command == "" { - return "" + return []string{""} } command = strings.TrimSpace(command) cmd := NewCommand(command, printer) if cmd == nil { - return "Unknown command\n" - } - - if m155Cmd, ok := cmd.(*M155Command); ok { - go func() { - for result := range m155Cmd.resultChan { - if printer.WSConnection != nil { - printerResponse := map[string]interface{}{ - "printerid": printer.Id, - "response": result, - } - - jsonResponse, err := json.Marshal(printerResponse) - - if err != nil { - log.Println("Error marshaling printer response:", err) - continue - } - - err = printer.WriteSerial("gcode_response", string(jsonResponse)) - - if err != nil { - log.Println("Error sending gcode_response:", err) - continue - } - } - //fmt.Println(result) - } - }() + return []string{"Unknown command"} } return cmd.Execute(printer) @@ -82,15 +53,18 @@ func NewCommand(command string, printer *Printer) Command { type G28Command struct{} -func (cmd *G28Command) Execute(printer *Printer) string { +func (cmd *G28Command) Execute(printer *Printer) []string { homePos := printer.HomePos if err := printer.MoveExtruder(homePos); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - // TODO: send as seperate messages for socket - return fmt.Sprintf("ok\nX:%.2f Y:%.2f Z:%.2f\nok\n", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z) + return []string{ + "ok", + fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f", printer.Extruder.Position.X, printer.Extruder.Position.Y, printer.Extruder.Position.Z), + "ok", + } } type G0G1Command struct { @@ -104,13 +78,13 @@ func NewG0G1Command(command string, printer *Printer) *G0G1Command { return &G0G1Command{target: target, feedRate: feedRate} } -func (cmd *G0G1Command) Execute(printer *Printer) string { +func (cmd *G0G1Command) Execute(printer *Printer) []string { // TODO: Implement feed rate if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - return fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return []string{fmt.Sprintf("Moved to X:%.2f Y:%.2f Z:%.2f", cmd.target.X, cmd.target.Y, cmd.target.Z)} } type G2G3Command struct { @@ -125,9 +99,9 @@ func NewG2G3Command(command string, clockwise bool, printer *Printer) *G2G3Comma return &G2G3Command{target: target, radius: radius, clockwise: clockwise} } -func (cmd *G2G3Command) Execute(printer *Printer) string { +func (cmd *G2G3Command) Execute(printer *Printer) []string { if printer.Paused { - return "Printer is paused\n" + return []string{"Printer is paused"} } currentPos := printer.Extruder.Position @@ -143,10 +117,10 @@ func (cmd *G2G3Command) Execute(printer *Printer) string { } if err := printer.MoveExtruder(cmd.target); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - return fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f\n", cmd.target.X, cmd.target.Y, cmd.target.Z) + return []string{fmt.Sprintf("Arc move to X:%.2f Y:%.2f Z:%.2f", cmd.target.X, cmd.target.Y, cmd.target.Z)} } type G4Command struct { @@ -158,24 +132,24 @@ func NewG4Command(command string) *G4Command { return &G4Command{duration: duration} } -func (cmd *G4Command) Execute(printer *Printer) string { - return fmt.Sprintf("Dwelling for %d ms\n", cmd.duration) +func (cmd *G4Command) Execute(printer *Printer) []string { + return []string{fmt.Sprintf("Dwelling for %d ms", cmd.duration)} } type G90Command struct{} -func (cmd *G90Command) Execute(printer *Printer) string { +func (cmd *G90Command) Execute(printer *Printer) []string { printer.Extruder.AbsolutePositioning = true - return "Set to Absolute Positioning\n" + return []string{"Set to Absolute Positioning"} } type G91Command struct{} -func (cmd *G91Command) Execute(printer *Printer) string { +func (cmd *G91Command) Execute(printer *Printer) []string { printer.Extruder.AbsolutePositioning = false - return "Set to Relative Positioning\n" + return []string{"Set to Relative Positioning"} } type G92Command struct { @@ -188,28 +162,30 @@ func NewG92Command(command string) *G92Command { return &G92Command{position: position} } -func (cmd *G92Command) Execute(printer *Printer) string { +func (cmd *G92Command) Execute(printer *Printer) []string { if err := printer.MoveExtruder(cmd.position); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - return fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f\n", cmd.position.X, cmd.position.Y, cmd.position.Z) + return []string{fmt.Sprintf("Position set to X:%.2f Y:%.2f Z:%.2f", cmd.position.X, cmd.position.Y, cmd.position.Z)} } // ==================== Unit Conversion Commands ==================== type G20Command struct{} -func (cmd *G20Command) Execute(printer *Printer) string { +func (cmd *G20Command) Execute(printer *Printer) []string { printer.Units = "inches" - return "Units set to inches\n" + + return []string{"Units set to inches"} } type G21Command struct{} -func (cmd *G21Command) Execute(printer *Printer) string { +func (cmd *G21Command) Execute(printer *Printer) []string { printer.Units = "mm" - return "Units set to millimeters\n" + + return []string{"Units set to millimeters"} } // ==================== Temperature and Fan Control Commands ==================== @@ -224,12 +200,12 @@ func NewM104Command(command string) *M104Command { return &M104Command{temperature: temperature} } -func (cmd *M104Command) Execute(printer *Printer) string { +func (cmd *M104Command) Execute(printer *Printer) []string { if err := printer.SetExtruderTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - return "ok\n" + return []string{"ok"} } // M106Command sets the fan speed @@ -239,25 +215,28 @@ type M106Command struct { func NewM106Command(command string) *M106Command { fanSpeed := parseFanSpeed(command) + return &M106Command{fanSpeed: fanSpeed} } -func (cmd *M106Command) Execute(printer *Printer) string { +func (cmd *M106Command) Execute(printer *Printer) []string { if cmd.fanSpeed > 255 { - return "Error: invalid fan speed. Valid range: 0 to 255\n" + return []string{"Error: invalid fan speed. Valid range: 0 to 255"} } + if err := printer.SetFanSpeed(cmd.fanSpeed); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } - return fmt.Sprintf("Fan speed set to %.2f\n", cmd.fanSpeed) + + return []string{fmt.Sprintf("Fan speed set to %.2f", cmd.fanSpeed)} } type M107Command struct{} -func (cmd *M107Command) Execute(printer *Printer) string { +func (cmd *M107Command) Execute(printer *Printer) []string { printer.SetFanSpeed(0) - return "Fan turned off\n" + return []string{"Fan turned off"} } type M140Command struct { @@ -270,13 +249,13 @@ func NewM140Command(command string) *M140Command { return &M140Command{temperature: temperature} } -func (cmd *M140Command) Execute(printer *Printer) string { +func (cmd *M140Command) Execute(printer *Printer) []string { if err := printer.SetBedTemperature(cmd.temperature); err != nil { - return fmt.Sprintf("Error: %s\n", err.Error()) + return []string{fmt.Sprintf("Error: %s", err.Error())} } printer.Heatbed.Heating = true - return "ok\n" + return []string{"ok"} } // M190Command waits until the bed reaches the target temperature before allowing further commands @@ -290,15 +269,15 @@ func NewM190Command(command string) *M190Command { return &M190Command{temperature: temperature} } -func (cmd *M190Command) Execute(printer *Printer) string { +func (cmd *M190Command) Execute(printer *Printer) []string { printer.Heatbed.TargetTemp = cmd.temperature if printer.Heatbed.Temp < printer.Heatbed.TargetTemp { - return fmt.Sprintf("B:%.2f / %.2f\n", printer.Heatbed.Temp, printer.Heatbed.TargetTemp) + return []string{fmt.Sprintf("B:%.2f / %.2f", printer.Heatbed.Temp, printer.Heatbed.TargetTemp)} } //TODO: Error handling printer.Heatbed.Heating = false - return "ok\n" + return []string{"ok"} } type M109Command struct { @@ -311,14 +290,14 @@ func NewM109Command(command string) *M109Command { return &M109Command{temperature: temperature} } -func (cmd *M109Command) Execute(printer *Printer) string { +func (cmd *M109Command) Execute(printer *Printer) []string { printer.Extruder.TargetTemp = cmd.temperature if printer.Extruder.ExtruderTemp < printer.Extruder.TargetTemp { - return fmt.Sprintf("T:%.2f / %.2f\n", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp) + return []string{fmt.Sprintf("T:%.2f / %.2f", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp)} } - return "ok\n" + return []string{"ok"} } type M113Command struct{} @@ -327,9 +306,10 @@ func NewM113Command(command string) *M113Command { return &M113Command{} } -func (cmd *M113Command) Execute(printer *Printer) string { +func (cmd *M113Command) Execute(printer *Printer) []string { printer.KeepAliveTime = time.Now() - return "Keepalive signal sent\n" + + return []string{"Keepalive signal sent"} } type M204Command struct { @@ -338,12 +318,14 @@ type M204Command struct { func NewM204Command(command string) *M204Command { acceleration := parseAcceleration(command) + return &M204Command{acceleration: acceleration} } -func (cmd *M204Command) Execute(printer *Printer) string { +func (cmd *M204Command) Execute(printer *Printer) []string { printer.Acceleration = cmd.acceleration - return fmt.Sprintf("Acceleration set to %.2f\n", cmd.acceleration) + + return []string{fmt.Sprintf("Acceleration set to %.2f", cmd.acceleration)} } type M73Command struct { @@ -354,77 +336,74 @@ type M73Command struct { func NewM73Command(command string) *M73Command { progress := parseProgress(command) remaining := parseRemainingTime(command) + return &M73Command{progress: progress, remaining: remaining} } -func (cmd *M73Command) Execute(printer *Printer) string { +func (cmd *M73Command) Execute(printer *Printer) []string { printer.UpdateProgress(cmd.progress) - return fmt.Sprintf("Progress set to %d%%, %d minutes remaining\nok\n", cmd.progress, cmd.remaining) -} -// Parsing helpers -func parseRemainingTime(command string) int { - reR := regexp.MustCompile(`R([0-9]+)`) - remaining := 0 - if rMatch := reR.FindStringSubmatch(command); rMatch != nil { - remaining, _ = strconv.Atoi(rMatch[1]) - } - return remaining + return []string{fmt.Sprintf("Progress set to %d%%, %d minutes remaining", cmd.progress, cmd.remaining), "ok"} } // ==================== Control and Status Commands ==================== type CancelCommand struct{} -func (cmd *CancelCommand) Execute(printer *Printer) string { +func (cmd *CancelCommand) Execute(printer *Printer) []string { printer.Pause() - return "Emergency stop activated, printer paused\n" + return []string{"Emergency stop activated, printer paused"} } type M114Command struct{} -func (cmd *M114Command) Execute(printer *Printer) string { +func (cmd *M114Command) Execute(printer *Printer) []string { position := printer.Extruder.Position - return fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f\nok\n", position.X, position.Y, position.Z) + + return []string{fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f", position.X, position.Y, position.Z), "ok"} } type M115Command struct{} -func (cmd *M115Command) Execute(printer *Printer) string { - return "Firmware: TotallyRealMarlin 2.1.2.5\n" +func (cmd *M115Command) Execute(printer *Printer) []string { + return []string{"Firmware: QViewGCode 2.1.2.5"} } type M17Command struct{} -func (cmd *M17Command) Execute(printer *Printer) string { +func (cmd *M17Command) Execute(printer *Printer) []string { printer.EnableMotor("x") printer.EnableMotor("y") printer.EnableMotor("z") - return "Motors enabled\n" + + return []string{"Motors enabled"} } type M18Command struct{} -func (cmd *M18Command) Execute(printer *Printer) string { +func (cmd *M18Command) Execute(printer *Printer) []string { printer.DisableMotor("x") printer.DisableMotor("y") printer.DisableMotor("z") - return "Motors disabled\n" + + return []string{"Motors disabled"} } type M82Command struct{} -func (cmd *M82Command) Execute(printer *Printer) string { +func (cmd *M82Command) Execute(printer *Printer) []string { printer.Extruder.AbsolutePositioning = true - return "Extruder set to absolute positioning\n" + + return []string{"Extruder set to absolute positioning"} } type M83Command struct{} -func (cmd *M83Command) Execute(printer *Printer) string { +func (cmd *M83Command) Execute(printer *Printer) []string { printer.Extruder.AbsolutePositioning = false - return "Extruder set to relative positioning\n" + + return []string{"Extruder set to relative positioning"} } type M302Command struct { @@ -433,42 +412,44 @@ type M302Command struct { func NewM302Command(command string) *M302Command { allowColdExtrusion := parseAllowColdExtrusion(command) + return &M302Command{allowColdExtrusion: allowColdExtrusion} } -func (cmd *M302Command) Execute(printer *Printer) string { +func (cmd *M302Command) Execute(printer *Printer) []string { if cmd.allowColdExtrusion { - return "Cold extrusion allowed\n" + return []string{"Cold extrusion allowed"} } - return "Cold extrusion not allowed\n" + + return []string{"Cold extrusion not allowed"} } type M503Command struct{} -func (cmd *M503Command) Execute(printer *Printer) string { - return printer.String() +func (cmd *M503Command) Execute(printer *Printer) []string { + return []string{printer.String()} } type M997Command struct{} -func (cmd *M997Command) Execute(printer *Printer) string { - return fmt.Sprintf("Machine name: %s", printer.Device) +func (cmd *M997Command) Execute(printer *Printer) []string { + return []string{fmt.Sprintf("Machine name: %s", printer.Device)} } type M601Command struct{} -func (cmd *M601Command) Execute(printer *Printer) string { +func (cmd *M601Command) Execute(printer *Printer) []string { printer.Pause() - return "Printer paused\n" + return []string{"Printer paused"} } type M602Command struct{} -func (cmd *M602Command) Execute(printer *Printer) string { +func (cmd *M602Command) Execute(printer *Printer) []string { printer.Resume() - return "Printer not paused\n" + return []string{"Printer not paused"} } type M900Command struct { @@ -481,10 +462,10 @@ func NewM900Command(command string) *M900Command { return &M900Command{kFactor: kFactor} } -func (cmd *M900Command) Execute(printer *Printer) string { +func (cmd *M900Command) Execute(printer *Printer) []string { printer.LinearAdvanceFactor = cmd.kFactor - return fmt.Sprintf("Linear Advance factor set to %.2f\n", cmd.kFactor) + return []string{fmt.Sprintf("Linear Advance factor set to %.2f", cmd.kFactor)} } type M142Command struct { @@ -497,10 +478,10 @@ func NewM142Command(command string) *M142Command { return &M142Command{temperature: temperature} } -func (cmd *M142Command) Execute(printer *Printer) string { +func (cmd *M142Command) Execute(printer *Printer) []string { printer.HeatbreakTemp = cmd.temperature - return fmt.Sprintf("Heatbreak target temperature set to %.2f\n", cmd.temperature) + return []string{fmt.Sprintf("Heatbreak target temperature set to %.2f", cmd.temperature)} } type M84Command struct { @@ -513,12 +494,12 @@ func NewM84Command(command string) *M84Command { return &M84Command{axes: axes} } -func (cmd *M84Command) Execute(printer *Printer) string { +func (cmd *M84Command) Execute(printer *Printer) []string { for _, axis := range cmd.axes { printer.DisableMotor(axis) } - return fmt.Sprintf("Motors %v disabled\n", cmd.axes) + return []string{fmt.Sprintf("Motors %v disabled", cmd.axes)} } type M155Command struct { @@ -541,17 +522,18 @@ func NewM155Command(command string) *M155Command { } } -func (cmd *M155Command) Execute(printer *Printer) string { +func (cmd *M155Command) Execute(printer *Printer) []string { if cmd.interval == 0 { printer.CommandStatus.m155Status.Stop() - return "ok" + return []string{"ok"} } if !printer.CommandStatus.m155Status.IsRunning { - return printer.CommandStatus.m155Status.Start(cmd.interval, cmd.resultChan, printer) + status := printer.CommandStatus.m155Status.Start(cmd.interval, cmd.resultChan, printer) + return []string{"ok", status, "ok"} } - return "ok" + return []string{"ok"} } // ==================== Parsing Helpers ==================== @@ -693,6 +675,16 @@ func parseM155Interval(command string) int { return interval } +func parseRemainingTime(command string) int { + reR := regexp.MustCompile(`R([0-9]+)`) + remaining := 0 + + if rMatch := reR.FindStringSubmatch(command); rMatch != nil { + remaining, _ = strconv.Atoi(rMatch[1]) + } + return remaining +} + // ==================== Command Registry ==================== var commandRegistry = map[string]func(string, *Printer) Command{ diff --git a/printeremu/testserver/src/index.ts b/printeremu/testserver/src/index.ts index 9b559012..f640b836 100644 --- a/printeremu/testserver/src/index.ts +++ b/printeremu/testserver/src/index.ts @@ -40,6 +40,7 @@ const eventHandlers = { function sendGCodeCommands(ws) { const gcodeCommands = [ "G28", // Home all axes + "M114", // Report current position "G1 X10 Y10 Z10 F1500", // Move to X:10 Y:10 Z:10 "G1 X20 Y20 Z20 F1500", // Move to X:20 Y:20 Z:20 "G1 Z0", // Move to Z:0 From fc8e6dfb1e903b518dcdcca2aef4c4718a582f1f Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 9 Dec 2024 13:25:05 -0500 Subject: [PATCH 186/194] fix: m155 remove old ok responses --- printeremu/src/printer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printeremu/src/printer.go b/printeremu/src/printer.go index a2a2bfde..934f4547 100644 --- a/printeremu/src/printer.go +++ b/printeremu/src/printer.go @@ -277,7 +277,7 @@ func (m *M155Status) Start(interval time.Duration, resultChan chan string, print } if !m.IsRunning && printer.WSConnection != nil { - result := fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + result := fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp, printer.Heatbed.Temp, @@ -340,7 +340,7 @@ func (m *M155Status) Start(interval time.Duration, resultChan chan string, print } }() - return fmt.Sprintf("ok T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", + return fmt.Sprintf("T:%.1f /%.1f B:%.1f /%.1f T0:%.1f /%.1f @:0 B@:0 P:%.1f A:26.4", printer.Extruder.ExtruderTemp, printer.Extruder.TargetTemp, printer.Heatbed.Temp, From bad2af79af006aae90e35408f9135e61c48ea173 Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 9 Dec 2024 15:27:37 -0500 Subject: [PATCH 187/194] fix: temp make port always EMU0 --- printeremu/src/printerregistry.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/printeremu/src/printerregistry.go b/printeremu/src/printerregistry.go index 52bcfb72..b4390ea2 100644 --- a/printeremu/src/printerregistry.go +++ b/printeremu/src/printerregistry.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "os" - "strconv" "time" "golang.org/x/exp/rand" @@ -51,7 +50,10 @@ func LoadPrinters(filePath string) ([]Printer, error) { var printers []Printer for _, printerConfig := range printersConfig { - _, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", "EMU-"+RandomString(8), printerConfig.Name, "Init") + emuPort := "EMU-0" + + //_, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", "EMU-"+RandomString(8), printerConfig.Name, "Init") + _, printer, err := Init(printerConfig.Id, printerConfig.Brand.PrinterName+" "+printerConfig.Brand.PrinterModel, "Marlin GCode", emuPort, printerConfig.Name, "Init") // always add the model as an attribute so we always know what the model is printer.AddAttribute("model", printerConfig.Brand.PrinterName) @@ -90,7 +92,8 @@ func LoadPrinters(filePath string) ([]Printer, error) { printer.SetHwid(hwid) - printer.AddData("port", "EMU"+strconv.Itoa(RandomRange(0, 9))) + // printer.AddData("port", "EMU"+strconv.Itoa(RandomRange(0, 9))) + printer.AddData("port", emuPort) // handle stuff like the bed size and such PostRegistry(printer) From dc94e19852f14f58173c1d902c869137c965a51f Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 11 Dec 2024 14:40:52 -0500 Subject: [PATCH 188/194] feat: documentation! get your docstrings here! All backend python files have docstrings for all methods where deemed important --- server/Classes/EventEmitter.py | 19 ++- server/Classes/FabricatorList.py | 128 +++++++++++++---- server/Classes/Fabricators/Device.py | 50 ++++--- server/Classes/Fabricators/Fabricator.py | 134 ++++++++++-------- .../Classes/Fabricators/Printers/Printer.py | 23 +-- server/Classes/Logger.py | 85 +++++++++-- server/Classes/Ports.py | 30 +++- server/Classes/Queue.py | 109 +++++++++----- server/Classes/Vector3.py | 6 + server/Mixins/hasEndingSequence.py | 1 + 10 files changed, 405 insertions(+), 180 deletions(-) diff --git a/server/Classes/EventEmitter.py b/server/Classes/EventEmitter.py index 7a46d511..e468ca9b 100644 --- a/server/Classes/EventEmitter.py +++ b/server/Classes/EventEmitter.py @@ -1,26 +1,35 @@ import asyncio +from typing import Callable class EventEmitter: def __init__(self): self._listeners = None self._events = {} - def on(self, event_name, callback): - """Register a handler for an event.""" + def on(self, event_name: str, callback: Callable): + """ + Register a handler for an event. + :param str event_name: The name of the event to listen for + :param Callable callback: The function to call when the event is emitted + """ if event_name not in self._events: self._events[event_name] = [] self._events[event_name].append(callback) - def emit(self, event_name, *args, **kwargs): - """Emit an event, calling all registered callbacks with the event data.""" + def emit(self, event_name: str, *args, **kwargs): + """ + Emit an event, calling all registered callbacks with the event data. + :param str event_name: The name of the event to emit + """ if event_name in self._events: for callback in self._events[event_name]: # Trigger the callback with the provided data asyncio.create_task(callback(*args, **kwargs)) - def remove_event(self, event_name): + def remove_event(self, event_name: str): """ Remove a specific listener for an event + :param str event_name: The name of the event to remove the listener from """ if event_name in self._events: self._events.pop(event_name) diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index f4feb3da..10fe531f 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -36,8 +36,7 @@ def __getitem__(self, key): def __to_JSON__(self): """ - Convert the FabricatorList to a JSON object - :return: JSON object + Convert the FabricatorList to a JSON object that can be sent to the frontend :rtype: dict """ fab_list = [] @@ -59,7 +58,11 @@ def teardown(self): self.fabricator_threads = [] def addFabricator(self, serialPortName: str, name: str = ""): - """add a fabricator to the list, and to the database, then start a thread for it""" + """ + add a fabricator to the list, and to the database, then start a thread for it + :param str serialPortName: the name of the serial port to add + :param str name: the name of the fabricator to add + """ serialPort: ListPortInfo | SysFS | None = Ports.getPortByName(serialPortName) dbFab: Fabricator | None = next((fabricator for fabricator in Fabricator.queryAll() if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) listFab: Fabricator | None = next((fabricator for fabricator in self if fabricator.getHwid() == serialPort.hwid.split(' LOCATION=')[0]), None) @@ -85,9 +88,14 @@ def addFabricator(self, serialPortName: str, name: str = ""): print("starting new fabricator thread") self.start_fabricator_thread(newFab) - def deleteFabricator(self, fabricatorid): - """delete a fabricator from the list, and from the database""" - fabricator = self.getFabricatorById(fabricatorid) + def deleteFabricator(self, fabricator_id): + """ + delete a fabricator from the list, and from the database + :param int fabricator_id: the id of the fabricator to delete + :return: True if the fabricator was deleted, False otherwise + :rtype: bool + """ + fabricator = self.getFabricatorById(fabricator_id) if fabricator: try: self.fabricators.remove(fabricator) @@ -102,24 +110,39 @@ def deleteFabricator(self, fabricatorid): return True def getFabricatorByName(self, name) -> Fabricator | None: - """find the first fabricator with the given name""" + """ + find the first fabricator with the given name + :param str name: the name to search for + :return: the first fabricator with the given name, or None if no fabricator has that name + :rtype: Fabricator | None + """ return next((fabricator for fabricator in self if fabricator.getName() == name), None) def getFabricatorByHwid(self, hwid) -> Fabricator | None: - """find the first fabricator with the given hwid""" + """ + find the first fabricator with the given hwid + :param str hwid: the hwid to search for + :return: the first fabricator with the given hwid, or None if no fabricator has that hwid + :rtype: Fabricator | None + """ return next((fabricator for fabricator in self if fabricator.getHwid() == hwid), None) - def getFabricatorById(self, id) -> Fabricator | None: - """find the first fabricator with the given id""" - return next((fabricator for fabricator in self if fabricator.dbID == id), None) - + def getFabricatorById(self, dbID) -> Fabricator | None: + """ + find the first fabricator with the given id + :param int dbID: the id to search for + :return: the first fabricator with the given id, or None if no fabricator has that id + :rtype: Fabricator | None + """ + return next((fabricator for fabricator in self if fabricator.dbID == dbID), None) def getFabricatorByPort(self, port) -> Fabricator | None: """ find the first fabricator with the given port - :param port: the port to search for - :type port: str | ListPortInfo | SysFS + :param str | ListPortInfo | SysFS port: the port to search for + :return: the first fabricator with the given port, or None if no fabricator has that port + :rtype: Fabricator | None """ if isinstance(port, ListPortInfo or SysFS): port = port.device assert isinstance(port, str), f"port={port}, type(port)={type(port)}" @@ -158,13 +181,19 @@ def getFabricatorByPort(self, port) -> Fabricator | None: def start_fabricator_thread(self, fabricator: Fabricator): - thread = FabricatorThread(fabricator, app=self.app, target=self.update_thread, args=(fabricator,)) - thread.daemon = True + """ + Start a thread for the given fabricator + :param Fabricator fabricator: the given fabricator + :return: + :rtype: FabricatorThread + """ + thread = FabricatorThread(fabricator, passed_app=self.app, **{"daemon": True}) thread.start() return thread def create_fabricator_threads(self): + """Create a thread for each fabricator in the list and start it""" for fabricator in self: fabricator.queue = Queue() # Ensure each fabricator has its own queue fabricator_thread = self.start_fabricator_thread(fabricator) @@ -172,15 +201,23 @@ def create_fabricator_threads(self): self.ping_thread = Thread(target=self.pingForStatus) def get_fabricator_thread(self, fabricator): + """ + Get the thread for the given fabricator + :param fabricator: the given fabricator + :return: that fabricator's thread + """ assert fabricator in self, f"fabricator {fabricator} not in self" thread = next(thread for thread in self.fabricator_threads if thread.fabricator == fabricator) assert thread.is_alive(), f"thread {thread} is not alive" assert thread.daemon, f"thread {thread} is not daemon" return thread - - - def queue_restore(self, status, queue): + def queue_restore(self, status: str, queue: Queue): + """ + Restore the queue for the given fabricator + :param str status: the status of the fabricator + :param Queue queue: the queue to restore + """ for fabricator in self.fabricators: for job in queue: if job.status != 'inqueue': @@ -191,7 +228,12 @@ def queue_restore(self, status, queue): fabricator_thread = self.start_fabricator_thread(fabricator) self.fabricator_threads.append(fabricator_thread) - def update_thread(self, fabricator): + def update_thread(self, fabricator: Fabricator): + """ + Update the thread for the given fabricator + :param Fabricator fabricator: + :return: + """ # thread = next(thread for thread in self.fabricator_threads if thread.fabricator.id == fabricator.id) while True: time.sleep(2) @@ -203,7 +245,13 @@ def update_thread(self, fabricator): if status != "offline": fabricator.printNextInQueue() - def resetThread(self, fabricator_id): + def resetThread(self, fabricator_id: int) -> tuple[Response, int]: + """ + Reset the thread for the given fabricator + :param int fabricator_id: the dbID of the fabricator to reset + :return: a json response for the client + :rtype: tuple[Response, int] + """ try: for thread in self.fabricator_threads: if thread.fabricator.dbID == fabricator_id: @@ -224,7 +272,14 @@ def resetThread(self, fabricator_id): app.handle_errors_and_logging(e) return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - def queueRestore(self, fabricator_id, status): + def queueRestore(self, fabricator_id: int, status: str) -> tuple[Response, int]: + """ + Restore the queue for the given fabricator + :param int fabricator_id: + :param str status: + :return: a json response for the client + :rtype: tuple[Response, int] + """ try: for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: @@ -246,7 +301,13 @@ def queueRestore(self, fabricator_id, status): return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 - def deleteThread(self, fabricator_id): + def deleteThread(self, fabricator_id: int) -> tuple[Response, int]: + """ + Delete the thread for the given fabricator + :param int fabricator_id: the dbID of the fabricator to delete + :return: a json response for the client + :rtype: tuple[Response, int] + """ try: for thread in self.fabricator_threads: if thread.fabricator.dbID == fabricator_id: @@ -269,7 +330,13 @@ def pingForStatus(self): pass - def moveFabricatorList(self, fabricator_ids): + def moveFabricatorList(self, fabricator_ids: list[int]) -> tuple[Response, int]: + """ + Move the fabricator list to the given order + :param list[int] fabricator_ids: + :return: a json response for the client + :rtype: tuple[Response, int] + """ new_thread_list = [] for id in fabricator_ids: for thread in self.fabricator_threads: @@ -279,7 +346,14 @@ def moveFabricatorList(self, fabricator_ids): self.fabricator_threads = new_thread_list return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) - def editName(self, fabricator_id, name): + def editName(self, fabricator_id: int, name: str) -> tuple[Response, int]: + """ + Edit the name of a registered fabricator. + :param int fabricator_id: the dbID of the fabricator to edit + :param str name: the new name for the fabricator + :return: a json response for the client + :rtype: tuple[Response, int] + """ fabricator = self.getFabricatorById(fabricator_id) if fabricator: fabricator.setName(name) @@ -301,6 +375,10 @@ def __repr__(self): return f"FabricatorThread(fabricator={self.fabricator}, daemon={self.daemon}, running={self.is_alive()})" def __to_JSON__(self): + """ + Convert the FabricatorThread to a JSON object that can be sent to the frontend + :rtype: dict + """ return { "fabricator": self.fabricator, "app": self.app, diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 5f63b00c..9958594b 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -127,13 +127,11 @@ def goTo(self, loc: Vector3, isVerbose: bool = False): return loc == self.getToolHeadLocation() return True - def parseGcode(self, job: Job, isVerbose: bool = False): + def parseGcode(self, job: Job, isVerbose: bool = False) -> bool: """ Parse a G-code file and send the commands to the device. - :param job: The Job object, with file name to parse. - :type job: Job - :param isVerbose: Whether to log the commands - :type isVerbose: bool + :param Job job: The Job object, with file name to parse. + :param bool isVerbose: Whether to log the commands :return: True if the job is complete, else False :rtype: bool :raises AssertionError: if the file is not a string or if isVerbose is not a bool @@ -187,20 +185,25 @@ def parseGcode(self, job: Job, isVerbose: bool = False): return True - def pause(self): + def pause(self) -> bool: + """ + Pause the device, if the pause command is implemented. + :rtype: bool + """ pass - def resume(self): + def resume(self) -> bool: + """ + Resume the device, if the resume command is implemented. + :rtype: bool + """ pass - def sendGcode(self, gcode: bytes, isVerbose: bool = False): + def sendGcode(self, gcode: bytes, isVerbose: bool = False) -> bool: """ Send a G-code command to the device. - :param gcode: The line to send to the hardware - :type gcode: bytes - :param isVerbose: Whether to log the command - :type isVerbose: bool - :return: True if the command was sent successfully, else False + :param bytes gcode: The line to send to the hardware + :param bool isVerbose: Whether to log the command :rtype: bool :raises AssertionError: if the serial connection is not open, if gcode is not bytes, or if isVerbose is not a bool """ @@ -215,11 +218,10 @@ def sendGcode(self, gcode: bytes, isVerbose: bool = False): else: print(gcode.decode("utf-8")) return True - def getToolHeadLocation(self, isVerbose = False): + def getToolHeadLocation(self, isVerbose: bool = False) -> Vector3: """ Get the current location of the tool head. - :param isVerbose: Whether to log the command - :type isVerbose: bool + :param bool isVerbose: Whether to log the command :return: the current location of the tool head :rtype: Vector3 """ @@ -232,8 +234,11 @@ def getToolHeadLocation(self, isVerbose = False): loc = LocationResponse(response) return Vector3(loc.x, loc.y, loc.z) - def repair(self): - """Attempt to repair the device connection by closing and reopening the serial connection.""" + def repair(self) -> str: + """ + Attempt to repair the device connection by closing and reopening the serial connection. + :rtype: str + """ try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip specific repair commands @@ -258,8 +263,11 @@ def repair(self): if self.logger is not None: self.logger.error(f"Error during repair: {e}") return f"Repair failed with error: {e}" - def diagnose(self): - """Diagnose the device by sending basic G-code commands and checking responses.""" + def diagnose(self) -> str: + """ + Diagnose the device by sending basic G-code commands and checking responses. + :rtype: str + """ try: if self.MODEL and "Ender" in self.MODEL: # If the device is an Ender, skip the diagnosis @@ -322,7 +330,7 @@ def __init__(self, response: str): self.count_y = float(loc[5]) if '.' in loc[5] else int(loc[5]) self.count_z = float(loc[6]) if '.' in loc[6] else int(loc[6]) - def __to_JSON__(self): + def __to_JSON__(self) -> dict[str, float | int]: return { "x": self.x, "y": self.y, diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index 9e0fb12e..d3b719de 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -2,7 +2,7 @@ from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy.exc import SQLAlchemyError -from Classes.FabricatorConnection import FabricatorConnection, EmuListPortInfo +from Classes.FabricatorConnection import FabricatorConnection from Classes.Fabricators.Device import Device from typing_extensions import TextIO from Classes.Jobs import Job @@ -23,14 +23,19 @@ class Fabricator(db.Model): ) devicePort = db.Column(db.String(50), nullable=False) - def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLogger=None, fileLogger=None): + def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLogger: TextIO | None = None, fileLogger: str | None = None): + """ + Initialize a new Fabricator instance. + :param ListPortInfo | SysFS | None port: the serial port to connect to + :param str name: the name to show the frontend + :param TextIO | None consoleLogger: the console to log to + :param str | None fileLogger: the file path to log to + """ if port is None: return assert isinstance(port, ListPortInfo) or isinstance(port, SysFS), f"Invalid port type: {type(port)}" assert isinstance(name, str), f"Invalid name type: {type(name)}" from Classes.Queue import Queue - from Classes.Jobs import Job - self.dbID = None # Initialize dbID self.job: Job | None = None self.queue: Queue = Queue() @@ -58,9 +63,9 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog def __repr__(self): return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" - def __to_JSON__(self): + def __to_JSON__(self) -> dict: """ - Converts the Fabricator object to a JSON object + Converts the Fabricator object to a JSON object that can be sent to the front end :return: JSON object :rtype: dict """ @@ -79,7 +84,11 @@ def __to_JSON__(self): @staticmethod def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: - """returns the model of the printer based on the response to M997""" + """ + returns the model of the printer based on the response to M997, NOTE: this is meant for use with Ender printers only for now. + :param ListPortInfo | SysFS | None serialPort: the serial port to connect to + :rtype: str + """ testName = FabricatorConnection.staticCreateConnection(port=serialPort.device, baudrate=115200, timeout=60) testName.write(b"M997\n") while True: @@ -92,16 +101,13 @@ def getModelFromGcodeCommand(serialPort: ListPortInfo | SysFS | None) -> str: return response @staticmethod - def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, websocket_connection=None): + def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger: TextIO | None = None, fileLogger: str | None = None, websocket_connection=None) -> Device | None: """ creates the correct printer object based on the serial port info - :param websocket_connection: the websocket connection to the emulator, if it exists - :param serialPort: - :type serialPort: ListPortInfo | SysFS | None - :param consoleLogger: - :type consoleLogger: TextIO | None - :param fileLogger: - :type fileLogger: str | None + :param Websocket | None websocket_connection: the websocket connection to the emulator, if it exists + :param ListPortInfo | SysFS | None serialPort: the serial port info + :param TextIO | None consoleLogger: the console stream to output to + :param str | None fileLogger: the file path to log to :return: device without a fabricator object :rtype: Device | None """ @@ -139,20 +145,15 @@ def staticCreateDevice(serialPort: ListPortInfo | SysFS | None, consoleLogger=No #TODO: assume generic printer, do stuff return None - def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=None, fileLogger=None, addLogger = False, websocket_connection=None): + def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger: TextIO | None = None, fileLogger: str | None = None, addLogger: bool = False, websocket_connection=None) -> Device | None: """ creates the correct printer object based on the serial port info - :param websocket_connection: the websocket connection to the emulator, if it exists - :type websocket_connection: WebSocket | None - :param serialPort: the serial port info - :type serialPort: ListPortInfo | SysFS | None - :param consoleLogger: the console stream to output to - :type consoleLogger: TextIO | None - :param fileLogger: the file path to output file logs to - :type fileLogger: str | None - :param addLogger: whether to add a logger to the device - :type addLogger: bool - :return: the printer object + :param WebSocket | None websocket_connection: the websocket connection to the emulator, if it exists + :param ListPortInfo | SysFS | None serialPort: the serial port info + :param TextIO | None consoleLogger: the console stream to output to + :param str | None fileLogger: the file path to output file logs to + :param bool addLogger: whether to add a logger to the device + :return: the fabricator object :rtype: Device | None """ if serialPort is None: @@ -193,10 +194,10 @@ def createDevice(self, serialPort: ListPortInfo | SysFS | None, consoleLogger=No return None @classmethod - def queryAll(cls): + def queryAll(cls) -> list["Fabricator"]: """ Returns all fabricators in the database as a list of the Fabricator objects - :return: list of Fabricator objects + :return: list of Fabricator objects in the DB. :rtype: list[Fabricator] """ fabList = [] @@ -209,22 +210,10 @@ def queryAll(cls): fabList.append(cls(EmuListPortInfo(fake_port, "Emulator", fake_hwid), fake_name)) return fabList - @classmethod - def updateDB(cls): - """commits all changes to the db""" - db.session.commit() - - def addToDB(self): - """adds the fabricator to the db""" - db.session.add(self) - db.session.commit() - - def begin(self, isVerbose: bool = False): + def begin(self, isVerbose: bool = False) -> bool: """ starts the fabrication process - :param isVerbose: whether to print verbose output - :type isVerbose: bool - :return: whether the fabrication process was successful + :param bool isVerbose: whether to print verbose output :rtype: bool """ try: @@ -244,12 +233,14 @@ def begin(self, isVerbose: bool = False): if isVerbose and hasattr(self.device,"logger"): self.device.logger.debug(f"Verdict handled, status: {self.status}") return True except Exception as e: - return current_app.handle_errors_and_logging(e, self) - - def pause(self): - """pauses the fabrication process if the fabricator supports it""" + def pause(self) -> bool: + """ + pauses the fabrication process if the fabricator supports it + :rtype: bool + :raises AssertionError: if the device doesn't support pausing, or if the fabricator isn't paused despite being capable of it. + """ assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}, type: {type(self.device)}" if not self.device.pauseCMD: @@ -260,8 +251,12 @@ def pause(self): self.setStatus("paused") return self.status == self.device.status == "paused" - def resume(self): - """resumes the fabrication process if the fabricator supports it""" + def resume(self) -> bool: + """ + resumes the fabrication process if the fabricator supports it + :rtype: bool + :raises AssertionError: if the device doesn't support resuming, or if the fabricator hasn't resumed despite being capable of it. + """ assert isinstance(self.device, Device), f"Device is not a Device object or subclass: {self.device}, type: {type(self.device)}" if not self.device.resumeCMD: @@ -271,8 +266,12 @@ def resume(self): self.setStatus("printing") return self.status == self.device.status == "printing" - def cancel(self): - """cancels the fabrication process""" + def cancel(self) -> bool: + """ + cancels the fabrication process + :rtype: bool + :raises AssertionError: if the fabricator isn't printing, or if the fabricator hasn't cancelled despite being capable of it + """ try: assert self.job is not None, "Job is None" assert self.device is not None, "Device is None" @@ -283,10 +282,19 @@ def cancel(self): except Exception as e: return current_app.handle_errors_and_logging(e, self) - def getStatus(self): + def getStatus(self) -> str: + """ + gets the status of the fabricator + :rtype: str + """ return self.status - def setStatus(self, newStatus): + def setStatus(self, newStatus: str) -> bool: + """ + sets the status of the fabricator + :param str newStatus: new status to set + :rtype: bool + """ try: assert newStatus in ["idle", "printing", "paused", "complete", "error", "cancelled", "misprint", "ready", "offline"], f"Invalid status: {newStatus}" assert self.device is not None, "Device is None" @@ -319,6 +327,7 @@ def resetToIdle(self): self.setStatus("idle") def handleVerdict(self): + """handles the verdict of the device, this is used for handling the completion of a job""" assert self.device.verdict in ["complete", "error", "cancelled", "misprint"], f"Invalid verdict: {self.device.verdict}" assert self.job is not None, "Job is None" if self.device.verdict == "complete": @@ -334,10 +343,19 @@ def handleVerdict(self): elif self.device.verdict== "misprint": self.setStatus("misprint") - def getName(self): + def getName(self) -> str: + """ + gets the name of the fabricator + :rtype: str + """ return self.name def setName(self, name: str) -> Response: + """ + sets the name of the fabricator + :param str name: new name to set + :rtype: Response + """ try: Fabricator.query.filter_by(hwid=self.hwid).first().name = name self.name = name @@ -393,8 +411,12 @@ def checkValidJob(self): self.job = None -def getFileConfig(file): - """Get the config lines from the job file.""" +def getFileConfig(file: str) -> dict: + """ + Get the config lines from the job file. + :param str file: the file path to the job file + :rtype: dict + """ with open(file, 'r') as f: lines = f.readlines() comment_lines = [line.lstrip(';').strip() for line in lines if line.startswith(';') or ':' in line] diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 3f8b6b0e..806f9bed 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -280,11 +280,8 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): def changeFilament(self, filamentType: str, filamentDiameter: float): """ Method to change filament - :param filamentType: type of plastic the filament is made of - :param filamentDiameter: diameter of the filament in mm - :type filamentType: str - :type filamentDiameter: float - :return: None + :param str filamentType: type of plastic the filament is made of + :param float filamentDiameter: diameter of the filament in mm """ if not isinstance(filamentDiameter, float): filamentDiameter = float(filamentDiameter) @@ -337,9 +334,7 @@ def handleTempLine(self, line: str): def extractIndex(self, gcode: bytes) -> str: """ Method to extract the index of the gcode for use in the callablesHashtable - :param gcode: the line of gcode to extract the index from - :type gcode: bytes - :return: the hash index of the gcode + :param bytes gcode: the line of gcode to extract the index from :rtype: str """ hashIndex = gcode.decode().split("\n")[0].split(" ")[0] @@ -351,11 +346,6 @@ def extractIndex(self, gcode: bytes) -> str: return hashIndex def pause(self): - """ - Pause the device, if the pause command is implemented. - :return: True if the device was successfully paused, else False - :rtype: bool - """ if not self.pauseCMD: if self.logger is not None: self.logger.error("Pause command not implemented.") return True @@ -372,8 +362,7 @@ def pause(self): except Exception as e: return current_app.handle_errors_and_logging(e, self) - def resume(self): - """Resume the device, if the resume command is implemented.""" + def resume(self) -> bool: if self.resumeCMD is None: if self.logger is not None: self.logger.error("Resume command not implemented.") return False @@ -388,7 +377,7 @@ def resume(self): except Exception as e: return current_app.handle_errors_and_logging(e, self) - def connect(self): + def connect(self) -> bool: super().connect() try: if self.serialConnection and self.serialConnection.is_open: @@ -397,7 +386,7 @@ def connect(self): except Exception as e: return current_app.handle_errors_and_logging(e, self) - def disconnect(self: Device): + def disconnect(self) -> None: if self.serialConnection and self.serialConnection.is_open: self.sendGcode(b"M155 S100\n") self.sendGcode(b"M155 S0\n") diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index 41c10d90..eb2d7b8a 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -2,6 +2,8 @@ import os import sys import traceback +from typing import TextIO + from _pytest._code.code import ExceptionChainRepr, ReprEntry, ReprEntryNative from _pytest.fixtures import FixtureLookupErrorRepr from typing_extensions import Sequence @@ -15,6 +17,18 @@ class Logger(logging.Logger): CRITICAL = logging.CRITICAL def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=None, loggingLevel=logging.INFO, showFile=True, showLevel=True, showDate=True, consoleLevel=None): + """ + Initialize a logger for a device with a console and file handler. + :param str deviceName: The name of the device + :param str port: com port of device + :param TextIO consoleLogger: The console to output to + :param str fileLogger: File path to log to + :param int loggingLevel: Logging level + :param bool showFile: whether to show the file in each log line + :param bool showLevel: whether to show the level in each log line + :param bool showDate: whether to show the date in each log line + :param consoleLevel: The level to log to the console, this is to allow for different levels to be logged to the console and file. + """ title = [] if port: title.append(port) @@ -57,7 +71,14 @@ def __init__(self, deviceName, port=None, consoleLogger=sys.stdout, fileLogger=N self.fileLogger = fileLogger self.addHandler(fileLogger) - def formatLog(self, msg): + @staticmethod + def formatLog(msg): + """ + Format the log message. + :param str | ExceptionChainRepr | Exception | list | tuple msg: input to format + :return: formatted message + :rtype: str + """ if isinstance(msg, str): pass elif isinstance(msg, ExceptionChainRepr): @@ -70,32 +91,63 @@ def formatLog(self, msg): msg = str(msg) return msg - def info(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): - """Log a message with level INFO and append `end` after the message.""" + def info(self, msg: str | Exception | ExceptionChainRepr | list | tuple, end='', stacklevel: int = 2, *args, **kwargs): + """ + Log a message with level INFO and append `end` after the message. + :param str | Exception | ExceptionChainRepr | list | tuple msg: The message to log + :param str end: The string to append to the message + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ msg = self.formatLog(msg) super().info(msg + end, *args, **kwargs, stacklevel=stacklevel) - def debug(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): - """Log a message with level DEBUG and append `end` after the message.""" + def debug(self, msg: str | Exception | ExceptionChainRepr | list | tuple, end='', stacklevel: int = 2, *args, **kwargs): + """ + Log a message with level DEBUG and append `end` after the message. + :param str | Exception | ExceptionChainRepr | list | tuple msg: The message to log + :param str end: The string to append to the message + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ msg = self.formatLog(msg) super().debug(msg + end, *args, **kwargs, stacklevel=stacklevel) - def warning(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): - """Log a message with level WARNING and append `end` after the message.""" + def warning(self, msg: str | Exception | ExceptionChainRepr | list | tuple, end='', stacklevel: int = 2, *args, **kwargs): + """ + Log a message with level WARNING and append `end` after the message. + :param str | Exception | ExceptionChainRepr | list | tuple msg: The message to log + :param str end: The string to append to the message + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ msg = self.formatLog(msg) super().warning(msg + end, *args, **kwargs, stacklevel=stacklevel) - def error(self, msg: str | Exception | ExceptionChainRepr, end='', stacklevel: int = 2, *args, **kwargs): - """Log a message with level ERROR and append `end` after the message.""" + def error(self, msg: str | Exception | ExceptionChainRepr | list | tuple, end='', stacklevel: int = 2, *args, **kwargs): + """ + Log a message with level ERROR and append `end` after the message. + :param str | Exception | ExceptionChainRepr | list | tuple msg: The message to log + :param str end: The string to append to the message + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ msg = self.formatLog(msg) super().error(msg + end, *args, **kwargs, stacklevel=stacklevel) - def critical(self, msg: str | Exception | ExceptionChainRepr, end='',stacklevel: int = 2, *args, **kwargs): - """Log a message with level CRITICAL and append `end` after the message.""" + def critical(self, msg: str | Exception | ExceptionChainRepr | list | tuple, end='',stacklevel: int = 2, *args, **kwargs): + """ + Log a message with level CRITICAL and append `end` after the message. + :param str | Exception | ExceptionChainRepr | list | tuple msg: The message to log + :param str end: The string to append to the message + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ msg = self.formatLog(msg) super().critical(msg + end, *args, **kwargs, stacklevel=stacklevel) def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *args, **kwargs): + """ + Log a message without any additional formatting, removing the log level, date, time, and file info. + :param str msg: the message to log + :param int logLevel: the level to log the message at + :param int stacklevel: The level in the stack to log from. this is to stop the logger from logging from the logger itself. + """ if logLevel is None: logLevel = self.level """Log a message without any additional formatting.""" @@ -116,6 +168,10 @@ def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *a handler.setFormatter(formatter) def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRepr | list[ReprEntry | ReprEntryNative] | Sequence[ReprEntry | ReprEntryNative]| list[FixtureLookupErrorRepr] | FixtureLookupErrorRepr | ReprEntry | str): + """ + Log an exception chain, for use in pytests. + :param list[ExceptionChainRepr] | ExceptionChainRepr | list[ReprEntry | ReprEntryNative] | Sequence[ReprEntry | ReprEntryNative]| list[FixtureLookupErrorRepr] | FixtureLookupErrorRepr | ReprEntry | str reprentries: The exception chain to log + """ if isinstance(reprentries, ExceptionChainRepr) or isinstance(reprentries, ReprEntry) or isinstance(reprentries, FixtureLookupErrorRepr): reprentries = [reprentries] if isinstance(reprentries, list): @@ -159,11 +215,18 @@ def logException(self, reprentries: list[ExceptionChainRepr] | ExceptionChainRep self.logMessageOnly(line) def setLevel(self, level): + """ + Set the logging level for the logger and its handlers. + :param int level: The logging level, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL + """ super().setLevel(level) if self.consoleLogger is not None: self.consoleLogger.setLevel(level) self.fileLogger.setLevel(level) class CustomFormatter(logging.Formatter): + """ + A custom formatter for the logger, used to apply color to the log messages. + """ # ANSI escape codes for colors COLOR_CODES = { "DEBUG": "\033[94m", # Blue diff --git a/server/Classes/Ports.py b/server/Classes/Ports.py index 503b809b..e392360a 100644 --- a/server/Classes/Ports.py +++ b/server/Classes/Ports.py @@ -9,8 +9,11 @@ class Ports: @staticmethod - def getPorts(): - """Get a list of all connected serial ports.""" + def getPorts() -> list[dict]: + """ + Get a list of all connected serial ports in JSON format + :rtype: list[dict] + """ ports = serial.tools.list_ports.comports() emu_port, emu_name, emu_hwid = app.get_emu_ports() if emu_port and emu_name and emu_hwid: @@ -37,6 +40,10 @@ def getPorts(): @staticmethod def getListPorts(): + """ + Get a list of all connected serial ports. + :rtype: list[ListPortInfo | SysFS] + """ return serial.tools.list_ports.comports() @staticmethod @@ -62,7 +69,11 @@ def getPortByName(name: str): @staticmethod def getPortByHwid(hwid: str): - """Get a specific port by its hardware ID.""" + """ + Get a specific port by its hardware ID. + :param str hwid: The hardware ID of the device. + :rtype: ListPortInfo | SysFS | None + """ assert isinstance(hwid, str), f"HWID must be a string: {hwid} : {type(hwid)}" ports = Ports.getListPorts() for port in ports: @@ -71,8 +82,11 @@ def getPortByHwid(hwid: str): return None @staticmethod - def getRegisteredFabricators() -> list[Fabricator]: - """Get a list of all registered fabricators.""" + def getRegisteredFabricators(): + """ + Get a list of all registered fabricators. + :rtype: list[Fabricator] + """ fabricators = Fabricator.queryAll() registered_fabricators = [] for fab in fabricators: @@ -83,7 +97,11 @@ def getRegisteredFabricators() -> list[Fabricator]: @staticmethod def diagnosePort(port: ListPortInfo | SysFS) -> str: - """Diagnose a port to check if it is functional by sending basic G-code commands.""" + """ + Diagnose a port to check if it is functional by sending basic G-code commands. + :param ListPortInfo | SysFS port: The port to diagnose + :rtype: str + """ try: if app: device = app.fabricator_list.getFabricatorByPort(port).device diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 7b898373..ce7a51f5 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -4,6 +4,7 @@ from Classes.Jobs import Job class Queue(deque): + """Represents a queue of jobs, used to manage the order of jobs to be fabricated.""" def setToInQueue(self): for job in self: job.status = "inqueue" @@ -35,6 +36,11 @@ def addToFront(self, job): return True def bump(self, up, jobid): + """ + Move a job up or down in the queue. + :param bool up: True to move the job up, False to move it down + :param int jobid: The ID of the job to move + """ index = next( ( index @@ -58,6 +64,10 @@ def bump(self, up, jobid): ) def reorder(self, arr): + """ + Reorder the queue based on a list of job IDs. + :param list[int] arr: The list of job IDs to reorder the queue by + """ new_queue = deque() for jobid in arr: for job in self: @@ -72,48 +82,40 @@ def reorder(self, arr): "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} ) - def deleteJob(self, jobid, printerid): - deletedjob = None + def deleteJob(self, jobid: int, fabricator_id: int) -> Job | str: + """ + Delete a job from the queue. + :param int jobid: job id to delete + :param int fabricator_id: printer id for frontend. + :return: the deleted job or a message indicating the job was not found + :rtype: Job | str + """ for job in self: if job.getJobId() == jobid: deletedjob = job self.remove(job) current_app.socketio.emit( "queue_update", - {"queue": self.convertQueueToJson(), "printerid": printerid}, + {"queue": self.convertQueueToJson(), "printerid": fabricator_id}, ) return deletedjob return "Job not found in queue." - def convertQueueToJson(self): - queue = [job.__to_JSON__() for job in self] - # for job in self: - # job_info = { - # "id": job.id, - # "name": job.name, - # "status": job.status, - # "date": job.date.strftime('%a, %d %b %Y %H:%M:%S'), - # "printerid": job.fabricator_id, - # "errorid": job.error_id, - # "file_name_original": job.file_name_original, - # "progress": job.progress, - # "sent_lines": job.sent_lines, - # "favorite": job.favorite, - # "released": job.released, - # "file_pause": job.filePause, - # "comments": job.comments, - # "extruded": job.extruded, - # "td_id": job.td_id, - # "time_started": job.time_started, - # "printer_name": job.fabricator_name, - # "max_layer_height": job.max_layer_height, - # "current_layer_height": job.current_layer_height, - # "filament": job.filament, - # } - # queue.append(job_info) - return queue - - def bumpExtreme(self, front, jobid, printerid): + def convertQueueToJson(self) -> list[dict]: + """ + Convert the queue to a JSON-serializable format. + :return: list of job dictionaries + :rtype: list[dict] + """ + return [job.__to_JSON__() for job in self] + + def bumpExtreme(self, front: bool, jobid: int, fabricator_id: int): + """ + Move a job to the front or back of the queue. + :param bool front: True to move the job to the front, False to move it to the back + :param int jobid: The ID of the job to move + :param int fabricator_id: The ID of the printer to move the job to + """ index = next( ( index @@ -133,35 +135,64 @@ def bumpExtreme(self, front, jobid, printerid): else: self.insert(0, job_to_move) else: - self.addToBack(job_to_move, printerid) + self.addToBack(job_to_move, fabricator_id) if current_app: current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + "queue_update", {"queue": self.convertQueueToJson(), "printerid": fabricator_id} ) - def getJob(self, job_to_find): + def getJob(self, job_to_find) -> Job | None: + """ + Get a job from the queue. This is used to make sure that a Job is in the queue, after fetching it from the db with a query. + :param Job job_to_find: The job to find + :return: a job object if found, None otherwise + :rtype: Job | None + """ for job in self: if job.getJobId() == job_to_find.getJobId(): return job return None - def getJobById(self, job_to_find): + def getJobById(self, job_to_find: int) -> Job | None: + """ + Get a job from the queue by its ID. + :param int job_to_find: The ID of the job to find + :return: a job object if found, None otherwise + :rtype: Job | None + """ for job in self: if job.getJobId() == job_to_find: return job return None - def jobExists(self, jobid): + def jobExists(self, jobid: int) -> bool: + """ + Check if a job exists in the queue. + :param int jobid: The ID of the job to check for + :return: True if the job exists, False otherwise + :rtype: bool + """ for job in self: if job.id == jobid: return True return False - def getNext(self): + def getNext(self) -> Job | None: + """ + Get the next job in the queue. + :rtype: Job | None + """ return self[0] if len(self) > 0 else None - def removeJob(self): + def removeJob(self) -> Job | None: + """ + Remove the next job from the queue. + :return: the removed job or None if the queue is empty + :rtype Job | None + """ + if len(self) == 0: + return None self.pop() if current_app: current_app.socketio.emit("job_removed", {"queue": list(self)}) \ No newline at end of file diff --git a/server/Classes/Vector3.py b/server/Classes/Vector3.py index f64b4eb8..70e529ed 100644 --- a/server/Classes/Vector3.py +++ b/server/Classes/Vector3.py @@ -2,6 +2,12 @@ class Vector3: def __init__(self, x=0.0, y=0.0, z=0.0): + """ + A 3D vector class used for representing the position of the tool head/ + :param float x: + :param float y: + :param float z: + """ self.x = x self.y = y self.z = z diff --git a/server/Mixins/hasEndingSequence.py b/server/Mixins/hasEndingSequence.py index 5acbdb26..a8cfdb93 100644 --- a/server/Mixins/hasEndingSequence.py +++ b/server/Mixins/hasEndingSequence.py @@ -4,4 +4,5 @@ class hasEndingSequence(metaclass=ABCMeta): @abstractmethod def endSequence(self): + """Define the ending sequence for a fabricator.""" pass \ No newline at end of file From dccbccd119e3cc00b2a420569e836ba26ca0d64b Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 11 Dec 2024 14:52:14 -0500 Subject: [PATCH 189/194] fix: general formatting and debugging cleanup --- server/Classes/FabricatorConnection.py | 1 - server/Classes/FabricatorList.py | 112 ++++++------------ server/Classes/Fabricators/Device.py | 3 +- server/Classes/Fabricators/Fabricator.py | 9 +- .../Classes/Fabricators/Printers/Printer.py | 44 ++++--- .../Fabricators/Printers/Prusa/PrusaMK3.py | 2 +- .../Printers/Prusa/PrusaPrinter.py | 19 +-- server/Classes/Jobs.py | 1 - server/Classes/Logger.py | 12 +- server/Classes/Queue.py | 13 +- server/Mixins/hasResponseCodes.py | 4 +- server/controllers/jobs.py | 2 - server/controllers/ports.py | 4 - 13 files changed, 92 insertions(+), 134 deletions(-) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index af76fd3f..5c7d2533 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -119,7 +119,6 @@ def read(self): async def on_message_received(client_id, message): try: data = json.loads(message) - print(data) if data.get("event") != ("gcode_response"): return diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 10fe531f..2f852386 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -1,4 +1,4 @@ -from flask import jsonify +from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy import inspect @@ -76,7 +76,8 @@ def addFabricator(self, serialPortName: str, name: str = ""): else: # means that the fabricator is not in the db if listFab is not None: # means that the fabricator is in the list but not in the db newFab = listFab - newFab.addToDB() + db.session.add(newFab) + db.session.commit() else: # means that the fabricator is not in the list or the db newFab = Fabricator(serialPort, name=name) self.fabricators.append(newFab) @@ -99,14 +100,10 @@ def deleteFabricator(self, fabricator_id): if fabricator: try: self.fabricators.remove(fabricator) - Fabricator.query.filter_by(dbID=fabricatorid).delete() - Fabricator.updateDB() - except ValueError as e: - app.handle_errors_and_logging(e) - return e + Fabricator.query.filter_by(dbID=fabricator_id).delete() + db.session.commit() except Exception as e: - app.handle_errors_and_logging(e) - return False + return app.handle_errors_and_logging(e) return True def getFabricatorByName(self, name) -> Fabricator | None: @@ -152,34 +149,6 @@ def getFabricatorByPort(self, port) -> Fabricator | None: return fabricator return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) - - # def diagnose(self, device: Device | Fabricator): - # """diagnose a fabricator""" - # if isinstance(device, Fabricator): - # device = device.device - # try: - # diagnoseString = "" - # for port in serial.tools.list_ports.comports(): - # if port.device == device.getSerialPort().device: - # diagnoseString += f"The system has found a <b>matching port</b> with the following details: <br><br> <b>Device:</b> {port.device}, <br> <b>Description:</b> {port.description}, <br> <b>HWID:</b> {port.hwid}" - # hwid = device.getHWID() - # fabricatorExists = self.getFabricatorByHwid(hwid) - # if fabricatorExists: - # fabricator = self.getFabricatorByHwid(hwid) - # diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {fabricator.name} <br> <b>Device:</b> {fabricator.device}, <br> <b>Description:</b> {fabricator.description}, <br><b> HWID:</b> {fabricator.hwid}" - # if diagnoseString == "": - # diagnoseString = "The port this fabricator is registered under is <b>not found</b>. Please check the connection and try again." - # return { - # "success": True, - # "message": "fabricator successfully diagnosed.", - # "diagnoseString": diagnoseString, - # } - # - # except Exception as e: - # print(f"Unexpected error: {e}") - # return jsonify({"error": "Unexpected error occurred"}), 500 - - def start_fabricator_thread(self, fabricator: Fabricator): """ Start a thread for the given fabricator @@ -207,7 +176,10 @@ def get_fabricator_thread(self, fabricator): :return: that fabricator's thread """ assert fabricator in self, f"fabricator {fabricator} not in self" - thread = next(thread for thread in self.fabricator_threads if thread.fabricator == fabricator) + thread = next((thread for thread in self.fabricator_threads if thread.fabricator == fabricator), None) + if thread is None: + raise ValueError(f"Fabricator {fabricator} has no thread") + assert isinstance(thread, FabricatorThread), f"thread={thread}, type(thread)={type(thread)}" assert thread.is_alive(), f"thread {thread} is not alive" assert thread.daemon, f"thread {thread} is not daemon" return thread @@ -225,8 +197,7 @@ def queue_restore(self, status: str, queue: Queue): job.setDBstatus(job.id, 'inqueue') fabricator.setQueue(queue) fabricator.setStatus(status) - fabricator_thread = self.start_fabricator_thread(fabricator) - self.fabricator_threads.append(fabricator_thread) + self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) def update_thread(self, fabricator: Fabricator): """ @@ -238,7 +209,7 @@ def update_thread(self, fabricator: Fabricator): while True: time.sleep(2) status = fabricator.getStatus() - queueSize = len(fabricator) + queueSize = len(fabricator.queue) fabricator.responseCount = 0 if status == "ready" and queueSize > 0: time.sleep(2) @@ -256,18 +227,11 @@ def resetThread(self, fabricator_id: int) -> tuple[Response, int]: for thread in self.fabricator_threads: if thread.fabricator.dbID == fabricator_id: fabricator = thread.fabricator - fabricator.terminated = 1 - thread_data = { - "id": fabricator.dbID, - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name, - } + thread.stop() self.fabricator_threads.remove(thread) self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: app.handle_errors_and_logging(e) return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -284,18 +248,11 @@ def queueRestore(self, fabricator_id: int, status: str) -> tuple[Response, int]: for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: fabricator = thread.fabricator - fabricator.terminated = 1 - thread_data = { - "id": fabricator.id, - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name, - } + thread.stop() self.fabricator_threads.remove(thread) self.queue_restore(status, fabricator.queue) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -316,7 +273,7 @@ def deleteThread(self, fabricator_id: int) -> tuple[Response, int]: fabricator.terminated = 1 self.fabricator_threads.remove(thread) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -344,7 +301,7 @@ def moveFabricatorList(self, fabricator_ids: list[int]) -> tuple[Response, int]: new_thread_list.append(thread) break self.fabricator_threads = new_thread_list - return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) + return jsonify({"success": True, "message": "Fabricator list reordered successfully"}), 200 def editName(self, fabricator_id: int, name: str) -> tuple[Response, int]: """ @@ -357,19 +314,24 @@ def editName(self, fabricator_id: int, name: str) -> tuple[Response, int]: fabricator = self.getFabricatorById(fabricator_id) if fabricator: fabricator.setName(name) - return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}), 200 else: return jsonify({"error": "Fabricator not found"}), 404 class FabricatorThread(Thread): - def __init__(self, fabricator, app=None, *args, **kwargs): + def __init__(self, fabricator: Fabricator, passed_app=None, *args, **kwargs): + """ + create a new FabricatorThread for the given fabricator + :param Fabricator fabricator: the fabricator to create a thread for + :param MyFlaskApp passed_app: the app for context actions + """ super().__init__(*args, **kwargs) self.fabricator: Fabricator = fabricator - if app: - self.app = app + if passed_app: + self.app = passed_app else: - from globals import current_app - self.app = current_app + self.app = app + self.daemon = kwargs.get('daemon', False) def __repr__(self): return f"FabricatorThread(fabricator={self.fabricator}, daemon={self.daemon}, running={self.is_alive()})" @@ -388,19 +350,17 @@ def __to_JSON__(self): def run(self): with self.app.app_context(): + self.fabricator.responseCount = 0 + print(f"Starting thread for fabricator {self.fabricator.getName()}") while True: time.sleep(2) status = self.fabricator.getStatus() queueSize = len(self.fabricator.queue) - self.fabricator.responseCount = 0 - if status == "printing": - if queueSize > 0: - assert isinstance(self.fabricator.queue[0], - Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" - if status == "printing" and queueSize > 0 and self.fabricator.queue[0].released == 1: - time.sleep(2) - if status != "offline": - self.fabricator.begin() + if status == "printing" and queueSize > 0: + assert isinstance(self.fabricator.queue[0], Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" + if self.fabricator.queue[0].released == 1: + if status != "offline": + self.fabricator.begin() def stop(self): self.fabricator.terminated = 1 diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 9958594b..188b7124 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -179,8 +179,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False) -> bool: if self.logger is None: print(e) else: - self.logger.error("Error cancelling job:") - self.logger.error(e) + current_app.handle_errors_and_logging(e, self) self.verdict = "error" return True diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index d3b719de..b38cb234 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -61,7 +61,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self, "device") and hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (hasattr(self, "device") and self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" def __to_JSON__(self) -> dict: """ @@ -205,9 +205,6 @@ def queryAll(cls) -> list["Fabricator"]: for fab in cls.query.all(): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) - fake_port, fake_name, fake_hwid = current_app.get_emu_ports() - if fake_port and fake_name and fake_hwid: - fabList.append(cls(EmuListPortInfo(fake_port, "Emulator", fake_hwid), fake_name)) return fabList def begin(self, isVerbose: bool = False) -> bool: @@ -405,7 +402,7 @@ def checkValidJob(self): # return False pass except AssertionError as e: - self.device.logger.error(f"Invalid job: {e}") + current_app.handle_errors_and_logging(e, self.device) self.setStatus("error") self.queue.removeJob() self.job = None @@ -419,7 +416,7 @@ def getFileConfig(file: str) -> dict: """ with open(file, 'r') as f: lines = f.readlines() - comment_lines = [line.lstrip(';').strip() for line in lines if line.startswith(';') or ':' in line] + comment_lines = [line.strip().lstrip(';').strip() for line in lines if line.strip().startswith(';') or ':' in line] if len(comment_lines) > 0 and "prusaslicer" in comment_lines[0].lower(): settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} import re diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 806f9bed..128888bf 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -176,6 +176,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): self.sendGcode(self.cancelCMD) self.verdict = "cancelled" if self.logger is not None: self.logger.debug("Job cancelled") + current_app.socketio.emit("console_update", {"message": "Job cancelled", "level": "critical", "printerid": self.dbID}) return True elif self.status == "printing": self.resume() @@ -209,15 +210,18 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "complete": self.verdict = "complete" if self.logger is not None: self.logger.debug("Job complete") + current_app.socketio.emit("console_update", {"message": "Job complete", "level": "critical", "printerid": self.dbID}) return True if self.status == "error": self.verdict = "error" if self.logger is not None: self.logger.debug("Job error") + current_app.socketio.emit("console_update", {"message": "Job error", "level": "critical", "printerid": self.dbID}) return False self.verdict = "complete" self.status = "complete" if self.logger is not None: self.logger.debug("Job complete") + current_app.socketio.emit("console_update", {"message": "Job complete", "level": "critical", "printerid": self.dbID}) return True except Exception as e: # self.setStatus("error") @@ -241,6 +245,7 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): assert isinstance(gcode, bytes), f"Expected bytes, got {type(gcode)}" assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" callables = self.callablesHashtable.get(self.extractIndex(gcode), [checkOK]) + current_app.socketio.emit("gcode_line", {"line": gcode.decode(), "printerid": self.dbID}) self.serialConnection.write(gcode) line = '' for func in callables: @@ -249,21 +254,20 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): try: line = self.serialConnection.readline() decLine = line.decode("utf-8").strip() - # print(decLine if decLine != "" else "No line") - print("send gcode ok" if "ok" in decLine.lower() else "send gcode no ok") if "processing" in decLine or "echo" in decLine: continue if "T:" in decLine and "B:" in decLine: self.handleTempLine(decLine) - if func != checkBedTemp and func != checkExtruderTemp and func != checkOK: + if func != checkBedTemp and func != checkExtruderTemp and "ok" not in decLine.lower(): continue - if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") - current_app.socketio.emit("console_update", {"message": decLine, "level": "debug", "printerid": self.dbID}) if func(line, self): break + if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") + current_app.socketio.emit("console_update",{"message": decLine, "level": "debug", "printerid": self.dbID}) except UnicodeDecodeError: if isVerbose: if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") else: print(f"{gcode.decode().strip()}: {line.strip()}") + current_app.socketio.emit("console_update",{"message": gcode.decode().strip(), "level": "debug", "printerid": self.dbID}) continue except Exception as e: if current_app: return current_app.handle_errors_and_logging(e, self) @@ -271,10 +275,12 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): else: print(traceback.format_exc()) return False if not callables: + current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: ok", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info(f"{gcode.decode().strip()}: Always True") else: + current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info( - gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) + f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}") return True def changeFilament(self, filamentType: str, filamentDiameter: float): @@ -295,8 +301,7 @@ def changeFilament(self, filamentType: str, filamentDiameter: float): def changeNozzle(self, nozzleDiameter: float): """ Method to change nozzle size - :param nozzleDiameter: The diameter of the nozzle in mm - :type nozzleDiameter: float + :param float nozzleDiameter: The diameter of the nozzle in mm """ try: if not isinstance(nozzleDiameter, float): @@ -306,11 +311,10 @@ def changeNozzle(self, nozzleDiameter: float): except Exception as e: current_app.handle_errors_and_logging(e, self) - def handleTempLine(self, line: str): + def handleTempLine(self, line: str) -> None: """ Method to handle temperature lines in the serial response - :param line: - :type line: str + :param str line: the line to parse """ try: temp_t = re.search(r'T:(\d+.\d+)', line) @@ -323,7 +327,6 @@ def handleTempLine(self, line: str): self.nozzleTemperature = float(temp_t.group(1)) if temp_b: self.bedTemperature = float(temp_b.group(1)) - from globals import current_app if current_app: current_app.socketio.emit('temp_update', {'printerid': self.dbID, 'extruder_temp': self.nozzleTemperature, 'bed_temp': self.bedTemperature}) @@ -338,12 +341,17 @@ def extractIndex(self, gcode: bytes) -> str: :rtype: str """ hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if self.logger is not None: - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") - return hashIndex + + if hashIndex == "M109" or hashIndex == "M190": + if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") + current_app.socketio.emit("console_update", + {"message": "Waiting for temperature to stabilize...", "level": "info", + "printerid": self.dbID}) + elif hashIndex == "G28": + if self.logger is not None: self.logger.info("Homing...") + current_app.socketio.emit("console_update", + {"message": "Homing...", "level": "info", "printerid": self.dbID}) + return hashIndex def pause(self): if not self.pauseCMD: diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index b9541612..a038a77b 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -18,7 +18,7 @@ class PrusaMK3(PrusaPrinter): callablesHashtable = { "M31": [checkTime, checkOK], # Print time - "G28": [checkOK], + "G28": [checkOK, checkOK], # Home "G29.02": [checkOK, checkOK], "G29.01": [checkOK, checkXYZ, checkXYZ, checkOK], # Auto bed leveling "M601": [] # Pause diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index acc98b9e..524e4b95 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -2,7 +2,7 @@ from Classes.Fabricators.Printers.Printer import Printer from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkXYZ, checkOK, checkTime - +from globals import current_app class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): VENDORID = 0x2C99 @@ -19,7 +19,6 @@ class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): "G29.01": [checkXYZ, checkOK], # Auto bed leveling "G29.02": [checkOK], # Auto bed leveling "M31": [checkOK, checkTime, checkOK], # Print time - "M73": [checkOK], # Set build percentage } callablesHashtable = {**Printer.callablesHashtable, **callablesHashtable} @@ -27,15 +26,17 @@ class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): def extractIndex(self, gcode: bytes) -> str: hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if hashIndex == "G29": + if hashIndex == "M109" or hashIndex == "M190": + if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") + current_app.socketio.emit("console_update", {"message": "Waiting for temperature to stabilize...", "level": "info", "printerid": self.dbID}) + elif hashIndex == "G28": + if self.logger is not None: self.logger.info("Homing...") + current_app.socketio.emit("console_update", {"message": "Homing...", "level": "info", "printerid": self.dbID}) + elif hashIndex == "G29": try: g29addon = gcode.decode().split("\n")[0].split(" ")[1] hashIndex += ".01" if g29addon == "P1" else ".02" - except IndexError as e: + except IndexError: hashIndex += ".01" - if self.logger is not None: - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") + current_app.socketio.emit("console_update", {"message": "Auto bed leveling...", "level": "info", "printerid": self.dbID}) return hashIndex diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index c999e07d..09a55f14 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -2,7 +2,6 @@ import os import re from models.db import db - from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory from datetime import timezone, timedelta from flask import jsonify diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index eb2d7b8a..ff37030b 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -8,7 +8,6 @@ from _pytest.fixtures import FixtureLookupErrorRepr from typing_extensions import Sequence - class Logger(logging.Logger): DEBUG = logging.DEBUG INFO = logging.INFO @@ -150,19 +149,18 @@ def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *a """ if logLevel is None: logLevel = self.level - """Log a message without any additional formatting.""" oldFormatters = [handler.formatter for handler in self.handlers] for handler in self.handlers: handler.setFormatter(CustomFormatter("%(message)s")) - if logLevel == self.DEBUG: + if logLevel <= self.DEBUG: self.debug(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.INFO: + elif logLevel <= self.INFO: self.info(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.WARNING: + elif logLevel <= self.WARNING: self.warning(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.ERROR: + elif logLevel <= self.ERROR: self.error(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.CRITICAL: + elif logLevel <= self.CRITICAL: self.critical(msg, stacklevel=stacklevel, *args, **kwargs) for handler, formatter in zip(self.handlers, oldFormatters): handler.setFormatter(formatter) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index ce7a51f5..7c20a4c0 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,6 +1,5 @@ from collections import deque from globals import current_app - from Classes.Jobs import Job class Queue(deque): @@ -21,8 +20,13 @@ def addToBack(self, job: Job, printerid): ) return True - def addToFront(self, job): - assert isinstance(job, Job) + def addToFront(self, job: Job) -> bool: + """ + add new job to the front of the Queue + :param Job job: the job to add + :rtype: bool + """ + assert isinstance(job, Job), f"Job must be an instance of Job: {job} : {type(job)}" if self.count(job) > 0: return False if len(self) >= 1 and self[0].status == "printing": @@ -79,7 +83,7 @@ def reorder(self, arr): if current_app: current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} + "queue_update", {"queue": self.convertQueueToJson(), "printerid": self[0].fabricator_id if len(self) > 0 else None} ) def deleteJob(self, jobid: int, fabricator_id: int) -> Job | str: @@ -173,7 +177,6 @@ def jobExists(self, jobid: int) -> bool: :rtype: bool """ for job in self: - if job.id == jobid: return True return False diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index f7235505..d0c5be00 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -29,7 +29,7 @@ def checkEcho(line, dev): def checkBedTemp(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip().lower() try: - return checkTemp([temp.strip() for temp in line.split("b:")[1].split("t0:")[0].split("x:")[0].split("/")]) + return checkTemp([temp.strip() for temp in line.split("b:")[1].split("t0:")[0].split("x:")[0].split("/")], dev) except IndexError: return False except Exception as e: @@ -38,7 +38,7 @@ def checkBedTemp(line, dev): def checkExtruderTemp(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip() try: - return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")]) + return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")], dev) except IndexError: return False except Exception as e: diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 8267e404..a834e4eb 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -270,7 +270,6 @@ def releasejob(): fabricator = findPrinterObject(printerid) if fabricator is None: return jsonify({"error": "Printer not found."}), 404 - print(fabricator) fabricator.error = "" if len(fabricator.queue) > 0: assert len(fabricator.queue) > 0, "Queue is empty" @@ -653,7 +652,6 @@ def findPrinterObject(fabricator_id): :rtype: Fabricator | None """ threads = current_app.fabricator_list.getThreadArray() - print(threads) fabricatorThread = list(filter(lambda thread: thread.fabricator.dbID == fabricator_id, threads)) return fabricatorThread[0].fabricator if len(fabricatorThread) > 0 else None diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 569120b1..c7430e97 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -40,13 +40,11 @@ def registerFabricator(): name = printer['name'] # Create a new fabricator instance using the Fabricator class - print(f"Registering fabricator: device: {device}, name: {name}, printer: {printer}") try: app.fabricator_list.addFabricator(device, name) except AssertionError as ae: return jsonify({"error": f"Failed to add fabricator: {ae}"}), 500 new_fabricator = Fabricator.query.filter_by(devicePort=device).first() - print(f"New fabricator: {new_fabricator}") return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: @@ -61,7 +59,6 @@ def deleteFabricator(): """Delete a fabricator from the system.""" try: data = request.get_json() - print(data) fabricator_id = data['fabricator_id'] res = app.fabricator_list.deleteFabricator(fabricator_id) if isinstance(res, ValueError): @@ -107,7 +104,6 @@ def diagnoseFabricator(): diagnosis_result = device.diagnose() return jsonify({"success": True, "message": "Diagnosis successful", "diagnoseString": diagnosis_result}) else: - print("no device?") return jsonify({"error": "Failed to create device for diagnosis"}), 500 else: return jsonify({"error": "Device not found"}), 404 From 9ac6e8282bfe61296887b2fd24e280d6f61dad98 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 11 Dec 2024 14:52:14 -0500 Subject: [PATCH 190/194] fix: general formatting and debugging cleanup --- server/Classes/FabricatorConnection.py | 1 - server/Classes/FabricatorList.py | 112 ++++++------------ server/Classes/Fabricators/Device.py | 3 +- server/Classes/Fabricators/Fabricator.py | 9 +- .../Classes/Fabricators/Printers/Printer.py | 44 ++++--- .../Fabricators/Printers/Prusa/PrusaMK3.py | 2 +- .../Printers/Prusa/PrusaPrinter.py | 19 +-- server/Classes/Jobs.py | 1 - server/Classes/Logger.py | 12 +- server/Classes/Queue.py | 13 +- server/Mixins/hasResponseCodes.py | 4 +- server/app.py | 6 +- server/controllers/jobs.py | 2 - server/controllers/ports.py | 4 - 14 files changed, 95 insertions(+), 137 deletions(-) diff --git a/server/Classes/FabricatorConnection.py b/server/Classes/FabricatorConnection.py index af76fd3f..5c7d2533 100644 --- a/server/Classes/FabricatorConnection.py +++ b/server/Classes/FabricatorConnection.py @@ -119,7 +119,6 @@ def read(self): async def on_message_received(client_id, message): try: data = json.loads(message) - print(data) if data.get("event") != ("gcode_response"): return diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 10fe531f..2f852386 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -1,4 +1,4 @@ -from flask import jsonify +from flask import jsonify, Response from serial.tools.list_ports_common import ListPortInfo from serial.tools.list_ports_linux import SysFS from sqlalchemy import inspect @@ -76,7 +76,8 @@ def addFabricator(self, serialPortName: str, name: str = ""): else: # means that the fabricator is not in the db if listFab is not None: # means that the fabricator is in the list but not in the db newFab = listFab - newFab.addToDB() + db.session.add(newFab) + db.session.commit() else: # means that the fabricator is not in the list or the db newFab = Fabricator(serialPort, name=name) self.fabricators.append(newFab) @@ -99,14 +100,10 @@ def deleteFabricator(self, fabricator_id): if fabricator: try: self.fabricators.remove(fabricator) - Fabricator.query.filter_by(dbID=fabricatorid).delete() - Fabricator.updateDB() - except ValueError as e: - app.handle_errors_and_logging(e) - return e + Fabricator.query.filter_by(dbID=fabricator_id).delete() + db.session.commit() except Exception as e: - app.handle_errors_and_logging(e) - return False + return app.handle_errors_and_logging(e) return True def getFabricatorByName(self, name) -> Fabricator | None: @@ -152,34 +149,6 @@ def getFabricatorByPort(self, port) -> Fabricator | None: return fabricator return next((fabricator for fabricator in self.fabricators if fabricator.devicePort == port), None) - - # def diagnose(self, device: Device | Fabricator): - # """diagnose a fabricator""" - # if isinstance(device, Fabricator): - # device = device.device - # try: - # diagnoseString = "" - # for port in serial.tools.list_ports.comports(): - # if port.device == device.getSerialPort().device: - # diagnoseString += f"The system has found a <b>matching port</b> with the following details: <br><br> <b>Device:</b> {port.device}, <br> <b>Description:</b> {port.description}, <br> <b>HWID:</b> {port.hwid}" - # hwid = device.getHWID() - # fabricatorExists = self.getFabricatorByHwid(hwid) - # if fabricatorExists: - # fabricator = self.getFabricatorByHwid(hwid) - # diagnoseString += f"<hr><br>Device <b>{port.device}</b> is registered with the following details: <br><br> <b>Name:</b> {fabricator.name} <br> <b>Device:</b> {fabricator.device}, <br> <b>Description:</b> {fabricator.description}, <br><b> HWID:</b> {fabricator.hwid}" - # if diagnoseString == "": - # diagnoseString = "The port this fabricator is registered under is <b>not found</b>. Please check the connection and try again." - # return { - # "success": True, - # "message": "fabricator successfully diagnosed.", - # "diagnoseString": diagnoseString, - # } - # - # except Exception as e: - # print(f"Unexpected error: {e}") - # return jsonify({"error": "Unexpected error occurred"}), 500 - - def start_fabricator_thread(self, fabricator: Fabricator): """ Start a thread for the given fabricator @@ -207,7 +176,10 @@ def get_fabricator_thread(self, fabricator): :return: that fabricator's thread """ assert fabricator in self, f"fabricator {fabricator} not in self" - thread = next(thread for thread in self.fabricator_threads if thread.fabricator == fabricator) + thread = next((thread for thread in self.fabricator_threads if thread.fabricator == fabricator), None) + if thread is None: + raise ValueError(f"Fabricator {fabricator} has no thread") + assert isinstance(thread, FabricatorThread), f"thread={thread}, type(thread)={type(thread)}" assert thread.is_alive(), f"thread {thread} is not alive" assert thread.daemon, f"thread {thread} is not daemon" return thread @@ -225,8 +197,7 @@ def queue_restore(self, status: str, queue: Queue): job.setDBstatus(job.id, 'inqueue') fabricator.setQueue(queue) fabricator.setStatus(status) - fabricator_thread = self.start_fabricator_thread(fabricator) - self.fabricator_threads.append(fabricator_thread) + self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) def update_thread(self, fabricator: Fabricator): """ @@ -238,7 +209,7 @@ def update_thread(self, fabricator: Fabricator): while True: time.sleep(2) status = fabricator.getStatus() - queueSize = len(fabricator) + queueSize = len(fabricator.queue) fabricator.responseCount = 0 if status == "ready" and queueSize > 0: time.sleep(2) @@ -256,18 +227,11 @@ def resetThread(self, fabricator_id: int) -> tuple[Response, int]: for thread in self.fabricator_threads: if thread.fabricator.dbID == fabricator_id: fabricator = thread.fabricator - fabricator.terminated = 1 - thread_data = { - "id": fabricator.dbID, - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name, - } + thread.stop() self.fabricator_threads.remove(thread) self.fabricator_threads.append(self.start_fabricator_thread(fabricator)) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: app.handle_errors_and_logging(e) return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -284,18 +248,11 @@ def queueRestore(self, fabricator_id: int, status: str) -> tuple[Response, int]: for thread in self.fabricator_threads: if thread.fabricator.id == fabricator_id: fabricator = thread.fabricator - fabricator.terminated = 1 - thread_data = { - "id": fabricator.id, - "device": fabricator.device, - "description": fabricator.description, - "hwid": fabricator.hwid, - "name": fabricator.name, - } + thread.stop() self.fabricator_threads.remove(thread) self.queue_restore(status, fabricator.queue) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -316,7 +273,7 @@ def deleteThread(self, fabricator_id: int) -> tuple[Response, int]: fabricator.terminated = 1 self.fabricator_threads.remove(thread) break - return jsonify({"success": True, "message": "Fabricator thread reset successfully"}) + return jsonify({"success": True, "message": "Fabricator thread reset successfully"}), 200 except Exception as e: print(f"Unexpected error: {e}") return jsonify({"success": False, "error": "Unexpected error occurred"}), 500 @@ -344,7 +301,7 @@ def moveFabricatorList(self, fabricator_ids: list[int]) -> tuple[Response, int]: new_thread_list.append(thread) break self.fabricator_threads = new_thread_list - return jsonify({"success": True, "message": "Fabricator list reordered successfully"}) + return jsonify({"success": True, "message": "Fabricator list reordered successfully"}), 200 def editName(self, fabricator_id: int, name: str) -> tuple[Response, int]: """ @@ -357,19 +314,24 @@ def editName(self, fabricator_id: int, name: str) -> tuple[Response, int]: fabricator = self.getFabricatorById(fabricator_id) if fabricator: fabricator.setName(name) - return jsonify({"success": True, "message": "Fabricator name updated successfully"}) + return jsonify({"success": True, "message": "Fabricator name updated successfully"}), 200 else: return jsonify({"error": "Fabricator not found"}), 404 class FabricatorThread(Thread): - def __init__(self, fabricator, app=None, *args, **kwargs): + def __init__(self, fabricator: Fabricator, passed_app=None, *args, **kwargs): + """ + create a new FabricatorThread for the given fabricator + :param Fabricator fabricator: the fabricator to create a thread for + :param MyFlaskApp passed_app: the app for context actions + """ super().__init__(*args, **kwargs) self.fabricator: Fabricator = fabricator - if app: - self.app = app + if passed_app: + self.app = passed_app else: - from globals import current_app - self.app = current_app + self.app = app + self.daemon = kwargs.get('daemon', False) def __repr__(self): return f"FabricatorThread(fabricator={self.fabricator}, daemon={self.daemon}, running={self.is_alive()})" @@ -388,19 +350,17 @@ def __to_JSON__(self): def run(self): with self.app.app_context(): + self.fabricator.responseCount = 0 + print(f"Starting thread for fabricator {self.fabricator.getName()}") while True: time.sleep(2) status = self.fabricator.getStatus() queueSize = len(self.fabricator.queue) - self.fabricator.responseCount = 0 - if status == "printing": - if queueSize > 0: - assert isinstance(self.fabricator.queue[0], - Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" - if status == "printing" and queueSize > 0 and self.fabricator.queue[0].released == 1: - time.sleep(2) - if status != "offline": - self.fabricator.begin() + if status == "printing" and queueSize > 0: + assert isinstance(self.fabricator.queue[0], Job), f"self.fabricator.queue[0]={self.fabricator.queue[0]}, type(self.fabricator.queue[0])={type(self.fabricator.queue[0])}, self.fabricator.queue={self.fabricator.queue}, type(self.fabricator.queue)={type(self.fabricator.queue)}" + if self.fabricator.queue[0].released == 1: + if status != "offline": + self.fabricator.begin() def stop(self): self.fabricator.terminated = 1 diff --git a/server/Classes/Fabricators/Device.py b/server/Classes/Fabricators/Device.py index 9958594b..188b7124 100644 --- a/server/Classes/Fabricators/Device.py +++ b/server/Classes/Fabricators/Device.py @@ -179,8 +179,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False) -> bool: if self.logger is None: print(e) else: - self.logger.error("Error cancelling job:") - self.logger.error(e) + current_app.handle_errors_and_logging(e, self) self.verdict = "error" return True diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index d3b719de..b38cb234 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -61,7 +61,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self, "device") and hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (hasattr(self, "device") and self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" def __to_JSON__(self) -> dict: """ @@ -205,9 +205,6 @@ def queryAll(cls) -> list["Fabricator"]: for fab in cls.query.all(): if Ports.getPortByName(fab.devicePort) is not None: fabList.append(cls(Ports.getPortByName(fab.devicePort), fab.name)) - fake_port, fake_name, fake_hwid = current_app.get_emu_ports() - if fake_port and fake_name and fake_hwid: - fabList.append(cls(EmuListPortInfo(fake_port, "Emulator", fake_hwid), fake_name)) return fabList def begin(self, isVerbose: bool = False) -> bool: @@ -405,7 +402,7 @@ def checkValidJob(self): # return False pass except AssertionError as e: - self.device.logger.error(f"Invalid job: {e}") + current_app.handle_errors_and_logging(e, self.device) self.setStatus("error") self.queue.removeJob() self.job = None @@ -419,7 +416,7 @@ def getFileConfig(file: str) -> dict: """ with open(file, 'r') as f: lines = f.readlines() - comment_lines = [line.lstrip(';').strip() for line in lines if line.startswith(';') or ':' in line] + comment_lines = [line.strip().lstrip(';').strip() for line in lines if line.strip().startswith(';') or ':' in line] if len(comment_lines) > 0 and "prusaslicer" in comment_lines[0].lower(): settingsDict = {line.split('=')[0].strip(): line.split('=')[1].strip() for line in comment_lines if '=' in line} import re diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 806f9bed..128888bf 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -176,6 +176,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): self.sendGcode(self.cancelCMD) self.verdict = "cancelled" if self.logger is not None: self.logger.debug("Job cancelled") + current_app.socketio.emit("console_update", {"message": "Job cancelled", "level": "critical", "printerid": self.dbID}) return True elif self.status == "printing": self.resume() @@ -209,15 +210,18 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if self.status == "complete": self.verdict = "complete" if self.logger is not None: self.logger.debug("Job complete") + current_app.socketio.emit("console_update", {"message": "Job complete", "level": "critical", "printerid": self.dbID}) return True if self.status == "error": self.verdict = "error" if self.logger is not None: self.logger.debug("Job error") + current_app.socketio.emit("console_update", {"message": "Job error", "level": "critical", "printerid": self.dbID}) return False self.verdict = "complete" self.status = "complete" if self.logger is not None: self.logger.debug("Job complete") + current_app.socketio.emit("console_update", {"message": "Job complete", "level": "critical", "printerid": self.dbID}) return True except Exception as e: # self.setStatus("error") @@ -241,6 +245,7 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): assert isinstance(gcode, bytes), f"Expected bytes, got {type(gcode)}" assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" callables = self.callablesHashtable.get(self.extractIndex(gcode), [checkOK]) + current_app.socketio.emit("gcode_line", {"line": gcode.decode(), "printerid": self.dbID}) self.serialConnection.write(gcode) line = '' for func in callables: @@ -249,21 +254,20 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): try: line = self.serialConnection.readline() decLine = line.decode("utf-8").strip() - # print(decLine if decLine != "" else "No line") - print("send gcode ok" if "ok" in decLine.lower() else "send gcode no ok") if "processing" in decLine or "echo" in decLine: continue if "T:" in decLine and "B:" in decLine: self.handleTempLine(decLine) - if func != checkBedTemp and func != checkExtruderTemp and func != checkOK: + if func != checkBedTemp and func != checkExtruderTemp and "ok" not in decLine.lower(): continue - if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") - current_app.socketio.emit("console_update", {"message": decLine, "level": "debug", "printerid": self.dbID}) if func(line, self): break + if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") + current_app.socketio.emit("console_update",{"message": decLine, "level": "debug", "printerid": self.dbID}) except UnicodeDecodeError: if isVerbose: if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") else: print(f"{gcode.decode().strip()}: {line.strip()}") + current_app.socketio.emit("console_update",{"message": gcode.decode().strip(), "level": "debug", "printerid": self.dbID}) continue except Exception as e: if current_app: return current_app.handle_errors_and_logging(e, self) @@ -271,10 +275,12 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): else: print(traceback.format_exc()) return False if not callables: + current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: ok", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info(f"{gcode.decode().strip()}: Always True") else: + current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info( - gcode.decode().strip() + ": " + (line.decode() if isinstance(line, bytes) else line).strip()) + f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}") return True def changeFilament(self, filamentType: str, filamentDiameter: float): @@ -295,8 +301,7 @@ def changeFilament(self, filamentType: str, filamentDiameter: float): def changeNozzle(self, nozzleDiameter: float): """ Method to change nozzle size - :param nozzleDiameter: The diameter of the nozzle in mm - :type nozzleDiameter: float + :param float nozzleDiameter: The diameter of the nozzle in mm """ try: if not isinstance(nozzleDiameter, float): @@ -306,11 +311,10 @@ def changeNozzle(self, nozzleDiameter: float): except Exception as e: current_app.handle_errors_and_logging(e, self) - def handleTempLine(self, line: str): + def handleTempLine(self, line: str) -> None: """ Method to handle temperature lines in the serial response - :param line: - :type line: str + :param str line: the line to parse """ try: temp_t = re.search(r'T:(\d+.\d+)', line) @@ -323,7 +327,6 @@ def handleTempLine(self, line: str): self.nozzleTemperature = float(temp_t.group(1)) if temp_b: self.bedTemperature = float(temp_b.group(1)) - from globals import current_app if current_app: current_app.socketio.emit('temp_update', {'printerid': self.dbID, 'extruder_temp': self.nozzleTemperature, 'bed_temp': self.bedTemperature}) @@ -338,12 +341,17 @@ def extractIndex(self, gcode: bytes) -> str: :rtype: str """ hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if self.logger is not None: - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") - return hashIndex + + if hashIndex == "M109" or hashIndex == "M190": + if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") + current_app.socketio.emit("console_update", + {"message": "Waiting for temperature to stabilize...", "level": "info", + "printerid": self.dbID}) + elif hashIndex == "G28": + if self.logger is not None: self.logger.info("Homing...") + current_app.socketio.emit("console_update", + {"message": "Homing...", "level": "info", "printerid": self.dbID}) + return hashIndex def pause(self): if not self.pauseCMD: diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index b9541612..a038a77b 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -18,7 +18,7 @@ class PrusaMK3(PrusaPrinter): callablesHashtable = { "M31": [checkTime, checkOK], # Print time - "G28": [checkOK], + "G28": [checkOK, checkOK], # Home "G29.02": [checkOK, checkOK], "G29.01": [checkOK, checkXYZ, checkXYZ, checkOK], # Auto bed leveling "M601": [] # Pause diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index acc98b9e..524e4b95 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -2,7 +2,7 @@ from Classes.Fabricators.Printers.Printer import Printer from Mixins.hasEndingSequence import hasEndingSequence from Mixins.hasResponseCodes import checkXYZ, checkOK, checkTime - +from globals import current_app class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): VENDORID = 0x2C99 @@ -19,7 +19,6 @@ class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): "G29.01": [checkXYZ, checkOK], # Auto bed leveling "G29.02": [checkOK], # Auto bed leveling "M31": [checkOK, checkTime, checkOK], # Print time - "M73": [checkOK], # Set build percentage } callablesHashtable = {**Printer.callablesHashtable, **callablesHashtable} @@ -27,15 +26,17 @@ class PrusaPrinter(Printer, hasEndingSequence, metaclass=ABCMeta): def extractIndex(self, gcode: bytes) -> str: hashIndex = gcode.decode().split("\n")[0].split(" ")[0] - if hashIndex == "G29": + if hashIndex == "M109" or hashIndex == "M190": + if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") + current_app.socketio.emit("console_update", {"message": "Waiting for temperature to stabilize...", "level": "info", "printerid": self.dbID}) + elif hashIndex == "G28": + if self.logger is not None: self.logger.info("Homing...") + current_app.socketio.emit("console_update", {"message": "Homing...", "level": "info", "printerid": self.dbID}) + elif hashIndex == "G29": try: g29addon = gcode.decode().split("\n")[0].split(" ")[1] hashIndex += ".01" if g29addon == "P1" else ".02" - except IndexError as e: + except IndexError: hashIndex += ".01" - if self.logger is not None: - if hashIndex == "M109" or hashIndex == "M190": - self.logger.info("Waiting for temperature to stabilize...") - elif hashIndex == "G28": - self.logger.info("Homing...") + current_app.socketio.emit("console_update", {"message": "Auto bed leveling...", "level": "info", "printerid": self.dbID}) return hashIndex diff --git a/server/Classes/Jobs.py b/server/Classes/Jobs.py index c999e07d..09a55f14 100644 --- a/server/Classes/Jobs.py +++ b/server/Classes/Jobs.py @@ -2,7 +2,6 @@ import os import re from models.db import db - from models.issues import Issue # assuming the Issue model is defined in the issue.py file in the models directory from datetime import timezone, timedelta from flask import jsonify diff --git a/server/Classes/Logger.py b/server/Classes/Logger.py index eb2d7b8a..ff37030b 100644 --- a/server/Classes/Logger.py +++ b/server/Classes/Logger.py @@ -8,7 +8,6 @@ from _pytest.fixtures import FixtureLookupErrorRepr from typing_extensions import Sequence - class Logger(logging.Logger): DEBUG = logging.DEBUG INFO = logging.INFO @@ -150,19 +149,18 @@ def logMessageOnly(self, msg: str, logLevel: int = None, stacklevel: int = 3, *a """ if logLevel is None: logLevel = self.level - """Log a message without any additional formatting.""" oldFormatters = [handler.formatter for handler in self.handlers] for handler in self.handlers: handler.setFormatter(CustomFormatter("%(message)s")) - if logLevel == self.DEBUG: + if logLevel <= self.DEBUG: self.debug(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.INFO: + elif logLevel <= self.INFO: self.info(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.WARNING: + elif logLevel <= self.WARNING: self.warning(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.ERROR: + elif logLevel <= self.ERROR: self.error(msg, stacklevel=stacklevel, *args, **kwargs) - elif logLevel == self.CRITICAL: + elif logLevel <= self.CRITICAL: self.critical(msg, stacklevel=stacklevel, *args, **kwargs) for handler, formatter in zip(self.handlers, oldFormatters): handler.setFormatter(formatter) diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index ce7a51f5..7c20a4c0 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -1,6 +1,5 @@ from collections import deque from globals import current_app - from Classes.Jobs import Job class Queue(deque): @@ -21,8 +20,13 @@ def addToBack(self, job: Job, printerid): ) return True - def addToFront(self, job): - assert isinstance(job, Job) + def addToFront(self, job: Job) -> bool: + """ + add new job to the front of the Queue + :param Job job: the job to add + :rtype: bool + """ + assert isinstance(job, Job), f"Job must be an instance of Job: {job} : {type(job)}" if self.count(job) > 0: return False if len(self) >= 1 and self[0].status == "printing": @@ -79,7 +83,7 @@ def reorder(self, arr): if current_app: current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": arr[0].fabricator_id if arr else None} + "queue_update", {"queue": self.convertQueueToJson(), "printerid": self[0].fabricator_id if len(self) > 0 else None} ) def deleteJob(self, jobid: int, fabricator_id: int) -> Job | str: @@ -173,7 +177,6 @@ def jobExists(self, jobid: int) -> bool: :rtype: bool """ for job in self: - if job.id == jobid: return True return False diff --git a/server/Mixins/hasResponseCodes.py b/server/Mixins/hasResponseCodes.py index f7235505..d0c5be00 100644 --- a/server/Mixins/hasResponseCodes.py +++ b/server/Mixins/hasResponseCodes.py @@ -29,7 +29,7 @@ def checkEcho(line, dev): def checkBedTemp(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip().lower() try: - return checkTemp([temp.strip() for temp in line.split("b:")[1].split("t0:")[0].split("x:")[0].split("/")]) + return checkTemp([temp.strip() for temp in line.split("b:")[1].split("t0:")[0].split("x:")[0].split("/")], dev) except IndexError: return False except Exception as e: @@ -38,7 +38,7 @@ def checkBedTemp(line, dev): def checkExtruderTemp(line, dev): line = (line.decode() if isinstance(line, bytes) else line).strip() try: - return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")]) + return checkTemp([temp.strip() for temp in line.split("T:")[1].split("B:")[0].split("/")], dev) except IndexError: return False except Exception as e: diff --git a/server/app.py b/server/app.py index 60b7c738..000384e0 100644 --- a/server/app.py +++ b/server/app.py @@ -5,6 +5,7 @@ import os import shutil import websockets +from websockets.asyncio.server import Server from MyFlaskApp import MyFlaskApp from globals import emulator_connections, event_emitter @@ -53,7 +54,7 @@ async def handle_client(websocket): del emulator_connections[client_id] try: - server = await websockets.serve(handle_client, "localhost", 8001) + server: Server = await websockets.serve(handle_client, "localhost", 8001) await server.wait_closed() except Exception: print(f"WebSocket server error: {traceback.format_exc()}") @@ -62,8 +63,7 @@ def start_websocket(): print("Starting WebSocket server...") asyncio.run(websocket_server()) -websocket_thread = threading.Thread(target=start_websocket) -websocket_thread.daemon = True # Make it a daemon thread to exit with the main program +websocket_thread = threading.Thread(target=start_websocket, daemon=True) websocket_thread.start() diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 8267e404..a834e4eb 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -270,7 +270,6 @@ def releasejob(): fabricator = findPrinterObject(printerid) if fabricator is None: return jsonify({"error": "Printer not found."}), 404 - print(fabricator) fabricator.error = "" if len(fabricator.queue) > 0: assert len(fabricator.queue) > 0, "Queue is empty" @@ -653,7 +652,6 @@ def findPrinterObject(fabricator_id): :rtype: Fabricator | None """ threads = current_app.fabricator_list.getThreadArray() - print(threads) fabricatorThread = list(filter(lambda thread: thread.fabricator.dbID == fabricator_id, threads)) return fabricatorThread[0].fabricator if len(fabricatorThread) > 0 else None diff --git a/server/controllers/ports.py b/server/controllers/ports.py index 569120b1..c7430e97 100644 --- a/server/controllers/ports.py +++ b/server/controllers/ports.py @@ -40,13 +40,11 @@ def registerFabricator(): name = printer['name'] # Create a new fabricator instance using the Fabricator class - print(f"Registering fabricator: device: {device}, name: {name}, printer: {printer}") try: app.fabricator_list.addFabricator(device, name) except AssertionError as ae: return jsonify({"error": f"Failed to add fabricator: {ae}"}), 500 new_fabricator = Fabricator.query.filter_by(devicePort=device).first() - print(f"New fabricator: {new_fabricator}") return jsonify({"success": True, "message": "Fabricator registered successfully", "fabricator_id": new_fabricator.dbID}) except SQLAlchemyError as db_err: @@ -61,7 +59,6 @@ def deleteFabricator(): """Delete a fabricator from the system.""" try: data = request.get_json() - print(data) fabricator_id = data['fabricator_id'] res = app.fabricator_list.deleteFabricator(fabricator_id) if isinstance(res, ValueError): @@ -107,7 +104,6 @@ def diagnoseFabricator(): diagnosis_result = device.diagnose() return jsonify({"success": True, "message": "Diagnosis successful", "diagnoseString": diagnosis_result}) else: - print("no device?") return jsonify({"error": "Failed to create device for diagnosis"}), 500 else: return jsonify({"error": "Device not found"}), 404 From 95af6ac09fa0d44d24893ea481a49921fcf9aa1a Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Wed, 11 Dec 2024 19:20:16 -0500 Subject: [PATCH 191/194] feat: live real-time render of gcode. still needs lots of work. --- client/src/App.vue | 10 +- client/src/components/ConsoleTerminal.vue | 30 +- client/src/components/GCode3DImageViewer.vue | 56 ++-- client/src/components/GCode3DLiveViewer.vue | 170 ++++------ client/src/components/GCodeThumbnail.vue | 5 +- client/src/model/jobs.ts | 77 +++-- client/src/model/ports.ts | 26 +- client/src/model/sockets.ts | 201 +++++++----- client/src/views/MainView.vue | 297 ++++++++++-------- .../Classes/Fabricators/Printers/Printer.py | 12 +- server/Classes/Queue.py | 6 +- server/controllers/jobs.py | 10 +- 12 files changed, 443 insertions(+), 457 deletions(-) diff --git a/client/src/App.vue b/client/src/App.vue index f709e5c7..3b10ad90 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -9,16 +9,16 @@ import SettingsPanel from '@/components/SettingsPanel.vue' import { onMounted } from 'vue'; import {setupSockets} from '@/model/sockets'; import {useRetrievePrintersInfo, printers} from '@/model/ports'; -import {isLoading} from '@/model/jobs'; +import {isLoading, setupTimeSocket} from '@/model/jobs'; const { retrieveInfo } = useRetrievePrintersInfo(); onMounted(async () => { - printers.value = await retrieveInfo() - console.log(printers.value) + printers.value = await retrieveInfo() - // sockets - setupSockets(printers.value) + // sockets + setupSockets(printers.value) + setupTimeSocket(printers.value) }) </script> diff --git a/client/src/components/ConsoleTerminal.vue b/client/src/components/ConsoleTerminal.vue index f1d77209..ba02d0fb 100644 --- a/client/src/components/ConsoleTerminal.vue +++ b/client/src/components/ConsoleTerminal.vue @@ -11,14 +11,6 @@ watch(() => props.lines, (newLines) => { lines.value = newLines; }); -// Watch for changes in the lines prop and trigger scrolling -watch( - () => lines, - () => { - scrollToBottom(); - } -); - // Ref for the terminal container to enable automatic scrolling const terminalOutput = ref<HTMLDivElement | null>(null); @@ -30,11 +22,16 @@ const processedLines = computed(() => lines.value!.map((line) => ansiConverter.toHtml(line)) ); +// Watch for changes in the lines prop and trigger scrolling +watch(processedLines, () => {scrollToBottom();}); + // Scroll to the bottom whenever lines update const scrollToBottom = () => { - if (terminalOutput.value) { - terminalOutput.value.scrollTop = terminalOutput.value.scrollHeight; - } + requestAnimationFrame(() => { + if (terminalOutput.value) { + terminalOutput.value.scrollTop = terminalOutput.value.scrollHeight; + } + }); }; </script> @@ -55,27 +52,28 @@ const scrollToBottom = () => { <style scoped> .terminal-container { width: 100%; - height: 400px; + height: 400px; /* Constrain height, but allow dynamic resizing */ + overflow-y: auto; /* Allow scrolling for overflowing content */ background-color: #1e1e1e; color: #ffffff; - font-family: "Courier New", Courier, monospace; - font-size: 14px; + font-size: 11px; border: 1px solid #333; border-radius: 4px; - overflow: hidden; display: flex; flex-direction: column; } .terminal-output { - flex: 1; + flex: 1; /* Let it grow or shrink with the container */ overflow-y: auto; padding: 10px; } .terminal-line { white-space: pre-wrap; + text-align: left; } + </style> diff --git a/client/src/components/GCode3DImageViewer.vue b/client/src/components/GCode3DImageViewer.vue index c40d13e3..b5af0c0d 100644 --- a/client/src/components/GCode3DImageViewer.vue +++ b/client/src/components/GCode3DImageViewer.vue @@ -24,24 +24,16 @@ const modal = document.getElementById('gcodeImageModal'); const canvas = ref<HTMLCanvasElement | null>(null); let preview: GCodePreview.WebGLPreview | null = null; -const renderTravel = ref(true); // Ref to control renderTravel dynamically - const processGCodeCommand = (gcode: string) => { try { // Dynamically process G-code and determine if renderTravel should be enabled - if (gcode.includes('G0') || gcode.includes('G1')) { - renderTravel.value = true; // Enable renderTravel for movement commands - } else { - renderTravel.value = false; // Disable for other commands - } if (preview) { - preview.renderTravel = renderTravel.value; + preview.renderTravel = true; } } catch (error) { console.error('Error processing GCode command. Reverting to default renderTravel:', error); // Ensure proper fallback - renderTravel.value = true; if (preview) { preview.renderTravel = true; } @@ -56,22 +48,22 @@ onMounted(async () => { if (canvas.value) { try { - preview = GCodePreview.init({ + preview = (GCodePreview.init({ canvas: canvas.value, extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--bs-primary-color').trim() || '#7561A9', backgroundColor: 'black', - buildVolume: { x: 250, y: 210, z: 220 }, + buildVolume: {x: 250, y: 210, z: 220}, travelColor: 'limegreen', lineWidth: 1, - lineHeight: 1, - extrusionWidth: 0.4, + lineHeight: 1, + extrusionWidth: 1, renderExtrusion: true, - renderTravel: renderTravel.value, - renderTubes: true, - }); + renderTravel: false, + renderTubes: true, + })); - preview.camera.position.set(-200, 232, 200); - preview.camera.lookAt(0, 0, 0); + preview?.camera.position.set(-200, 232, 200); + preview?.camera.lookAt(0, 0, 0); const fileValue = await file(); if (fileValue) { @@ -79,21 +71,28 @@ onMounted(async () => { try { // Process and render G-code with timeout - const commands = gcode.split('\n'); + let commands = gcode.split('\n'); + commands = commands.filter(command => !command.trim().startsWith(';')); for (const command of commands) { - setTimeout(() => { - processGCodeCommand(command); // Update renderTravel dynamically - preview?.processGCode(command); - }, 2000); + await new Promise(resolve => setTimeout(resolve, 150)); + processGCodeCommand(command); // Update renderTravel dynamically + preview?.processGCode(command); + // try { + // + // } catch (error: unknown) { + // if (error instanceof TypeError) { + // continue; + // } + // throw error; + // } } } catch (error) { console.error('Failed to process GCode:', error); // Reset to original behavior on failure - renderTravel.value = true; if (preview) { preview.renderTravel = true; - preview.processGCode(gcode); // Fallback to processing entire file + preview?.processGCode(gcode); // Fallback to processing entire file } } } @@ -101,7 +100,6 @@ onMounted(async () => { console.error('Error initializing GCodePreview:', error); // Fallback logic if initialization fails - renderTravel.value = true; if (preview) { preview.renderTravel = true; } @@ -111,18 +109,16 @@ onMounted(async () => { modal.addEventListener('hidden.bs.modal', () => { // Clean up when the modal is hidden if (preview) { - preview.processGCode(''); preview.clear(); - preview = null; + setPreview(null); } }); }); onUnmounted(() => { if (preview) { - preview.processGCode(''); preview.clear(); - preview = null; + setPreview(null); } }); diff --git a/client/src/components/GCode3DLiveViewer.vue b/client/src/components/GCode3DLiveViewer.vue index 981184a4..21a8e807 100644 --- a/client/src/components/GCode3DLiveViewer.vue +++ b/client/src/components/GCode3DLiveViewer.vue @@ -1,127 +1,89 @@ <script setup lang="ts"> -import { nextTick, onMounted, onActivated, onDeactivated, ref, toRef, watchEffect, onUnmounted } from 'vue'; -import { useGetFile, type Job } from '@/model/jobs'; +import {ref, onMounted, onUnmounted, watch, type Ref} from 'vue'; +import {type Device} from '@/model/ports'; import * as GCodePreview from 'gcode-preview'; -const { getFile } = useGetFile(); - const props = defineProps({ - job: Object as () => Job -}) + device: Object as () => Device, +}); -const job = toRef(props, 'job'); +const canvas = ref<HTMLCanvasElement | null>(null); -const modal = document.getElementById('gcodeLiveViewModal'); +const gcodeBuffer: Ref<Array<string>> = ref([] as Array<string>); -// Create a ref for the canvas -const canvas = ref<HTMLCanvasElement | null>(null); let preview: GCodePreview.WebGLPreview | null = null; -let layers: string[][] = []; +const originalConsoleWarn = console.warn; +const bufferSizeToRender = 50; onMounted(async () => { - if (!modal) { - console.error('Modal element is not available'); - return; + if (canvas.value) { + try { + console.warn = () => {}; // Suppress warnings from gcode-preview + preview = (GCodePreview.init({ + canvas: canvas.value, + extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim() || '#7561A9', + backgroundColor: 'black', + buildVolume: {x: 250, y: 210, z: 220}, + travelColor: 'limegreen', + lineWidth: 1, + lineHeight: 1, + extrusionWidth: 1, + renderExtrusion: true, + renderTravel: false, + renderTubes: false, + })); + + preview.camera.position.set(-200, 232, 200); + preview.camera.lookAt(0, 0, 0); + console.warn = originalConsoleWarn; + + } catch (error) { + console.error('Error initializing GCodePreview:', error); } - - const gcodeFile = await getFile(props.job!); - if (!gcodeFile) { - console.error('Failed to get the file'); - return; + } + if(preview?.renderTubes) { + watch(() => props.device!.gcodeLines?.length, () => { + gcodeBuffer.value.push(props.device!.gcodeLines![props.device!.gcodeLines!.length - 1]); + if (gcodeBuffer.value?.length > bufferSizeToRender) { + renderAllNoWarn(gcodeBuffer.value); + gcodeBuffer.value = []; + } + }); + if (props.device!.gcodeLines) { + renderAllNoWarn(props.device!.gcodeLines!); } - - const fileString = await fileToString(gcodeFile); - const lines = fileString.split('\n'); - layers = lines.reduce((layers, line) => { - if (line.startsWith(";LAYER_CHANGE")) { - layers.push([]); - } - if (layers.length > 0) { - layers[layers.length - 1].push(line as never); - } - return layers; - }, [[]]); - - watchEffect(() => { - if (job.value?.current_layer_height && preview) { - try { - // process gcode of layers up to current_layer_height - const currentLayerIndex = layers.findIndex(layer => layer.includes(`;Z:${job.value!.current_layer_height}`)); - if (currentLayerIndex !== -1) { - preview.clear(); - preview.processGCode(layers.slice(0, currentLayerIndex + 1).flat()); - } - } catch (error) { - console.error('Failed to process GCode:', error); - } - } + } else { + watch(() => props.device!.gcodeLines?.length, () => { + gcodeBuffer.value.push(props.device!.gcodeLines![props.device!.gcodeLines!.length - 1]); + if (gcodeBuffer.value?.length > bufferSizeToRender) { + renderAll(gcodeBuffer.value); + gcodeBuffer.value = []; + } }); + if(props.device!.gcodeLines) { + renderAll(props.device!.gcodeLines!); + } + } +}); - modal.addEventListener('shown.bs.modal', async () => { - // Initialize the GCodePreview and show the GCode when the modal is shown - if (canvas.value) { - preview = GCodePreview.init({ - canvas: canvas.value, - extrusionColor: getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim() || '#7561A9', - backgroundColor: 'black', - buildVolume: { x: 250, y: 210, z: 220 }, - lineWidth: 1, - lineHeight: 1, - extrusionWidth: 1, - renderExtrusion: true, - renderTubes: true, - }); - - preview.camera.position.set(-200, 232, 200); - preview.camera.lookAt(0, 0, 0); - if (job.value?.current_layer_height && preview) { - try { - // process gcode of layers up to current_layer_height - const currentLayerIndex = layers.findIndex(layer => layer.includes(`;Z:${job.value!.current_layer_height}`)); - if (currentLayerIndex !== -1) { - preview.clear(); - const gcode = layers.slice(0, currentLayerIndex + 1).flat(); - preview.processGCode(gcode); - } - } catch (error) { - console.error('Failed to process GCode:', error); - } - } - } - }); +function renderAll(gcode: string[]){ + preview?.processGCode(gcode); +} - modal.addEventListener('hidden.bs.modal', () => { - // Clean up when the modal is hidden - preview?.processGCode(''); - preview?.clear(); - preview = null; - }); -}); +function renderAllNoWarn(gcode: string[]){ + console.warn = () => {}; // Suppress warnings from gcode-preview + preview?.processGCode(gcode); + console.warn = originalConsoleWarn; +} onUnmounted(() => { - preview?.processGCode(''); - preview?.clear(); + if (preview) { + preview.clear(); preview = null; + } }); -const fileToString = (file: File | undefined) => { - if (!file) { - console.error('File is not available'); - return ''; - } - - const reader = new FileReader(); - reader.readAsText(file); - return new Promise<string>((resolve, reject) => { - reader.onload = () => { - resolve(reader.result as string); - }; - reader.onerror = (error) => { - reject(error); - }; - }); -}; </script> <template> @@ -131,7 +93,7 @@ const fileToString = (file: File | undefined) => { <style scoped> canvas { width: 100%; - height: 100%; + height: 400px; display: block; } </style> \ No newline at end of file diff --git a/client/src/components/GCodeThumbnail.vue b/client/src/components/GCodeThumbnail.vue index 844c959d..9860241f 100644 --- a/client/src/components/GCodeThumbnail.vue +++ b/client/src/components/GCodeThumbnail.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import { nextTick, onMounted, onActivated, onDeactivated, ref, toRef, onUnmounted } from 'vue'; +import { onMounted, ref, onUnmounted } from 'vue'; import { useGetFile, type Job } from '@/model/jobs'; import * as GCodePreview from 'gcode-preview'; @@ -44,7 +44,6 @@ onMounted(async () => { try { // Extract the thumbnail from the metadata const { metadata } = preview.parser.parseGCode(gcode); - console.debug('GCode metadata:', metadata); let thumbnailData = null; if (metadata.thumbnails) { if(metadata.thumbnails['640x480']) thumbnailData = metadata.thumbnails['640x480']; @@ -94,7 +93,7 @@ const fileToString = (file: File | undefined) => { </script> <template> - <canvas v-show="false" style="display: hidden" ref="canvas"></canvas> + <canvas v-show="false" ref="canvas"></canvas> <img v-if="thumbnailSrc" :src="thumbnailSrc" alt="GCode Thumbnail" /> <div v-else>This file doesn't have a thumbnail attached, you can check the viewer instead!</div> </template> diff --git a/client/src/model/jobs.ts b/client/src/model/jobs.ts index 02195494..908b49ec 100644 --- a/client/src/model/jobs.ts +++ b/client/src/model/jobs.ts @@ -1,7 +1,7 @@ // ts file to retrieve job information import { api } from './ports' import { toast } from './toast' -import { type Device } from '@/model/ports' +import { printers, type Device } from '@/model/ports' import { socket, API_ROOT } from './myFetch' import { saveAs } from 'file-saver' import { ref } from 'vue' @@ -80,7 +80,7 @@ export async function jobTime(job: Job, printers: any) { if (!job.job_server) { job.job_server = [0, '00:00:00', '00:00:00', '00:00:00'] - for (const printer of printers.value) { + for (const printer of printers) { // let time_server = Array(4) // this saves all of the data from the backend.Only changed if there is a pause involved. // Here 'printer' represents each Device object in the 'printers' array if (printer.queue && printer.queue.length != 0 && printer.queue[0].status != 'inqueue') { @@ -98,10 +98,10 @@ export async function jobTime(job: Job, printers: any) { } const printerid = job.printerid - const printer = printers.value.find((printer: { id: number }) => printer.id === printerid) + const printer = printers.find((printer: Device) => printer.id === printerid) const updateJobTime = () => { - if (printer.status !== 'printing') { + if (printer?.status !== 'printing') { clearInterval(job.timer) delete job.timer return @@ -117,9 +117,9 @@ export async function jobTime(job: Job, printers: any) { job.job_client!.eta = eta if ( - printer.status === 'printing' || - printer.status === 'colorchange' || - printer.status === 'paused' + printer?.status === 'printing' || + printer?.status === 'colorchange' || + printer?.status === 'paused' ) { const now = Date.now() const elapsedTime = now - new Date(job.job_server![2]).getTime() @@ -153,32 +153,35 @@ export async function jobTime(job: Job, printers: any) { } } -export function setupTimeSocket(printers: any) { +export function setupTimeSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('set_time', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) - - if (!job.job_client || !job.job_server) { - job.job_client = { - total_time: 0, - eta: 0, - elapsed_time: 0, - extra_time: 0, - remaining_time: NaN + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) + if(job) { + if (!job.job_client || !job.job_server) { + job.job_client = { + total_time: 0, + eta: 0, + elapsed_time: 0, + extra_time: 0, + remaining_time: NaN + } + job.job_server = [0, '00:00:00', '00:00:00', '00:00:00'] + } + + if (typeof data.new_time === 'number') { + job.job_server[data.index] = data.new_time + } else { + job.job_server[data.index] = Date.parse(data.new_time) } - job.job_server = [0, '00:00:00', '00:00:00', '00:00:00'] - } - if (typeof data.new_time === 'number') { - job.job_server[data.index] = data.new_time + jobTime(job, printers) } else { - job.job_server[data.index] = Date.parse(data.new_time) + console.error('job is undefined') } - - jobTime(job, printers) } else { console.error('printers or printers.value is undefined') } @@ -187,8 +190,7 @@ export function setupTimeSocket(printers: any) { async function refetchtime(printerid: number, jobid: number) { try { - const response = await api('refetchtimedata', { printerid, jobid }) - return response + return await api('refetchtimedata', { printerid, jobid }) } catch (error) { console.error(error) toast.error('An error occurred while updating the job status') @@ -236,8 +238,7 @@ export function useGetJobs() { }, async getFavoriteJobs() { try { - const response = await api('getfavoritejobs') - return response + return await api('getfavoritejobs') } catch (error) { console.error(error) toast.error('An error occurred while retrieving the jobs') @@ -250,8 +251,7 @@ export function useUpdateJobStatus() { return { async updateJobStatus(jobid: number, status: string) { try { - const response = await api('assigntoerror', { jobid, status }) - return response + return await api('assigntoerror', { jobid, status }) } catch (error) { console.error(error) toast.error('An error occurred while updating the job status') @@ -408,6 +408,8 @@ export function useReleaseJob() { toast.error(response.message) } else if (response.success === true) { toast.success(response.message) + const printer = printers!.value!.find((p: Device) => p.id === printerid) + if(printer) printer.gcodeLines = [] } else { console.error('Unexpected response:', response) toast.error('Failed to release job. Unexpected response.') @@ -427,8 +429,7 @@ export function useGetGcode() { return { async getgcode(job: Job) { try { - const response = await api('getgcode', job) - return response + return await api('getgcode', job) } catch (error) { console.error(error) toast.error('An error occurred while retrieving the gcode') @@ -459,8 +460,7 @@ export function useGetFile() { try { const jobid = job.id const response = await api(`getfile?jobid=${jobid}`) - const file = new File([response.file], response.file_name, { type: 'text/plain' }) - return file + return new File([response.file], response.file_name, { type: 'text/plain' }) } catch (error) { console.error(error) toast.error('An error occurred while retrieving the file') @@ -503,7 +503,7 @@ export function useFavoriteJob() { try { const response = await api(`favoritejob`, { jobid, favorite }) if (response.success) { - job.favorite = favorite ? true : false + job.favorite = favorite } return response } catch (error) { @@ -538,8 +538,7 @@ export function useDeleteJob() { async deleteJob(job: Job) { const jobid = job?.id try { - const response = await api(`deletejob`, { jobid }) - return response + return await api(`deletejob`, { jobid }) } catch (error) { console.error(error) toast.error('An error occurred while deleting the job') diff --git a/client/src/model/ports.ts b/client/src/model/ports.ts index 1ea8e3c0..d9f14220 100644 --- a/client/src/model/ports.ts +++ b/client/src/model/ports.ts @@ -25,7 +25,8 @@ export interface Device { bed_temp?: number colorChangeBuffer?: number colorbuff?: number, - consoles?: [string[], string[], string[], string[], string[]] // array of debug, info, and error console messages + consoles?: [string[], string[], string[], string[], string[]] // array of debug, info, warning, error and critical console messages + gcodeLines?: string[] // array of gocde lines sent to the printer } export const printers = ref<Device[]>([]) @@ -34,9 +35,7 @@ export function useGetPorts() { return { async ports() { try { - const response = await api('getports') - console.log('response:', response) - return response + return await api('getports') } catch (error) { console.error(error) } @@ -163,21 +162,7 @@ export function useDeletePrinter() { return { async deletePrinter(fabricator_id: number | undefined) { try { - const response = await api('deletefabricator', { fabricator_id }) - // if (response) { - // if (response.success == false) { - // toast.error(response.message) - // } else if (response.success === true) { - // toast.success(response.message) - // } else { - // console.error('Unexpected response:', response) - // toast.error('Failed to delete printer. Unexpected response.') - // } - // } else { - // console.error('Response is undefined or null') - // toast.error('Failed to delete printer. Unexpected response') - // } - return response + return await api('deletefabricator', { fabricator_id }) } catch (error) { console.error(error) } @@ -333,8 +318,7 @@ export function useMovePrinterList() { try { // make new array of printer id's in the order they are in the printers array const printersIds = printers.map((printer) => printer.id) - const response = await api('moveprinterlist', { printersIds }) - return response + return await api('moveprinterlist', { printersIds }) } catch (error) { console.error(error) } diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index cc954099..02aa9133 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -1,9 +1,8 @@ import { socket } from './myFetch' -import type { Device } from './ports' -import { jobTime } from './jobs' -import { ref } from 'vue' +import {type Device} from './ports' +import {type Job} from "@/model/jobs"; -export function setupSockets(printers: any) { +export function setupSockets(printers: Array<Device>) { setupTempSocket(printers) setupStatusSocket(printers) setupQueueSocket(printers) @@ -21,12 +20,13 @@ export function setupSockets(printers: any) { setupColorChangeBuffer(printers) setupMaxLayerHeightSocket(printers) setupCurrentLayerHeightSocket(printers) + setupConsoleSocket(printers) } // *** PORTS *** -export function setupTempSocket(printers: any) { +export function setupTempSocket(printers: Array<Device>) { socket.value.on('temp_update', (data: any) => { - const printer = printers.value.find((p: Device) => p.id === data.printerid) + const printer = printers.find((p: Device) => p.id === data.printerid) if (printer) { printer.extruder_temp = data.extruder_temp printer.bed_temp = data.bed_temp @@ -35,118 +35,118 @@ export function setupTempSocket(printers: any) { } // function to set up the socket for status updates -export function setupStatusSocket(printers: any) { +export function setupStatusSocket(printers: Array<Device>) { socket.value.on('status_update', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.fabricator_id) + const printer = printers.find((p: Device) => p.id === data.fabricator_id) if (printer) { printer.status = data.status } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupJobStatusSocket(printers: any) { +export function setupJobStatusSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('job_status_update', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.status = data.status } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupQueueSocket(printers: any) { +export function setupQueueSocket(printers: Array<Device>) { socket.value.on('queue_update', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printerid) + const printer = printers.find((p: Device) => p.id === data.printerid) if (printer) { printer.queue = data.queue } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupErrorSocket(printers: any) { +export function setupErrorSocket(printers: Array<Device>) { socket.value.on('error_update', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printerid) + const printer = printers.find((p: Device) => p.id === data.printerid) if (printer) { printer.error = data.error } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupCanPauseSocket(printers: any) { +export function setupCanPauseSocket(printers: Array<Device>) { socket.value.on('can_pause', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printerid) + const printer = printers.find((p: Device) => p.id === data.printerid) if (printer) { printer.canPause = data.canPause } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } // *** JOBS *** -export function setupPauseFeedbackSocket(printers: any) { +export function setupPauseFeedbackSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('file_pause_update', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.file_pause = data.file_pause } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupTimeStartedSocket(printers: any) { +export function setupTimeStartedSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('set_time_started', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.time_started = data.time_started } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } // function to constantly update progress of job -export function setupProgressSocket(printers: any) { +export function setupProgressSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('progress_update', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.progress = data.progress @@ -157,131 +157,162 @@ export function setupProgressSocket(printers: any) { } } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupReleaseSocket(printers: any) { +export function setupReleaseSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('release_job', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.released = data.released } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupPortRepairSocket(printers: any) { +export function setupPortRepairSocket(printers: Array<Device>) { // Always set up the socket connection and event listener socket.value.on('port_repair', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printer_id) - console.log('printer device: ' + printer.device, ' data device: ' + data.device) - printer.device = data.device + const printer = printers.find((p: Device) => p.id === data.printer_id) + if (printer) { + console.log('printer device: ' + printer.device, ' data device: ' + data.device) + printer.device = data.device + } else{ + console.error('printer is undefined') + } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupGCodeViewerSocket(printers: any) { +export function setupGCodeViewerSocket(printers: Array<Device>) { socket.value.on('gcode_viewer', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.gcode_num = data.gcode_num } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -import * as GCodePreview from 'gcode-preview'; -export const preview = ref<GCodePreview.WebGLPreview>(); -export function setupGCodeLineSocket(printers: any) { - socket.value.on('gcode_line', (data: any) => { - if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) - if (job) { - //TODO: add gcode_line to wherever it needs to go - preview?.value?.processGCode(data.gcode_line) - } +export function setupGCodeLineSocket(printers: Array<Device>) { + socket.value.on('gcode_line', (data: any) => { + if (printers) { + const printer: Device | undefined = printers.find((p: Device) => p.id === data.printerid) + if (printer) { + if (!printer.gcodeLines) { + printer.gcodeLines = [] + } + printer.gcodeLines!.push(data.line) + } else { + console.error('printer is undefined') + } + } else { + console.error('printers is undefined') + } + }) +} + +const arrayLevels = ['critical', 'error', 'warning', 'info', 'debug'] +const colors = ['\x1b[95m', '\x1b[91m', '\x1b[93m', '\x1b[0m', '\x1b[94m'] + +export function setupConsoleSocket(printers: Array<Device>) { + socket.value.on('console_update', (data: any) => { + if (printers) { + const printer = printers.find((p: Device) => p.id === data.printerid) + if (printer && printer.consoles) { + if (data.level) { + const maxLevelToAdd = arrayLevels.indexOf(data.level) + if (maxLevelToAdd === -1) { + console.error('Invalid console level:', data.level) + } else { + for (let i = maxLevelToAdd; i < printer.consoles.length; i++) { + printer.consoles[i].push(colors[maxLevelToAdd] + data.message + '\x1b[0m') + } + } } else { - console.error('printers or printers.value is undefined') + console.error('data.level is undefined') } - }) + } + } else { + console.error('printers is undefined') + } + }) } -export function setupExtrusionSocket(printers: any) { +export function setupExtrusionSocket(printers: Array<Device>) { socket.value.on('extruded_update', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.extruded = data.extruded } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupColorChangeBuffer(printers: any) { +export function setupColorChangeBuffer(printers: Array<Device>) { socket.value.on('color_buff', (data: any) => { if (printers) { - const printer = printers.value.find((p: Device) => p.id === data.printerid) + const printer = printers.find((p: Device) => p.id === data.printerid) if (printer) { printer.colorbuff = data.colorbuff } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupMaxLayerHeightSocket(printers: any) { +export function setupMaxLayerHeightSocket(printers: Array<Device>) { socket.value.on('max_layer_height', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.max_layer_height = data.max_layer_height } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } -export function setupCurrentLayerHeightSocket(printers: any) { +export function setupCurrentLayerHeightSocket(printers: Array<Device>) { socket.value.on('current_layer_height', (data: any) => { if (printers) { - const job = printers.value - .flatMap((printer: { queue: any }) => printer.queue) - .find((job: { id: any }) => job?.id === data.job_id) + const job = printers + .flatMap((printer: Device) => printer.queue) + .find((job: Job | undefined) => job?.id === data.job_id) if (job) { job.current_layer_height = data.current_layer_height } } else { - console.error('printers or printers.value is undefined') + console.error('printers is undefined') } }) } \ No newline at end of file diff --git a/client/src/views/MainView.vue b/client/src/views/MainView.vue index f3a491a8..10f6b14d 100644 --- a/client/src/views/MainView.vue +++ b/client/src/views/MainView.vue @@ -22,6 +22,9 @@ const { movePrinterList } = useMovePrinterList() const router = useRouter() +const consoles = [{name: "Critical", color: "#ff00ff"}, {name: "Error", color: "#ff0000"}, {name: "Warning", color: "#ffff00"}, {name: "Info", color: "#ffffff"}, {name: "Debug", color: "#00ffff"}] +const tdWidth = ["64px", "130px", "142px", "110px", "110px", "314px", "315px", "75px", "58px"] +const displayLevel = ref(1) const selectedIssue = ref<Issue>() const selectedJob = ref<Job>() const jobComments = ref('') @@ -116,7 +119,7 @@ const doAssignIssue = async () => { } const openModal = async (job: Job, printerName: string, num: number, printer: Device) => { - await jobTime(job, printers) + await jobTime(job, printers.value) currentJob.value = job currentJob.value.printer = printerName currentPrinter.value = printer @@ -162,11 +165,9 @@ const startPrint = async (printerid: number, jobid: number) => { const openPrinterInfo = async (printer: Device) => { if (printer.queue && printer.queue[0]) { - await jobTime(printer.queue[0], printers) + await jobTime(printer.queue[0], printers.value) } - printer.isInfoExpanded = !printer.isInfoExpanded; - } const releasePrinter = async (jobToFind: Job | undefined, key: number, printerIdToPrintTo: number) => { @@ -193,7 +194,6 @@ const handleDragEnd = async () => { </script> <template> - <div class="modal fade" id="issueModal" tabindex="-1" aria-labelledby="assignIssueLabel" aria-hidden="true" data-bs-backdrop="static"> <div class="modal-dialog modal-dialog-centered"> @@ -248,7 +248,7 @@ const handleDragEnd = async () => { </div> <div class="modal-body"> <div class="row"> - <GCode3DLiveViewer v-if="isGcodeLiveViewVisible" :job="currentJob" /> + <GCode3DImageViewer v-if="isGcodeLiveViewVisible" :job="currentJob" /> </div> </div> </div> @@ -282,46 +282,48 @@ const handleDragEnd = async () => { <div class="container"> <table ref="table"> - <tr> - <!-- NEED TO FIX THIS FOR EVERY DISPLAYS --> - <th style="width: 64px">ID</th> - <th style="width: 130px">Printer Name</th> - <th style="width: 142px">Printer Status</th> - <th style="width: 110px">Job Name</th> - <th style="width: 110px">File</th> - <th style="width: 314px">Printer Options</th> - <th style="width: 315px">Progress</th> - <th style="width: 75px;">Actions</th> - <th style="width: 58px">Move</th> - </tr> + <thead> + <tr> + <!-- NEED TO FIX THIS FOR EVERY DISPLAYS --> + <th :style="{width: tdWidth[0]}">ID</th> + <th :style="{width: tdWidth[1]}">Printer Name</th> + <th :style="{width: tdWidth[2]}">Printer Status</th> + <th :style="{width: tdWidth[3]}">Job Name</th> + <th :style="{width: tdWidth[4]}">File</th> + <th :style="{width: tdWidth[5]}">Printer Options</th> + <th :style="{width: tdWidth[6]}">Progress</th> + <th :style="{width: tdWidth[7]}">Actions</th> + <th :style="{width: tdWidth[8]}">Move</th> + </tr> + </thead> <draggable v-model="printers" tag="tbody" :animation="300" item-key="printer.id" handle=".handle" - dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" - @end="restoreExpandedState"> + dragClass="hidden-ghost" :onEnd="handleDragEnd" v-if="printers.length > 0" @start="collapseAll" + @end="restoreExpandedState" style="width: inherit"> <template #item="{ element: printer }"> <div v-if="printer.isInfoExpanded" class="expanded-info"> - <tr :id="printer.id" style="vertical-align: middle"> - <td - v-if="(printer.status == 'printing' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + <tr :id="printer.id" style="vertical-align: middle; width: inherit"> + <td v-if="(printer.status == 'printing' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))" + :style="{width: tdWidth[0]}"> {{ printer.queue?.[0]?.td_id }} </td> - <td v-else><i>idle</i></td> + <td v-else :style="{width: tdWidth[0]}"><i>idle</i></td> - <td class="truncate" :title="printer.name"> + <td class="truncate" :title="printer.name" :style="{width: tdWidth[1]}"> <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" - style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> + style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> {{ printer.name }} </div> </button> </td> - <td> + <td :style="{width: tdWidth[2]}"> <div class="d-flex align-items-center justify-content-center"> <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> <p v-if="printer.status === 'ready' && printer.queue?.[0]?.released === 0" style="color: #ad6060" - class="mb-0 me-2"> + class="mb-0 me-2"> Waiting release </p> <p v-else class="mb-0 me-2"> @@ -330,64 +332,64 @@ const handleDragEnd = async () => { </div> </td> - <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + <td class="truncate" :title="printer.queue?.[0]?.name" :style="{width: tdWidth[3]}" + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.name }} </td> - <td v-else></td> + <td v-else :style="{width: tdWidth[3]}"></td> - <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + <td class="truncate" :title="printer.queue?.[0]?.file_name_original" :style="{width: tdWidth[4]}" + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.file_name_original }} </td> - <td v-else></td> + <td v-else :style="{width: tdWidth[4]}"></td> - <td> + <td :style="{width: tdWidth[5]}"> <div class="buttons"> <button class="btn btn-primary" - v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" - @click="setPrinterStatus(printer, 'ready')"> + v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" + @click="setPrinterStatus(printer, 'ready')"> Set to Ready </button> <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" - @click="setPrinterStatus(printer, 'offline')"> + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" + @click="setPrinterStatus(printer, 'offline')"> Turn Offline </button> <button class="btn btn-secondary" - v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" - @click="startPrint(printer.id, printer.queue[0].id)"> + v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" + @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'paused')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + @click="setPrinterStatus(printer, 'paused')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> Pause </button> <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'colorchange')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + @click="setPrinterStatus(printer, 'colorchange')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> Color Change </button> <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" - v-if="printer.status == 'paused'"> + v-if="printer.status == 'paused'"> Unpause </button> <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" - v-if="(printer.status == 'printing' || printer.status == 'colorchange')"> + v-if="(printer.status == 'printing' || printer.status == 'colorchange')"> Stop </button> <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" - class="mt-2"> + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" + class="mt-2"> Ready for color change. </div> <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> @@ -397,47 +399,51 @@ const handleDragEnd = async () => { </div> </td> - <td style="width: 250px;"> + <td :style="{width: tdWidth[6]}"> <div - v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0].released == 1"> + v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0].released == 1"> <!-- <div v-for="job in printer.queue" :key="job.id"> --> <!-- Display the elapsed time --> <div class="progress" style="position: relative;"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> </div> <!-- job progress set to 2 decimal places --> - <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{printer.queue?.[0]?.progress ? `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> + <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);"> + {{ printer.queue?.[0]?.progress ? `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> </div> <!-- </div> --> </div> <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')"> + v-else-if="printer.queue?.[0] && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')"> <div class="buttons-progress"> <div type="button" class="btn btn-secondary" - @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> + @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> Clear </div> <div class="btn-group"> <div class="btn btn-primary no-wrap" @click="releasePrinter(printer.queue?.[0], 2, printer.id)"> Clear/Rerun </div> - <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"> </div> + <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" + aria-expanded="false"></div> <div class="dropdown-menu"> <div class="dropdown-item" v-for="printer in printers" :key="printer.id" - @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> + @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> {{ printer.name }} </div> </div> </div> - <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" @click=setJob(printer.queue[0])>Fail</div> + <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" + @click=setJob(printer.queue[0])>Fail + </div> </div> </div> <div - v-else-if="printer.queue?.[0] && printer.queue?.[0]?.status == 'printing' && printer.status == 'complete'"> + v-else-if="printer.queue?.[0] && printer.queue?.[0]?.status == 'printing' && printer.status == 'complete'"> <div style="display: flex; justify-content: center; align-items: center;"> <button class="btn btn-primary w-100" type="button" disabled> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> @@ -452,41 +458,41 @@ const handleDragEnd = async () => { </td> - <td style="width: 1%; white-space: nowrap;"> + <td style="white-space: nowrap;" :style="{width: tdWidth[7]}"> <div style="display: flex; justify-content: space-between; align-items: center;"> <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" - @click="openPrinterInfo(printer)"> + @click="openPrinterInfo(printer)"> </i> <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" - style="background: none; border: none;"> + style="background: none; border: none;"> <i class="fa-solid fa-bars" - :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> + :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> </button> <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> <li> <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> + data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> <i class="fa-solid fa-image"></i> <span class="ms-2">GCode Image</span> </a> </li> <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0]?.extruded)"> <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> + data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> <i class="fas fa-code"></i> <span class="ms-2">GCode Live</span> </a> </li> <li v-if="printer.queue[0]"> <a class="dropdown-item d-flex align-items-center" - @click="getFileDownload(printer.queue[0]?.id)" - :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> + @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> <i class="fas fa-download"></i> <span class="ms-2">Download</span> </a> @@ -498,10 +504,10 @@ const handleDragEnd = async () => { </td> <td class="text-center handle" - :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" - :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> + :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" + :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' , width: tdWidth[8]}"> <i class="fas fa-grip-vertical" - :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> + :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> </td> </tr> <tr> @@ -532,13 +538,13 @@ const handleDragEnd = async () => { </tr> <tr> <td class="borderless-top"> - <span - v-if="printer.queue[0] && printer.queue[0]?.current_layer_height != null && printer.queue[0]?.max_layer_height != null && printer.queue[0]?.max_layer_height !== 0"> - {{ printer.queue[0]?.current_layer_height + '/' + printer.queue[0]?.max_layer_height }} - </span> + <span + v-if="printer.queue[0] && printer.queue[0]?.current_layer_height != null && printer.queue[0]?.max_layer_height != null && printer.queue[0]?.max_layer_height !== 0"> + {{ printer.queue[0]?.current_layer_height + '/' + printer.queue[0]?.max_layer_height }} + </span> <span v-else> - <i>idle</i> - </span> + <i>idle</i> + </span> </td> <td class="borderless-top"> <span v-html="printer.queue[0]?.filament ? printer.queue[0]?.filament : '<i>idle</i>'"></span> @@ -550,39 +556,51 @@ const handleDragEnd = async () => { <span v-html="printer?.bed_temp ? printer.bed_temp + '°C' : '<i>idle</i>'"></span> </td> <td class="borderless-top"> - <span - v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> + <span + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.elapsed_time)"></span> </td> <td class="borderless-top"> - <span v-if="printer.queue[0]?.job_client?.remaining_time !== 0" - v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> + <span v-if="printer.queue[0]?.job_client?.remaining_time !== 0" + v-html="printer?.status === 'colorchange' || printer?.status === 'ready' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.remaining_time)"></span> <span v-else v-html="'00:00:00'"></span> </td> <td class="borderless-top"> - <span - v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.total_time)"></span> + <span + v-html="printer?.status === 'colorchange' ? 'Waiting...' : formatTime(printer.queue[0]?.job_client?.total_time)"></span> </td> <td class="borderless-top" colspan="2"> - <span - v-html="printer?.status === 'colorchange' ? 'Waiting...' : (printer.queue[0]?.extruded ? formatETA(printer.queue[0]?.job_client?.eta) : '<i>Waiting...</i>')"></span> + <span + v-html="printer?.status === 'colorchange' ? 'Waiting...' : (printer.queue[0]?.extruded ? formatETA(printer.queue[0]?.job_client?.eta) : '<i>Waiting...</i>')"></span> </td> </tr> <tr> - <td colspan="9"> - <ConsoleTerminal :lines="printer.consoles![4] || []"/> + <td colspan="4" style="vertical-align: top"> + <ConsoleTerminal :lines="printer.consoles![displayLevel]"/> + </td> + <td colspan="1" style="vertical-align: middle"> + <div class="btn-group-vertical"> + <button class="btn" v-for="(console, index) in consoles" :key="index" + :style="{backgroundColor: console.color}" @click="displayLevel = index"> + {{ console.name }} + </button> + </div> + </td> + <td colspan="4"> + <GCode3DLiveViewer v-if="printer.isInfoExpanded" :device="printer"/> </td> + </tr> </div> <tr v-else :id="printer.id"> <td - v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.td_id }} </td> <td v-else><i>idle</i></td> <td class="truncate" :title="printer.name"> <button type="button" class="btn btn-link" @click="sendToQueueView(printer)" - style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> + style="padding: 0; border: none; display: inline-block; width: 100%; text-align: center;"> <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> {{ printer.name }} </div> @@ -594,8 +612,9 @@ const handleDragEnd = async () => { <!-- <p class="mb-0 me-2" v-if="printer.status === 'colorchange'" style="color: red"> Change filament </p> --> - <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" style="color: #ad6060" - class="mb-0 me-2"> + <p v-if="(printer.status === 'ready' || printer.status === 'idle') && printer.queue?.[0]?.released === 0" + style="color: #ad6060" + class="mb-0 me-2"> Waiting release </p> <p v-else class="mb-0 me-2"> @@ -605,13 +624,13 @@ const handleDragEnd = async () => { </td> <td class="truncate" :title="printer.queue?.[0]?.name" - v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange' || (printer.status == 'offline' || printer.status == 'idle' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.name }} </td> <td v-else></td> <td class="truncate" :title="printer.queue?.[0]?.file_name_original" - v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> + v-if="(printer.queue && printer.queue.length > 0 && (printer.status === 'ready' || printer.status == 'printing' || printer.status == 'complete' || printer.status == 'paused' || printer.status == 'colorchange') || (printer.status == 'offline' && (printer.queue?.[0]?.status == 'complete' || printer.queue?.[0]?.status == 'cancelled')))"> {{ printer.queue?.[0]?.file_name_original }} </td> <td v-else></td> @@ -620,47 +639,47 @@ const handleDragEnd = async () => { <div class="buttons"> <button class="btn btn-primary" - v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" - @click="setPrinterStatus(printer, 'ready')"> + v-if="printer.status == 'configuring' || printer.status == 'offline' || printer.status == 'error'" + @click="setPrinterStatus(printer, 'ready')"> Set to Ready </button> <button class="btn btn-danger" - v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" - @click="setPrinterStatus(printer, 'offline')"> + v-if="printer.status == 'configuring' || printer.status == 'ready' || printer.status == 'error' || printer.status == 'complete' || printer.status == 'idle'" + @click="setPrinterStatus(printer, 'offline')"> Turn Offline </button> <button class="btn btn-secondary" v-if="printer.status == 'ready' && printer.queue?.[0]?.released == 0" - @click="startPrint(printer.id, printer.queue[0].id)"> + @click="startPrint(printer.id, printer.queue[0].id)"> Start Print </button> <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'paused')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + @click="setPrinterStatus(printer, 'paused')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> Pause </button> <button class="btn btn-secondary" :disabled="printer.queue?.[0]?.extruded == 0" - @click="setPrinterStatus(printer, 'colorchange')" - v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> + @click="setPrinterStatus(printer, 'colorchange')" + v-if="(printer.status === 'printing' && printer.queue?.[0]?.released !== 0)"> Color Change </button> <button class="btn btn-secondary" @click="setPrinterStatus(printer, 'printing')" - v-if="printer.status == 'paused'"> + v-if="printer.status == 'paused'"> Unpause </button> <button class="btn btn-danger" @click="setPrinterStatus(printer, 'complete')" - v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> + v-if="(printer.status == 'printing' || printer.status == 'colorchange' || printer.status == 'paused')"> Stop </button> <div - v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" - class="mt-2"> + v-if="printer.status == 'colorchange' && (printer.colorbuff == 1 || printer.queue[0]?.file_pause == 1)" + class="mt-2"> Ready for color change. </div> <div v-else-if="printer.status == 'colorchange' && printer.queue[0]?.file_pause == 0" class="mt-2"> @@ -672,27 +691,30 @@ const handleDragEnd = async () => { <td style="width: 250px;"> <div - v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> + v-if="(printer.status === 'printing' || printer.status == 'paused' || printer.status == 'colorchange') && printer.queue && printer.queue[0]?.released == 1"> <!-- <div v-for="job in printer.queue" :key="job.id"> --> <!-- Display the elapsed time --> <div class="progress" style="position: relative;"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" - :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" - :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> + :style="{ width: (printer.queue?.[0]?.progress || 0) + '%' }" + :aria-valuenow="printer.queue?.[0]?.progress" aria-valuemin="0" aria-valuemax="100"> </div> <!-- job progress set to 2 decimal places --> - <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);">{{ - printer.queue?.[0]?.progress - ? - `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' }}</p> + <p style="position: absolute; width: 100%; text-align: center; color: var(--color-background-font);"> + {{ + printer.queue?.[0]?.progress + ? + `${printer.queue?.[0]?.progress.toFixed(2)}%` : '0.00%' + }}</p> </div> <!-- </div> --> </div> - <div v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> + <div + v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'complete' || printer.queue?.[0].status == 'cancelled')"> <div class="buttons-progress"> <div type="button" class="btn btn-secondary" - @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> + @click="releasePrinter(printer.queue?.[0], 1, printer.id)"> Clear </div> <div class="btn-group"> @@ -700,24 +722,24 @@ const handleDragEnd = async () => { Clear/Rerun </div> <div class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" - aria-expanded="false"> + aria-expanded="false"> </div> <div class="dropdown-menu"> <div class="dropdown-item" v-for="printer in printers" :key="printer.id" - @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> + @click="releasePrinter(printer.queue?.[0], 2, printer.id!)"> {{ printer.name }} </div> </div> </div> <div type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#issueModal" - @click=setJob(printer.queue[0])> + @click=setJob(printer.queue[0])> Fail </div> </div> </div> <div - v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> + v-else-if="printer.queue?.[0] && (printer.queue?.[0].status == 'printing' && printer.status == 'complete')"> <div style="display: flex; justify-content: center; align-items: center;"> <button class="btn btn-primary w-100" type="button" disabled> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> @@ -726,7 +748,7 @@ const handleDragEnd = async () => { </div> </div> <div v-else-if="printer.status == 'error'" class="alert alert-danger truncate" role="alert" - :title="printer?.error"> + :title="printer?.error"> {{ printer?.error }} </div> <div v-else></div> @@ -736,37 +758,38 @@ const handleDragEnd = async () => { <td style="width: 1%; white-space: nowrap;"> <div style="display: flex; justify-content: space-between; align-items: center;"> <i :class="{ 'fa fa-chevron-down': !printer.isInfoExpanded, 'fa fa-chevron-up': printer.isInfoExpanded }" - @click="openPrinterInfo(printer)"> + @click="openPrinterInfo(printer)"> </i> <div :class="{ 'not-draggable': printer.queue && printer.queue.length == 0 }" class="dropdown"> <div style="display: flex; justify-content: center; align-items: center; height: 100%;"> <button type="button" id="settingsDropdown" data-bs-toggle="dropdown" aria-expanded="false" - style="background: none; border: none;"> + style="background: none; border: none;"> <i class="fa-solid fa-bars" - :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> + :class="{ 'icon-disabled': printer.queue && printer.queue.length == 0 }"></i> </button> <ul class="dropdown-menu" aria-labelledby="settingsDropdown"> <li> <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> + data-bs-target="#gcodeImageModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 2, printer)"> <i class="fa-solid fa-image"></i> <span class="ms-2">GCode Image</span> </a> </li> <li v-if="printer.queue.length > 0 && (printer.queue[0] && printer.queue[0].extruded)"> <a class="dropdown-item d-flex align-items-center" data-bs-toggle="modal" - data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" - v-bind:job="printer.queue[0]" - @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> + data-bs-target="#gcodeLiveViewModal" v-if="printer.queue && printer.queue.length > 0" + v-bind:job="printer.queue[0]" + @click="printer.name && openModal(printer.queue[0], printer.name, 1, printer)"> <i class="fas fa-code"></i> <span class="ms-2">GCode Live</span> </a> </li> <li v-if="printer.queue[0]"> - <a class="dropdown-item d-flex align-items-center" @click="getFileDownload(printer.queue[0]?.id)" - :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> + <a class="dropdown-item d-flex align-items-center" + @click="getFileDownload(printer.queue[0]?.id)" + :disabled="printer.queue[0]?.file_name_original.includes('.gcode:')"> <i class="fas fa-download"></i> <span class="ms-2">Download</span> </a> @@ -778,9 +801,9 @@ const handleDragEnd = async () => { </td> <td class="text-center handle" :class="{ 'not-draggable': printers.length <= 1 || printer.isInfoExpanded }" - :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> + :style="{ 'vertical-align': printer.isInfoExpanded ? 'middle' : '' }"> <i class="fas fa-grip-vertical" - :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> + :class="{ 'icon-disabled': printers.length <= 1 || printer.isInfoExpanded }"></i> </td> </tr> </template> diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 128888bf..5901bdbf 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -124,9 +124,6 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if len(line) == 0 or line.startswith(";"): continue - if current_app: - with current_app.app_context(): - current_app.socketio.emit("gcode_line", {"line": line.strip("\n"), "printerid": self.dbID}) if ("M569" in line) and job.getTimeStarted() == 0: job.setTimeStarted(1) job.setTime(job.calculateEta(), 1) @@ -230,11 +227,8 @@ def parseGcode(self, job: Job, isVerbose: bool = False): def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): """ Method to send gcode to the printer - :param gcode: the line of gcode to send to the printer - :type gcode: bytes | str | LiteralString - :param isVerbose: whether to log or not - :type isVerbose: bool - :return: True if the gcode was successfully sent, else False + :param bytes | str | LiteralString gcode: the line of gcode to send to the printer + :param bool isVerbose: whether to log or not :rtype: bool """ assert self.serialConnection is not None, "Serial connection is None" @@ -245,7 +239,7 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): assert isinstance(gcode, bytes), f"Expected bytes, got {type(gcode)}" assert isinstance(isVerbose, bool), f"Expected bool, got {type(isVerbose)}" callables = self.callablesHashtable.get(self.extractIndex(gcode), [checkOK]) - current_app.socketio.emit("gcode_line", {"line": gcode.decode(), "printerid": self.dbID}) + current_app.socketio.emit("gcode_line", {"line": (gcode.decode() if isinstance(gcode, bytes) else gcode).strip(), "printerid": self.dbID}) self.serialConnection.write(gcode) line = '' for func in callables: diff --git a/server/Classes/Queue.py b/server/Classes/Queue.py index 7c20a4c0..e65a3463 100644 --- a/server/Classes/Queue.py +++ b/server/Classes/Queue.py @@ -8,7 +8,7 @@ def setToInQueue(self): for job in self: job.status = "inqueue" - def addToBack(self, job: Job, printerid): + def addToBack(self, job: Job): assert isinstance(job, Job) if self.count(job) > 0: @@ -16,7 +16,7 @@ def addToBack(self, job: Job, printerid): self.append(job) if current_app: current_app.socketio.emit( - "queue_update", {"queue": self.convertQueueToJson(), "printerid": printerid} + "queue_update", {"queue": self.convertQueueToJson(), "printerid": job.fabricator_id} ) return True @@ -139,7 +139,7 @@ def bumpExtreme(self, front: bool, jobid: int, fabricator_id: int): else: self.insert(0, job_to_move) else: - self.addToBack(job_to_move, fabricator_id) + self.addToBack(job_to_move) if current_app: current_app.socketio.emit( "queue_update", {"queue": self.convertQueueToJson(), "printerid": fabricator_id} diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index a834e4eb..46579040 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -94,9 +94,9 @@ def add_job_to_queue(): if fabricator is None: return jsonify({"error": "Fabricator not found."}), 404 if priority == 'true': - fabricator.queue.addToFront(job, printer_id) + fabricator.queue.addToFront(job) else: - fabricator.queue.addToBack(job, printer_id) + fabricator.queue.addToBack(job) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 @@ -145,7 +145,7 @@ def auto_queue(): fabricator = findPrinterObject(printer_id) if fabricator is None: return jsonify({"error": "Fabricator not found."}), 404 - fabricator.queue.addToBack(job, printer_id) + fabricator.queue.addToBack(job) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 except Exception as e: @@ -685,8 +685,8 @@ def rerunjob(printerpk, jobpk, position): if fabricator is None: return jsonify({"error": "Fabricator not found."}), 404 if position == "back": - findPrinterObject(printerpk).getQueue().addToBack(rjob, rjob.fabricator_id) + findPrinterObject(printerpk).getQueue().addToBack(rjob) else: - findPrinterObject(printerpk).getQueue().addToFront(rjob, rjob.fabricator_id) + findPrinterObject(printerpk).getQueue().addToFront(rjob) return jsonify({"success": True, "message": "Job added to printer queue."}), 200 From d7303d9d27f12e1876b6e0a4fb440fc95e0a149c Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Thu, 12 Dec 2024 12:25:21 -0500 Subject: [PATCH 192/194] fix: temp values to see if emulator will work properly with backend --- printeremu/src/gcode_commands.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/printeremu/src/gcode_commands.go b/printeremu/src/gcode_commands.go index 146409f5..0fc5281d 100644 --- a/printeremu/src/gcode_commands.go +++ b/printeremu/src/gcode_commands.go @@ -361,7 +361,8 @@ type M114Command struct{} func (cmd *M114Command) Execute(printer *Printer) []string { position := printer.Extruder.Position - return []string{fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f", position.X, position.Y, position.Z), "ok"} + // TODO: temp values + return []string{fmt.Sprintf("X:%.2f Y:%.2f Z:%.2f E:%.2f Count X:%.2f", position.X, position.Y, position.Z, 1.0, 0.0), "ok"} } type M115Command struct{} From 404d91dec0f703abc2af397f2bbe8c1519e41771 Mon Sep 17 00:00:00 2001 From: L10nhunter <yegera1@newpaltz.edu> Date: Mon, 16 Dec 2024 13:31:48 -0500 Subject: [PATCH 193/194] fix: time and eta updating --- client/src/model/sockets.ts | 11 +++++-- server/Classes/FabricatorList.py | 4 +-- .../Classes/Fabricators/Printers/Printer.py | 26 ++++++++------- .../Fabricators/Printers/Prusa/PrusaMK3.py | 1 + .../Fabricators/Printers/Prusa/PrusaMK4.py | 1 + .../Printers/Prusa/PrusaPrinter.py | 6 ++-- server/controllers/jobs.py | 33 ++++++++++++------- 7 files changed, 50 insertions(+), 32 deletions(-) diff --git a/client/src/model/sockets.ts b/client/src/model/sockets.ts index 02aa9133..c74cb1aa 100644 --- a/client/src/model/sockets.ts +++ b/client/src/model/sockets.ts @@ -212,13 +212,13 @@ export function setupGCodeViewerSocket(printers: Array<Device>) { } export function setupGCodeLineSocket(printers: Array<Device>) { + for (let i = 0; i < printers.length; i++) { + printers[i].gcodeLines = [] + } socket.value.on('gcode_line', (data: any) => { if (printers) { const printer: Device | undefined = printers.find((p: Device) => p.id === data.printerid) if (printer) { - if (!printer.gcodeLines) { - printer.gcodeLines = [] - } printer.gcodeLines!.push(data.line) } else { console.error('printer is undefined') @@ -233,6 +233,9 @@ const arrayLevels = ['critical', 'error', 'warning', 'info', 'debug'] const colors = ['\x1b[95m', '\x1b[91m', '\x1b[93m', '\x1b[0m', '\x1b[94m'] export function setupConsoleSocket(printers: Array<Device>) { + for (let i = 0; i < printers.length; i++) { + printers[i].gcodeLines = [] + } socket.value.on('console_update', (data: any) => { if (printers) { const printer = printers.find((p: Device) => p.id === data.printerid) @@ -246,6 +249,8 @@ export function setupConsoleSocket(printers: Array<Device>) { printer.consoles[i].push(colors[maxLevelToAdd] + data.message + '\x1b[0m') } } + if (data.level === 'info') printer.gcodeLines!.push(data.message.split(':')[0]); + } else { console.error('data.level is undefined') } diff --git a/server/Classes/FabricatorList.py b/server/Classes/FabricatorList.py index 2f852386..1b165e09 100644 --- a/server/Classes/FabricatorList.py +++ b/server/Classes/FabricatorList.py @@ -86,8 +86,7 @@ def addFabricator(self, serialPortName: str, name: str = ""): # TODO: figure out how to check if the fabricator is in the db # assert all(fabricator in self.fabricators for fabricator in dbFabricators), f"self={self.fabricators}, dbFabricators={dbFabricators}" if newFab: - print("starting new fabricator thread") - self.start_fabricator_thread(newFab) + self.fabricator_threads.append(self.start_fabricator_thread(newFab)) def deleteFabricator(self, fabricator_id): """ @@ -351,7 +350,6 @@ def __to_JSON__(self): def run(self): with self.app.app_context(): self.fabricator.responseCount = 0 - print(f"Starting thread for fabricator {self.fabricator.getName()}") while True: time.sleep(2) status = self.fabricator.getStatus() diff --git a/server/Classes/Fabricators/Printers/Printer.py b/server/Classes/Fabricators/Printers/Printer.py index 5901bdbf..e6af0912 100644 --- a/server/Classes/Fabricators/Printers/Printer.py +++ b/server/Classes/Fabricators/Printers/Printer.py @@ -18,6 +18,7 @@ class Printer(Device, metaclass=ABCMeta): pauseCMD: bytes = b"M601\n" resumeCMD: bytes = b"M602\n" getMachineNameCMD: bytes = b"M997\n" + startTimeCMD: str = "M75" callablesHashtable = { "M31": [checkTime], # Print time @@ -39,12 +40,12 @@ def __init__(self, dbID, serialPort, consoleLogger=None, fileLogger=None, addLog self.nozzleDiameter = None def parseGcode(self, job: Job, isVerbose: bool = False): - assert isinstance(job, Job) + assert isinstance(job, Job), f"Expected Job, got {type(job)}" file = job.file_path - assert isinstance(file, str) - assert isinstance(isVerbose, bool) - assert self.serialConnection.is_open - assert self.status == "printing" + assert isinstance(file, str), f"Expected file to be a str, got {type(file)}" + assert isinstance(isVerbose, bool), f"Expected isVerbose to be a bool, got {type(isVerbose)}" + assert self.serialConnection.is_open, "Serial connection is not open" + assert self.status == "printing", f"Printer status is {self.status}, expected printing" try: with open(file, "r") as g: # Read the file and store the lines in a list @@ -93,6 +94,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): prev_line = "" # Replace file with the path to the file. "r" means read mode. # now instead of reading from 'g', we are reading line by line + current_app.socketio.emit("console_update", {"message": "Starting Job", "level": "critical", "printerid": self.dbID}) for line in lines: if self.status == "cancelled": self.sendGcode(self.cancelCMD) @@ -124,7 +126,7 @@ def parseGcode(self, job: Job, isVerbose: bool = False): if len(line) == 0 or line.startswith(";"): continue - if ("M569" in line) and job.getTimeStarted() == 0: + if job.getTimeStarted() == 0 and ("M75" in line or self.startTimeCMD in line) : job.setTimeStarted(1) job.setTime(job.calculateEta(), 1) job.setTime(datetime.now(), 2) @@ -256,12 +258,12 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): if func(line, self): break if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {decLine}") - current_app.socketio.emit("console_update",{"message": decLine, "level": "debug", "printerid": self.dbID}) + # current_app.socketio.emit("console_update",{"message": decLine, "level": "debug", "printerid": self.dbID}) except UnicodeDecodeError: if isVerbose: if self.logger is not None: self.logger.debug(f"{gcode.decode().strip()}: {line.strip()}") else: print(f"{gcode.decode().strip()}: {line.strip()}") - current_app.socketio.emit("console_update",{"message": gcode.decode().strip(), "level": "debug", "printerid": self.dbID}) + # current_app.socketio.emit("console_update",{"message": gcode.decode().strip(), "level": "debug", "printerid": self.dbID}) continue except Exception as e: if current_app: return current_app.handle_errors_and_logging(e, self) @@ -269,10 +271,10 @@ def sendGcode(self, gcode: bytes | str, isVerbose: bool = False): else: print(traceback.format_exc()) return False if not callables: - current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: ok", "level": "info", "printerid": self.dbID}) + # current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: ok", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info(f"{gcode.decode().strip()}: Always True") else: - current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}", "level": "info", "printerid": self.dbID}) + # current_app.socketio.emit("console_update", {"message": f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}", "level": "info", "printerid": self.dbID}) if self.logger is not None: self.logger.info( f"{gcode.decode().strip()}: {(line.decode() if isinstance(line, bytes) else line).strip()}") return True @@ -339,12 +341,12 @@ def extractIndex(self, gcode: bytes) -> str: if hashIndex == "M109" or hashIndex == "M190": if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") current_app.socketio.emit("console_update", - {"message": "Waiting for temperature to stabilize...", "level": "info", + {"message": "Waiting for temperature to stabilize...", "level": "critical", "printerid": self.dbID}) elif hashIndex == "G28": if self.logger is not None: self.logger.info("Homing...") current_app.socketio.emit("console_update", - {"message": "Homing...", "level": "info", "printerid": self.dbID}) + {"message": "Homing...", "level": "critical", "printerid": self.dbID}) return hashIndex def pause(self): diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py index a038a77b..bf5c3beb 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK3.py @@ -15,6 +15,7 @@ class PrusaMK3(PrusaPrinter): homeCMD = b"G28\n" keepAliveCMD = None doNotKeepAliveCMD = None + startTimeCMD = "M107" callablesHashtable = { "M31": [checkTime, checkOK], # Print time diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py index 80cf82e8..d6687587 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaMK4.py @@ -7,6 +7,7 @@ class PrusaMK4(PrusaPrinter): DESCRIPTION = "Original Prusa MK4 - CDC" MAXFEEDRATE = 36000 homePosition = Vector3(14.0, -4.0, 2.0) + startTimeCMD = "M569" def endSequence(self): self.sendGcode(b"M104 S0\n") # ; turn off temperature diff --git a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py index 524e4b95..91c8dc5d 100644 --- a/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py +++ b/server/Classes/Fabricators/Printers/Prusa/PrusaPrinter.py @@ -28,15 +28,15 @@ def extractIndex(self, gcode: bytes) -> str: hashIndex = gcode.decode().split("\n")[0].split(" ")[0] if hashIndex == "M109" or hashIndex == "M190": if self.logger is not None: self.logger.info("Waiting for temperature to stabilize...") - current_app.socketio.emit("console_update", {"message": "Waiting for temperature to stabilize...", "level": "info", "printerid": self.dbID}) + current_app.socketio.emit("console_update", {"message": "Waiting for temperature to stabilize...", "level": "critical", "printerid": self.dbID}) elif hashIndex == "G28": if self.logger is not None: self.logger.info("Homing...") - current_app.socketio.emit("console_update", {"message": "Homing...", "level": "info", "printerid": self.dbID}) + current_app.socketio.emit("console_update", {"message": "Homing...", "level": "critical", "printerid": self.dbID}) elif hashIndex == "G29": try: g29addon = gcode.decode().split("\n")[0].split(" ")[1] hashIndex += ".01" if g29addon == "P1" else ".02" except IndexError: hashIndex += ".01" - current_app.socketio.emit("console_update", {"message": "Auto bed leveling...", "level": "info", "printerid": self.dbID}) + current_app.socketio.emit("console_update", {"message": "Auto bed leveling...", "level": "critical", "printerid": self.dbID}) return hashIndex diff --git a/server/controllers/jobs.py b/server/controllers/jobs.py index 46579040..2ad75cb8 100644 --- a/server/controllers/jobs.py +++ b/server/controllers/jobs.py @@ -1,5 +1,5 @@ import shutil -from flask import Blueprint, jsonify, request +from flask import Blueprint, jsonify, request, Response from Classes.Jobs import Job from models.db import db from models.printers import Printer @@ -119,7 +119,7 @@ def auto_queue(): favoriteOne = False # for i in range(int(quantity)): status = 'inqueue' # set status - printer_id = getSmallestQueue() + fabricator_id = getSmallestQueue() if(favorite == 'true' and not favoriteOne): favorite = 1 @@ -128,7 +128,7 @@ def auto_queue(): favorite = 0 # favorite = 1 if _favorite == 'true' else 0 - res = Job.jobHistoryInsert(name, printer_id, status, file, file_name_original, favorite, td_id) # insert into DB + res = Job.jobHistoryInsert(name, fabricator_id, status, file, file_name_original, favorite, td_id) # insert into DB id = res['id'] @@ -142,7 +142,7 @@ def auto_queue(): job.setFileName(file_name_pk) # set unique in-memory file name job.setFilament(filament) # set filament type - fabricator = findPrinterObject(printer_id) + fabricator = findPrinterObject(fabricator_id) if fabricator is None: return jsonify({"error": "Fabricator not found."}), 404 fabricator.queue.addToBack(job) @@ -644,23 +644,34 @@ def refetch_time(): current_app.handle_errors_and_logging(e) return jsonify({"error": format_exc()}), 500 -def findPrinterObject(fabricator_id): +def findPrinterObject(fabricator_id: int) -> Fabricator | None: """ Find the printer object by its ID. - :param fabricator_id: The ID of the printer. - :type fabricator_id: int + :param int fabricator_id: The ID of the printer. :rtype: Fabricator | None """ - threads = current_app.fabricator_list.getThreadArray() + threads = current_app.fabricator_list.fabricator_threads fabricatorThread = list(filter(lambda thread: thread.fabricator.dbID == fabricator_id, threads)) return fabricatorThread[0].fabricator if len(fabricatorThread) > 0 else None -def getSmallestQueue(): - threads = current_app.fabricator_list.getThreadArray() +def getSmallestQueue() -> int: + """ + Get the printer with the smallest queue. + :rtype: int + """ + threads = current_app.fabricator_list.fabricator_threads smallest_queue_thread = min(threads, key=lambda thread: len(thread.fabricator.queue)) return smallest_queue_thread.fabricator.dbID -def rerunjob(printerpk, jobpk, position): +def rerunjob(printerpk: int, jobpk: int, position: str) -> tuple[Response, int]: + """ + Rerun a job on a printer. + :param int printerpk: dbID of the fabricator to rerun the job on + :param int jobpk: id of the job to rerun + :param str position: where to put the job + :return: JSON response for the frontend + :rtype: tuple[Response, int] + """ job = Job.findJob(jobpk) # retrieve Job to rerun status = 'inqueue' # set status From ca6c741e4132aa9864019d4c3abc9c900261dbce Mon Sep 17 00:00:00 2001 From: CJ <iron768gamer@gmail.com> Date: Mon, 16 Dec 2024 13:42:08 -0500 Subject: [PATCH 194/194] fix: f string issue --- server/Classes/Fabricators/Fabricator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Classes/Fabricators/Fabricator.py b/server/Classes/Fabricators/Fabricator.py index b38cb234..9d2ce36c 100644 --- a/server/Classes/Fabricators/Fabricator.py +++ b/server/Classes/Fabricators/Fabricator.py @@ -61,7 +61,7 @@ def __init__(self, port: ListPortInfo | SysFS | None, name: str = "", consoleLog db.session.commit() def __repr__(self): - return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self, "device") and hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if (hasattr(self, "device") and self.device is not None and self.device.serialConnection is not None) else None}, queue: {self.queue}, job: {self.job}" + return f"Fabricator: {self.name}, description: {self.description}, HWID: {self.hwid}, port: {self.devicePort}, status: {self.status}, logger: {self.device.logger if hasattr(self, 'device') and hasattr(self.device, 'logger') else 'None'}, port open: {self.device.serialConnection.is_open if hasattr(self, 'device') and self.device and self.device.serialConnection else None}, queue: {self.queue}, job: {self.job}" def __to_JSON__(self) -> dict: """