Teacup_Firmware/configtool.py

514 lines
15 KiB
Python
Executable File

#!/usr/bin/env python
import sys
if sys.version_info.major >= 3:
print("You are currently running Python3. Python3 is not supported, because\n"
"there is no wxPython for Python3. Please try running with Python2.\n"
"It often works to type \"python2 configtool.py\" in the command line.")
sys.exit(-1)
try:
import wx
except:
print("ImportError: No module named wx\n\n"
"wxPython is not installed. This program requires wxPython to run.\n"
"See your package manager and/or http://wxpython.org/download.php.")
sys.exit(-1)
import os.path
import inspect
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(
inspect.currentframe()))[0]))
from configtool.decoration import Decoration
from configtool.settings import Settings, SettingsDlg
from configtool.printerpanel import PrinterPanel
from configtool.boardpanel import BoardPanel
from configtool.build import Build, Upload
from configtool.data import reInclude
ID_LOAD_PRINTER = 1000
ID_SAVE_PRINTER = 1001
ID_SAVE_PRINTER_AS = 1002
ID_LOAD_BOARD = 1010
ID_SAVE_BOARD = 1011
ID_SAVE_BOARD_AS = 1012
ID_LOAD_CONFIG = 1020
ID_SAVE_CONFIG = 1021
ID_BUILD = 1030
ID_UPLOAD = 1031
ID_SETTINGS = 1040
class ConfigFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Teacup Configtool", size = (880, 550))
self.Bind(wx.EVT_CLOSE, self.onClose)
self.Bind(wx.EVT_SIZE, self.onResize)
self.deco = Decoration()
panel = wx.Panel(self, -1)
panel.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)
self.settings = Settings(self, cmd_folder)
self.settings.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
self.settings.folder = cmd_folder
self.heaters = []
self.savePrtEna = False
self.saveBrdEna = False
self.protPrtFile = False
self.protBrdFile = False
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 550),
style = wx.BK_DEFAULT)
self.nb.SetFont(self.settings.font)
self.printerFileName = None
self.printerTabDecor = ""
self.printerBaseText = "Printer"
self.pgPrinter = PrinterPanel(self, self.nb, self.settings)
self.nb.AddPage(self.pgPrinter, self.printerBaseText)
self.boardFileName = None
self.boardTabDecor = ""
self.boardBaseText = "Board"
self.pgBoard = BoardPanel(self, self.nb, self.settings)
self.nb.AddPage(self.pgBoard, self.boardBaseText)
panel.Fit()
self.panel = panel
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz)
self.makeMenu()
def onClose(self, evt):
if not self.pgPrinter.confirmLoseChanges("exit"):
return
if not self.pgBoard.confirmLoseChanges("exit"):
return
self.Destroy()
def onResize(self, evt):
self.panel.SetSize(self.GetClientSize())
self.Refresh()
evt.Skip();
def setPrinterTabFile(self, fn):
self.printerFileName = fn
self.updatePrinterTab()
def setPrinterTabDecor(self, prefix):
self.printerTabDecor = prefix
self.updatePrinterTab()
def updatePrinterTab(self):
txt = self.printerTabDecor + self.printerBaseText
if self.printerFileName:
txt += " <%s>" % self.printerFileName
self.nb.SetPageText(0, txt)
def setBoardTabFile(self, fn):
self.boardFileName = fn
self.updateBoardTab()
def setBoardTabDecor(self, prefix):
self.boardTabDecor = prefix
self.updateBoardTab()
def updateBoardTab(self):
txt = self.boardTabDecor + self.boardBaseText
if self.boardFileName:
txt += " <%s>" % self.boardFileName
self.nb.SetPageText(1, txt)
def setHeaters(self, ht):
self.heaters = ht
self.pgPrinter.setHeaters(ht)
def makeMenu(self):
file_menu = wx.Menu()
file_menu.Append(ID_LOAD_CONFIG, "Load config.h",
"Load config.h and its named printer and board files.")
self.Bind(wx.EVT_MENU, self.onLoadConfig, id = ID_LOAD_CONFIG)
file_menu.Enable(ID_LOAD_CONFIG, False)
file_menu.Append(ID_SAVE_CONFIG, "Save config.h", "Save config.h file.")
self.Bind(wx.EVT_MENU, self.onSaveConfig, id = ID_SAVE_CONFIG)
file_menu.Enable(ID_SAVE_CONFIG, False)
file_menu.AppendSeparator()
file_menu.Append(ID_LOAD_PRINTER, "Load printer",
"Load a printer configuration file.")
self.Bind(wx.EVT_MENU, self.pgPrinter.onLoadConfig, id = ID_LOAD_PRINTER)
file_menu.Append(ID_SAVE_PRINTER, "Save printer",
"Save printer configuration.")
self.Bind(wx.EVT_MENU, self.onSavePrinterConfig, id = ID_SAVE_PRINTER)
file_menu.Enable(ID_SAVE_PRINTER, False)
file_menu.Append(ID_SAVE_PRINTER_AS, "Save printer as...",
"Save printer configuration to a new file.")
self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id = ID_SAVE_PRINTER_AS)
file_menu.Enable(ID_SAVE_PRINTER_AS, False)
file_menu.AppendSeparator()
file_menu.Append(ID_LOAD_BOARD, "Load board",
"Load a board configuration file.")
self.Bind(wx.EVT_MENU, self.pgBoard.onLoadConfig, id = ID_LOAD_BOARD)
file_menu.Append(ID_SAVE_BOARD, "Save board", "Save board configuration.")
self.Bind(wx.EVT_MENU, self.onSaveBoardConfig, id = ID_SAVE_BOARD)
file_menu.Enable(ID_SAVE_BOARD, False)
file_menu.Append(ID_SAVE_BOARD_AS, "Save board as...",
"Save board configuration to a new file.")
self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id = ID_SAVE_BOARD_AS)
file_menu.Enable(ID_SAVE_BOARD_AS, False)
file_menu.AppendSeparator()
file_menu.Append(wx.ID_EXIT, "E&xit", "Exit the application.")
self.Bind(wx.EVT_MENU, self.onClose, id = wx.ID_EXIT)
self.fileMenu = file_menu
menu_bar = wx.MenuBar()
menu_bar.Append(file_menu, "&File")
edit_menu = wx.Menu()
edit_menu.Append(ID_SETTINGS, "Settings", "Change settings.")
self.Bind(wx.EVT_MENU, self.onEditSettings, id = ID_SETTINGS)
self.editMenu = edit_menu
menu_bar.Append(edit_menu, "&Edit")
build_menu = wx.Menu()
build_menu.Append(ID_BUILD, "Build", "Build the executable.")
self.Bind(wx.EVT_MENU, self.onBuild, id = ID_BUILD)
build_menu.Append(ID_UPLOAD, "Upload", "Upload the executable.")
self.Bind(wx.EVT_MENU, self.onUpload, id = ID_UPLOAD)
self.buildMenu = build_menu
menu_bar.Append(build_menu, "&Build")
self.SetMenuBar(menu_bar)
loadFlag = self.checkEnableLoadConfig()
self.checkEnableUpload()
if loadFlag:
self.loadConfigFile("config.h")
def onSaveBoardConfig(self, evt):
rc = self.pgBoard.onSaveConfig(evt)
if rc:
self.checkEnableLoadConfig()
return rc
def onSaveBoardConfigAs(self, evt):
rc = self.pgBoard.onSaveConfigAs(evt)
if rc:
self.checkEnableLoadConfig()
return rc
def onSavePrinterConfig(self, evt):
rc = self.pgPrinter.onSaveConfig(evt)
if rc:
self.checkEnableLoadConfig()
return rc
def onSavePrinterConfigAs(self, evt):
rc = self.pgPrinter.onSaveConfigAs(evt)
if rc:
self.checkEnableLoadConfig()
return rc
def checkEnableLoadConfig(self):
fn = os.path.join(cmd_folder, "config.h")
if os.path.isfile(fn):
self.fileMenu.Enable(ID_LOAD_CONFIG, True)
self.buildMenu.Enable(ID_BUILD, True)
return True
else:
self.fileMenu.Enable(ID_LOAD_CONFIG, False)
self.buildMenu.Enable(ID_BUILD, False)
return False
def checkEnableUpload(self):
fn = os.path.join(cmd_folder, "teacup.hex")
if os.path.isfile(fn):
self.buildMenu.Enable(ID_UPLOAD, True)
else:
self.buildMenu.Enable(ID_UPLOAD, False)
def enableSavePrinter(self, saveFlag, saveAsFlag):
self.fileMenu.Enable(ID_SAVE_PRINTER, saveFlag)
self.fileMenu.Enable(ID_SAVE_PRINTER_AS, saveAsFlag)
self.savePrtEna = saveAsFlag
self.protPrtFile = not saveFlag
if self.savePrtEna and self.saveBrdEna:
self.enableSaveConfig(True)
else:
self.enableSaveConfig(False)
def enableSaveBoard(self, saveFlag, saveAsFlag):
self.fileMenu.Enable(ID_SAVE_BOARD, saveFlag)
self.fileMenu.Enable(ID_SAVE_BOARD_AS, saveAsFlag)
self.saveBrdEna = saveAsFlag
self.protBrdFile = not saveFlag
if self.savePrtEna and self.saveBrdEna:
self.enableSaveConfig(True)
else:
self.enableSaveConfig(False)
def enableSaveConfig(self, flag):
self.fileMenu.Enable(ID_SAVE_CONFIG, flag)
def onLoadConfig(self, evt):
self.loadConfigFile("config.h")
def loadConfigFile(self, fn):
if not self.pgPrinter.confirmLoseChanges("load config"):
return False
if not self.pgBoard.confirmLoseChanges("load config"):
return False
pfile, bfile = self.getConfigFileNames(fn)
if not pfile:
self.message("Config file did not contain a printer file "
"include statement.", "Config error")
return False
else:
if not self.pgPrinter.loadConfigFile(pfile):
self.message("There was a problem loading the printer config file:\n%s"
% pfile, "Config error")
return False
if not bfile:
self.message("Config file did not contain a board file "
"include statement.", "Config error")
return False
else:
if not self.pgBoard.loadConfigFile(bfile):
self.message("There was a problem loading the board config file:\n%s"
% bfile, "Config error")
return False
return True
def getConfigFileNames(self, fn):
pfile = None
bfile = None
path = os.path.join(cmd_folder, fn)
try:
cfgBuffer = list(open(path))
except:
self.message("Unable to process config file %s." % fn, "File error")
return None, None
for ln in cfgBuffer:
if not ln.lstrip().startswith("#include"):
continue
m = reInclude.search(ln)
if m:
t = m.groups()
if len(t) == 1:
if "printer." in t[0]:
if pfile:
self.message("Multiple printer file include statements.\n"
"Ignoring %s." % ln, "Config error",
wx.OK + wx.ICON_WARNING)
else:
pfile = os.path.join(cmd_folder, t[0])
elif "board." in t[0]:
if bfile:
self.message("Multiple board file include statements.\n"
"Ignoring %s." % ln, "Config error",
wx.OK + wx.ICON_WARNING)
else:
bfile = os.path.join(cmd_folder, t[0])
else:
self.message("Unable to parse include statement:\n%s" % ln,
"Config error")
return pfile, bfile
def onSaveConfig(self, evt):
fn = os.path.join(cmd_folder, "config.h")
try:
fp = open(fn, 'w')
except:
self.message("Unable to open config.h for output.", "File error")
return False
bfn = self.pgBoard.getFileName()
if self.pgBoard.isModified() and self.pgBoard.isValid():
if not self.pgBoard.saveConfigFile(bfn):
return False
else:
self.pgBoard.generateTempTables()
pfn = self.pgPrinter.getFileName()
if self.pgPrinter.isModified() and self.pgPrinter.isValid():
if not self.pgPrinter.saveConfigFile(pfn):
return False
prefix = cmd_folder + os.path.sep
lpfx = len(prefix)
if bfn.startswith(prefix):
rbfn = bfn[lpfx:]
else:
rbfn = bfn
if pfn.startswith(prefix):
rpfn = pfn[lpfx:]
else:
rpfn = pfn
fp.write("\n")
fp.write("// Configuration for controller board.\n")
fp.write("#include \"%s\"\n" % rbfn)
fp.write("\n")
fp.write("// Configuration for printer board.\n")
fp.write("#include \"%s\"\n" % rpfn)
fp.close()
self.checkEnableLoadConfig()
return True
def onBuild(self, evt):
self.onBuildorUpload(True)
def onUpload(self, evt):
self.onBuildorUpload(False)
def onBuildorUpload(self, buildFlag):
if not (self.pgPrinter.hasData() or self.pgBoard.hasData()):
dlg = wx.MessageDialog(self, "Data needs to be loaded. "
"Click Yes to load config.h.",
"Data missing",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return
self.loadConfigFile("config.h")
else:
if self.pgPrinter.isModified():
dlg = wx.MessageDialog(self, "Printer data needs to be saved. Click "
"Yes to save printer configuration.",
"Changes pending",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return
if self.protPrtFile:
rc = self.onSavePrinterConfigAs(None)
else:
rc = self.onSavePrinterConfig(None)
if not rc:
return
if self.pgBoard.isModified():
dlg = wx.MessageDialog(self, "Board data needs to be saved. Click "
"Yes to save board configuration.",
"Changes pending",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return
if self.protBrdFile:
rc = self.onSaveBoardConfigAs(None)
else:
rc = self.onSaveBoardConfig(None)
if not rc:
return
if not self.verifyConfigLoaded():
dlg = wx.MessageDialog(self, "Loaded configuration does not match the "
"config.h file. Click Yes to save config.h.",
"Configuration changed",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return
if not self.onSaveConfig(None):
return
f_cpu, cpu = self.pgBoard.getCPUInfo()
if not cpu:
dlg = wx.MessageDialog(self, "Unable to determine CPU type.",
"CPU type error", wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
if not f_cpu:
dlg = wx.MessageDialog(self, "Unable to determine CPU clock rate.",
"CPU clock rate error", wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
if buildFlag:
dlg = Build(self, self.settings, f_cpu, cpu)
dlg.ShowModal()
dlg.Destroy()
self.checkEnableUpload()
else:
dlg = Upload(self, self.settings, f_cpu, cpu)
dlg.ShowModal()
dlg.Destroy()
def verifyConfigLoaded(self):
pfile, bfile = self.getConfigFileNames("config.h")
lpfile = self.pgPrinter.getFileName()
lbfile = self.pgBoard.getFileName()
return ((pfile == lpfile) and (bfile == lbfile))
def onEditSettings(self, evt):
dlg = SettingsDlg(self, self.settings)
rc = dlg.ShowModal()
dlg.Destroy()
def message(self, text, title, style = wx.OK + wx.ICON_ERROR):
dlg = wx.MessageDialog(self, text, title, style)
dlg.ShowModal()
dlg.Destroy()
if __name__ == '__main__':
app = wx.App(False)
frame = ConfigFrame()
frame.Show(True)
app.MainLoop()