ChangeAtZ.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. # ChangeAtZ script - Change printing parameters at a given height
  2. # This script is the successor of the TweakAtZ plugin for legacy Cura.
  3. # It contains code from the TweakAtZ plugin V1.0-V4.x and from the ExampleScript by Jaime van Kessel, Ultimaker B.V.
  4. # It runs with the PostProcessingPlugin which is released under the terms of the AGPLv3 or higher.
  5. # This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms
  6. #Authors of the ChangeAtZ plugin / script:
  7. # Written by Steven Morlock, smorloc@gmail.com
  8. # Modified by Ricardo Gomez, ricardoga@otulook.com, to add Bed Temperature and make it work with Cura_13.06.04+
  9. # Modified by Stefan Heule, Dim3nsioneer@gmx.ch since V3.0 (see changelog below)
  10. # Modified by Jaime van Kessel (Ultimaker), j.vankessel@ultimaker.com to make it work for 15.10 / 2.x
  11. # Modified by Ruben Dulek (Ultimaker), r.dulek@ultimaker.com, to debug.
  12. ##history / changelog:
  13. ##V3.0.1: TweakAtZ-state default 1 (i.e. the plugin works without any TweakAtZ comment)
  14. ##V3.1: Recognizes UltiGCode and deactivates value reset, fan speed added, alternatively layer no. to tweak at,
  15. ## extruder three temperature disabled by "#Ex3"
  16. ##V3.1.1: Bugfix reset flow rate
  17. ##V3.1.2: Bugfix disable TweakAtZ on Cool Head Lift
  18. ##V3.2: Flow rate for specific extruder added (only for 2 extruders), bugfix parser,
  19. ## added speed reset at the end of the print
  20. ##V4.0: Progress bar, tweaking over multiple layers, M605&M606 implemented, reset after one layer option,
  21. ## extruder three code removed, tweaking print speed, save call of Publisher class,
  22. ## uses previous value from other plugins also on UltiGCode
  23. ##V4.0.1: Bugfix for doubled G1 commands
  24. ##V4.0.2: uses Cura progress bar instead of its own
  25. ##V4.0.3: Bugfix for cool head lift (contributed by luisonoff)
  26. ##V4.9.91: First version for Cura 15.06.x and PostProcessingPlugin
  27. ##V4.9.92: Modifications for Cura 15.10
  28. ##V4.9.93: Minor bugfixes (input settings) / documentation
  29. ##V4.9.94: Bugfix Combobox-selection; remove logger
  30. ##V5.0: Bugfix for fall back after one layer and doubled G0 commands when using print speed tweak, Initial version for Cura 2.x
  31. ##V5.0.1: Bugfix for calling unknown property 'bedTemp' of previous settings storage and unkown variable 'speed'
  32. ##V5.1: API Changes included for use with Cura 2.2
  33. ## Uses -
  34. ## M220 S<factor in percent> - set speed factor override percentage
  35. ## M221 S<factor in percent> - set flow factor override percentage
  36. ## M221 S<factor in percent> T<0-#toolheads> - set flow factor override percentage for single extruder
  37. ## M104 S<temp> T<0-#toolheads> - set extruder <T> to target temperature <S>
  38. ## M140 S<temp> - set bed target temperature
  39. ## M106 S<PWM> - set fan speed to target speed <S>
  40. ## M605/606 to save and recall material settings on the UM2
  41. from ..Script import Script
  42. #from UM.Logger import Logger
  43. import re
  44. class ChangeAtZ(Script):
  45. version = "5.1.1"
  46. def __init__(self):
  47. super().__init__()
  48. def getSettingDataString(self):
  49. return """{
  50. "name":"ChangeAtZ """ + self.version + """ (Experimental)",
  51. "key":"ChangeAtZ",
  52. "metadata": {},
  53. "version": 2,
  54. "settings":
  55. {
  56. "a_trigger":
  57. {
  58. "label": "Trigger",
  59. "description": "Trigger at height or at layer no.",
  60. "type": "enum",
  61. "options": {"height":"Height","layer_no":"Layer No."},
  62. "default_value": "height"
  63. },
  64. "b_targetZ":
  65. {
  66. "label": "Change Height",
  67. "description": "Z height to change at",
  68. "unit": "mm",
  69. "type": "float",
  70. "default_value": 5.0,
  71. "minimum_value": "0",
  72. "minimum_value_warning": "0.1",
  73. "maximum_value_warning": "230",
  74. "enabled": "a_trigger == 'height'"
  75. },
  76. "b_targetL":
  77. {
  78. "label": "Change Layer",
  79. "description": "Layer no. to change at",
  80. "unit": "",
  81. "type": "int",
  82. "default_value": 1,
  83. "minimum_value": "-100",
  84. "minimum_value_warning": "-1",
  85. "enabled": "a_trigger == 'layer_no'"
  86. },
  87. "c_behavior":
  88. {
  89. "label": "Behavior",
  90. "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only",
  91. "type": "enum",
  92. "options": {"keep_value":"Keep value","single_layer":"Single Layer"},
  93. "default_value": "keep_value"
  94. },
  95. "d_twLayers":
  96. {
  97. "label": "Layer Spread",
  98. "description": "The change will be gradual over this many layers. Enter 1 to make the change immediate.",
  99. "unit": "",
  100. "type": "int",
  101. "default_value": 1,
  102. "minimum_value": "1",
  103. "maximum_value_warning": "50",
  104. "enabled": "c_behavior == 'keep_value'"
  105. },
  106. "e1_Change_speed":
  107. {
  108. "label": "Change Speed",
  109. "description": "Select if total speed (print and travel) has to be changed",
  110. "type": "bool",
  111. "default_value": false
  112. },
  113. "e2_speed":
  114. {
  115. "label": "Speed",
  116. "description": "New total speed (print and travel)",
  117. "unit": "%",
  118. "type": "int",
  119. "default_value": 100,
  120. "minimum_value": "1",
  121. "minimum_value_warning": "10",
  122. "maximum_value_warning": "200",
  123. "enabled": "e1_Change_speed"
  124. },
  125. "f1_Change_printspeed":
  126. {
  127. "label": "Change Print Speed",
  128. "description": "Select if print speed has to be changed",
  129. "type": "bool",
  130. "default_value": false
  131. },
  132. "f2_printspeed":
  133. {
  134. "label": "Print Speed",
  135. "description": "New print speed",
  136. "unit": "%",
  137. "type": "int",
  138. "default_value": 100,
  139. "minimum_value": "1",
  140. "minimum_value_warning": "10",
  141. "maximum_value_warning": "200",
  142. "enabled": "f1_Change_printspeed"
  143. },
  144. "g1_Change_flowrate":
  145. {
  146. "label": "Change Flow Rate",
  147. "description": "Select if flow rate has to be changed",
  148. "type": "bool",
  149. "default_value": false
  150. },
  151. "g2_flowrate":
  152. {
  153. "label": "Flow Rate",
  154. "description": "New Flow rate",
  155. "unit": "%",
  156. "type": "int",
  157. "default_value": 100,
  158. "minimum_value": "1",
  159. "minimum_value_warning": "10",
  160. "maximum_value_warning": "200",
  161. "enabled": "g1_Change_flowrate"
  162. },
  163. "g3_Change_flowrateOne":
  164. {
  165. "label": "Change Flow Rate 1",
  166. "description": "Select if first extruder flow rate has to be changed",
  167. "type": "bool",
  168. "default_value": false
  169. },
  170. "g4_flowrateOne":
  171. {
  172. "label": "Flow Rate One",
  173. "description": "New Flow rate Extruder 1",
  174. "unit": "%",
  175. "type": "int",
  176. "default_value": 100,
  177. "minimum_value": "1",
  178. "minimum_value_warning": "10",
  179. "maximum_value_warning": "200",
  180. "enabled": "g3_Change_flowrateOne"
  181. },
  182. "g5_Change_flowrateTwo":
  183. {
  184. "label": "Change Flow Rate 2",
  185. "description": "Select if second extruder flow rate has to be changed",
  186. "type": "bool",
  187. "default_value": false
  188. },
  189. "g6_flowrateTwo":
  190. {
  191. "label": "Flow Rate two",
  192. "description": "New Flow rate Extruder 2",
  193. "unit": "%",
  194. "type": "int",
  195. "default_value": 100,
  196. "minimum_value": "1",
  197. "minimum_value_warning": "10",
  198. "maximum_value_warning": "200",
  199. "enabled": "g5_Change_flowrateTwo"
  200. },
  201. "h1_Change_bedTemp":
  202. {
  203. "label": "Change Bed Temp",
  204. "description": "Select if Bed Temperature has to be changed",
  205. "type": "bool",
  206. "default_value": false
  207. },
  208. "h2_bedTemp":
  209. {
  210. "label": "Bed Temp",
  211. "description": "New Bed Temperature",
  212. "unit": "C",
  213. "type": "float",
  214. "default_value": 60,
  215. "minimum_value": "0",
  216. "minimum_value_warning": "30",
  217. "maximum_value_warning": "120",
  218. "enabled": "h1_Change_bedTemp"
  219. },
  220. "i1_Change_extruderOne":
  221. {
  222. "label": "Change Extruder 1 Temp",
  223. "description": "Select if First Extruder Temperature has to be changed",
  224. "type": "bool",
  225. "default_value": false
  226. },
  227. "i2_extruderOne":
  228. {
  229. "label": "Extruder 1 Temp",
  230. "description": "New First Extruder Temperature",
  231. "unit": "C",
  232. "type": "float",
  233. "default_value": 190,
  234. "minimum_value": "0",
  235. "minimum_value_warning": "160",
  236. "maximum_value_warning": "250",
  237. "enabled": "i1_Change_extruderOne"
  238. },
  239. "i3_Change_extruderTwo":
  240. {
  241. "label": "Change Extruder 2 Temp",
  242. "description": "Select if Second Extruder Temperature has to be changed",
  243. "type": "bool",
  244. "default_value": false
  245. },
  246. "i4_extruderTwo":
  247. {
  248. "label": "Extruder 2 Temp",
  249. "description": "New Second Extruder Temperature",
  250. "unit": "C",
  251. "type": "float",
  252. "default_value": 190,
  253. "minimum_value": "0",
  254. "minimum_value_warning": "160",
  255. "maximum_value_warning": "250",
  256. "enabled": "i3_Change_extruderTwo"
  257. },
  258. "j1_Change_fanSpeed":
  259. {
  260. "label": "Change Fan Speed",
  261. "description": "Select if Fan Speed has to be changed",
  262. "type": "bool",
  263. "default_value": false
  264. },
  265. "j2_fanSpeed":
  266. {
  267. "label": "Fan Speed",
  268. "description": "New Fan Speed (0-255)",
  269. "unit": "PWM",
  270. "type": "int",
  271. "default_value": 255,
  272. "minimum_value": "0",
  273. "minimum_value_warning": "15",
  274. "maximum_value_warning": "255",
  275. "enabled": "j1_Change_fanSpeed"
  276. }
  277. }
  278. }"""
  279. def getValue(self, line, key, default = None): #replace default getvalue due to comment-reading feature
  280. if not key in line or (";" in line and line.find(key) > line.find(";") and
  281. not ";ChangeAtZ" in key and not ";LAYER:" in key):
  282. return default
  283. subPart = line[line.find(key) + len(key):] #allows for string lengths larger than 1
  284. if ";ChangeAtZ" in key:
  285. m = re.search("^[0-4]", subPart)
  286. elif ";LAYER:" in key:
  287. m = re.search("^[+-]?[0-9]*", subPart)
  288. else:
  289. #the minus at the beginning allows for negative values, e.g. for delta printers
  290. m = re.search("^[-]?[0-9]*\.?[0-9]*", subPart)
  291. if m == None:
  292. return default
  293. try:
  294. return float(m.group(0))
  295. except:
  296. return default
  297. def execute(self, data):
  298. #Check which changes should apply
  299. ChangeProp = {"speed": self.getSettingValueByKey("e1_Change_speed"),
  300. "flowrate": self.getSettingValueByKey("g1_Change_flowrate"),
  301. "flowrateOne": self.getSettingValueByKey("g3_Change_flowrateOne"),
  302. "flowrateTwo": self.getSettingValueByKey("g5_Change_flowrateTwo"),
  303. "bedTemp": self.getSettingValueByKey("h1_Change_bedTemp"),
  304. "extruderOne": self.getSettingValueByKey("i1_Change_extruderOne"),
  305. "extruderTwo": self.getSettingValueByKey("i3_Change_extruderTwo"),
  306. "fanSpeed": self.getSettingValueByKey("j1_Change_fanSpeed")}
  307. ChangePrintSpeed = self.getSettingValueByKey("f1_Change_printspeed")
  308. ChangeStrings = {"speed": "M220 S%f\n",
  309. "flowrate": "M221 S%f\n",
  310. "flowrateOne": "M221 T0 S%f\n",
  311. "flowrateTwo": "M221 T1 S%f\n",
  312. "bedTemp": "M140 S%f\n",
  313. "extruderOne": "M104 S%f T0\n",
  314. "extruderTwo": "M104 S%f T1\n",
  315. "fanSpeed": "M106 S%d\n"}
  316. target_values = {"speed": self.getSettingValueByKey("e2_speed"),
  317. "printspeed": self.getSettingValueByKey("f2_printspeed"),
  318. "flowrate": self.getSettingValueByKey("g2_flowrate"),
  319. "flowrateOne": self.getSettingValueByKey("g4_flowrateOne"),
  320. "flowrateTwo": self.getSettingValueByKey("g6_flowrateTwo"),
  321. "bedTemp": self.getSettingValueByKey("h2_bedTemp"),
  322. "extruderOne": self.getSettingValueByKey("i2_extruderOne"),
  323. "extruderTwo": self.getSettingValueByKey("i4_extruderTwo"),
  324. "fanSpeed": self.getSettingValueByKey("j2_fanSpeed")}
  325. old = {"speed": -1, "flowrate": 100, "flowrateOne": -1, "flowrateTwo": -1, "platformTemp": -1, "extruderOne": -1,
  326. "extruderTwo": -1, "bedTemp": -1, "fanSpeed": -1, "state": -1}
  327. twLayers = self.getSettingValueByKey("d_twLayers")
  328. if self.getSettingValueByKey("c_behavior") == "single_layer":
  329. behavior = 1
  330. else:
  331. behavior = 0
  332. try:
  333. twLayers = max(int(twLayers),1) #for the case someone entered something as "funny" as -1
  334. except:
  335. twLayers = 1
  336. pres_ext = 0
  337. done_layers = 0
  338. z = 0
  339. x = None
  340. y = None
  341. layer = -100000 #layer no. may be negative (raft) but never that low
  342. # state 0: deactivated, state 1: activated, state 2: active, but below z,
  343. # state 3: active and partially executed (multi layer), state 4: active and passed z
  344. state = 1
  345. # IsUM2: Used for reset of values (ok for Marlin/Sprinter),
  346. # has to be set to 1 for UltiGCode (work-around for missing default values)
  347. IsUM2 = False
  348. oldValueUnknown = False
  349. TWinstances = 0
  350. if self.getSettingValueByKey("a_trigger") == "layer_no":
  351. targetL_i = int(self.getSettingValueByKey("b_targetL"))
  352. targetZ = 100000
  353. else:
  354. targetL_i = -100000
  355. targetZ = self.getSettingValueByKey("b_targetZ")
  356. index = 0
  357. for active_layer in data:
  358. modified_gcode = ""
  359. lines = active_layer.split("\n")
  360. for line in lines:
  361. if line.strip() == "":
  362. continue
  363. if ";Generated with Cura_SteamEngine" in line:
  364. TWinstances += 1
  365. modified_gcode += ";ChangeAtZ instances: %d\n" % TWinstances
  366. if not ("M84" in line or "M25" in line or ("G1" in line and ChangePrintSpeed and (state==3 or state==4)) or
  367. ";ChangeAtZ instances:" in line):
  368. modified_gcode += line + "\n"
  369. IsUM2 = ("FLAVOR:UltiGCode" in line) or IsUM2 #Flavor is UltiGCode!
  370. if ";ChangeAtZ-state" in line: #checks for state change comment
  371. state = self.getValue(line, ";ChangeAtZ-state", state)
  372. if ";ChangeAtZ instances:" in line:
  373. try:
  374. tempTWi = int(line[20:])
  375. except:
  376. tempTWi = TWinstances
  377. TWinstances = tempTWi
  378. if ";Small layer" in line: #checks for begin of Cool Head Lift
  379. old["state"] = state
  380. state = 0
  381. if ";LAYER:" in line: #new layer no. found
  382. if state == 0:
  383. state = old["state"]
  384. layer = self.getValue(line, ";LAYER:", layer)
  385. if targetL_i > -100000: #target selected by layer no.
  386. if (state == 2 or targetL_i == 0) and layer == targetL_i: #determine targetZ from layer no.; checks for change on layer 0
  387. state = 2
  388. targetZ = z + 0.001
  389. if (self.getValue(line, "T", None) is not None) and (self.getValue(line, "M", None) is None): #looking for single T-cmd
  390. pres_ext = self.getValue(line, "T", pres_ext)
  391. if "M190" in line or "M140" in line and state < 3: #looking for bed temp, stops after target z is passed
  392. old["bedTemp"] = self.getValue(line, "S", old["bedTemp"])
  393. if "M109" in line or "M104" in line and state < 3: #looking for extruder temp, stops after target z is passed
  394. if self.getValue(line, "T", pres_ext) == 0:
  395. old["extruderOne"] = self.getValue(line, "S", old["extruderOne"])
  396. elif self.getValue(line, "T", pres_ext) == 1:
  397. old["extruderTwo"] = self.getValue(line, "S", old["extruderTwo"])
  398. if "M107" in line: #fan is stopped; is always updated in order not to miss switch off for next object
  399. old["fanSpeed"] = 0
  400. if "M106" in line and state < 3: #looking for fan speed
  401. old["fanSpeed"] = self.getValue(line, "S", old["fanSpeed"])
  402. if "M221" in line and state < 3: #looking for flow rate
  403. tmp_extruder = self.getValue(line, "T", None)
  404. if tmp_extruder == None: #check if extruder is specified
  405. old["flowrate"] = self.getValue(line, "S", old["flowrate"])
  406. if old["flowrate"] == -1:
  407. old["flowrate"] = 100.0
  408. elif tmp_extruder == 0: #first extruder
  409. old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"])
  410. elif tmp_extruder == 1: #second extruder
  411. old["flowrateTwo"] = self.getValue(line, "S", old["flowrateTwo"])
  412. if ("M84" in line or "M25" in line):
  413. if state>0 and ChangeProp["speed"]: #"finish" commands for UM Original and UM2
  414. modified_gcode += "M220 S100 ; speed reset to 100% at the end of print\n"
  415. modified_gcode += "M117 \n"
  416. modified_gcode += line + "\n"
  417. if "G1" in line or "G0" in line:
  418. newZ = self.getValue(line, "Z", z)
  419. x = self.getValue(line, "X", None)
  420. y = self.getValue(line, "Y", None)
  421. e = self.getValue(line, "E", None)
  422. f = self.getValue(line, "F", None)
  423. if 'G1' in line and ChangePrintSpeed and (state==3 or state==4):
  424. # check for pure print movement in target range:
  425. if x != None and y != None and f != None and e != None and newZ==z:
  426. modified_gcode += "G1 F%d X%1.3f Y%1.3f E%1.5f\n" % (int(f / 100.0 * float(target_values["printspeed"])), self.getValue(line, "X"),
  427. self.getValue(line, "Y"), self.getValue(line, "E"))
  428. else: #G1 command but not a print movement
  429. modified_gcode += line + "\n"
  430. # no changing on retraction hops which have no x and y coordinate:
  431. if (newZ != z) and (x is not None) and (y is not None):
  432. z = newZ
  433. if z < targetZ and state == 1:
  434. state = 2
  435. if z >= targetZ and state == 2:
  436. state = 3
  437. done_layers = 0
  438. for key in ChangeProp:
  439. if ChangeProp[key] and old[key]==-1: #old value is not known
  440. oldValueUnknown = True
  441. if oldValueUnknown: #the changing has to happen within one layer
  442. twLayers = 1
  443. if IsUM2: #Parameters have to be stored in the printer (UltiGCode=UM2)
  444. modified_gcode += "M605 S%d;stores parameters before changing\n" % (TWinstances-1)
  445. if behavior == 1: #single layer change only and then reset
  446. twLayers = 1
  447. if ChangePrintSpeed and behavior == 0:
  448. twLayers = done_layers + 1
  449. if state==3:
  450. if twLayers-done_layers>0: #still layers to go?
  451. if targetL_i > -100000:
  452. modified_gcode += ";ChangeAtZ V%s: executed at Layer %d\n" % (self.version,layer)
  453. modified_gcode += "M117 Printing... ch@L%4d\n" % layer
  454. else:
  455. modified_gcode += (";ChangeAtZ V%s: executed at %1.2f mm\n" % (self.version,z))
  456. modified_gcode += "M117 Printing... ch@%5.1f\n" % z
  457. for key in ChangeProp:
  458. if ChangeProp[key]:
  459. modified_gcode += ChangeStrings[key] % float(old[key]+(float(target_values[key])-float(old[key]))/float(twLayers)*float(done_layers+1))
  460. done_layers += 1
  461. else:
  462. state = 4
  463. if behavior == 1: #reset values after one layer
  464. if targetL_i > -100000:
  465. modified_gcode += ";ChangeAtZ V%s: reset on Layer %d\n" % (self.version,layer)
  466. else:
  467. modified_gcode += ";ChangeAtZ V%s: reset at %1.2f mm\n" % (self.version,z)
  468. if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting
  469. modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1)
  470. else: #executes on RepRap, UM2 with Ultigcode and Cura setting
  471. for key in ChangeProp:
  472. if ChangeProp[key]:
  473. modified_gcode += ChangeStrings[key] % float(old[key])
  474. # re-activates the plugin if executed by pre-print G-command, resets settings:
  475. if (z < targetZ or layer == 0) and state >= 3: #resets if below change level or at level 0
  476. state = 2
  477. done_layers = 0
  478. if targetL_i > -100000:
  479. modified_gcode += ";ChangeAtZ V%s: reset below Layer %d\n" % (self.version, targetL_i)
  480. else:
  481. modified_gcode += ";ChangeAtZ V%s: reset below %1.2f mm\n" % (self.version, targetZ)
  482. if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting
  483. modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1)
  484. else: #executes on RepRap, UM2 with Ultigcode and Cura setting
  485. for key in ChangeProp:
  486. if ChangeProp[key]:
  487. modified_gcode += ChangeStrings[key] % float(old[key])
  488. data[index] = modified_gcode
  489. index += 1
  490. return data