From 622d88fc8b53c3ef384dea7ec6882e8efdeee23f Mon Sep 17 00:00:00 2001 From: Andy Werner Date: Tue, 11 Sep 2018 10:38:54 +0200 Subject: [PATCH 1/4] v0.1.44: Reducing polling of status from grbl to avoid gcode error 24 --- octoprint_mrbeam/comm_acc2.py | 158 +++++++++++++++++++++++----------- octoprint_mrbeam/printer.py | 8 +- setup.py | 2 +- 3 files changed, 113 insertions(+), 55 deletions(-) diff --git a/octoprint_mrbeam/comm_acc2.py b/octoprint_mrbeam/comm_acc2.py index ada3cabd7..31470f35c 100644 --- a/octoprint_mrbeam/comm_acc2.py +++ b/octoprint_mrbeam/comm_acc2.py @@ -79,6 +79,12 @@ class MachineCom(object): COMMAND_FLUSH = 'FLUSH' COMMAND_SYNC = 'SYNC' # experimental + STATUS_POLL_FREQUENCY_OPERATIONAL = 2.0 + STATUS_POLL_FREQUENCY_PRINTING = 5.0 # set back top 1.0 if it's not causing gcode24 + STATUS_POLL_FREQUENCY_PAUSED = 0.2 + STATUS_POLL_FREQUENCY_SYNCING = 0.2 + STATUS_POLL_FREQUENCY_DEFAULT = STATUS_POLL_FREQUENCY_PRINTING + GRBL_SYNC_COMMAND_WAIT_STATES = (GRBL_STATE_RUN, GRBL_STATE_QUEUE) GRBL_HEX_FOLDER = 'files/grbl/' @@ -87,6 +93,8 @@ class MachineCom(object): pattern_grbl_version = re.compile("Grbl (?P\S+)\s.*") pattern_grbl_setting = re.compile("\$(?P\d+)=(?P\S+)\s\((?P.*)\)") + pattern_get_x_coord_from_gcode = re.compile("^G.*X(\d{1,3}\.?\d{0,3})\D.*") + pattern_get_y_coord_from_gcode = re.compile("^G.*Y(\d{1,3}\.?\d{0,3})\D.*") def __init__(self, port=None, baudrate=None, callbackObject=None, printerProfileManager=None): self._logger = mrb_logger("octoprint.plugins.mrbeam.comm_acc2") @@ -119,8 +127,13 @@ def __init__(self, port=None, baudrate=None, callbackObject=None, printerProfile self._errorValue = "Unknown Error" self._serial = None self._currentFile = None - self._status_timer = None + self._status_polling_timer = None + self._status_polling_next_ts = 0 + self._status_polling_interval = self.STATUS_POLL_FREQUENCY_DEFAULT self._acc_line_buffer = [] + self._last_acknowledged_command = None + self._last_wx = -1 + self._last_wy = -1 self._cmd = None self._pauseWaitStartTime = None self._pauseWaitTimeLost = 0.0 @@ -166,6 +179,7 @@ def __init__(self, port=None, baudrate=None, callbackObject=None, printerProfile self.monitoring_thread = None self.sending_thread = None self.start_monitoring_thread() + self.start_status_polling_timer() def start_monitoring_thread(self): @@ -180,6 +194,12 @@ def start_sending_thread(self): self.sending_thread.daemon = True self.sending_thread.start() + def start_status_polling_timer(self): + if self._status_polling_timer is not None: + self._status_polling_timer.cancel() + self._status_polling_timer = RepeatedTimer(0.1, self._poll_status) + self._status_polling_timer.start() + def get_home_position(self): """ @@ -407,7 +427,10 @@ def _readline(self): self._send_event.set() if('ok' in ret or 'error' in ret): if(len(self._acc_line_buffer) > 0): + self._last_acknowledged_command = self._acc_line_buffer[0] del self._acc_line_buffer[0] # Delete the commands character count corresponding to the last 'ok' + else: + self._logger.error("Received OK but internal _acc_line_buffer counter is empty.") except serial.SerialException: self._logger.error("Unexpected error while reading serial port: %s" % (get_exception_string()), terminal_as_comm=True) self._errorValue = get_exception_string() @@ -484,6 +507,8 @@ def _handle_status_report(self, line): self.MPosY = float(groups['mpos_y']) wx = float(groups['pos_x']) wy = float(groups['pos_y']) + self._last_wx = wx + self._last_wy = wy self._callback.on_comm_pos_update([self.MPosX, self.MPosY, 0], [wx, wy, 0]) except: self._logger.exception("Exception while handling position updates from GRBL.") @@ -517,29 +542,60 @@ def _handle_laser_intensity_for_analytics(self, laser_state, laser_intensity): analytics.add_laser_intensity_value(int(laser_intensity)) - def _handle_ok_message(self, line=None): + def _handle_ok_message(self, line): if self._state == self.STATE_HOMING: self._changeState(self.STATE_OPERATIONAL) - if not self.grbl_feat_report_rx_buffer_state: - return - - # important that we add every call and count the invalid values internally! - rx_free = None - try: - if line is not None: - rx_free = int(line.split(':')[1]) # ok:127 - except e: - self._logger.warn("_handle_ok_message() Can't read free_rx_bytes from line: '%s', error: %s", line, e) - self._rx_stats.add(rx_free) + # update working pos from acknowledged gcode + my_cmd = self._last_acknowledged_command + if my_cmd.startswith('G'): + x_pos = self._last_wx + y_pos = self._last_wy + match = self.pattern_get_x_coord_from_gcode.match(my_cmd) + if match: + try: + x_pos = float(match.group(1)) + self._last_wx = x_pos + except: + self._logger.exception("Exception in parsing x coord from gcode. my_cmd: '%s' ", my_cmd) + pass + + match = self.pattern_get_y_coord_from_gcode.match(my_cmd) + if match: + try: + y_pos = float(match.group(1)) + self._last_wy = y_pos + except: + self._logger.exception("Exception in parsing y coord from gcode. my_cmd: '%s' ", my_cmd) + pass + + if x_pos >= 0 or y_pos >= 0: + self._callback.on_comm_pos_update(None, [x_pos, y_pos, 0]) + # since we just got a postion update we can reset the wait time for the next status poll + # ideally we never poll statuses during engravings + self._reset_status_polling_waittime() + + if self.grbl_feat_report_rx_buffer_state: + # important that we add every call and count the invalid values internally! + rx_free = None + try: + if line is not None: + rx_free = int(line.split(':')[1]) # ok:127 + except e: + self._logger.warn("_handle_ok_message() Can't read free_rx_bytes from line: '%s', error: %s", line, e) + self._rx_stats.add(rx_free) def _handle_error_message(self, line): + # grbl repots an error if there was never any data written to it's eeprom. + # it's going to write default values to eeprom and everything is fine then.... if "EEPROM read fail" in line: + self._logger.debug("_handle_error_message() Ignoring this error message: '%s'", line) return self._errorValue = line eventManager().fire(OctoPrintEvents.ERROR, {"error": self.getErrorString()}) self._changeState(self.STATE_LOCKED) self._logger.dump_terminal_buffer(level=logging.ERROR) + self._rx_stats.pp(print_time=self.getPrintTime()) def _handle_alarm_message(self, line): if "Hard/soft limit" in line: @@ -764,21 +820,7 @@ def _changeState(self, newState): if self._state == newState: return - if newState == self.STATE_PRINTING: - if self._status_timer is not None: - self._status_timer.cancel() - self._status_timer = RepeatedTimer(1, self._poll_status) - self._status_timer.start() - elif newState == self.STATE_OPERATIONAL: - if self._status_timer is not None: - self._status_timer.cancel() - self._status_timer = RepeatedTimer(2, self._poll_status) - self._status_timer.start() - elif newState == self.STATE_PAUSED: - if self._status_timer is not None: - self._status_timer.cancel() - self._status_timer = RepeatedTimer(0.2, self._poll_status) - self._status_timer.start() + self._set_status_polling_interval_for_state(state=newState) if newState == self.STATE_CLOSED or newState == self.STATE_CLOSED_WITH_ERROR: if self._currentFile is not None: @@ -819,9 +861,38 @@ def _detectPort(self, close): return None def _poll_status(self): - if self.isOperational(): - self._real_time_commands['poll_status']=True - self._send_event.set() + """ + Called by RepeatedTimer self._status_polling_timer every 0.1 secs + We need to descide here if we should send a status request + """ + try: + if self.isOperational(): + if time.time() >= self._status_polling_next_ts: + self._real_time_commands['poll_status'] = True + self._send_event.set() + self._status_polling_next_ts = time.time() + self._status_polling_interval + except: + self._logger.exception("Exception in status polling call: ") + + def _reset_status_polling_waittime(self): + """ + Resets wait time till we should do next status polling + This is typically called after we received an ok with a position + """ + self._status_polling_next_ts = time.time() + self._status_polling_interval + + def _set_status_polling_interval_for_state(self, state=None): + """ + Sets polling interval according to current state + :param state: (optional) state, if None, self._state is used + """ + state = state or self._state + if state == self.STATE_PRINTING: + self._status_polling_interval = self.STATUS_POLL_FREQUENCY_PRINTING + elif state == self.STATE_OPERATIONAL: + self._status_polling_interval = self.STATUS_POLL_FREQUENCY_OPERATIONAL + elif state == self.STATE_PAUSED: + self._status_polling_interval = self.STATUS_POLL_FREQUENCY_PAUSED def _soft_reset(self): if self.isOperational(): @@ -1149,22 +1220,15 @@ def sendCommand(self, cmd, cmd_type=None, processed=False): tokens = cmd.split(' ') specialcmd = tokens[0].lower() if specialcmd.startswith('/togglestatusreport'): - if self._status_timer is None: - self._status_timer = RepeatedTimer(1, self._poll_status) - self._status_timer.start() + if self._status_polling_interval <= 0: + self._set_status_polling_interval_for_state() else: - self._status_timer.cancel() - self._status_timer = None + self._status_polling_interval = 0 elif specialcmd.startswith('/setstatusfrequency'): try: - frequency = float(tokens[1]) + self._status_polling_interval = float(tokens[1]) except ValueError: - self._log("No frequency setting found! Using 1 sec.") - frequency = 1 - if self._status_timer is not None: - self._status_timer.cancel() - self._status_timer = RepeatedTimer(frequency, self._poll_status) - self._status_timer.start() + self._log("No frequency setting found! No change") elif specialcmd.startswith('/disconnect'): self.close() elif specialcmd.startswith('/feedrate'): @@ -1474,15 +1538,9 @@ def getGrblVersion(self): return self._grbl_version def close(self, isError=False, next_state=None): - if self._status_timer is not None: - try: - self._status_timer.cancel() - self._status_timer = None - except AttributeError: - pass - self._monitoring_active = False self._sending_active = False + self._status_polling_interval = 0 printing = self.isPrinting() or self.isPaused() if self._serial is not None: diff --git a/octoprint_mrbeam/printer.py b/octoprint_mrbeam/printer.py index 13af723fc..8c16f4463 100644 --- a/octoprint_mrbeam/printer.py +++ b/octoprint_mrbeam/printer.py @@ -162,10 +162,10 @@ def on_comm_progress(self): self._stateMonitor.trigger_progress_update() def _add_position_data(self, MPos, WPos): - if MPos is None or WPos is None: - MPos = WPos = [0, 0, 0] - self._stateMonitor.setWorkPosition(WPos) - self._stateMonitor.setMachinePosition(MPos) + if MPos is not None: + self._stateMonitor.setMachinePosition(MPos) + if WPos is not None: + self._stateMonitor.setWorkPosition(WPos) def _init_terminal(self): from collections import deque diff --git a/setup.py b/setup.py index 054dfd4f8..9a3720c91 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ plugin_name = "Mr_Beam" # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module -plugin_version = "0.1.42" +plugin_version = "0.1.44" # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # module From e800353d4cc4fdc7dff9dbe66c8d671e3db0d886 Mon Sep 17 00:00:00 2001 From: Andy Werner Date: Mon, 9 Jul 2018 19:19:22 +0200 Subject: [PATCH 2/4] v0.1.41: new logrotate conf (rotate hourly and by file size) --- .../files/migrate/logrotate/haproxy | 11 ++++++ .../files/migrate/logrotate/iobeam | 9 +++++ .../files/migrate/logrotate/mrbeam_ledstrips | 10 +++++ .../files/migrate/logrotate/netconnectd | 9 +++++ .../files/migrate/logrotate/rsyslog | 37 +++++++++++++++++++ octoprint_mrbeam/migrate.py | 23 ++++++++++-- 6 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 octoprint_mrbeam/files/migrate/logrotate/haproxy create mode 100644 octoprint_mrbeam/files/migrate/logrotate/iobeam create mode 100644 octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips create mode 100644 octoprint_mrbeam/files/migrate/logrotate/netconnectd create mode 100644 octoprint_mrbeam/files/migrate/logrotate/rsyslog diff --git a/octoprint_mrbeam/files/migrate/logrotate/haproxy b/octoprint_mrbeam/files/migrate/logrotate/haproxy new file mode 100644 index 000000000..de8e829d9 --- /dev/null +++ b/octoprint_mrbeam/files/migrate/logrotate/haproxy @@ -0,0 +1,11 @@ +/var/log/haproxy.log { + daily + rotate 4 + missingok + notifempty + compress + delaycompress + postrotate + invoke-rc.d rsyslog rotate >/dev/null 2>&1 || true + endscript +} diff --git a/octoprint_mrbeam/files/migrate/logrotate/iobeam b/octoprint_mrbeam/files/migrate/logrotate/iobeam new file mode 100644 index 000000000..b6e4f8898 --- /dev/null +++ b/octoprint_mrbeam/files/migrate/logrotate/iobeam @@ -0,0 +1,9 @@ +/var/log/iobeam.log { + size 2M + rotate 4 + compress + delaycompress + missingok + notifempty + copytruncate +} diff --git a/octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips b/octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips new file mode 100644 index 000000000..816b2ac85 --- /dev/null +++ b/octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips @@ -0,0 +1,10 @@ +/var/log/mrbeam_ledstrips.log { + size 2M + rotate 2 + compress + delaycompress + missingok + notifempty + copytruncate + create 644 root root +} diff --git a/octoprint_mrbeam/files/migrate/logrotate/netconnectd b/octoprint_mrbeam/files/migrate/logrotate/netconnectd new file mode 100644 index 000000000..4cd6caf3a --- /dev/null +++ b/octoprint_mrbeam/files/migrate/logrotate/netconnectd @@ -0,0 +1,9 @@ +/var/log/netconnectd.log { + size 2M + rotate 2 + compress + delaycompress + missingok + notifempty + copytruncate +} diff --git a/octoprint_mrbeam/files/migrate/logrotate/rsyslog b/octoprint_mrbeam/files/migrate/logrotate/rsyslog new file mode 100644 index 000000000..58e6725d7 --- /dev/null +++ b/octoprint_mrbeam/files/migrate/logrotate/rsyslog @@ -0,0 +1,37 @@ +/var/log/syslog +{ + rotate 2 + size 5M + missingok + notifempty + delaycompress + compress + postrotate + invoke-rc.d rsyslog rotate > /dev/null + endscript +} + +/var/log/mail.info +/var/log/mail.warn +/var/log/mail.err +/var/log/mail.log +/var/log/daemon.log +/var/log/kern.log +/var/log/auth.log +/var/log/user.log +/var/log/lpr.log +/var/log/cron.log +/var/log/debug +/var/log/messages +{ + rotate 2 + size 1M + missingok + notifempty + compress + delaycompress + sharedscripts + postrotate + invoke-rc.d rsyslog rotate > /dev/null + endscript +} diff --git a/octoprint_mrbeam/migrate.py b/octoprint_mrbeam/migrate.py index 89fd69265..addb5fa66 100644 --- a/octoprint_mrbeam/migrate.py +++ b/octoprint_mrbeam/migrate.py @@ -14,10 +14,11 @@ def migrate(plugin): class Migration(object): - VERSION_SETUP_IPTABLES = '0.1.19' - VERSION_SYNC_GRBL_SETTINGS = '0.1.24' - VERSION_FIX_SSH_KEY_PERMISSION = '0.1.28' + VERSION_SETUP_IPTABLES = '0.1.19' + VERSION_SYNC_GRBL_SETTINGS = '0.1.24' + VERSION_FIX_SSH_KEY_PERMISSION = '0.1.28' VERSION_UPDATE_CHANGE_HOSTNAME_SCRIPTS = '0.1.37' + VERSION_UPDATE_LOGROTATE_CONF = '0.1.41' # this is where we have files needed for migrations MIGRATE_FILES_FOLDER = 'files/migrate/' @@ -64,6 +65,9 @@ def run(self): if self.version_previous is None or self._compare_versions(self.version_previous, self.VERSION_UPDATE_CHANGE_HOSTNAME_SCRIPTS, equal_ok=False): self.update_change_hostename_apname_scripts() + if self.version_previous is None or self._compare_versions(self.version_previous, self.VERSION_UPDATE_LOGROTATE_CONF, equal_ok=False): + self.update_logrotate_conf() + # migrations end self.save_current_version() @@ -256,6 +260,19 @@ def update_change_hostename_apname_scripts(self): self._logger.error("update_change_hostename_apname_scripts() One or more source files not found! Not Updated!") + def update_logrotate_conf(self): + self._logger.info("update_logrotate_conf() ") + + logrotate_d_files = ['haproxy', 'iobeam', 'mrbeam_ledstrips', 'netconnectd', 'rsyslog'] + for f in logrotate_d_files: + my_file = os.path.join(__package_path__, self.MIGRATE_FILES_FOLDER, 'logrotate', f) + exec_cmd("sudo cp {src} /etc/logrotate.d/".format(src=my_file)) + + exec_cmd("sudo mv /etc/cron.daily/logrotate /etc/cron.hourly") + exec_cmd("sudo logrotate /etc/logrotate.conf") + exec_cmd("sudo service cron restart") + + ########################################################## ##### lasercutterProfiles ##### From 340b4bbd25740a73a7714b79147391b98aab05ae Mon Sep 17 00:00:00 2001 From: Andy Werner Date: Mon, 9 Jul 2018 19:36:20 +0200 Subject: [PATCH 3/4] IMPORTANT: migration logrotate more development --- octoprint_mrbeam/files/README.md | 5 +++++ .../{migrate/logrotate => migrate_logrotate}/haproxy | 0 .../{migrate/logrotate => migrate_logrotate}/iobeam | 0 octoprint_mrbeam/files/migrate_logrotate/mount_manager | 10 ++++++++++ octoprint_mrbeam/files/migrate_logrotate/mrb_check | 10 ++++++++++ .../logrotate => migrate_logrotate}/mrbeam_ledstrips | 0 .../logrotate => migrate_logrotate}/netconnectd | 0 .../{migrate/logrotate => migrate_logrotate}/rsyslog | 0 octoprint_mrbeam/migrate.py | 8 +++++--- setup.py | 3 ++- 10 files changed, 32 insertions(+), 4 deletions(-) rename octoprint_mrbeam/files/{migrate/logrotate => migrate_logrotate}/haproxy (100%) rename octoprint_mrbeam/files/{migrate/logrotate => migrate_logrotate}/iobeam (100%) create mode 100644 octoprint_mrbeam/files/migrate_logrotate/mount_manager create mode 100644 octoprint_mrbeam/files/migrate_logrotate/mrb_check rename octoprint_mrbeam/files/{migrate/logrotate => migrate_logrotate}/mrbeam_ledstrips (100%) rename octoprint_mrbeam/files/{migrate/logrotate => migrate_logrotate}/netconnectd (100%) rename octoprint_mrbeam/files/{migrate/logrotate => migrate_logrotate}/rsyslog (100%) diff --git a/octoprint_mrbeam/files/README.md b/octoprint_mrbeam/files/README.md index 0a5627b1a..e46d4440f 100644 --- a/octoprint_mrbeam/files/README.md +++ b/octoprint_mrbeam/files/README.md @@ -1,2 +1,7 @@ All files added here into this file folder must also be added to setup.py "additional_setup_parameters" to be copied during installation! + + +WARNING: +Only Two levels of folders are supported. +Therefor it's not possible to have a subfolder in `migrate` like this `files/migrate/my_subfolder` diff --git a/octoprint_mrbeam/files/migrate/logrotate/haproxy b/octoprint_mrbeam/files/migrate_logrotate/haproxy similarity index 100% rename from octoprint_mrbeam/files/migrate/logrotate/haproxy rename to octoprint_mrbeam/files/migrate_logrotate/haproxy diff --git a/octoprint_mrbeam/files/migrate/logrotate/iobeam b/octoprint_mrbeam/files/migrate_logrotate/iobeam similarity index 100% rename from octoprint_mrbeam/files/migrate/logrotate/iobeam rename to octoprint_mrbeam/files/migrate_logrotate/iobeam diff --git a/octoprint_mrbeam/files/migrate_logrotate/mount_manager b/octoprint_mrbeam/files/migrate_logrotate/mount_manager new file mode 100644 index 000000000..b29361734 --- /dev/null +++ b/octoprint_mrbeam/files/migrate_logrotate/mount_manager @@ -0,0 +1,10 @@ +/var/log/mount_manager.log { + size 2M + rotate 2 + compress + delaycompress + missingok + notifempty + copytruncate + create 644 root root +} diff --git a/octoprint_mrbeam/files/migrate_logrotate/mrb_check b/octoprint_mrbeam/files/migrate_logrotate/mrb_check new file mode 100644 index 000000000..ed448832f --- /dev/null +++ b/octoprint_mrbeam/files/migrate_logrotate/mrb_check @@ -0,0 +1,10 @@ +/var/log/mrb_check.log { + size 5M + rotate 4 + compress + delaycompress + missingok + notifempty + copytruncate + create 644 root root +} diff --git a/octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips b/octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips similarity index 100% rename from octoprint_mrbeam/files/migrate/logrotate/mrbeam_ledstrips rename to octoprint_mrbeam/files/migrate_logrotate/mrbeam_ledstrips diff --git a/octoprint_mrbeam/files/migrate/logrotate/netconnectd b/octoprint_mrbeam/files/migrate_logrotate/netconnectd similarity index 100% rename from octoprint_mrbeam/files/migrate/logrotate/netconnectd rename to octoprint_mrbeam/files/migrate_logrotate/netconnectd diff --git a/octoprint_mrbeam/files/migrate/logrotate/rsyslog b/octoprint_mrbeam/files/migrate_logrotate/rsyslog similarity index 100% rename from octoprint_mrbeam/files/migrate/logrotate/rsyslog rename to octoprint_mrbeam/files/migrate_logrotate/rsyslog diff --git a/octoprint_mrbeam/migrate.py b/octoprint_mrbeam/migrate.py index addb5fa66..9838b5f39 100644 --- a/octoprint_mrbeam/migrate.py +++ b/octoprint_mrbeam/migrate.py @@ -21,7 +21,8 @@ class Migration(object): VERSION_UPDATE_LOGROTATE_CONF = '0.1.41' # this is where we have files needed for migrations - MIGRATE_FILES_FOLDER = 'files/migrate/' + MIGRATE_FILES_FOLDER = 'files/migrate/' + MIGRATE_LOGROTATE_FOLDER = 'files/migrate_logrotate/' def __init__(self, plugin): @@ -263,11 +264,12 @@ def update_change_hostename_apname_scripts(self): def update_logrotate_conf(self): self._logger.info("update_logrotate_conf() ") - logrotate_d_files = ['haproxy', 'iobeam', 'mrbeam_ledstrips', 'netconnectd', 'rsyslog'] + logrotate_d_files = ['haproxy', 'iobeam', 'mount_manager', 'mrb_check', 'mrbeam_ledstrips', 'netconnectd', 'rsyslog'] for f in logrotate_d_files: - my_file = os.path.join(__package_path__, self.MIGRATE_FILES_FOLDER, 'logrotate', f) + my_file = os.path.join(__package_path__, self.MIGRATE_LOGROTATE_FOLDER, f) exec_cmd("sudo cp {src} /etc/logrotate.d/".format(src=my_file)) + exec_cmd("sudo rm /var/log/*.gz") exec_cmd("sudo mv /etc/cron.daily/logrotate /etc/cron.hourly") exec_cmd("sudo logrotate /etc/logrotate.conf") exec_cmd("sudo service cron restart") diff --git a/setup.py b/setup.py index 9a3720c91..9aeb23c0b 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,8 @@ additional_setup_parameters = {'package_data': { 'octoprint_mrbeam': ['profiles/*.yaml', 'files/grbl/*.hex', - 'files/migrate/*']}, + 'files/migrate/*', + 'files/migrate_logrotate/*']}, 'setup_requires': ['numpy==1.11.2']} ######################################################################################################################## From cd06e4bd6d5e3810984f466915f48e2bef2d58dd Mon Sep 17 00:00:00 2001 From: Andy Werner Date: Mon, 17 Sep 2018 17:46:34 +0200 Subject: [PATCH 4/4] Hotfix check disk space during conversion (#332) Checks free disk space during conversion and if value is below min_required_disk_space (from config.yaml) the conversion will end with an OutOfSpaceException which triggers a nice explanation on frontend side. --- octoprint_mrbeam/__init__.py | 8 ++++- octoprint_mrbeam/gcodegenerator/converter.py | 36 +++++++++++++++++++- octoprint_mrbeam/static/js/convert.js | 12 ++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/octoprint_mrbeam/__init__.py b/octoprint_mrbeam/__init__.py index a406fe241..23d42a711 100644 --- a/octoprint_mrbeam/__init__.py +++ b/octoprint_mrbeam/__init__.py @@ -206,6 +206,7 @@ def get_settings_defaults(self): job_time = 0.0, terminal=False, vorlon=False, + converter_min_required_disk_space=100 * 1024 * 1024, # 100MB, in theory 371MB is the maximum expected file size for full working area engraving at highest resolution. dev=dict( debug=False, # deprected terminalMaxLines = 2000, @@ -1288,12 +1289,13 @@ def is_job_cancelled(): try: from .gcodegenerator.converter import Converter + from .gcodegenerator.converter import OutOfSpaceException is_job_cancelled() #check before conversion started #TODO implement cancelled_Jobs, to check if this particular Job has been canceled #TODO implement check "_cancel_job"-loop inside engine.convert(...), to stop during conversion, too - engine = Converter(params, model_path) + engine = Converter(params, model_path, min_required_disk_space=self._settings.get(['converter_min_required_disk_space'])) engine.convert(on_progress, on_progress_args, on_progress_kwargs) is_job_cancelled() #check if canceled during conversion @@ -1302,6 +1304,10 @@ def is_job_cancelled(): except octoprint.slicing.SlicingCancelled as e: self._logger.info("Conversion cancelled") raise e + except OutOfSpaceException as e: + msg = "{}: {}".format(type(e).__name__, e) + self._logger.exception("Conversion failed: {0}".format(msg)) + return False, msg except Exception as e: print e.__doc__ print e.message diff --git a/octoprint_mrbeam/gcodegenerator/converter.py b/octoprint_mrbeam/gcodegenerator/converter.py index 54c9c7939..4de2ad9cd 100644 --- a/octoprint_mrbeam/gcodegenerator/converter.py +++ b/octoprint_mrbeam/gcodegenerator/converter.py @@ -45,7 +45,7 @@ class Converter(): _tempfile = "/tmp/_converter_output.tmp" - def __init__(self, params, model_path): + def __init__(self, params, model_path, min_required_disk_space=0): self._log = logging.getLogger("octoprint.plugins.mrbeam.converter") # debugging @@ -59,6 +59,7 @@ def __init__(self, params, model_path): self.setoptions(params) self.svg_file = model_path self.document=None + self._min_required_disk_space = min_required_disk_space self._log.info('Converter Initialized: %s' % self.options) # todo need material,bounding_box_area here _mrbeam_plugin_implementation._analytics_handler.store_conversion_details(self.options) @@ -83,10 +84,39 @@ def init_output_file(self): pass # create new file and return file handle. + def check_free_space(self): + disk = os.statvfs("/") + # calculation of disk usage + totalBytes = disk.f_bsize * disk.f_blocks # disk size in bytes + totalUsedSpace = disk.f_bsize * (disk.f_blocks - disk.f_bfree) # used bytes + totalAvailSpace = float(disk.f_bsize * disk.f_bfree) # + totalAvailSpaceNonRoot = float(disk.f_bsize * disk.f_bavail) + self._log.info( + "Disk space: total: " + self._get_human_readable_bytes(totalBytes) + + ", used: " + self._get_human_readable_bytes(totalUsedSpace) + + ", available: " + self._get_human_readable_bytes(totalAvailSpace) + + ", available for non-super user: " + self._get_human_readable_bytes(totalAvailSpaceNonRoot) + + ", min required: " + self._get_human_readable_bytes(self._min_required_disk_space) + ) + if(self._min_required_disk_space > 0 and totalAvailSpaceNonRoot < self._min_required_disk_space): + msg ="Only " + self._get_human_readable_bytes(totalAvailSpaceNonRoot) + " disk space available. Min required: " + self._get_human_readable_bytes(self._min_required_disk_space) + raise OutOfSpaceException(msg) + + def _get_human_readable_bytes(self, amount): + str = "%d Bytes" % amount + if(amount > 1024 and amount <= 1024*1024): # kB + str += " (%.2f kB)" % (amount / 1024) + if(amount > 1024*1024 and amount <= 1024*1024*1024): # MB + str += " (%.2f MB)" % (amount / 1024/1024) + if(amount > 1024*1024*1024): # GB + str += " (%.2f GB)" % (amount / 1024/1024/1024) + return str def convert(self, on_progress=None, on_progress_args=None, on_progress_kwargs=None): self.init_output_file() + self.check_free_space() # has to be after init_output_file (which removes old temp files occasionally) + self.parse() options = self.options options['doc_root'] = self.document.getroot() @@ -817,3 +847,7 @@ def _get_document_viewbox_matrix(self): return [[1,0,0],[0,1,0], [0,0,1]] +class OutOfSpaceException(Exception): + pass + + diff --git a/octoprint_mrbeam/static/js/convert.js b/octoprint_mrbeam/static/js/convert.js index e9dd4e474..fce162102 100644 --- a/octoprint_mrbeam/static/js/convert.js +++ b/octoprint_mrbeam/static/js/convert.js @@ -1152,7 +1152,17 @@ $(function(){ }; self.onEventSlicingFailed = function(payload){ self.slicing_in_progress(false); - //console.log("onSlicingFailed" , payload); + + if ('reason' in payload && typeof payload['reason'] === 'string' && payload['reason'].startsWith('OutOfSpaceException')) { + var html = "
    "; + html += "To free up some disk space you may want to perform one or all of the following suggestions:"; + html += "
  • Delete CGODE files: Go to design library and click 'Only show GCode files' on the left. Here you can delete files from the according context menu.
  • "; + html += "
  • Delete design files: Go to design library and click 'Only show design files' on the left. Here you can delete files from the according context menu.
  • "; + html += "
  • Delete log files: Go to Settings -> logs and delete old log files per click on the trash bin icon.
  • "; + html += "
"; + html += 'Find more details online.'; + new PNotify({title: gettext("Get more free disk space"), text: html, type: "info", hide: false}); + } };