Configtool: add the ability to build and upload.

This commit is contained in:
jbernardis 2015-01-19 22:56:44 -05:00 committed by Markus Hitter
parent d6b2a2293e
commit b9f524d256
11 changed files with 781 additions and 183 deletions

247
config.py
View File

@ -7,8 +7,10 @@ import inspect
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(
inspect.currentframe()))[0]))
from configtool.settings import Settings
from configtool.printerpanel import PrinterPanel
from configtool.boardpanel import BoardPanel
from configtool.build import Build, Upload
from configtool.data import VERSION, reInclude
ID_LOAD_PRINTER = 1000
@ -20,6 +22,8 @@ ID_SAVE_BOARD_AS = 1012
ID_LOAD_CONFIG = 1020
ID_LOAD_DEFAULT = 1021
ID_SAVE_CONFIG = 1022
ID_BUILD = 1030
ID_UPLOAD = 1031
class ConfigFrame(wx.Frame):
@ -29,11 +33,13 @@ class ConfigFrame(wx.Frame):
size = (880, 550))
self.Bind(wx.EVT_CLOSE, self.onClose)
self.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD)
panel = wx.Panel(self, -1)
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
@ -42,13 +48,19 @@ class ConfigFrame(wx.Frame):
self.nb = wx.Notebook(panel, wx.ID_ANY, size = (880, 550),
style = wx.BK_DEFAULT)
self.nb.SetFont(self.font)
self.nb.SetFont(self.settings.font)
self.pgPrinter = PrinterPanel(self, self.nb, self.font, cmd_folder)
self.nb.AddPage(self.pgPrinter, "Printer")
self.printerFileName = None
self.printerTabDecor = ""
self.printerBaseText = "Printer"
self.pgPrinter = PrinterPanel(self, self.nb, self.settings)
self.nb.AddPage(self.pgPrinter, self.printerBaseText)
self.pgBoard = BoardPanel(self, self.nb, self.font, cmd_folder)
self.nb.AddPage(self.pgBoard, "Board")
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()
@ -65,10 +77,32 @@ class ConfigFrame(wx.Frame):
self.Destroy()
def setPrinterTabText(self, txt):
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 setBoardTabText(self, 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):
@ -81,10 +115,12 @@ class ConfigFrame(wx.Frame):
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_LOAD_DEFAULT, "Load default",
"Load default config.h and its named printer and board files.")
self.Bind(wx.EVT_MENU, self.onLoadDefault, id = ID_LOAD_DEFAULT)
file_menu.Enable(ID_LOAD_DEFAULT, 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)
@ -98,13 +134,12 @@ class ConfigFrame(wx.Frame):
file_menu.Append(ID_SAVE_PRINTER, "Save printer",
"Save printer configuration.")
self.Bind(wx.EVT_MENU, self.pgPrinter.onSaveConfig, id = ID_SAVE_PRINTER)
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.pgPrinter.onSaveConfigAs,
id = ID_SAVE_PRINTER_AS)
self.Bind(wx.EVT_MENU, self.onSavePrinterConfigAs, id = ID_SAVE_PRINTER_AS)
file_menu.Enable(ID_SAVE_PRINTER_AS, False)
file_menu.AppendSeparator()
@ -114,12 +149,12 @@ class ConfigFrame(wx.Frame):
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.pgBoard.onSaveConfig, id = ID_SAVE_BOARD)
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.pgBoard.onSaveConfigAs, id = ID_SAVE_BOARD_AS)
self.Bind(wx.EVT_MENU, self.onSaveBoardConfigAs, id = ID_SAVE_BOARD_AS)
file_menu.Enable(ID_SAVE_BOARD_AS, False)
file_menu.AppendSeparator()
@ -133,7 +168,59 @@ class ConfigFrame(wx.Frame):
menu_bar.Append(file_menu, "&File")
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)
self.checkEnableLoadConfig()
self.checkEnableUpload()
def onSaveBoardConfig(self, evt):
self.pgBoard.onSaveConfig(evt)
self.checkEnableLoadConfig()
def onSaveBoardConfigAs(self, evt):
self.pgBoard.onSaveConfigAs(evt)
self.checkEnableLoadConfig()
def onSavePrinterConfig(self, evt):
self.pgPrinter.onSaveConfig(evt)
self.checkEnableLoadConfig()
def onSavePrinterConfigAs(self, evt):
self.pgPrinter.onSaveConfigAs(evt)
self.checkEnableLoadConfig()
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)
else:
self.fileMenu.Enable(ID_LOAD_CONFIG, False)
self.buildMenu.Enable(ID_BUILD, False)
fn = os.path.join(cmd_folder, "config.default.h")
if os.path.isfile(fn):
self.fileMenu.Enable(ID_LOAD_DEFAULT, True)
else:
self.fileMenu.Enable(ID_LOAD_DEFAULT, 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, flag):
self.fileMenu.Enable(ID_SAVE_PRINTER, flag)
@ -168,6 +255,23 @@ class ConfigFrame(wx.Frame):
if not self.pgBoard.confirmLoseChanges("load config"):
return
pfile, bfile = self.getConfigFileNames(fn)
if not pfile:
self.message("Config file did not contain a printer file "
"include statement.", "Config error")
return
if not bfile:
self.message("Config file did not contain a board file "
"include statement.", "Config error")
return
self.pgPrinter.loadConfigFile(pfile)
self.pgBoard.loadConfigFile(bfile)
def getConfigFileNames(self, fn):
pfile = None
bfile = None
path = os.path.join(cmd_folder, fn)
@ -203,19 +307,7 @@ class ConfigFrame(wx.Frame):
self.message("Unable to parse include statement:\n%s" % ln,
"Config error")
if not pfile:
self.message("Config file did not contain a printer file include "
"statement.", "Config error")
return
if not bfile:
self.message("Config file did not contain a board file include "
"statement.", "Config error")
return
self.pgPrinter.loadConfigFile(pfile)
self.pgBoard.loadConfigFile(bfile)
return pfile, bfile
def onSaveConfig(self, evt):
fn = os.path.join(cmd_folder, "config.h")
@ -263,6 +355,105 @@ class ConfigFrame(wx.Frame):
"%s successfully saved.\nconfig.h successfully saved.") % (rbfn, rpfn)
self.message(m, "Save configuration success", wx.OK + wx.ICON_INFORMATION)
self.checkEnableLoadConfig()
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
self.onSavePrinterConfig(None)
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
self.onSaveBoardConfig(None)
if not self.verifyConfigLoaded():
dlg = wx.MessageDialog(self, "Loaded configuration does not match what "
"the config.h file. Click Yes to load "
"config.h.",
"Incorrect data loaded",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return
self.loadConfigFile("config.h")
f_cpu, cpu, baud = 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 not baud:
# TODO: It looks like serial port baud rate is confused with bootloader
# baud rate here. These two can be the same, but don't have to.
# Bootloader baud rate isn't user selectable, it's a property of
# the bootloader and can be changed only by overwriting the
# bootloader.
dlg = wx.MessageDialog(self, "Unable to determine CPU baud rate.",
"CPU baud rate error", wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
if buildFlag:
# TODO: building the executable needs no baud rate.
dlg = Build(self, self.settings, f_cpu, cpu, baud)
dlg.ShowModal()
dlg.Destroy()
self.checkEnableUpload()
else:
dlg = Upload(self, self.settings, f_cpu, cpu, baud)
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 message(self, text, title, style = wx.OK + wx.ICON_ERROR):
dlg = wx.MessageDialog(self, text, title, style)
dlg.ShowModal()

View File

@ -5,20 +5,29 @@
* *
\***************************************************************************/
//PROCESSORS_START
#ifndef __AVR_ATmega644__
#ifndef __AVR_ATmega644P__
#ifndef __AVR_ATmega1284__
#ifndef __AVR_ATmega1284P__
#error Wrong CPU type.
#endif
#endif
#endif
#endif
//PROCESSORS_END
/** \def CPU_TYPE
CPU types a user should be able to choose from in configtool. All
commented out.
*/
//#define CPU_TYPE atmega644
//#define CPU_TYPE atmega644p
//#define CPU_TYPE atmega1284
//#define CPU_TYPE atmega1284p
/** \def CPU
CPU actually present on the board.
*/
#define CPU atmega1284p
/** \def F_CPU_OPT
CPU clock frequencies a user should be able to choose from in configtool.
All commented out.
*/
//#define F_CPU_OPT 16000000UL
//#define F_CPU_OPT 20000000UL
/** \def F_CPU
CPU clock rate. #ifndef required for Arduino compatibility.
Actual CPU clock rate. #ifndef required for Arduino compatibility.
*/
#ifndef F_CPU
#define F_CPU 20000000UL

View File

@ -5,16 +5,26 @@
* *
\***************************************************************************/
//PROCESSORS_START
#ifndef __AVR_ATmega1280__
#ifndef __AVR_ATmega2560__
#error Wrong CPU type.
#endif
#endif
//PROCESSORS_END
/** \def CPU_TYPE
CPU types a user should be able to choose from in configtool. All
commented out.
*/
//#define CPU_TYPE atmega1280
//#define CPU_TYPE atmega2560
/** \def CPU
CPU actually present on the board.
*/
#define CPU atmega2560
/** \def F_CPU_OPT
CPU clock frequencies a user should be able to choose from in configtool.
All commented out.
*/
//#define F_CPU_OPT 16000000UL
/** \def F_CPU
CPU clock rate. #ifndef required for Arduino compatibility.
Actual CPU clock rate. #ifndef required for Arduino compatibility.
*/
#ifndef F_CPU
#define F_CPU 16000000UL

View File

@ -67,35 +67,32 @@ The start of a help text block is as follows:
All subsequent lines are copied exactly as entered into the help text until
the end delimiter is reached. The end delimiter is a line that starts with '*/'.
Note that it is possible to specify multiple names on the start line. This
allows identical help text for similar or related fields.
The names are the actual #define variable names and are used to associate the
help text with a specific field. Note that it is possible to specify multiple
names on the start line. This allows identical help text for similar or related
fields.
Processor Preamble - Board file
-------------------------------
The utility parses out the processor types from the config file when it loads
it, and it provides an interface where processors may be added or removed.
When a file is saved, the utility needs to know 1) where to insert the new
processor preamble, and 2) what to remove from the old file. To achieve this,
there is a sequence that identifies the start of the processor preamble, and
a sequence that indicates the end. As follows:
The various CPU_TYPE lines define the options that the user can select from
for CPU type. These must be legal values acceptable to avr-gcc for the -MMCU
parameter. The CPU line is the currently chosen value from these options.
//PROCESSORS_START
#ifndef __AVR_ATmega1280__
#ifndef __AVR_ATmega2560__
#error Wrong CPU type.
#endif
#endif
//PROCESSORS_END
Example:
when a file is saved, all of the lines between these two delimiters are removed
and a new preamble is generated based on the values from the screen. The
supported processor types are defined in a python list in the data.py file. As
additional processors are supported, then can be added to this list.
//#define CPU_TYPE atmega1280
//#define CPU_TYPE atmega2560
#define CPU atmega2560
If the START/END delimiters are not present, then the processor preamble will
not be generated and the content from the original file will be retained.
There can be multiple F_CPU_OPT lines specifying the various CPU clock values.
The final F_CPU line shows the currently chose value from the options.
Example:
//#define F_CPU_OPT 16000000UL
#define F_CPU 16000000UL
Temperature Sensor Considerations - Board file

38
configtool.default.ini Normal file
View File

@ -0,0 +1,38 @@
[configtool]
# Where to find the arduino tools (avr-gcc, avrdude, etc). This is only used
# for windows. For linux it is assumed that the tools are available through
# the normal PATH.
arduinodir = C:/Program Files (x86)/Arduino/hardware/tools/avr/bin
# Flags passed into the avr-gcc compiler. These flags can have 3 different
# variabled embedded within them:
#
# %F_CPU% will be replaced by the value of the CPU Clock Rate entered
# through the GUI.
#
# %CPU% will be replaced by the value of the CPU entered through the GUI.
#
# %ALNAME% is the name of the source file being compiled with the .c
# extension replaced by .al.
#
# Note: the flag -save-temps=obj does not appear to be a valid flag for win32.
# Omit the "=obj", or omit it entirely.
cflags = -DF_CPU=%F_CPU% -mmcu=%CPU% -Wall -Wstrict-prototypes -std=gnu99
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
-Winline -fno-move-loop-invariants -fno-tree-scev-cprop -Os
-ffunction-sections -finline-functions-called-once -mcall-prologues
-Wa,-adhlns=build/%ALNAME%
# Flags passed to avr-gcc to be passed on to the linker.
ldflags = -Wl,--as-needed -Wl,--gc-sections
# Flags passed to avr-objcopy.
objcopyflags = -j .text -j .data -O ihex -R .eeprom -R .fuse -R .lock
# The programmer type - passed to avrdude.
programmer = stk500v2
# The port through which the firmware will be uploaded - passed to avrdude.
port = /dev/ttyACM0

View File

@ -3,13 +3,13 @@ import os
import wx
import re
from configtool.data import (supportedCPUs, defineValueFormat,
from configtool.data import (defineValueFormat,
defineBoolFormat, defineHeaterFormat, reCommDefBL,
reCommDefBoolBL, reHelpTextStart, reHelpTextEnd,
reStartSensors, reEndSensors, reStartHeaters,
reEndHeaters, reStartProcessors, reEndProcessors,
reCandHeatPins, reCandThermPins, reFloatAttr,
reAVR, reDefine, reDefineBL, reDefQS, reDefQSm,
reEndHeaters, reCandHeatPins, reCandThermPins,
reCandProcessors, reCandCPUClocks, reFloatAttr,
reDefine, reDefineBL, reDefQS, reDefQSm,
reDefQSm2, reDefBool, reDefBoolBL, reDefHT,
reDefTS, reHeater, reSensor3, reSensor4)
from configtool.pinoutspage import PinoutsPage
@ -20,32 +20,32 @@ from configtool.cpupage import CpuPage
class BoardPanel(wx.Panel):
def __init__(self, parent, nb, font, folder):
def __init__(self, parent, nb, settings):
wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent
self.settings = settings
self.configFile = None
self.cfgValues = {}
self.heaters = []
self.sensors = []
self.processors = []
self.candHeatPins = []
self.candThermPins = []
self.dir = os.path.join(folder, "config")
self.dir = os.path.join(self.settings.folder, "config")
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21),
style = wx.BK_DEFAULT)
self.nb.SetFont(font)
self.nb.SetFont(self.settings.font)
self.pages = []
self.titles = []
self.pageModified = []
self.pageValid = []
self.pgCpu = CpuPage(self, self.nb, len(self.pages), font)
self.pgCpu = CpuPage(self, self.nb, len(self.pages), self.settings.font)
text = "CPU"
self.nb.AddPage(self.pgCpu, text)
self.pages.append(self.pgCpu)
@ -53,7 +53,8 @@ class BoardPanel(wx.Panel):
self.pageModified.append(False)
self.pageValid.append(True)
self.pgPins = PinoutsPage(self, self.nb, len(self.pages), font)
self.pgPins = PinoutsPage(self, self.nb, len(self.pages),
self.settings.font)
text = "Pinouts"
self.nb.AddPage(self.pgPins, text)
self.pages.append(self.pgPins)
@ -61,7 +62,8 @@ class BoardPanel(wx.Panel):
self.pageModified.append(False)
self.pageValid.append(True)
self.pgSensors = SensorsPage(self, self.nb, len(self.pages), font)
self.pgSensors = SensorsPage(self, self.nb, len(self.pages),
self.settings.font)
text = "Temperature Sensors"
self.nb.AddPage(self.pgSensors, text)
self.pages.append(self.pgSensors)
@ -69,7 +71,8 @@ class BoardPanel(wx.Panel):
self.pageModified.append(False)
self.pageValid.append(True)
self.pgHeaters = HeatersPage(self, self.nb, len(self.pages), font)
self.pgHeaters = HeatersPage(self, self.nb, len(self.pages),
self.settings.font)
text = "Heaters"
self.nb.AddPage(self.pgHeaters, text)
self.pages.append(self.pgHeaters)
@ -78,7 +81,7 @@ class BoardPanel(wx.Panel):
self.pageValid.append(True)
self.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages),
font)
self.settings.font)
text = "Communications"
self.nb.AddPage(self.pgCommunications, text)
self.pages.append(self.pgCommunications)
@ -91,6 +94,23 @@ class BoardPanel(wx.Panel):
self.SetSizer(sz)
self.Fit()
def getCPUInfo(self):
vF_CPU = None
if 'F_CPU' in self.cfgValues.keys():
vF_CPU = self.cfgValues['F_CPU']
vCPU = None
if 'CPU' in self.cfgValues.keys():
vCPU = self.cfgValues['CPU']
# TODO: this is probably obsolete, because the build process doesn't need
# the firmware baud rate, but the bootloader baud rate.
vBaud = None
if 'BAUD' in self.cfgValues.keys():
vBaud = self.cfgValues['BAUD']
return vF_CPU, vCPU, vBaud
def assertModified(self, pg, flag = True):
self.pageModified[pg] = flag
self.modifyTab(pg)
@ -98,6 +118,9 @@ class BoardPanel(wx.Panel):
def isModified(self):
return (True in self.pageModified)
def hasData(self):
return (self.configFile != None)
def getFileName(self):
return self.configFile
@ -121,6 +144,15 @@ class BoardPanel(wx.Panel):
pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg])
if True in self.pageModified and False in self.pageValid:
pfx = "?* "
elif True in self.pageModified:
pfx = "* "
elif False in self.pageValid:
pfx = "? "
else:
pfx = ""
self.parent.setBoardTabDecor(pfx)
def setHeaters(self, ht):
self.parent.setHeaters(ht)
@ -190,6 +222,8 @@ class BoardPanel(wx.Panel):
self.heaters = []
self.candHeatPins = []
self.candThermPins = []
self.candProcessors = []
self.candClocks = []
gatheringHelpText = False
helpTextString = ""
helpKey = None
@ -242,33 +276,20 @@ class BoardPanel(wx.Panel):
self.candHeatPins.append(t[0])
continue
continue
m = reCandProcessors.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candProcessors.append(t[0])
continue
if ln.lstrip().startswith("#if"):
m = re.findall(reAVR, ln)
inv = []
for p in m:
if p in supportedCPUs:
self.processors.append(p)
else:
inv.append(p)
if len(inv) > 0:
if len(inv) == 1:
a = " is"
b = "it"
else:
a = "s are"
b = "them"
dlg = wx.MessageDialog(self,
("The following processor type%s not "
"supported:\n %s\nPlease add %s to "
"\"supportedCPUs\".") %
(a, ", ".join(inv), b),
"Unsupported processor type",
wx.OK + wx.ICON_INFORMATION)
m = reCandCPUClocks.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candClocks.append(t[0])
continue
dlg.ShowModal()
dlg.Destroy()
continue
if ln.lstrip().startswith("#define"):
@ -322,9 +343,11 @@ class BoardPanel(wx.Panel):
continue
self.parent.enableSaveBoard(True)
self.parent.setBoardTabText("Board <%s>" % os.path.basename(fn))
self.parent.setBoardTabFile(os.path.basename(fn))
self.pgHeaters.setCandidatePins(self.candHeatPins)
self.pgSensors.setCandidatePins(self.candThermPins)
self.pgCpu.setCandidateProcessors(self.candProcessors)
self.pgCpu.setCandidateClocks(self.candClocks)
for pg in self.pages:
pg.insertValues(self.cfgValues)
@ -332,7 +355,6 @@ class BoardPanel(wx.Panel):
self.pgSensors.setSensors(self.sensors)
self.pgHeaters.setHeaters(self.heaters)
self.pgCpu.setProcessors(self.processors)
return True
@ -415,11 +437,8 @@ class BoardPanel(wx.Panel):
for k in v1.keys():
values[k] = v1[k]
self.processors = self.pgCpu.getProcessors()
skipToSensorEnd = False
skipToHeaterEnd = False
skipToProcessorEnd = False
for ln in self.cfgBuffer:
m = reStartSensors.match(ln)
if m:
@ -457,26 +476,6 @@ class BoardPanel(wx.Panel):
skipToHeaterEnd = False
continue
m = reStartProcessors.match(ln)
if m:
fp.write(ln)
for i in range(len(self.processors)):
fp.write("%s#ifndef __AVR_%s__\n" % (i * " ", self.processors[i]))
fp.write("%s#error Wrong CPU type.\n" % ((i + 1) * " "))
for s in self.processors:
fp.write("%s#endif\n" % (i * " "))
i -= 1
skipToProcessorEnd = True
continue
if skipToProcessorEnd:
m = reEndProcessors.match(ln)
if m:
fp.write(ln)
skipToProcessorEnd = False
continue
m = reDefineBL.match(ln)
if m:
t = m.groups()
@ -525,6 +524,6 @@ class BoardPanel(wx.Panel):
fp.close()
self.parent.setBoardTabText("Board <%s>" % os.path.basename(path))
self.parent.setBoardTabFile(os.path.basename(path))
return True

309
configtool/build.py Normal file
View File

@ -0,0 +1,309 @@
import wx
import wx.lib.newevent
import thread, shlex, subprocess
import os
from os.path import isfile, join
from sys import platform
if platform == "win32":
from _subprocess import STARTF_USESHOWWINDOW
(scriptEvent, EVT_SCRIPT_UPDATE) = wx.lib.newevent.NewEvent()
SCRIPT_RUNNING = 1
SCRIPT_FINISHED = 2
SCRIPT_CANCELLED = 3
class ScriptThread:
def __init__(self, win, script):
self.win = win
self.running = False
self.cancelled = False
self.script = script
def Start(self):
self.running = True
self.cancelled = False
thread.start_new_thread(self.Run, ())
def Stop(self):
self.cancelled = True
def IsRunning(self):
return self.running
def Run(self):
if platform == "win32":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
for cmd in self.script:
evt = scriptEvent(msg = cmd, state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
args = shlex.split(str(cmd))
try:
if platform == "win32":
p = subprocess.Popen(args, stderr = subprocess.STDOUT,
stdout = subprocess.PIPE,
startupinfo = startupinfo)
else:
p = subprocess.Popen(args, stderr = subprocess.STDOUT,
stdout = subprocess.PIPE)
except:
evt = scriptEvent(msg = "Exception occurred trying to run\n\n%s" % cmd,
state = SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
self.running = False
return
obuf = ''
while not self.cancelled:
o = p.stdout.read(1)
if o == '': break
if o == '\r' or o == '\n':
if obuf.strip() != "":
evt = scriptEvent(msg = obuf, state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
obuf = ''
elif ord(o) < 32:
pass
else:
obuf += o
if self.cancelled:
evt = scriptEvent(msg = None, state = SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
p.kill()
self.running = False
return
rc = p.wait()
if rc != 0:
msg = "RC = " + str(rc) + " - Build terminated"
evt = scriptEvent(msg = msg, state = SCRIPT_CANCELLED)
wx.PostEvent(self.win, evt)
self.running = False
return
evt = scriptEvent(msg = "", state = SCRIPT_RUNNING)
wx.PostEvent(self.win, evt)
evt = scriptEvent(msg = None, state = SCRIPT_FINISHED)
wx.PostEvent(self.win, evt)
self.running = False
class Build(wx.Dialog):
def __init__(self, parent, settings, f_cpu, cpu, baud):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Build teacup",
style = wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE)
self.settings = settings
self.SetFont(self.settings.font)
self.root = self.settings.folder
self.f_cpu = f_cpu
self.cpu = cpu
self.baud = baud
self.Bind(wx.EVT_CLOSE, self.onExit)
hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.AddSpacer((10, 10))
sz = wx.BoxSizer(wx.VERTICAL)
sz.AddSpacer((10, 10))
tc = wx.TextCtrl(self, wx.ID_ANY, size = (900, 300),
style = wx.TE_READONLY + wx.TE_MULTILINE)
sz.Add(tc, 1, wx.EXPAND)
self.log = tc
sz.AddSpacer((10, 10))
hsz.Add(sz, 1, wx.EXPAND)
hsz.AddSpacer((10, 10))
self.SetSizer(hsz)
self.Fit()
builddir = join(self.root, "build")
if not os.path.exists(builddir):
os.makedirs(builddir)
self.log.AppendText("Directory (%s) created.\n\n" % builddir)
self.compile()
def compile(self):
self.generateCompileScript()
if len(self.script) == 0:
self.log.AppendText("Nothing to compile!\n")
self.active = False
else:
self.Bind(EVT_SCRIPT_UPDATE, self.compileUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
def link(self):
self.generateLinkScript()
if len(self.script) == 0:
self.log.AppendText("Nothing to link!\n")
self.active = False
else:
self.Bind(EVT_SCRIPT_UPDATE, self.linkUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
def generateCompileScript(self):
self.script = []
if platform == "win32":
cmdpath = "\"" + join(self.settings.arduinodir, "avr-gcc") + "\""
else:
cmdpath = "avr-gcc"
cfiles = [f for f in os.listdir(self.root)
if isfile(join(self.root,f)) and f.endswith(".c")]
for f in cfiles:
basename = f[:-2]
ofile = basename + ".o"
alfile = basename + ".al"
opath = "\"" + join(self.root, "build", ofile) + "\""
cpath = "\"" + join(self.root, f) + "\""
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", alfile)
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = cmdpath + " -c " + opts + " -o " + opath + " " + cpath
self.script.append(cmd)
def generateLinkScript(self):
self.script = []
if platform == "win32":
cmdpath = "\"" + join(self.settings.arduinodir, "avr-gcc") + "\""
else:
cmdpath = "avr-gcc"
ofiles = ["\"" + join(self.root, "build", f) + "\""
for f in os.listdir(join(self.root, "build"))
if isfile(join(self.root, "build", f)) and f.endswith(".o")]
opath = " ".join(ofiles)
elfpath = "\"" + join(self.root, "build", "teacup.elf") + "\""
opts = self.settings.cflags
opts = opts.replace("%ALNAME%", "teacup.elf")
opts = opts.replace("%F_CPU%", self.f_cpu)
opts = opts.replace("%CPU%", self.cpu)
cmd = cmdpath + " " + self.settings.ldflags + " " + opts + " -o " + \
elfpath + " " + opath + " -lm"
self.script.append(cmd)
if platform == "win32":
cmdpath = "\"" + join(self.settings.arduinodir, "avr-objcopy") + "\""
else:
cmdpath = "avr-objcopy"
cmd = cmdpath + " " + self.settings.objcopyflags + " " + elfpath + \
" teacup.hex"
self.script.append(cmd)
def compileUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText("Compile terminated abnormally.\n\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Compile completed normally.\n\n")
self.link()
def linkUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText("Link terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Link completed normally.\n")
self.active = False
def onExit(self, evt):
if self.active:
return
self.EndModal(wx.ID_OK)
class Upload(wx.Dialog):
def __init__(self, parent, settings, f_cpu, cpu, baud):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Upload teacup",
style = wx.RESIZE_BORDER + wx.DEFAULT_DIALOG_STYLE)
self.settings = settings
self.SetFont(self.settings.font)
self.root = self.settings.folder
self.f_cpu = f_cpu
self.cpu = cpu
self.baud = baud
self.Bind(wx.EVT_CLOSE, self.onExit)
hsz = wx.BoxSizer(wx.HORIZONTAL)
hsz.AddSpacer((10, 10))
sz = wx.BoxSizer(wx.VERTICAL)
sz.AddSpacer((10, 10))
tc = wx.TextCtrl(self, wx.ID_ANY, size = (900, 300),
style = wx.TE_READONLY + wx.TE_MULTILINE)
sz.Add(tc, 1, wx.EXPAND)
self.log = tc
sz.AddSpacer((10, 10))
hsz.Add(sz, 1, wx.EXPAND)
hsz.AddSpacer((10, 10))
self.SetSizer(hsz)
self.Fit()
self.generateUploadScript()
if len(self.script) == 0:
self.log.AppendText("Nothing to upload!\n")
self.active = False
else:
self.Bind(EVT_SCRIPT_UPDATE, self.uploadUpdate)
t = ScriptThread(self, self.script)
self.active = True
t.Start()
def generateUploadScript(self):
self.script = []
if platform == "win32":
cmdpath = "\"" + join(self.settings.arduinodir, "avrdude") + "\""
else:
cmdpath = "avrdude"
cmd = cmdpath + " -c %s -b %s -p %s -P %s -U flash:w:teacup.hex" % \
(self.settings.programmer, self.baud, self.cpu, self.settings.port)
self.script.append(cmd)
def uploadUpdate(self, evt):
if evt.msg is not None:
self.log.AppendText(evt.msg + "\n")
if evt.state == SCRIPT_RUNNING:
pass
if evt.state == SCRIPT_CANCELLED:
self.log.AppendText("Upload terminated abnormally.\n")
self.active = False
if evt.state == SCRIPT_FINISHED:
self.log.AppendText("Upload completed normally.\n")
self.active = False
def onExit(self, evt):
if self.active:
return
self.EndModal(wx.ID_OK)

View File

@ -1,7 +1,6 @@
import wx
from configtool.page import Page
from configtool.data import supportedCPUs
class CpuPage(wx.Panel, Page):
@ -11,64 +10,49 @@ class CpuPage(wx.Panel, Page):
self.parent = parent
self.id = idPg
self.labels = {'F_CPU': "CPU Clock Rate:"}
self.defaultClock = '16000000UL'
self.clocks = ['8000000UL', self.defaultClock, '20000000UL']
self.labels = {'F_CPU': "CPU Clock Rate:", 'CPU': "Processor Type:"}
self.clocks = []
self.processors = []
sz = wx.GridBagSizer()
sz.AddSpacer((20, 40), pos = (0, 0))
k = 'F_CPU'
ch = self.addChoice(k, self.clocks, self.clocks.index(self.defaultClock),
100, self.onChoice)
ch = self.addChoice(k, self.clocks, 0, 100, self.onChoice)
sz.Add(ch, pos = (1, 1))
sz.AddSpacer((100, 10), pos = (1, 2))
b = wx.StaticBox(self, wx.ID_ANY, "Processor Type(s)")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
ht = "Choose the processor(s) this configuration will work with."
for k in supportedCPUs:
cb = self.addCheckBox(k, self.onCheckBox)
cb.SetToolTipString(ht)
sbox.Add(cb)
sbox.AddSpacer((120, 5))
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 3), span = (3, 1))
k = 'CPU'
ch = self.addChoice(k, self.processors, 0, 100, self.onChoice)
sz.Add(ch, pos = (1, 3))
self.SetSizer(sz)
self.enableAll(False)
def setProcessors(self, plist):
def setCandidateProcessors(self, plist):
k = 'CPU'
self.choices[k].Clear()
for p in plist:
self.choices[k].Append(p)
self.processors = plist
for p in supportedCPUs:
if p in self.processors:
self.checkBoxes[p].SetValue(True)
else:
self.checkBoxes[p].SetValue(False)
def getProcessors(self):
plist = []
for p in supportedCPUs:
if self.checkBoxes[p].IsChecked():
plist.append(p)
return plist
def setCandidateClocks(self, clist):
k = 'F_CPU'
self.choices[k].Clear()
for c in clist:
self.choices[k].Append(c)
self.clocks = clist
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
self.setChoice('F_CPU', cfgValues, self.defaultClock)
if len(self.clocks) > 0:
self.setChoice('F_CPU', cfgValues, self.clocks[0])
if len(self.processors) > 0:
self.setChoice('CPU', cfgValues, self.processors[0])
self.assertModified(False)

View File

@ -40,10 +40,10 @@ reStartSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_START")
reEndSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_END")
reStartHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_START")
reEndHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_END")
reStartProcessors = re.compile("^\s*//\s*PROCESSORS_START")
reEndProcessors = re.compile("^\s*//\s*PROCESSORS_END")
reCandHeatPins = re.compile("^\s*//\s*#define\s+HEATER_PIN\s+(\w+)")
reCandThermPins = re.compile("^\s*//\s*#define\s+TEMP_SENSOR_PIN\s+(\w+)")
reCandProcessors = re.compile("^\s*//\s*#define\s+CPU_TYPE\s+(\w+)")
reCandCPUClocks = re.compile("^\s*//\s*#define\s+F_CPU_OPT\s+(\w+)")
reHelpTextStart = re.compile("^\s*/\*\*\s+\\\\def\s+(.*)")
reHelpTextEnd = re.compile("^\s*\*/")
@ -55,8 +55,6 @@ reHeater = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reInteger = re.compile("^\d+U?L?$")
reFloat = re.compile("^\d+(\.\d*)?$")
reAVR = re.compile("__AVR_(\w+)__")
defineValueFormat = "#define %-24s %s\n"
defineBoolFormat = "#define %s\n"
defineHeaterFormat = "#define HEATER_%s HEATER_%s\n"

View File

@ -14,28 +14,31 @@ from configtool.miscellaneouspage import MiscellaneousPage
class PrinterPanel(wx.Panel):
def __init__(self, parent, nb, font, folder):
def __init__(self, parent, nb, settings):
wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent
self.configFile = None
self.settings = settings
self.cfgValues = {}
self.heaters = []
self.dir = os.path.join(folder, "config")
self.dir = os.path.join(self.settings.folder, "config")
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21),
style = wx.BK_DEFAULT)
self.nb.SetFont(font)
self.nb.SetFont(self.settings.font)
self.pages = []
self.titles = []
self.pageModified = []
self.pageValid = []
self.pgMech = MechanicalPage(self, self.nb, len(self.pages), font)
self.pgMech = MechanicalPage(self, self.nb, len(self.pages),
self.settings.font)
text = "Mechanical"
self.nb.AddPage(self.pgMech, text)
self.pages.append(self.pgMech)
@ -43,7 +46,8 @@ class PrinterPanel(wx.Panel):
self.pageModified.append(False)
self.pageValid.append(True)
self.pgAcc = AccelerationPage(self, self.nb, len(self.pages), font)
self.pgAcc = AccelerationPage(self, self.nb, len(self.pages),
self.settings.font)
text = "Acceleration"
self.nb.AddPage(self.pgAcc, text)
self.pages.append(self.pgAcc)
@ -52,7 +56,7 @@ class PrinterPanel(wx.Panel):
self.pageValid.append(True)
self.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages),
font)
self.settings.font)
text = "Miscellaneous"
self.nb.AddPage(self.pgMiscellaneous, text)
self.pages.append(self.pgMiscellaneous)
@ -75,6 +79,9 @@ class PrinterPanel(wx.Panel):
def isModified(self):
return (True in self.pageModified)
def hasData(self):
return (self.configFile != None)
def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag
self.modifyTab(pg)
@ -95,6 +102,15 @@ class PrinterPanel(wx.Panel):
pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg])
if True in self.pageModified and False in self.pageValid:
pfx = "?* "
elif True in self.pageModified:
pfx = "* "
elif False in self.pageValid:
pfx = "? "
else:
pfx = ""
self.parent.setPrinterTabDecor(pfx)
def setHeaters(self, ht):
return self.pgMiscellaneous.setHeaters(ht)
@ -229,7 +245,7 @@ class PrinterPanel(wx.Panel):
self.cfgValues[t[0]] = True
self.parent.enableSavePrinter(True)
self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(fn))
self.parent.setPrinterTabFile(os.path.basename(fn))
for pg in self.pages:
pg.insertValues(self.cfgValues)
@ -274,7 +290,7 @@ class PrinterPanel(wx.Panel):
if self.saveConfigFile(path):
dlg = wx.MessageDialog(self, "File %s successfully written." % path,
"Save successful", wx.OK + wx.ICON_INFORMATION)
self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(path))
self.parent.setPrinterTabFile(os.path.basename(path))
else:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,

47
configtool/settings.py Normal file
View File

@ -0,0 +1,47 @@
import ConfigParser
import os
INIFILE = "configtool.default.ini"
class Settings:
def __init__(self, app, folder):
self.app = app
self.cmdfolder = folder
self.inifile = os.path.join(folder, INIFILE)
self.section = "configtool"
self.arduinodir = ""
self.cflags = ""
self.ldflags = ""
self.objcopyflags = ""
self.programmer = "wiring"
self.port = "/dev/ttyACM0"
self.cfg = ConfigParser.ConfigParser()
self.cfg.optionxform = str
if not self.cfg.read(self.inifile):
print "Settings file %s does not exist. Using default values." % INIFILE
return
if self.cfg.has_section(self.section):
for opt, value in self.cfg.items(self.section):
value = value.replace('\n', ' ')
if opt == "arduinodir":
self.arduinodir = value
elif opt == "cflags":
self.cflags = value
elif opt == "ldflags":
self.ldflags = value
elif opt == "programmer":
self.programmer = value
elif opt == "port":
self.port = value
elif opt == "objcopyflags":
self.objcopyflags = value
else:
print "Unknown %s option: %s - ignoring." % (self.section, opt)
else:
print "Missing %s section - assuming defaults." % self.section