Files
2024-01-09 17:33:02 +08:00

156 lines
6.6 KiB
Python

# Virtual SDCard print stat tracking
#
# Copyright (C) 2020 Eric Callahan <arksine.code@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os, json, logging
class PrintStats:
def __init__(self, config):
printer = config.get_printer()
self.gcode_move = printer.load_object(config, 'gcode_move')
self.reactor = printer.get_reactor()
self.reset()
# Register commands
self.gcode = printer.lookup_object('gcode')
self.gcode.register_command(
"SET_PRINT_STATS_INFO", self.cmd_SET_PRINT_STATS_INFO,
desc=self.cmd_SET_PRINT_STATS_INFO_help)
# G28 down 12mm flag
self.power_loss = 0
self.print_duration = 0
self.z_pos_filepath = "/usr/data/creality/userdata/config/z_pos.json"
self.z_pos = self.get_z_pos()
def get_z_pos(self):
z_pos = 0
if os.path.exists(self.z_pos_filepath):
try:
with open(self.z_pos_filepath, "r") as f:
z_pos = float(json.loads(f.read()).get("z_pos", 0))
except Exception as err:
logging.error(err)
return z_pos
def _update_filament_usage(self, eventtime):
gc_status = self.gcode_move.get_status(eventtime)
cur_epos = gc_status['position'].e
self.filament_used += (cur_epos - self.last_epos) \
/ gc_status['extrude_factor']
self.last_epos = cur_epos
def set_current_file(self, filename):
self.reset()
self.filename = filename
def note_start(self, info_path=""):
curtime = self.reactor.monotonic()
# if self.print_start_time is None:
# self.print_start_time = curtime
# elif self.last_pause_time is not None:
# # Update pause time duration
# pause_duration = curtime - self.last_pause_time
# self.prev_pause_duration += pause_duration
# self.last_pause_time = None
# Reset last e-position
gc_status = self.gcode_move.get_status(curtime)
ret = {}
if info_path and os.path.exists(info_path):
try:
with open(info_path, "r") as f:
ret = json.loads(f.read())
self.filament_used = ret.get("filament_used", 0)
except Exception as err:
pass
if self.print_start_time is None:
if info_path and ret and ret.get("last_print_duration"):
self.print_start_time = curtime - int(ret.get("last_print_duration", 0))
else:
self.print_start_time = curtime
elif self.last_pause_time is not None:
# Update pause time duration
pause_duration = curtime - self.last_pause_time
self.prev_pause_duration += pause_duration
self.last_pause_time = None
self.last_epos = gc_status['position'].e
self.state = "printing"
self.error_message = ""
def note_pause(self):
if self.last_pause_time is None:
curtime = self.reactor.monotonic()
self.last_pause_time = curtime
# update filament usage
self._update_filament_usage(curtime)
if self.state != "error":
self.state = "paused"
def note_complete(self):
self._note_finish("complete")
def note_error(self, message):
self._note_finish("error", message)
def note_cancel(self):
self._note_finish("cancelled")
def _note_finish(self, state, error_message = ""):
if self.print_start_time is None:
return
self.state = state
self.error_message = error_message
eventtime = self.reactor.monotonic()
self.total_duration = eventtime - self.print_start_time
if self.filament_used < 0.0000001:
# No positive extusion detected during print
self.init_duration = self.total_duration - \
self.prev_pause_duration
self.print_start_time = None
cmd_SET_PRINT_STATS_INFO_help = "Pass slicer info like layer act and " \
"total to klipper"
def cmd_SET_PRINT_STATS_INFO(self, gcmd):
total_layer = gcmd.get_int("TOTAL_LAYER", self.info_total_layer, \
minval=0)
current_layer = gcmd.get_int("CURRENT_LAYER", self.info_current_layer, \
minval=0)
if total_layer == 0:
self.info_total_layer = None
self.info_current_layer = None
elif total_layer != self.info_total_layer:
self.info_total_layer = total_layer
self.info_current_layer = 0
if self.info_total_layer is not None and \
current_layer is not None and \
current_layer != self.info_current_layer:
self.info_current_layer = min(current_layer, self.info_total_layer)
def reset(self):
self.filename = self.error_message = ""
self.state = "standby"
self.prev_pause_duration = self.last_epos = 0.
self.filament_used = self.total_duration = 0.
self.print_start_time = self.last_pause_time = None
self.init_duration = 0.
self.info_total_layer = None
self.info_current_layer = None
def get_status(self, eventtime):
time_paused = self.prev_pause_duration
if self.print_start_time is not None:
if self.last_pause_time is not None:
# Calculate the total time spent paused during the print
time_paused += eventtime - self.last_pause_time
else:
# Accumulate filament if not paused
self._update_filament_usage(eventtime)
self.total_duration = eventtime - self.print_start_time
if self.filament_used < 0.0000001:
# Track duration prior to extrusion
self.init_duration = self.total_duration - time_paused
print_duration = self.total_duration - self.init_duration - time_paused
self.print_duration = print_duration
return {
'filename': self.filename,
'total_duration': self.total_duration,
'print_duration': print_duration,
'filament_used': self.filament_used,
'state': self.state,
'message': self.error_message,
'info': {'total_layer': self.info_total_layer,
'current_layer': self.info_current_layer},
'power_loss': self.power_loss,
'z_pos': self.z_pos,
}
def load_config(config):
return PrintStats(config)