Skip to content

Commit 3107851

Browse files
authored
Merge pull request #24 from Josef-MrBeam/beta
stable release
2 parents 944f9d6 + b2b9022 commit 3107851

File tree

64 files changed

+1590
-685
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1590
-685
lines changed

octoprint_mrbeam/__init__.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import __builtin__
55
import copy
66
import json
7+
import operator
78
import os
89
import platform
910
import pprint
@@ -13,7 +14,9 @@
1314
import datetime
1415
import collections
1516
import logging
17+
import unicodedata
1618
from subprocess import check_output
19+
import pkg_resources
1720

1821
import octoprint.plugin
1922
import requests
@@ -38,6 +41,9 @@
3841
from ._version import get_versions
3942

4043
__version__ = get_versions()["version"]
44+
if isinstance(__version__, unicode):
45+
__version__ = unicodedata.normalize('NFKD', __version__).encode('ascii', 'ignore')
46+
4147
del get_versions
4248

4349
from octoprint_mrbeam.iobeam.iobeam_handler import ioBeamHandler, IoBeamEvents
@@ -100,6 +106,7 @@
100106
from octoprint_mrbeam.util.uptime import get_uptime, get_uptime_human_readable
101107
from octoprint_mrbeam.util import get_thread
102108
from octoprint_mrbeam import camera
109+
from octoprint_mrbeam.util.version_comparator import compare_pep440_versions
103110

104111
# this is a easy&simple way to access the plugin and all injections everywhere within the plugin
105112
__builtin__._mrbeam_plugin_implementation = None
@@ -155,6 +162,7 @@ class MrBeamPlugin(
155162
TIME_NTP_SYNC_CHECK_INTERVAL_FAST = 10.0
156163
TIME_NTP_SYNC_CHECK_INTERVAL_SLOW = 120.0
157164

165+
158166
def __init__(self):
159167
self.mrbeam_plugin_initialized = False
160168
self._shutting_down = False
@@ -175,6 +183,7 @@ def __init__(self):
175183
self._serial_num = None
176184
self._mac_addrs = dict()
177185
self._model_id = None
186+
self._explicit_update_check = False
178187
self._grbl_version = None
179188
self._device_series = self._device_info.get_series()
180189
self.called_hosts = []
@@ -257,10 +266,10 @@ def initialize(self):
257266
self.led_event_listener.set_fps(self._settings.get(["leds", "fps"]))
258267
# start iobeam socket only once other handlers are already initialized so that we can handle info message
259268
self.iobeam = ioBeamHandler(self)
260-
self.temperature_manager = temperatureManager(self)
261269
self.dust_manager = dustManager(self)
262270
self.hw_malfunction_handler = hwMalfunctionHandler(self)
263271
self.laserhead_handler = laserheadHandler(self)
272+
self.temperature_manager = temperatureManager(self)
264273
self.compressor_handler = compressor_handler(self)
265274
self.wizard_config = WizardConfig(self)
266275
self.job_time_estimation = JobTimeEstimation(self)
@@ -738,6 +747,16 @@ def calibration_tool_mode(self):
738747
self._fixEmptyUserManager()
739748
return ret
740749

750+
@property
751+
def explicit_update_check(self):
752+
return self._explicit_update_check
753+
754+
def set_explicit_update_check(self):
755+
self._explicit_update_check = True
756+
757+
def clear_explicit_update_check(self):
758+
self._explicit_update_check = False
759+
741760
##~~ UiPlugin mixin
742761

743762
def will_handle_ui(self, request):
@@ -811,7 +830,7 @@ def on_ui_render(self, now, request, render_kwargs):
811830
now=now,
812831
init_ts_ms=time.time() * 1000,
813832
language=language,
814-
beamosVersionNumber=self._plugin_version,
833+
mrBeamPluginVersionNumber=self._plugin_version,
815834
beamosVersionBranch=self._branch,
816835
beamosVersionDisplayVersion=display_version_string,
817836
beamosVersionImage=self._octopi_info,
@@ -1281,6 +1300,16 @@ def generate_backlash_compenation_pattern_gcode(self, data):
12811300
res = dict(calibration_pattern=destFile, target=FileDestinations.LOCAL)
12821301
return jsonify(res)
12831302

1303+
# simpleApiCommand: compare_pep440_versions;
1304+
def handle_pep440_comparison_result(self, data):
1305+
try:
1306+
result = compare_pep440_versions(data['v1'], data['v2'], data['operator'])
1307+
return make_response(json.dumps(result), 200)
1308+
except KeyError as e:
1309+
self._logger.error("Key is missing in data: %s", e)
1310+
return make_response(json.dumps(None), 500)
1311+
1312+
12841313
# ~~ helpers
12851314

12861315
# helper method to write data to user settings
@@ -1347,7 +1376,7 @@ def calibration_wrapper(self):
13471376
locales=dict(),
13481377
supportedExtensions=[],
13491378
# beamOS version
1350-
beamosVersionNumber=self._plugin_version,
1379+
mrBeamPluginVersionNumber=self._plugin_version,
13511380
beamosVersionBranch=self._branch,
13521381
beamosVersionDisplayVersion=display_version_string,
13531382
beamosVersionImage=self._octopi_info,
@@ -1913,6 +1942,7 @@ def get_api_commands(self):
19131942
camera_stop_lens_calibration=[],
19141943
generate_calibration_markers_svg=[],
19151944
cancel_final_extraction=[],
1945+
compare_pep440_versions=[]
19161946
)
19171947

19181948
def on_api_command(self, command, data):
@@ -2040,6 +2070,8 @@ def on_api_command(self, command, data):
20402070
) # TODO move this func to other file
20412071
elif command == "cancel_final_extraction":
20422072
self.dust_manager.set_user_abort_final_extraction()
2073+
elif command == "compare_pep440_versions":
2074+
return self.handle_pep440_comparison_result(data)
20432075

20442076
return NO_CONTENT
20452077

@@ -2981,10 +3013,10 @@ def __calc_time_ntp_offset(self, log_out_of_sync=False):
29813013
timer.start()
29823014

29833015
def is_beta_channel(self):
2984-
return self._settings.get(["dev", "software_tier"]) == SWUpdateTier.BETA
3016+
return self._settings.get(["dev", "software_tier"]) == SWUpdateTier.BETA.value
29853017

29863018
def is_develop_channel(self):
2987-
return self._settings.get(["dev", "software_tier"]) == SWUpdateTier.DEV
3019+
return self._settings.get(["dev", "software_tier"]) == SWUpdateTier.DEV.value
29883020

29893021
def _get_mac_addresses(self):
29903022
if not self._mac_addrs:

octoprint_mrbeam/analytics/analytics_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def analyticsHandler(plugin):
3636

3737
class AnalyticsHandler(object):
3838
QUEUE_MAXSIZE = 1000
39-
ANALYTICS_LOG_VERSION = 22 # bumped for SW-653 - added frontend event update_info_call_failure to see when this backend call fails
39+
ANALYTICS_LOG_VERSION = 23 # bumped for SW-1317 - added pressure to test fan rpm event
4040

4141
def __init__(self, plugin):
4242
self._plugin = plugin

octoprint_mrbeam/analytics/timer_handler.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# coding=utf-8
2+
import re
23

34
import netifaces
45
import requests
@@ -215,7 +216,7 @@ def _software_versions(self):
215216
it made sens when we did a filesystem checksum calculation... _software_versions_and_checksums()
216217
"""
217218
sw_versions = self._get_software_versions()
218-
self._logger.debug("_software_versions: %s", sw_versions)
219+
self._logger.debug("Software Version info: \n{}".format(sw_versions))
219220
self._plugin.analytics_handler.add_software_versions(sw_versions)
220221

221222
def _software_versions_and_checksums(self):
@@ -279,31 +280,38 @@ def _software_versions_and_checksums(self):
279280

280281
def _get_software_versions(self):
281282
result = dict()
282-
configured_checks = None
283+
software_info = None
284+
283285
try:
284-
pluginInfo = self._plugin._plugin_manager.get_plugin_info("softwareupdate")
285-
if pluginInfo is not None:
286-
impl = pluginInfo.implementation
287-
configured_checks = impl._configured_checks
288-
else:
289-
self._logger.error(
290-
"_get_software_versions() Can't get pluginInfo.implementation"
291-
)
286+
plugin_info = self._plugin._plugin_manager.get_plugin_info("softwareupdate")
287+
impl = plugin_info.implementation
288+
# using the method get_current_versions() is OK as it is threadsafe
289+
software_info, _, _ = impl.get_current_versions()
290+
self._logger.debug("Software_info: \n {}".format(software_info))
292291
except Exception as e:
293292
self._logger.exception(
294-
"Exception while reading configured_checks from softwareupdate plugin. "
293+
"Exception while reading software_info from softwareupdate plugin. Error:{} ".format(e)
295294
)
295+
return result
296296

297-
if configured_checks is None:
297+
if isinstance(software_info, dict) is False:
298298
self._logger.warn(
299-
"_get_software_versions() Can't read software version from softwareupdate plugin."
299+
"get_current_versions() Can't read software version from softwareupdate plugin."
300+
)
301+
return result
302+
303+
# Reaching this section means we are OK so far
304+
for name, info in software_info.iteritems():
305+
commit_hash = info["information"]["local"].get("name", None)
306+
if commit_hash is not None:
307+
# regex: "Commit 89nhfbffnf7f8fbfgfndhf" --> "89nhfbffnf7f8fbfgfndhf"
308+
regex_match = re.match(r"Commit (\S+)", commit_hash)
309+
if regex_match is not None:
310+
commit_hash = regex_match.group(1)
311+
result[name] = dict(
312+
version=info.get("displayVersion", None),
313+
commit_hash=commit_hash,
300314
)
301-
else:
302-
for name, config in configured_checks.iteritems():
303-
result[name] = dict(
304-
version=config.get("displayVersion", None),
305-
commit_hash=config.get("current", None),
306-
)
307315
return result
308316

309317
def _num_files(self):

octoprint_mrbeam/analytics/usage_handler.py

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import os
22
import time
3+
import unicodedata
4+
35
import yaml
46

7+
from octoprint_mrbeam.iobeam.iobeam_handler import IoBeamValueEvents
58
from octoprint_mrbeam.mrb_logger import mrb_logger
69
from octoprint.events import Events as OctoPrintEvents
710
from octoprint_mrbeam.mrbeam_events import MrBeamEvents
@@ -18,7 +21,6 @@ def usageHandler(plugin):
1821

1922

2023
class UsageHandler(object):
21-
MAX_DUST_FACTOR = 2.0
2224
MIN_DUST_FACTOR = 0.5
2325
MAX_DUST_VALUE = 0.5
2426
MIN_DUST_VALUE = 0.2
@@ -40,12 +42,6 @@ def __init__(self, plugin):
4042
self.start_ntp_synced = None
4143

4244
self._last_dust_value = None
43-
self._dust_mapping_m = (self.MAX_DUST_FACTOR - self.MIN_DUST_FACTOR) / (
44-
self.MAX_DUST_VALUE - self.MIN_DUST_VALUE
45-
)
46-
self._dust_mapping_b = (
47-
self.MIN_DUST_FACTOR - self._dust_mapping_m * self.MIN_DUST_VALUE
48-
)
4945

5046
analyticsfolder = os.path.join(
5147
self._settings.getBaseFolder("base"),
@@ -78,12 +74,26 @@ def _on_mrbeam_plugin_initialized(self, event, payload):
7874
self._laser_head_serial = self._lh["serial"]
7975
else:
8076
self._laser_head_serial = "no_serial"
81-
77+
self._calculate_dust_mapping()
8278
self._init_missing_usage_data()
8379
self.log_usage()
8480

8581
self._subscribe()
8682

83+
def _calculate_dust_mapping(self):
84+
max_dust_factor = self._laserhead_handler.current_laserhead_max_dust_factor
85+
self._dust_mapping_m = (max_dust_factor - self.MIN_DUST_FACTOR) / (
86+
self.MAX_DUST_VALUE - self.MIN_DUST_VALUE
87+
)
88+
self._dust_mapping_b = (
89+
self.MIN_DUST_FACTOR - self._dust_mapping_m * self.MIN_DUST_VALUE
90+
)
91+
self._logger.debug(
92+
"new dust mapping -> {} - {} - {}".format(
93+
max_dust_factor, self._dust_mapping_m, self._dust_mapping_b
94+
)
95+
)
96+
8797
def log_usage(self):
8898
self._logger.info(
8999
"Usage: total_usage: {}, pre-filter: {}, main filter: {}, current laser head: {}, mechanics: {}, compressor: {} - {}".format(
@@ -117,6 +127,9 @@ def _subscribe(self):
117127
self._event_bus.subscribe(
118128
MrBeamEvents.LASER_HEAD_READ, self.event_laser_head_read
119129
)
130+
self._plugin.iobeam.subscribe(
131+
IoBeamValueEvents.LASERHEAD_CHANGED, self._event_laserhead_changed
132+
)
120133

121134
def event_laser_head_read(self, event, payload):
122135
# Update laser head info if necessary --> Only update if there is a serial number different than the previous
@@ -164,6 +177,17 @@ def event_stop(self, event, payload):
164177

165178
self.write_usage_analytics(action="job_finished")
166179

180+
def _event_laserhead_changed(self, event):
181+
"""
182+
will be triggered if the laser head changed,
183+
refreshes the laserhead max dust factor that will be used for the new laser head
184+
185+
Returns:
186+
187+
"""
188+
self._logger.debug("Laserhead changed recalculate dust mapping")
189+
self._calculate_dust_mapping()
190+
167191
def _set_time(self, job_duration):
168192
if job_duration is not None and job_duration > 0.0:
169193

@@ -356,36 +380,66 @@ def _load_usage_data(self):
356380
"Trying to recover from _backup_file file: %s", self._backup_file
357381
)
358382
recovery_try = True
359-
if os.path.isfile(self._backup_file):
383+
try:
384+
with open(self._backup_file, "r") as stream:
385+
data = yaml.safe_load(stream)
386+
if self._validate_data(data):
387+
data["restored"] = (
388+
data["restored"] + 1 if "restored" in data else 1
389+
)
390+
self._usage_data = data
391+
self._write_usage_data()
392+
success = True
393+
self._logger.info("Recovered from _backup_file file. Yayy!")
394+
except yaml.constructor.ConstructorError:
360395
try:
361-
data = None
362-
with open(self._backup_file, "r") as stream:
363-
data = yaml.safe_load(stream)
364-
if self._validate_data(data):
365-
data["restored"] = (
366-
data["restored"] + 1 if "restored" in data else 1
367-
)
368-
self._usage_data = data
369-
success = True
370-
self._write_usage_data()
371-
self._logger.info("Recovered from _backup_file file. Yayy!")
372-
except:
373-
self._logger.error("Can't read _backup_file file.")
396+
success = self._repair_backup_usage_data()
397+
except Exception:
398+
self._logger.error("Repair of the _backup_file failed.")
399+
except OSError:
400+
self._logger.error("There is no _backup_file file.")
401+
except yaml.YAMLError:
402+
self._logger.error("There was a YAMLError with the _backup_file file.")
403+
except:
404+
self._logger.error("Can't read _backup_file file.")
374405

375406
if not success:
376407
self._logger.warn("Resetting usage data. (marking as incomplete)")
377408
self._usage_data = self._get_usage_data_template()
378409
if recovery_try:
379410
self._write_usage_data()
380411

412+
def _repair_backup_usage_data(self):
413+
"""
414+
repairs a broken usage backup file, where the version is saved in unicode
415+
416+
Returns:
417+
boolean: successfull
418+
"""
419+
success = False
420+
with open(self._backup_file, "r") as stream:
421+
data = yaml.load(stream)
422+
if self._validate_data(data):
423+
#checks if the version is saved in unicode and converts it into a string see SW-1269
424+
if isinstance(data["version"], unicode):
425+
data["version"] = unicodedata.normalize('NFKD', data["version"]).encode('ascii', 'ignore')
426+
data["restored"] = (
427+
data["restored"] + 1 if "restored" in data else 1
428+
)
429+
self._usage_data = data
430+
success = True
431+
self._write_usage_data()
432+
self._logger.info("Could repair _backup_file file. Yayy!")
433+
return success
434+
381435
def _write_usage_data(self, file=None):
382436
self._usage_data["version"] = self._plugin_version
383437
self._usage_data["ts"] = time.time()
384438
self._usage_data["serial"] = self._device_serial
385439
file = self._storage_file if file is None else file
386440
try:
387441
with open(file, "w") as outfile:
388-
yaml.dump(self._usage_data, outfile, default_flow_style=False)
442+
yaml.safe_dump(self._usage_data, outfile, default_flow_style=False)
389443
except:
390444
self._logger.exception("Can't write file %s due to an exception: ", file)
391445

octoprint_mrbeam/decorator/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)