1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# 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.
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,
)
printer.register_event_handler(
"extruder:activate_extruder", self._handle_activate_extruder
)
def _handle_activate_extruder(self):
gc_status = self.gcode_move.get_status()
self.last_epos = gc_status["position"].e
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):
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)
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.0
self.filament_used = self.total_duration = 0.0
self.print_start_time = self.last_pause_time = None
self.init_duration = 0.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
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,
},
}
def load_config(config):
return PrintStats(config)
|