Fix response messages. Fix filesystem name transformations.
This commit is contained in:
parent
19cac21db6
commit
42ba306e4f
@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import, annotations
|
||||
import os
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
import flask
|
||||
@ -195,17 +196,14 @@ class BambuPrintPlugin(
|
||||
f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True
|
||||
)
|
||||
if self._settings.get(["device_type"]) in ["X1", "X1C"]:
|
||||
timelapse_file_list = ftp.list_files("timelapse/", ".mp4") or []
|
||||
timelapse_file_list = ftp.list_files("timelapse/", ".mp4")
|
||||
else:
|
||||
timelapse_file_list = ftp.list_files("timelapse/", ".avi") or []
|
||||
timelapse_file_list = ftp.list_files("timelapse/", ".avi")
|
||||
for entry in timelapse_file_list:
|
||||
if entry.startswith("/"):
|
||||
filename = entry[1:].replace("timelapse/", "")
|
||||
else:
|
||||
filename = entry.replace("timelapse/", "")
|
||||
filesize = ftp.ftps_session.size(f"timelapse/{filename}")
|
||||
filename = entry.name
|
||||
filesize = ftp.ftps_session.size(entry.as_posix())
|
||||
date_str = ftp.ftps_session.sendcmd(
|
||||
f"MDTM timelapse/{filename}"
|
||||
f"MDTM {entry.as_posix()}"
|
||||
).replace("213 ", "")
|
||||
filedate = (
|
||||
datetime.datetime.strptime(date_str, "%Y%m%d%H%M%S")
|
||||
|
@ -67,7 +67,7 @@ class BambuVirtualPrinter:
|
||||
self._running = True
|
||||
self._printer_thread = threading.Thread(
|
||||
target=self._printer_worker,
|
||||
name="octoprint.plugins.bambu_printer.printer_worker",
|
||||
name="octoprint.plugins.bambu_printer.printer_state",
|
||||
)
|
||||
self._state_change_queue = queue.Queue()
|
||||
|
||||
@ -229,7 +229,7 @@ class BambuVirtualPrinter:
|
||||
bambu_client.on_connect = self.on_connect(bambu_client.on_connect)
|
||||
bambu_client.connect(callback=self.new_update)
|
||||
self._log.info(f"bambu connection status: {bambu_client.connected}")
|
||||
self._serial_io.sendOk()
|
||||
self.sendOk()
|
||||
self._bambu_client = bambu_client
|
||||
|
||||
def __str__(self):
|
||||
@ -304,11 +304,21 @@ class BambuVirtualPrinter:
|
||||
|
||||
##~~ command implementations
|
||||
|
||||
@gcode_executor.register_no_data("M21")
|
||||
def _sd_status(self) -> None:
|
||||
self.sendIO("SD card ok")
|
||||
|
||||
@gcode_executor.register("M23")
|
||||
def _select_sd_file(self, data: str) -> bool:
|
||||
filename = data.split(maxsplit=1)[1].strip()
|
||||
self._list_sd()
|
||||
return self.file_system.select_file(filename)
|
||||
if self.file_system.select_file(filename):
|
||||
assert self.file_system.selected_file is not None
|
||||
self.sendIO(
|
||||
f"File opened: {self.file_system.selected_file.file_name} "
|
||||
f"Size: {self.file_system.selected_file.size}"
|
||||
)
|
||||
self.sendIO("File selected")
|
||||
|
||||
@gcode_executor.register("M26")
|
||||
def _set_sd_position(self, data: str) -> bool:
|
||||
@ -335,6 +345,7 @@ class BambuVirtualPrinter:
|
||||
else:
|
||||
self._sdstatus_reporter = None
|
||||
|
||||
self.update_print_job_info()
|
||||
self.report_print_job_status()
|
||||
return True
|
||||
|
||||
@ -350,8 +361,8 @@ class BambuVirtualPrinter:
|
||||
return self._processTemperatureQuery()
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@gcode_executor.register("M115")
|
||||
def _report_firmware_info(self, data: str) -> bool:
|
||||
@gcode_executor.register_no_data("M115")
|
||||
def _report_firmware_info(self) -> bool:
|
||||
self.sendIO("Bambu Printer Integration")
|
||||
self.sendIO("Cap:EXTENDED_M20:1")
|
||||
self.sendIO("Cap:LFN_WRITE:1")
|
||||
@ -412,7 +423,7 @@ class BambuVirtualPrinter:
|
||||
self._log.debug(f"processing gcode {gcode} command = {full_command}")
|
||||
handled = self.gcode_executor.execute(self, gcode, full_command)
|
||||
if handled:
|
||||
self._serial_io.sendOk()
|
||||
self.sendOk()
|
||||
return
|
||||
|
||||
# post gcode to printer otherwise
|
||||
@ -421,7 +432,7 @@ class BambuVirtualPrinter:
|
||||
GCODE_COMMAND["print"]["param"] = full_command + "\n"
|
||||
if self.bambu_client.publish(GCODE_COMMAND):
|
||||
self._log.info("command sent successfully")
|
||||
self._serial_io.sendOk()
|
||||
self.sendOk()
|
||||
|
||||
@gcode_executor.register_no_data("M112")
|
||||
def _shutdown(self):
|
||||
@ -432,8 +443,8 @@ class BambuVirtualPrinter:
|
||||
self._serial_io.close()
|
||||
return True
|
||||
|
||||
@gcode_executor.register_no_data("M20")
|
||||
def _list_sd(self):
|
||||
@gcode_executor.register("M20")
|
||||
def _list_sd(self, data: str = ""):
|
||||
self.sendIO("Begin file list")
|
||||
for item in map(lambda f: f.get_log_info(), self.file_system.get_all_files()):
|
||||
self.sendIO(item)
|
||||
@ -464,7 +475,7 @@ class BambuVirtualPrinter:
|
||||
else:
|
||||
self.sendIO("Not SD printing")
|
||||
|
||||
def _generateTemperatureOutput(self) -> str:
|
||||
def _create_temperature_message(self) -> str:
|
||||
template = "{heater}:{actual:.2f}/ {target:.2f}"
|
||||
temps = collections.OrderedDict()
|
||||
temps["T"] = (self._telemetry.temp[0], self._telemetry.targetTemp[0])
|
||||
@ -487,7 +498,7 @@ class BambuVirtualPrinter:
|
||||
def _processTemperatureQuery(self) -> bool:
|
||||
# includeOk = not self._okBeforeCommandOutput
|
||||
if self.bambu_client.connected:
|
||||
output = self._generateTemperatureOutput()
|
||||
output = self._create_temperature_message()
|
||||
self.sendIO(output)
|
||||
return True
|
||||
else:
|
||||
|
@ -26,6 +26,7 @@ wrapper for FTPS server interactions
|
||||
|
||||
import ftplib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import ssl
|
||||
from typing import Optional, Union, List
|
||||
@ -34,6 +35,7 @@ from contextlib import redirect_stdout
|
||||
import io
|
||||
import re
|
||||
|
||||
|
||||
class ImplicitTLS(ftplib.FTP_TLS):
|
||||
"""ftplib.FTP_TLS sub-class to support implicit SSL FTPS"""
|
||||
|
||||
@ -57,9 +59,9 @@ class ImplicitTLS(ftplib.FTP_TLS):
|
||||
conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
|
||||
|
||||
if self._prot_p:
|
||||
conn = self.context.wrap_socket(conn,
|
||||
server_hostname=self.host,
|
||||
session=self.sock.session) # this is the fix
|
||||
conn = self.context.wrap_socket(
|
||||
conn, server_hostname=self.host, session=self.sock.session
|
||||
) # this is the fix
|
||||
return conn, size
|
||||
|
||||
|
||||
@ -76,12 +78,12 @@ class IoTFTPSClient:
|
||||
welcome: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ftps_host: str,
|
||||
ftps_port: Optional[int] = 21,
|
||||
ftps_user: Optional[str] = "",
|
||||
ftps_pass: Optional[str] = "",
|
||||
ssl_implicit: Optional[bool] = False,
|
||||
self,
|
||||
ftps_host: str,
|
||||
ftps_port: Optional[int] = 21,
|
||||
ftps_user: Optional[str] = "",
|
||||
ftps_pass: Optional[str] = "",
|
||||
ssl_implicit: Optional[bool] = False,
|
||||
) -> None:
|
||||
self.ftps_host = ftps_host
|
||||
self.ftps_port = ftps_port
|
||||
@ -106,7 +108,8 @@ class IoTFTPSClient:
|
||||
self.ftps_session.set_debuglevel(0)
|
||||
|
||||
self.welcome = self.ftps_session.connect(
|
||||
host=self.ftps_host, port=self.ftps_port)
|
||||
host=self.ftps_host, port=self.ftps_port
|
||||
)
|
||||
|
||||
if self.ftps_user and self.ftps_pass:
|
||||
self.ftps_session.login(user=self.ftps_user, passwd=self.ftps_pass)
|
||||
@ -137,7 +140,7 @@ class IoTFTPSClient:
|
||||
# Taken from ftplib.storbinary but with custom ssl handling
|
||||
# due to the shitty bambu p1p ftps server TODO fix properly.
|
||||
with open(source, "rb") as fp:
|
||||
self.ftps_session.voidcmd('TYPE I')
|
||||
self.ftps_session.voidcmd("TYPE I")
|
||||
|
||||
with self.ftps_session.transfercmd(f"STOR {dest}", rest) as conn:
|
||||
while 1:
|
||||
@ -152,7 +155,9 @@ class IoTFTPSClient:
|
||||
callback(buf)
|
||||
|
||||
# shutdown ssl layer
|
||||
if ftplib._SSLSocket is not None and isinstance(conn, ftplib._SSLSocket):
|
||||
if ftplib._SSLSocket is not None and isinstance(
|
||||
conn, ftplib._SSLSocket
|
||||
):
|
||||
# Yeah this is suposed to be conn.unwrap
|
||||
# But since we operate in prot p mode
|
||||
# we can close the connection always.
|
||||
@ -185,19 +190,24 @@ class IoTFTPSClient:
|
||||
def mkdir(self, path: str) -> str:
|
||||
return self.ftps_session.mkd(path)
|
||||
|
||||
def list_files(self, path: str, file_pattern: Optional[str] = None) -> Union[List[str], None]:
|
||||
def list_files(self, list_path: str, extensions: str | list[str] | None = None):
|
||||
"""list files under a path inside the FTPS server"""
|
||||
|
||||
if extensions is None:
|
||||
_extension_acceptable = lambda p: True
|
||||
else:
|
||||
if isinstance(extensions, str):
|
||||
extensions = [extensions]
|
||||
_extension_acceptable = lambda p: any(s in p.suffixes for s in extensions)
|
||||
|
||||
try:
|
||||
files = self.ftps_session.nlst(path)
|
||||
if not files:
|
||||
return
|
||||
if file_pattern:
|
||||
return [f for f in files if file_pattern in f]
|
||||
return files
|
||||
list_result = self.ftps_session.nlst(list_path) or []
|
||||
for file_name in list_result:
|
||||
path = Path(list_path) / file_name
|
||||
if _extension_acceptable(path):
|
||||
yield path
|
||||
except Exception as ex:
|
||||
print(f"unexpected exception occurred: {ex}")
|
||||
pass
|
||||
return
|
||||
|
||||
def list_files_ex(self, path: str) -> Union[list[str], None]:
|
||||
"""list files under a path inside the FTPS server"""
|
||||
@ -208,7 +218,8 @@ class IoTFTPSClient:
|
||||
s = f.getvalue()
|
||||
files = []
|
||||
for row in s.split("\n"):
|
||||
if len(row) <= 0: continue
|
||||
if len(row) <= 0:
|
||||
continue
|
||||
|
||||
attribs = row.split(" ")
|
||||
|
||||
@ -219,7 +230,7 @@ class IoTFTPSClient:
|
||||
else:
|
||||
name = attribs[len(attribs) - 1]
|
||||
|
||||
file = ( attribs[0], name )
|
||||
file = (attribs[0], name)
|
||||
files.append(file)
|
||||
return files
|
||||
except Exception as ex:
|
||||
|
@ -17,4 +17,4 @@ class PrintJob:
|
||||
|
||||
@progress.setter
|
||||
def progress(self, value):
|
||||
self.file_position = int(self.file_info.size * ((value + 1) / 100))
|
||||
self.file_position = int(self.file_info.size * value / 100)
|
||||
|
@ -27,7 +27,7 @@ class PrinterSerialIO(threading.Thread, BufferedIOBase):
|
||||
write_timeout=10.0,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name="octoprint.plugins.bambu_printer.serial_io_thread", daemon=True
|
||||
name="octoprint.plugins.bambu_printer.printer_worker", daemon=True
|
||||
)
|
||||
self._handle_command_callback = handle_command_callback
|
||||
self._settings = settings
|
||||
@ -115,8 +115,8 @@ class PrinterSerialIO(threading.Thread, BufferedIOBase):
|
||||
return 0
|
||||
|
||||
try:
|
||||
self.input_bytes.put(data, timeout=self._write_timeout)
|
||||
self._log.debug(f"<<< {u_data}")
|
||||
self.input_bytes.put(data, timeout=self._write_timeout)
|
||||
return len(data)
|
||||
except queue.Full:
|
||||
self._log.error(
|
||||
|
@ -77,18 +77,14 @@ class RemoteSDCardFileList:
|
||||
|
||||
yield file_info
|
||||
existing_files.append(file_info.file_name)
|
||||
existing_files.append(file_info.dosname)
|
||||
|
||||
def _get_existing_files_info(self):
|
||||
ftp = self._connect_ftps_server()
|
||||
|
||||
file_list = []
|
||||
file_list.extend(ftp.list_files("", ".3mf") or [])
|
||||
file_list.extend(
|
||||
[
|
||||
(Path("cache/") / f).as_posix()
|
||||
for f in (ftp.list_files("cache/", ".3mf") or [])
|
||||
]
|
||||
)
|
||||
file_list.extend(ftp.list_files("", ".3mf"))
|
||||
file_list.extend(ftp.list_files("cache/", ".3mf"))
|
||||
|
||||
existing_files = []
|
||||
return list(self._scan_ftp_file_list(ftp, file_list, existing_files))
|
||||
@ -112,7 +108,7 @@ class RemoteSDCardFileList:
|
||||
|
||||
def get_all_files(self):
|
||||
self._update_existing_files_info()
|
||||
self._logger.debug(f"_getSdFiles return: {self._file_data_cache}")
|
||||
self._logger.debug(f"get_all_files return: {self._file_data_cache}")
|
||||
return list(self._file_data_cache.values())
|
||||
|
||||
def _update_existing_files_info(self):
|
||||
@ -124,7 +120,9 @@ class RemoteSDCardFileList:
|
||||
|
||||
def _get_cached_data_by_suffix(self, file_stem: str, allowed_suffixes: list[str]):
|
||||
for suffix in allowed_suffixes:
|
||||
file_data = self._get_cached_file_data(f"{file_stem}{suffix}")
|
||||
file_data = self._get_cached_file_data(
|
||||
Path(file_stem).with_suffix(suffix).as_posix()
|
||||
)
|
||||
if file_data is not None:
|
||||
return file_data
|
||||
return None
|
||||
@ -154,9 +152,6 @@ class RemoteSDCardFileList:
|
||||
return True
|
||||
|
||||
self._selected_file_info = file_info
|
||||
self._logger.info(
|
||||
f"File opened: {self._selected_file_info.file_name} Size: {self._selected_file_info.size}"
|
||||
)
|
||||
return True
|
||||
|
||||
def delete_file(self, file_path: str) -> None:
|
||||
|
@ -36,7 +36,6 @@ class PrintingState(APrinterState):
|
||||
self._start_worker_thread()
|
||||
|
||||
def finalize(self):
|
||||
|
||||
if self._sd_printing_thread is not None and self._sd_printing_thread.is_alive():
|
||||
self._is_printing = False
|
||||
self._sd_printing_thread.join()
|
||||
@ -82,7 +81,8 @@ class PrintingState(APrinterState):
|
||||
if self._printer.bambu_client.connected:
|
||||
if self._printer.bambu_client.publish(pybambu.commands.STOP):
|
||||
self._log.info("print cancelled")
|
||||
self._printer.change_state(self._printer._state_finished)
|
||||
self._finish_print()
|
||||
self._printer.change_state(self._printer._state_idle)
|
||||
else:
|
||||
self._log.info("print cancel failed")
|
||||
|
||||
@ -91,5 +91,6 @@ class PrintingState(APrinterState):
|
||||
self._log.debug(
|
||||
f"SD File Print finishing: {self._printer.current_print_job.file_info.file_name}"
|
||||
)
|
||||
self._printer.sendIO("Done printing file")
|
||||
|
||||
self._printer.change_state(self._printer._state_idle)
|
||||
|
@ -107,7 +107,10 @@ def ftps_session_mock(files_info_ftp):
|
||||
filter(lambda f: Path(f).parent == Path("."), all_files)
|
||||
),
|
||||
("cache/", ".3mf"): list(
|
||||
filter(lambda f: Path(f).parent == Path("cache/"), all_files)
|
||||
map(
|
||||
lambda f: Path(f).name,
|
||||
filter(lambda f: Path(f).parent == Path("cache/"), all_files),
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
@ -164,7 +167,7 @@ def printer(
|
||||
pass
|
||||
|
||||
BambuVirtualPrinter._create_client_connection_async = _mock_connection
|
||||
serial_obj = BambuVirtualPrinter(
|
||||
printer_test = BambuVirtualPrinter(
|
||||
settings,
|
||||
profile_manager,
|
||||
data_folder=output_test_folder,
|
||||
@ -172,9 +175,11 @@ def printer(
|
||||
read_timeout=0.01,
|
||||
faked_baudrate=115200,
|
||||
)
|
||||
serial_obj._bambu_client = bambu_client_mock
|
||||
yield serial_obj
|
||||
serial_obj.close()
|
||||
printer_test._bambu_client = bambu_client_mock
|
||||
printer_test.flush()
|
||||
printer_test.readlines()
|
||||
yield printer_test
|
||||
printer_test.close()
|
||||
|
||||
|
||||
def test_initial_state(printer: BambuVirtualPrinter):
|
||||
@ -206,7 +211,8 @@ def test_non_existing_file_not_selected(printer: BambuVirtualPrinter):
|
||||
printer.write(b"M23 non_existing.3mf\n")
|
||||
printer.flush()
|
||||
result = printer.readlines()
|
||||
assert result[0] == b"ok"
|
||||
assert result[-2] != b"File selected"
|
||||
assert result[-1] == b"ok"
|
||||
assert printer.file_system.selected_file is None
|
||||
|
||||
|
||||
@ -220,7 +226,8 @@ def test_print_started_with_selected_file(printer: BambuVirtualPrinter, print_jo
|
||||
printer.write(b"M23 print.3mf\n")
|
||||
printer.flush()
|
||||
result = printer.readlines()
|
||||
assert result[0] == b"ok"
|
||||
assert result[-2] == b"File selected"
|
||||
assert result[-1] == b"ok"
|
||||
|
||||
assert printer.file_system.selected_file is not None
|
||||
assert printer.file_system.selected_file.file_name == "print.3mf"
|
||||
|
Loading…
x
Reference in New Issue
Block a user