Teacup_Firmware/configtool/printer.py

318 lines
8.8 KiB
Python

from __future__ import print_function
import os
import re
from sys import platform
from configtool.data import (defineValueFormat, defineBoolFormat,
reHelpTextStart, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm,
reDefQSm2, reDefBool, reDefBoolBL,
reHomingOpts, reStartHoming, reEndHoming,
reDefHoming, reHoming4)
class Printer:
def __init__(self, settings):
self.configFile = None
self.cfgValues = {}
self.settings = settings
self.cfgDir = os.path.join(self.settings.folder, "configtool")
def getValues(self):
vars = [(x, self.cfgValues[x]) for x in self.cfgValues]
return dict(vars)
def hasData(self):
return (self.configFile != None)
def getFileName(self):
return self.configFile
def loadConfigFile(self, fn):
cfgFn = os.path.join(self.cfgDir, "printer.generic.h")
try:
self.cfgBuffer = list(open(cfgFn))
except:
return False, cfgFn
try:
self.userBuffer = list(open(fn))
except:
return False, fn
self.configFile = fn
gatheringHelpText = False
helpTextString = ""
helpKey = None
self.cfgValues = {}
self.cfgNames = []
self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
helpTextString = helpTextString.strip()
# Keep paragraphs with double-newline.
helpTextString = helpTextString.replace("\n\n ", "\n\n")
# Keep indented lines, typically a list.
helpTextString = helpTextString.replace("\n\n ", "\n\n ")
helpTextString = helpTextString.replace("\n ", "\n\n ")
# Remove all other newlines and indents.
helpTextString = helpTextString.replace("\n ", " ")
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
if self.parseCandidateValues(ln):
continue
if self.parseHoming(ln):
continue
self.parseDefineName(ln)
self.parseDefineValue(ln)
# Set all boolean generic configuration items to False, so items not yet
# existing in the user configuration default to disabled.
for k in self.cfgValues.keys():
if isinstance(self.cfgValues[k], bool):
self.cfgValues[k] = False
# Read the user configuration. This usually overwrites all of the items
# read above, but not those missing in the user configuration, e.g.
# when reading an older config.
gatheringHelpText = False
prevLines = ""
for ln in self.userBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
continue
if reHelpTextStart.match(ln):
gatheringHelpText = True
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
if self.parseCandidateValues(ln):
continue
if self.parseHoming(ln):
continue
self.parseDefineValue(ln)
# Parsing done. All parsed stuff is now in these array and dicts.
if self.settings.verbose >= 2:
print(self.cfgValues) # #defines with a value.
print(self.cfgNames) # Names found in the generic file.
if self.settings.verbose >= 3:
print(self.helpText)
return True, None
def parseDefineName(self, ln):
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
self.cfgNames.append(t[0])
return True
return False
def parseCandidateValues(self, ln):
m = reHomingOpts.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if 'HOMING_OPTS' in self.cfgValues:
if t[0] not in self.cfgValues['HOMING_OPTS']:
self.cfgValues['HOMING_OPTS'].append(t[0])
else:
self.cfgValues['HOMING_OPTS'] = [t[0]]
return True
def parseHoming(self, ln):
m = reDefHoming.search(ln)
if m:
t = m.groups()
if len(t) == 1:
n = reHoming4.search(t[0])
if n:
u = n.groups()
if len(u) == 4:
t2 = []
for x in u:
t2.append(x)
self.cfgValues['HOMING_STEP1'] = t2[0]
self.cfgValues['HOMING_STEP2'] = t2[1]
self.cfgValues['HOMING_STEP3'] = t2[2]
self.cfgValues['HOMING_STEP4'] = t2[3]
return True
return None
return True
def parseDefineValue(self, ln):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt[0], True
return True
elif len(tt) > 1 and (t[0] in self.cfgNames):
self.cfgValues[t[0]] = tt, True
return True
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2 and (t[0] in self.cfgNames):
if reDefineBL.search(ln):
self.cfgValues[t[0]] = t[1], True
else:
self.cfgValues[t[0]] = t[1], False
return True
m = reDefBool.search(ln)
if m:
t = m.groups()
# Accept booleans, but not those for which a value exists already.
# Booleans already existing as values are most likely misconfigured
# manual edits (or result of a bug).
if len(t) == 1 and t[0] in self.cfgNames \
and not (t[0] in self.cfgValues \
and isinstance(self.cfgValues[t[0]], tuple)):
if reDefBoolBL.search(ln):
self.cfgValues[t[0]] = True
else:
self.cfgValues[t[0]] = False
return True
return False
def saveConfigFile(self, path, values):
if not values:
values = self.cfgValues
if self.settings.verbose >= 1:
print("Saving printer: %s." % path)
if self.settings.verbose >= 2:
print(values)
fp = file(path, 'w')
self.configFile = path
skipToHomingEnd = False
for ln in self.cfgBuffer:
m = reStartHoming.match(ln)
if m:
fp.write(ln)
sstr = "%s, %s, %s, %s" % ((values['HOMING_STEP1']),
(values['HOMING_STEP2']),
(values['HOMING_STEP3']),
(values['HOMING_STEP4']))
fp.write("DEFINE_HOMING(%s)\n" % sstr)
skipToHomingEnd = True
continue
if skipToHomingEnd:
m = reEndHoming.match(ln)
if m:
fp.write(ln)
skipToHomingEnd = False
continue
m = reDefine.match(ln)
if m:
t = m.groups()
if len(t) == 2 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v[1] == False:
fp.write("//")
fp.write(defineValueFormat % (t[0], v[0]))
else:
if t[0] == 'CANNED_CYCLE':
# Known to be absent in the GUI. Worse, this value is replaced
# by the one in the metadata file.
#
# TODO: make value reading above recognize wether this value is
# commented out or not. Reading the value its self works
# already. Hint: it's the rule using reDefQS, reDefQSm, etc.
#
# TODO: add a multiline text field in the GUI to deal with this.
#
# TODO: write this value out properly. In /* comments */, if
# disabled.
#
# TODO: currently, the lines beyond the ones with the #define are
# treated like arbitrary comments. Having the former TODOs
# done, this will lead to duplicates.
fp.write(ln)
else:
print("Value key " + t[0] + " not found in GUI.")
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1 and t[0] in values.keys():
v = values[t[0]]
self.cfgValues[t[0]] = v
if v == "" or v == False:
fp.write("//")
fp.write(defineBoolFormat % t[0])
else:
print("Boolean key " + t[0] + " not found in GUI.")
continue
fp.write(ln)
fp.close()
return True