From 69175ad4105845f2a766c308aa246cc0b624ac08 Mon Sep 17 00:00:00 2001 From: Andreas Auras Date: Tue, 9 Feb 2010 15:30:49 +0100 Subject: First import into git --- df10ch_setup_pkg/__init__.py | 21 + df10ch_setup_pkg/areas_dlg.py | 235 +++++++++ df10ch_setup_pkg/bright_dlg.py | 83 +++ df10ch_setup_pkg/device_dlg.py | 428 ++++++++++++++++ df10ch_setup_pkg/device_drv.py | 1011 +++++++++++++++++++++++++++++++++++++ df10ch_setup_pkg/firmware.py | 135 +++++ df10ch_setup_pkg/layout_dlg.py | 111 ++++ df10ch_setup_pkg/map_dlg.py | 256 ++++++++++ df10ch_setup_pkg/setup_dlg.py | 58 +++ df10ch_setup_pkg/white_cal_dlg.py | 172 +++++++ 10 files changed, 2510 insertions(+) create mode 100644 df10ch_setup_pkg/__init__.py create mode 100644 df10ch_setup_pkg/areas_dlg.py create mode 100644 df10ch_setup_pkg/bright_dlg.py create mode 100644 df10ch_setup_pkg/device_dlg.py create mode 100644 df10ch_setup_pkg/device_drv.py create mode 100644 df10ch_setup_pkg/firmware.py create mode 100644 df10ch_setup_pkg/layout_dlg.py create mode 100644 df10ch_setup_pkg/map_dlg.py create mode 100644 df10ch_setup_pkg/setup_dlg.py create mode 100644 df10ch_setup_pkg/white_cal_dlg.py (limited to 'df10ch_setup_pkg') diff --git a/df10ch_setup_pkg/__init__.py b/df10ch_setup_pkg/__init__.py new file mode 100644 index 0000000..4ce758a --- /dev/null +++ b/df10ch_setup_pkg/__init__.py @@ -0,0 +1,21 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# \ No newline at end of file diff --git a/df10ch_setup_pkg/areas_dlg.py b/df10ch_setup_pkg/areas_dlg.py new file mode 100644 index 0000000..3eeb8c7 --- /dev/null +++ b/df10ch_setup_pkg/areas_dlg.py @@ -0,0 +1,235 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import device_drv + +class AreasDialog: + def __init__(self, master=None, **args): + self.root = Canvas(master, **args) + self.selectedArea = None + self.selectCallbacks = list() + + def configAreas(self, numAreas): + nTopLeft = numAreas[device_drv.AreaIndex("TopLeft")] + nTopRight = numAreas[device_drv.AreaIndex("TopRight")] + nBottomLeft = numAreas[device_drv.AreaIndex("BottomLeft")] + nBottomRight = numAreas[device_drv.AreaIndex("BottomRight")] + nCenter = numAreas[device_drv.AreaIndex("Center")] + nTop = numAreas[device_drv.AreaIndex("Top")] + nBottom = numAreas[device_drv.AreaIndex("Bottom")] + nLeft = numAreas[device_drv.AreaIndex("Left")] + nRight = numAreas[device_drv.AreaIndex("Right")] + + cv = self.root + size = 0 + width = int(cv["width"]) + height = int(cv["height"]) + + sTop = nTop + nTopLeft + nTopRight + if sTop: + topSize = int(width / sTop) + topSizeEnd = width - topSize * (sTop - 1) + if not size or topSize < size: + size = topSize + + sBottom = nBottom + nBottomLeft + nBottomRight + if sBottom: + bottomSize = int(width / sBottom) + bottomSizeEnd = width - bottomSize * (sBottom - 1) + if not size or bottomSize < size: + size = bottomSize + + sLeft = nLeft + nTopLeft + nBottomLeft + if sLeft: + leftSize = int(height / sLeft) + leftSizeEnd = height - leftSize * (sLeft - 1) + if not size or leftSize < size: + size = leftSize + + sRight = nRight + nTopRight + nBottomRight + if sRight: + rightSize = int(height / sRight) + rightSizeEnd = height - rightSize * (sRight - 1) + if not size or rightSize < size: + size = rightSize + + if not size or int(width / 2) < size: + size = int(width / 2) + if not size or int(height / 2) < size: + size = int(height / 2) + + pTop = [ None ] * nTop + x = 0 + + # top left corner + if nTopLeft: + pTopLeft = [ 0, 0, topSize, 0, topSize, size, size, size, size, leftSize, 0, leftSize ] + x = topSize + elif nTop == 1: + pTop[0] = [ 0, 0, width, 0, width - size, size, size, size ] + elif nTop > 1: + pTop[0] = [ 0, 0, topSize, 0, topSize, size, size, size ] + + # top right corner + if nTopRight: + pTopRight = [ width, 0, width, rightSize, width - size, rightSize, width - size, size, width - topSizeEnd, size, width - topSizeEnd, 0 ] + elif nTop > 1: + pTop[nTop - 1] = [ width, 0, width - size, size, width - topSizeEnd, size, width - topSizeEnd, 0 ] + + # top + for i in range(nTop): + if pTop[i] == None: + pTop[i] = [ x, 0, x + topSize, 0, x + topSize, size, x, size ] + x = x + topSize + + pBottom = [ None ] * nBottom + x = 0 + + # bottom left corner + if nBottomLeft: + pBottomLeft = [ 0, height, 0, height - leftSizeEnd, size, height - leftSizeEnd, size, height - size, bottomSize, height - size, bottomSize, height ] + x = bottomSize + elif nBottom == 1: + pBottom[0] = [ 0, height, size, height - size, width - size, height - size, width, height ] + elif nBottom > 1: + pBottom[0] = [ 0, height, size, height - size, bottomSize, height - size, bottomSize, height ] + + # bottom right corner + if nBottomRight: + pBottomRight = [ width, height, width - bottomSizeEnd, height, width - bottomSizeEnd, height - size, width - size, height - size, width - size, height - rightSizeEnd, width, height - rightSizeEnd ] + elif nBottom > 1: + pBottom[nBottom - 1] = [ width, height, width - bottomSizeEnd, height, width - bottomSizeEnd, height - size, width - size, height - size ] + + # bottom + for i in range(nBottom): + if pBottom[i] == None: + pBottom[i] = [ x, height, x, height - size, x + bottomSize, height - size, x + bottomSize, height ] + x = x + bottomSize + + pLeft = [ None ] * nLeft + y = 0 + + # left top corner + if nTopLeft: + y = leftSize + elif nLeft == 1: + pLeft[0] = [ 0, 0, size, size, size, height - size, 0, height ] + elif nLeft > 1: + pLeft[0] = [ 0, 0, size, size, size, leftSize, 0, leftSize ] + + # left bottom corner + if not nBottomLeft and nLeft > 1: + pLeft[nLeft - 1] = [ 0, height, 0, height - leftSizeEnd, size, height - leftSizeEnd, size, height - size ] + + # left + for i in range(nLeft): + if pLeft[i] == None: + pLeft[i] = [ 0, y, size, y, size, y + leftSize, 0, y + leftSize ] + y = y + leftSize + + pRight = [ None ] * nRight + y = 0 + + # right top corner + if nTopRight: + y = rightSize + elif nRight == 1: + pRight[0] = [ width, 0, width, height, width - size, height - size, width - size, size ] + elif nRight > 1: + pRight[0] = [ width, 0, width, rightSize, width - size, rightSize, width - size, size ] + + # right bottom corner + if not nBottomRight and nRight > 1: + pRight[nRight - 1] = [ width, height, width - size, height - size, width - size, height - rightSizeEnd, width, height - rightSizeEnd ] + + # right + for i in range(nRight): + if pRight[i] == None: + pRight[i] = [ width, y, width, y + rightSize, width - size, y + rightSize, width - size, y ] + y = y + rightSize + + # center + if nCenter: + pCenter = [ size, size, width - size, size, width - size, height - size, size, height - size ] + + # Build canvas items + cv.delete(ALL) + + if nTopLeft: + cv.create_polygon(pTopLeft, tags="TopLeft") + if nTopRight: + cv.create_polygon(pTopRight, tags="TopRight") + if nBottomLeft: + cv.create_polygon(pBottomLeft, tags="BottomLeft") + if nBottomRight: + cv.create_polygon(pBottomRight, tags="BottomRight") + if nCenter: + cv.create_polygon(pCenter, tags="Center") + + for i in range(nTop): + cv.create_polygon(pTop[i], tags="Top-{0}".format(i + 1)) + for i in range(nBottom): + cv.create_polygon(pBottom[i], tags="Bottom-{0}".format(i + 1)) + for i in range(nLeft): + cv.create_polygon(pLeft[i], tags="Left-{0}".format(i + 1)) + for i in range(nRight): + cv.create_polygon(pRight[i], tags="Right-{0}".format(i + 1)) + + for id in cv.find_all(): + cv.itemconfigure(id, outline="#808080", width=3) + cv.tag_bind(id, "", self.cbSelectArea) + + def initAreas(self, color): + self.root.configure(background=color) + for id in self.root.find_all(): + self.root.itemconfigure(id, fill=color, outline="#808080") + self.selectedArea = None + + def setAreaColor(self, area, color): + self.root.itemconfigure(area, fill=color) + + def selectArea(self, area): + if self.selectedArea != area: + cv = self.root + if self.selectedArea: + cv.itemconfigure(self.selectedArea, outline="#808080") + idList = cv.find_withtag(area) + if len(idList): + id = idList[0] + cv.tag_raise(id) + cv.itemconfigure(id, outline="yellow") + self.selectedArea = area + + def cbSelectArea(self, event): + cv = self.root + idList = cv.find_closest(cv.canvasx(event.x), cv.canvasx(event.y)) + if len(idList): + id = idList[0] + if self.selectedArea: + cv.itemconfigure(self.selectedArea, outline="#808080") + self.selectedArea = cv.gettags(id)[0] + cv.tag_raise(id) + cv.itemconfigure(id, outline="yellow") + + for cb in self.selectCallbacks: + cb.cbAreaSelected() diff --git a/df10ch_setup_pkg/bright_dlg.py b/df10ch_setup_pkg/bright_dlg.py new file mode 100644 index 0000000..0bf8bf4 --- /dev/null +++ b/df10ch_setup_pkg/bright_dlg.py @@ -0,0 +1,83 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import tkFont +import tkMessageBox +import device_drv + + +class BrightDialog: + def __init__(self, areasDlg, master=None, **args): + self.areasDlg = areasDlg + + root = Frame(master, **args) + self.root = root + root.bind("", self.cbSheetSelected) + + Label(root, text="Common Brightness Calibration", font=tkFont.Font(weight="bold")).grid(row=0, column=0, columnspan=5, padx=5, pady=5) + + self.varBright = DoubleVar() + self.scBright = Scale(root, label="Brightness", length=200, from_=0, to=1.0, resolution=0.01, orient=HORIZONTAL, variable=self.varBright, command=self.cbSetBright) + self.scBright.grid(row=1, column=0, rowspan=1, padx=20, pady=5) + self.varBright.set(0.0) + + def cbSheetSelected(self, event): + self.loadValues() + + def cbSetBright(self, val): + self.setBright() + + def loadValues(self): + for ctrlId in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[ctrlId] + pwmChannelMap = list() + for portName in config.channelMap.keys(): + configMapRec = config.channelMap[portName] + if configMapRec: + port, pin = device_drv.PORT_NAME_MAP[portName] + pwmChannelMap.append(dict(channel=0, port=port, pins=pin)) + + bright = float(config.commonBright) / float(config.commonPWMRes) + self.varBright.set(bright) + + while len(pwmChannelMap) < device_drv.NCHANNELS: + pwmChannelMap.append(dict(channel=0, port=0, pins=0)) + + try: + config.ctrl.set_channel_map(0, pwmChannelMap) + config.ctrl.set_brightness(0, [ config.pwmRes ] + [ 0 ] * (device_drv.NCHANNELS - 1)) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + self.areasDlg.initAreas("black") + self.setBright() + + def setBright(self): + try: + for ctrlId in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[ctrlId] + config.setCommonBright(int(round(self.varBright.get() * config.commonPWMRes))) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + diff --git a/df10ch_setup_pkg/device_dlg.py b/df10ch_setup_pkg/device_dlg.py new file mode 100644 index 0000000..a473117 --- /dev/null +++ b/df10ch_setup_pkg/device_dlg.py @@ -0,0 +1,428 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import tkFont +import tkMessageBox +import tkFileDialog +import usb +import pickle +import string +import time + +import device_drv +import firmware + +class DeviceDialog: + def __init__(self, layoutDlg, master=None, **args): + self.layoutDlg = layoutDlg + self.selectedConfig = None + self.selectedDeviceIdx = -1 + + root = Frame(master, **args) + self.root = root + root.bind("", self.cbSheetSelected) + + Label(root, text="Device Control", font=tkFont.Font(weight="bold")).grid(row=0, column=0, columnspan=7, padx=5, pady=5) + + self.varDeviceList = StringVar() + self.lbDevices = Listbox(root, selectmode=SINGLE, activestyle=NONE, width=20, height=8, listvariable=self.varDeviceList) + self.lbDevices.grid(row=1, column=0, columnspan=2, rowspan=4, padx=5, pady=5, sticky=N+S+E+W) + self.lbDevices.bind("", self.cbSelectDevice) + + self.varVersion = StringVar() + Label(root, text="Version:").grid(row=1, column=2, sticky=E) + self.etVersion = Label(root, textvariable=self.varVersion, anchor=W, relief=SUNKEN) + self.etVersion.grid(row=1, column=3, columnspan=4, padx=5, pady=5, sticky=W+E) + + self.varPWMRes = StringVar() + Label(root, text="PWM Resolution:").grid(row=2, column=2, sticky=E) + self.etPWMRes = Label(root, textvariable=self.varPWMRes, anchor=W, relief=SUNKEN) + self.etPWMRes.grid(row=2, column=3, columnspan=4, padx=5, pady=5, sticky=W+E) + + self.varPWMFreq = IntVar() + Label(root, text="PWM Frequency:").grid(row=3, column=2, sticky=E) + self.varPWMFreq.set(device_drv.MIN_PWM_FREQ) + self.scPWMFreq = Scale(root, length=250, from_=device_drv.MIN_PWM_FREQ, to=device_drv.MAX_PWM_FREQ, resolution=1, tickinterval=50, orient=HORIZONTAL, variable=self.varPWMFreq, command=self.cbSetPWMFreq) + self.scPWMFreq.grid(row=3, column=3, columnspan=4, padx=5, pady=5, sticky="W") + + self.varCommonBright = IntVar() + Label(root, text="Common Brightness:").grid(row=4, column=2, sticky=E) + self.varCommonBright.set(0) + self.scCommonBright = Scale(root, length=250, from_=0, to=255, resolution=1, tickinterval=50, orient=HORIZONTAL, variable=self.varCommonBright, command=self.cbSetCommonBrightness) + self.scCommonBright.grid(row=4, column=3, columnspan=4, padx=5, pady=5, sticky=W) + + self.btScanDevices = Button(root, text="Scan Devices", command=self.cbScanDevices) + self.btScanDevices.grid(row=5, column=0, padx=5, pady=5, ipadx=5) + + self.btResetSetup = Button(root, text="Firmware update", command=self.cbFirmwareUpdate) + self.btResetSetup.grid(row=5, column=1, padx=5, pady=5, ipadx=5) + + self.btStoreSetup = Button(root, text="Backup", command=self.cbBackupSetup) + self.btStoreSetup.grid(row=5, column=2, padx=5, pady=5, ipadx=5, sticky=E) + + self.btStoreSetup = Button(root, text="Restore", command=self.cbRestoreSetup) + self.btStoreSetup.grid(row=5, column=3, padx=5, pady=5, ipadx=5, sticky=W) + + self.btResetSetup = Button(root, text="Reset Setup", command=self.cbResetSetup) + self.btResetSetup.grid(row=5, column=4, padx=5, pady=5, ipadx=5, sticky=E) + + self.btStoreSetup = Button(root, text="Store Setup", command=self.cbStoreSetup) + self.btStoreSetup.grid(row=5, column=5, padx=5, pady=5, ipadx=5, sticky=W) + + self.varStatus = StringVar() + self.lbStatus = Label(root, textvariable=self.varStatus, anchor=W, relief=RIDGE) + self.lbStatus.grid(row=6, column=0, columnspan=6, padx=0, pady=0, sticky=W+E) + + + def cbSheetSelected(self, event): + if self.selectedConfig: + self.loadDeviceValues() + + def cbFirmwareUpdate(self): + if len(device_drv.DeviceList): + self.firmwareUpdate() + + def cbResetSetup(self): + if len(device_drv.ConfigMap): + self.resetSetup() + if self.selectedConfig: + self.loadDeviceValues() + + def cbStoreSetup(self): + if len(device_drv.ConfigMap): + self.storeSetup() + if self.selectedConfig: + self.loadDeviceValues() + + def cbBackupSetup(self): + if self.selectedConfig: + self.backupSetup() + + def cbRestoreSetup(self): + if self.selectedConfig: + self.restoreSetup() + + def cbSetCommonBrightness(self, val): + if self.selectedConfig: + try: + self.selectedConfig.setCommonBright(int(val)) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + def cbSetPWMFreq(self, val): + if self.selectedConfig: + try: + self.selectedConfig.setPWMFreq(int(val)) + self.varPWMRes.set(self.selectedConfig.pwmRes) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + def cbSelectDevice(self, event): + s = self.lbDevices.curselection() + if len(s) == 1: + self.selectDevice(int(s[0])) + + def cbScanDevices(self): + self.scanDevices() + + def scanDevices(self): + deviceList = None + self.selectedConfig = None + self.selectedDeviceIdx = -1 + while not deviceList: + try: + device_drv.LoadConfigs() + if len(device_drv.DeviceList): + deviceList = device_drv.DeviceList + except (device_drv.AtmoControllerError, usb.USBError) as err: + if not tkMessageBox.askretrycancel(self.root.winfo_toplevel().title(), "Scanning for controllers fails:" + err.__str__(), icon=tkMessageBox.ERROR): + return False + else: + if not deviceList: + if not tkMessageBox.askretrycancel(self.root.winfo_toplevel().title(), "No Controllers found!", icon=tkMessageBox.ERROR): + return False + + idList = "" + for dev in deviceList: + if len(idList) > 0: + idList = idList + " " + idList = idList + dev.id + self.varDeviceList.set(idList) + + self.layoutDlg.setLayoutFromConfig() + self.selectDevice(0) + return True + + def selectDevice(self, i): + self.selectedDeviceIdx = i + dev = device_drv.DeviceList[i] + id = dev.id + if id in device_drv.ConfigMap: + config = device_drv.ConfigMap[id] + if self.selectedConfig != config: + self.selectedConfig = config + self.scCommonBright.configure(to=self.selectedConfig.commonPWMRes) + self.loadDeviceValues() + else: + if dev.bootloader_mode(): + s = "Bootloader USB:{0}".format(dev.version) + else: + s = "USB:{0} Bootloader PWM:{1:04X}".format(dev.version, dev.get_pwm_version()) + self.varVersion.set(s) + self.lbDevices.selection_clear(0, END) + self.lbDevices.selection_set(i) + self.lbDevices.see(i) + + def loadDeviceValues(self): + self.varVersion.set(self.selectedConfig.version) + self.varPWMRes.set(self.selectedConfig.pwmRes) + self.varPWMFreq.set(self.selectedConfig.pwmFreq) + self.varCommonBright.set(self.selectedConfig.commonBright) + + def storeAndQuit(self, root): + if len(device_drv.ConfigMap): + if tkMessageBox.askokcancel(self.root.winfo_toplevel().title(), "Store Setup?", icon=tkMessageBox.QUESTION): + self.storeSetup() + self.resetDevices() + device_drv.ReleaseDevices() + root.quit() + + def storeSetup(self): + self.layoutDlg.setConfigFromLayout() + try: + for id in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[id] + config.write() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + def resetSetup(self): + if tkMessageBox.askokcancel(self.root.winfo_toplevel().title(), "Really reset setup of all controllers?", default="cancel", icon=tkMessageBox.QUESTION): + try: + for id in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[id] + config.reset() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + def resetDevices(self): + try: + for id in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[id] + config.ctrl.reset_pwm_ctrl() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + def backupSetup(self): + self.layoutDlg.setConfigFromLayout() + ctrl = self.selectedConfig.ctrl + initialfile = ctrl.id + initialfile = string.replace(initialfile, "[", "(") + initialfile = string.replace(initialfile, "]", ")") + initialfile = string.replace(initialfile, ",", "_") + initialfile = initialfile + ".dfc" + self.selectedConfig.ctrl = None + file = None + try: + file = tkFileDialog.asksaveasfile("w", title="Backup Configuration for " + self.selectedConfig.id, defaultextension=".dfc", filetypes=[ ("DF10CH Config", "*.dfc") ], initialfile=initialfile) + if file: + pickle.dump(self.selectedConfig, file) + except IOError: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "Could not save configuration to file!") + finally: + self.selectedConfig.ctrl = ctrl + if file: + file.close() + + def restoreSetup(self): + file = None + try: + file = tkFileDialog.askopenfile("r", title="Restore Configuration for " + self.selectedConfig.id, defaultextension=".dfc", filetypes=[ ("DF10CH Config", "*.dfc") ]) + if file: + config = pickle.load(file) + else: + return + except IOError: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "Could not read configuration from file!") + return + finally: + if file: + file.close() + + config.id = self.selectedConfig.id + config.ctrl = self.selectedConfig.ctrl + + try: + config.ctrl.set_common_brightness(config.commonBright) + config.ctrl.set_pwm_freq(config.pwmFreq) + config.write() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + device_drv.ConfigMap[config.id] = config + self.selectedConfig = config + self.layoutDlg.setLayoutFromConfig() + self.scCommonBright.configure(to=self.selectedConfig.commonPWMRes) + self.loadDeviceValues() + + def firmwareUpdate(self): + try: + filename = tkFileDialog.askopenfilename(title="Select firmware", defaultextension=".dff", filetypes=[ ("DF10CH Firmware", "*.dff") ]) + if not filename or len(filename) == 0: + return + fw = firmware.FlashMem(filename, 64, True) + except (IOError, firmware.FirmwareFlashError) as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + target = fw.target + if target != "DF10CH-USB" and target != "DF10CH-PWM": + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "Unknown firmware target '{0}'!".format(target)) + return + + if target == "DF10CH-PWM": + for ctrl in device_drv.DeviceList: + if ctrl.bootloader_mode(): + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "{0}: Firmware of USB controller must be installed first!".format(ctrl.id)) + return + + if not tkMessageBox.askokcancel(self.root.winfo_toplevel().title(), "Target controller: {0}, Firmware version: {1}. Really start firmware update?".format(target, fw.version), default="cancel", icon=tkMessageBox.QUESTION): + return + + self.displayStatus("Start bootloaders...") + + doFlash = True + doRefresh = False + try: + for ctrl in device_drv.DeviceList: + if target == "DF10CH-USB": + if not ctrl.bootloader_mode(): + doRefresh = True + ctrl.start_bootloader() + else: + if not ctrl.get_pwm_bootloader_mode(): + ctrl.start_pwm_ctrl_bootloader() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + doFlash = False + + if doRefresh: + time.sleep(5.0) + try: + device_drv.FindDevices() + except (device_drv.AtmoControllerError, usb.USBError) as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + doFlash = False + + for ctrl in device_drv.DeviceList: + try: + if target == "DF10CH-USB": + if ctrl.bootloader_mode(): + if doFlash: + pageSize = ctrl.get_flash_page_size() + if pageSize != fw.pageSize: + fw = firmware.FlashMem(filename, pageSize) + firstPage = fw.getPageForAddr(0x0000) + i = 1 + if firstPage: + fp = firmware.FlashPage(0x0000, pageSize) + ctrl.write_flash_page(fp.baseAddr, fp.data) + i = 2 + n = len(fw.pageList) + for fp in fw.pageList: + if fp != firstPage: + self.displayStatus("Flashing USB controller {0}: {1}%".format(ctrl.id, i * 100 / n)) + ctrl.write_flash_page(fp.baseAddr, fp.data) + i = i + 1 + if firstPage: + ctrl.write_flash_page(firstPage.baseAddr, firstPage.data) + time.sleep(1.0) + + i = 1 + for fp in fw.pageList: + self.displayStatus("Verifying USB controller {0}: {1}%".format(ctrl.id, i * 100 / n)) + data = ctrl.read_flash(fp.baseAddr, fp.pageSize) + fp.verify(data) + i = i + 1 + time.sleep(1.0) + else: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "{0}: Bootloader of USB controller does not start!".format(ctrl.id)) + doFlash = False + else: + if ctrl.get_pwm_bootloader_mode(): + if doFlash: + pageSize = ctrl.get_pwm_flash_page_size() + if pageSize != fw.pageSize: + fw = firmware.FlashMem(filename, pageSize) + firstPage = fw.getPageForAddr(0x0000) + i = 1 + if firstPage: + fp = firmware.FlashPage(0x0000, pageSize) + ctrl.write_pwm_flash_page(fp.baseAddr, fp.data) + i = 2 + n = len(fw.pageList) + for fp in fw.pageList: + if fp != firstPage: + self.displayStatus("Flashing PWM controller {0}: {1}%".format(ctrl.id, i * 100 / n)) + ctrl.write_pwm_flash_page(fp.baseAddr, fp.data) + i = i + 1 + if firstPage: + ctrl.write_pwm_flash_page(firstPage.baseAddr, firstPage.data) + time.sleep(1.0) + + i = 1 + for fp in fw.pageList: + self.displayStatus("Verifying PWM controller {0}: {1}%".format(ctrl.id, i * 100 / n)) + data = ctrl.read_pwm_flash(fp.baseAddr, fp.pageSize) + fp.verify(data) + i = i + 1 + time.sleep(1.0) + else: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), "{0}: Bootloader of PWM controller does not start!".format(ctrl.id)) + doFlash = False + except (device_drv.AtmoControllerError, firmware.FirmwareFlashError) as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + doFlash = False + + doRefresh = False + for ctrl in device_drv.DeviceList: + try: + if target == "DF10CH-USB": + if ctrl.bootloader_mode(): + doRefresh = True + ctrl.start_appl() + else: + if ctrl.get_pwm_bootloader_mode(): + ctrl.reset_pwm_ctrl() + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + + if doRefresh: + self.displayStatus("Start firmware...") + time.sleep(5.0) + self.displayStatus("") + self.scanDevices() + + def displayStatus(self, msg): + self.varStatus.set(msg) + self.root.update_idletasks() diff --git a/df10ch_setup_pkg/device_drv.py b/df10ch_setup_pkg/device_drv.py new file mode 100644 index 0000000..cf00272 --- /dev/null +++ b/df10ch_setup_pkg/device_drv.py @@ -0,0 +1,1011 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +import os +import usb +import time +import pickle +import string + +# --- +# Communication protocol related defines for 10 channel RGB Controller. +# +VENDOR_ID = 0x16c0 +PRODUCT_ID = 0x05dc +VENDOR_NAME = 'yak54@gmx.net' +DEVICE_NAME = 'DF10CH' + +REQ_USB_START = 0 # Start of usb controller requests +REQ_USB_BL_START = 64 # Start of usb controller boot loader requests +REQ_PWM_START = 128 # Start of pwm controller requests +REQ_PWM_BL_START = 192 # Start of pwm controller bootloader requests + + # usb controller requests +for i, key in enumerate([ + 'REQ_START_BOOTLOADER', # start boot loader of usb controller + + 'REQ_READ_EE_DATA', # read eeprom data (wLength: number of bytes, wIndex: eeprom start address) + 'REQ_WRITE_EE_DATA', # write eeprom data (wLength: number of bytes, wIndex: eeprom start address) + + 'REQ_STOP_PWM_CTRL', # stop PWM controller + 'REQ_RESET_PWM_CTRL', # reset PWM controller + 'REQ_BOOTLOADER_RESET_PWM_CTRL', # reset PWM controller and signal bootloader start + + 'REQ_SET_REPLY_TIMEOUT', # set reply timeout values (wValue: start timeout [ms], wIndex: timeout [ms]) + 'REQ_GET_REPLY_ERR_STATUS' # get reply error status (COMM_ERR_...) + ], start = REQ_USB_START): + exec key + ' = ' + repr(i) + + # usb controller boot loader requests +for i, key in enumerate([ + 'BL_REQ_WRITE_PAGE', # write flash page + 'BL_REQ_LEAVE_BOOT', # leave boot loader and start application + 'BL_REQ_GET_PAGE_SIZE', # return flash page size of device + 'BL_REQ_READ_FLASH' # read flash memory + ], start = REQ_USB_BL_START): + exec key + ' = ' + repr(i) + + # pwm controller requests +for i, key in enumerate([ + 'PWM_REQ_GET_VERSION', # Get firmware version + + 'PWM_REQ_SET_BRIGHTNESS', # Set channel brightness values (wLenght: number of bytes, wIndex: start channel) + 'PWM_REQ_SET_BRIGHTNESS_SYNCED', + 'PWM_REQ_GET_BRIGHTNESS', + + 'PWM_REQ_SET_CHANNEL_MAP', # Set channel to port mapping (wLength: number of bytes, wIndex: start channel) + 'PWM_REQ_GET_CHANNEL_MAP', + + 'PWM_REQ_SET_COMMON_PWM', # Set common pwm value (wValue.low: pwm value) + 'PWM_REQ_GET_COMMON_PWM', + + 'PWM_REQ_STORE_SETUP', # Store actual calibration values + 'PWM_REQ_RESET_SETUP', # Reset calibration values to default + + 'PWM_REQ_GET_REQUEST_ERR_STATUS', # Get request error status (COMM_ERR_...) + + 'PWM_REQ_GET_MAX_PWM', # Get maximum internal PWM value + + 'PWM_REQ_SET_PWM_FREQ', + 'PWM_REQ_GET_PWM_FREQ', + + 'PWM_REQ_ECHO_TEST' ## Reply 8 byte header + ], start = REQ_PWM_START): + exec key + '=' + repr(i) + + # pwm controller boot loader requests +for i, key in enumerate([ + 'BL_PWM_REQ_WRITE_PAGE', # write flash page + 'BL_PWM_REQ_GET_PAGE_SIZE', # return flash page size of device + 'BL_PWM_REQ_READ_FLASH', # read flash memory + 'BL_PWM_REQ_GET_REQUEST_ERR_STATUS', # Get request error status (COMM_ERR_...) + ], start = REQ_PWM_BL_START): + exec key + ' = ' + repr(i) + + # Data payload related +MAX_PWM_REQ_PAYLOAD_SIZE = 128 +MAX_PWM_REPLY_PAYLOAD_SIZE = 128 + +# Error flag definition for communication error's between usb and pwm controller +COMM_ERR_OVERRUN = 0 +COMM_ERR_FRAME = 1 +COMM_ERR_TIMEOUT = 2 +COMM_ERR_START = 3 +COMM_ERR_OVERFLOW = 4 +COMM_ERR_CRC = 5 +COMM_ERR_DUPLICATE = 6 +COMM_ERR_DEBUG = 7 + +# Port channel mapping related +NCHANNELS = 30 # Number of supported Channels + +NPORTS = 4 +PA_IDX = 0 +PB_IDX = 1 +PC_IDX = 2 +PD_IDX = 3 + +def CM_CODE(port, channel): + return(((channel) << 2) | (port)) +def CM_CHANNEL(code): + return((code) >> 2) +def CM_PORT(code): + return((code) & 0x03) +def CM_BV(bit): + return (1<> 1 + return i - 1 + + +PORT_NAME_MAP = { + # J3 +" 1.1": ( PA_IDX, CM_BV(2) ), +" 1.2": ( PA_IDX, CM_BV(1) ), +" 1.3": ( PA_IDX, CM_BV(0) ), + + # J4 +" 2.1": ( PA_IDX, CM_BV(5) ), +" 2.2": ( PA_IDX, CM_BV(4) ), +" 2.3": ( PA_IDX, CM_BV(3) ), + + # J5 +" 3.1": ( PC_IDX, CM_BV(7) ), +" 3.2": ( PA_IDX, CM_BV(7) ), +" 3.3": ( PA_IDX, CM_BV(6) ), + + # J6 +" 4.1": ( PC_IDX, CM_BV(4) ), +" 4.2": ( PC_IDX, CM_BV(5) ), +" 4.3": ( PC_IDX, CM_BV(6) ), + + # J7 +" 5.1": ( PC_IDX, CM_BV(1) ), +" 5.2": ( PC_IDX, CM_BV(2) ), +" 5.3": ( PC_IDX, CM_BV(3) ), + + # J8 +" 6.1": ( PD_IDX, CM_BV(6) ), +" 6.2": ( PD_IDX, CM_BV(7) ), +" 6.3": ( PC_IDX, CM_BV(0) ), + + # J9 +" 7.1": ( PD_IDX, CM_BV(3) ), +" 7.2": ( PD_IDX, CM_BV(4) ), +" 7.3": ( PD_IDX, CM_BV(5) ), + + # J10 +" 8.1": ( PB_IDX, CM_BV(6) ), +" 8.2": ( PB_IDX, CM_BV(7) ), +" 8.3": ( PD_IDX, CM_BV(2) ), + + # J11 +" 9.1": ( PB_IDX, CM_BV(3) ), +" 9.2": ( PB_IDX, CM_BV(4) ), +" 9.3": ( PB_IDX, CM_BV(5) ), + + # J12 +"10.1": ( PB_IDX, CM_BV(0) ), +"10.2": ( PB_IDX, CM_BV(1) ), +"10.3": ( PB_IDX, CM_BV(2) ) +} + +# Brightness related +NBRIGHTS = 256 +NCOMMONBRIGHTS = 256 + +# PWM frequency related +MIN_PWM_FREQ = 50 +MAX_PWM_FREQ = 400 + +# PWM controller version request related +PWM_VERS_APPL = 0 # Is application firmware +PWM_VERS_BOOT = 1 # Is bootloader firmware + +# USB related +DEF_USB_TIMEOUT = 100 +DEF_RETRY = 3 + +def GetCommErrMsg(stat): + if stat == 0: + rc = 'OK' + else: + rc = '' + if stat & (1<> 2 + if areaIdx < 4: + return "{0}-{1}".format(AREA_NAMES[areaIdx], areaNum + 1) + if areaIdx < NumBaseAreas(): + return AREA_NAMES[areaIdx] + return None + +COLOR_NAMES = "red", "green", "blue" + +def ColorIndex(name): + return COLOR_NAMES.index(name) + +def ColorName(code): + return COLOR_NAMES[code & 0x03] + +def AreaCode(area, color): + s = string.split(area, '-') + areaIdx = AreaIndex(s[0]) + if areaIdx < 4: + areaNum = int(s[1]) - 1 + else: + areaNum = 0 + return ((areaIdx << 2) + (color & 0x03)), areaNum + + +class AtmoControllerError(Exception): + def __init__(self, dev, msg): + self.id = dev.id + self.message = msg + + def __str__(self): + return '{0}: {1}'.format(self.id, self.message) + + + +class DF10CHController: + ''' + Interface to DF10CH RGB Controller + ''' + + def __init__(self, usbdev, busnum, devnum, version, serial): + self.usbdev = usbdev + self.busnum = busnum + self.devnum = devnum + self.version = version + self.serial = serial + self.id = 'DF10CH[{0},{1}]'.format(self.busnum, self.devnum) + + def release(self): + self.usbdev.releaseInterface() + + def bootloader_mode(self): + return self.serial == "BL" + + def ctrl_write(self, req, value, index, data, timeout = DEF_USB_TIMEOUT, retry = DEF_RETRY): + while retry > 0: + try: + retry = retry - 1 + written = self.usbdev.controlMsg(usb.ENDPOINT_OUT|usb.RECIP_DEVICE|usb.TYPE_VENDOR, req, data, value, index, timeout) + except usb.USBError as err: + if retry == 0: + raise AtmoControllerError(self, err.__str__()) + else: + if written != len(data): + raise AtmoControllerError(self, 'could not write all payload data to device') + break + + def ctrl_read(self, req, value = 0, index = 0, size = 0, timeout = DEF_USB_TIMEOUT, retry = DEF_RETRY): + while retry > 0: + try: + retry = retry - 1 + data = self.usbdev.controlMsg(usb.ENDPOINT_IN|usb.RECIP_DEVICE|usb.TYPE_VENDOR, req, size, value, index, timeout) + except usb.USBError as err: + if retry == 0: + raise AtmoControllerError(self, err.__str__()) + else: + if len(data) != size: + raise AtmoControllerError(self, 'could not read all payload data') + break + return data + + def pwm_ctrl_write(self, req, value, index, data, timeout = DEF_USB_TIMEOUT, retry = DEF_RETRY): + if len(data) > MAX_PWM_REQ_PAYLOAD_SIZE: + raise AtmoControllerError(self, 'to many bytes in payload request data') + self.ctrl_write(req, value, index, data, timeout, retry) + + def pwm_ctrl_read(self, req, value = 0, index = 0, size = 0, timeout = DEF_USB_TIMEOUT, retry = DEF_RETRY): + if size > MAX_PWM_REPLY_PAYLOAD_SIZE: + raise AtmoControllerError(self, 'to many bytes for reply payload data') + return self.ctrl_read(req, value, index, size, timeout, retry) + + def verify_reply_data(self, start, data, rdata, msg): + for i in range(len(data)): + if data[i] != rdata[i]: + raise AtmoControllerError(self, '{4}: verify of written {3} data fails {0:04X}: write {1:02X} read {2:02X}'.format(start + i, data[i], rdata[i], msg, self.id)) + + def read_ee_data(self, start, size): + return self.ctrl_read(REQ_READ_EE_DATA, 0, start, size) + + def write_ee_data(self, start, data): + self.ctrl_write(REQ_WRITE_EE_DATA, 0, start, data, DEF_USB_TIMEOUT + len(data) * 10) + eedata = self.read_ee_data(start, len(data)) + self.verify_reply_data(start, data, eedata, 'eeprom') + + def stop_pwm_ctrl(self): + self.ctrl_read(REQ_STOP_PWM_CTRL) + + def reset_pwm_ctrl(self): + self.ctrl_read(REQ_RESET_PWM_CTRL) + + def start_pwm_ctrl_bootloader(self): + self.ctrl_read(REQ_BOOTLOADER_RESET_PWM_CTRL) + + def set_reply_timeout(self, start_timeout, timeout): + self.ctrl_read(REQ_SET_REPLY_TIMEOUT, start_timeout, timeout) + + def get_reply_error_status(self): + data = self.ctrl_read(REQ_GET_REPLY_ERR_STATUS, 0, 0, 1) + return data[0] + + def start_bootloader(self): + self.ctrl_read(REQ_START_BOOTLOADER) + + def start_appl(self): + self.ctrl_read(BL_REQ_LEAVE_BOOT) + + def get_flash_page_size(self): + data = self.ctrl_read(BL_REQ_GET_PAGE_SIZE, 0, 0, 2) + return data[0] + data[1] * 256 + + def write_flash_page(self, addr, data): + self.ctrl_write(BL_REQ_WRITE_PAGE, 0, addr, data) + + def read_flash(self, addr, len): + return self.ctrl_read(BL_REQ_READ_FLASH, 0, addr, len) + + def get_request_error_status(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_REQUEST_ERR_STATUS, 0, 0, 1) + return data[0] + + def set_brightness(self, start, values): + data = list(); + for i in range(len(values)): + data.append(values[i] & 0x00FF) + data.append(values[i] / 256) + self.pwm_ctrl_write(PWM_REQ_SET_BRIGHTNESS, 0, start, data) + + def set_brightness_synced(self, start, values): + data = list(); + for i in range(len(values)): + data.append(values[i] & 0x00FF) + data.append(values[i] / 256) + self.pwm_ctrl_write(PWM_REQ_SET_BRIGHTNESS_SYNCED, 0, start, data) + + def get_brightness(self, start, nch): + data = self.pwm_ctrl_read(PWM_REQ_GET_BRIGHTNESS, 0, start, nch * 2) + values = list() + for i in range(nch): + values.append(data[i*2] + data[i*2+1] * 256) + return values; + + def get_channel_map(self, start, nch): + data = self.pwm_ctrl_read(PWM_REQ_GET_CHANNEL_MAP, 0, start, nch * 2) + map = list() + for i in range(nch): + map.append(dict(channel=CM_CHANNEL(data[i*2]), port=CM_PORT(data[i*2]), pins=data[i*2+1])) + return map; + + def set_channel_map(self, start, map): + data = list() + for mapRec in map: + data.append(CM_CODE(mapRec['port'], mapRec['channel'])) + data.append(mapRec['pins']) + self.pwm_ctrl_write(PWM_REQ_SET_CHANNEL_MAP, 0, start, data) + + def set_common_brightness(self, value): + self.pwm_ctrl_read(PWM_REQ_SET_COMMON_PWM, value) + + def get_common_brightness(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_COMMON_PWM, 0, 0, 2) + return data[0] + data[1] * 256; + + def get_max_pwm_value(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_MAX_PWM, 0, 0, 4) + return data[0] + 256 * data[1] + + def get_common_max_pwm_value(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_MAX_PWM, 0, 0, 4) + return data[2] + 256 * data[3] + + def set_pwm_freq(self, value): + self.pwm_ctrl_read(PWM_REQ_SET_PWM_FREQ, value) + + def get_pwm_freq(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_PWM_FREQ, 0, 0, 2) + return data[0] + 256 * data[1] + + def store_setup(self): + self.pwm_ctrl_read(PWM_REQ_STORE_SETUP, 0, 0, 0, 1500) + + def reset_setup(self): + self.pwm_ctrl_read(PWM_REQ_RESET_SETUP) + + def get_pwm_bootloader_mode(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_VERSION, 0, 0, 2) + return (data[0] == PWM_VERS_BOOT) + + def get_pwm_version(self): + data = self.pwm_ctrl_read(PWM_REQ_GET_VERSION, 0, 0, 2) + return data[1] + + def get_pwm_flash_page_size(self): + data = self.pwm_ctrl_read(BL_PWM_REQ_GET_PAGE_SIZE, 0, 0, 2) + return data[0] + data[1] * 256 + + def write_pwm_flash_page(self, addr, data): + self.pwm_ctrl_write(BL_PWM_REQ_WRITE_PAGE, 0, addr, data) + + def read_pwm_flash(self, addr, len): + return self.pwm_ctrl_read(BL_PWM_REQ_READ_FLASH, 0, addr, len) + + def get_bootloader_request_error_status(self): + data = self.pwm_ctrl_read(BL_PWM_REQ_GET_REQUEST_ERR_STATUS, 0, 0, 1) + return data[0] + + +dummySerial = "AP" + +class dummyController: + def __init__(self, busnum, devnum): + self.busnum = busnum + self.devnum = devnum + self.serial = dummySerial + self.id = 'DUMMY[{0},{1}]'.format(self.busnum, self.devnum) + self.version = 0x0101 + self.ee_data = [ 0xFF ] * 512 + self._reset_setup() + self.start_timeout = 50 + self.timeout = 10 + self.bright = [ 0] * NCHANNELS + self.flash = dict() + self.pwm_vers = PWM_VERS_APPL + self.pwm_flash = dict() + + def _reset_setup(self): + self.common_bright = NCOMMONBRIGHTS - 1 + self.pwm_freq = 100 + self.max_pwm = int(16000000 / (16 * 9 * self.pwm_freq) - 1); + self.ch_map = [ dict(channel=0, port=0, pins=0) ] * 30 + + def release(self): + print "{0}: release interface".format(self.id) + + def bootloader_mode(self): + return self.serial == "BL" + + def read_ee_data(self, start, size): + return self.ee_data[start: start + size] + + def write_ee_data(self, start, data): + self.ee_data[start: start + len(data)] = data + print "{0}: set ee data:".format(self.id), self.ee_data + + def stop_pwm_ctrl(self): + self.pwm_vers = PWM_VERS_APPL + print "{0}: stop pwm controller".format(self.id) + + def reset_pwm_ctrl(self): + self.pwm_vers = PWM_VERS_APPL + print "{0}: reset pwm controller".format(self.id) + + def start_pwm_ctrl_bootloader(self): + self.pwm_vers = PWM_VERS_BOOT + print "{0}: start pwm controller bootloader".format(self.id) + + def set_reply_timeout(self, start_timeout, timeout): + self.start_timeout = start_timeout + self.timeout = timeout + print "{0}: set start_timeout {1} timeout {2}".format(self.id, start_timeout, timeout) + + def get_reply_error_status(self): + return 0 + + def start_bootloader(self): + print "start bootloader" + global dummySerial + dummySerial = "BL" + + def start_appl(self): + print "start appl" + global dummySerial + dummySerial = "AP" + + def get_flash_page_size(self): + return 64 + + def write_flash_page(self, addr, data): + print "write flash page {0:04X}: {1}".format(addr, data) + self.flash[addr] = data + + def read_flash(self, addr, len): + if not addr in self.flash: + self.flash[addr] = [ 255 ] * len + return self.flash[addr] + + def get_request_error_status(self): + return 0 + + def set_brightness(self, start, values): + self.bright[start: start + len(values)] = values + print "{0}: set bright:".format(self.id), self.bright + + def set_brightness_synced(self, start, values): + self.bright[start: start + len(values)] = values + print "{0}: set bright synced:".format(self.id), self.bright + + def get_brightness(self, start, nch): + return self.bright[start: start + nch] + + def get_channel_map(self, start, nch): + return self.ch_map[start: start + nch] + + def set_channel_map(self, start, map): + self.ch_map[start: start + len(map)] = map + print "{0}: set channel map:".format(self.id), self.ch_map + + def set_common_brightness(self, value): + self.common_bright = value + print "{0}: set common brightness:".format(self.id), self.common_bright + + def get_common_brightness(self): + return self.common_bright + + def get_max_pwm_value(self): + return self.max_pwm + + def get_common_max_pwm_value(self): + return NCOMMONBRIGHTS - 1 + + def set_pwm_freq(self, value): + self.pwm_freq = value + self.max_pwm = int(16000000 / (16 * 9 * value) - 1); + print "{0}: set pwm freq {1} max pwm {2}".format(self.id, self.pwm_freq, self.max_pwm) + + def get_pwm_freq(self): + return self.pwm_freq + + def store_setup(self): + global dummyDevices + file = open("dummyctrls.objs", "w") + pickle.dump(dummyDevices, file) + file.close() + print "{0}: store setup".format(self.id) + + def reset_setup(self): + self._reset_setup() + global dummyDevices + file = open("dummyctrls.objs", "w") + pickle.dump(dummyDevices, file) + file.close() + print "{0}: reset setup".format(self.id) + + def get_pwm_bootloader_mode(self): + return self.pwm_vers == PWM_VERS_BOOT + + def get_pwm_version(self): + return 1 + + def get_bootloader_request_error_status(self): + return 0 + + def get_pwm_flash_page_size(self): + return 128 + + def write_pwm_flash_page(self, addr, data): + print "write pwm flash page {0:04X}: {1}".format(addr, data) + self.pwm_flash[addr] = data + + def read_pwm_flash(self, addr, len): + if not addr in self.pwm_flash: + self.pwm_flash[addr] = [ 255 ] * len + return self.pwm_flash[addr] + + +dummyDevices = None +SimulatedControllers = 0 + +def loadDummyDevices(): + global dummyDevices + try: + file = open("dummyctrls.objs", "r") + dummyDevices = pickle.load(file) + file.close() + except IOError: + dummyDevices = list() + + if len(dummyDevices) > SimulatedControllers: + dummyDevices[SimulatedControllers: len(dummyDevices)] = [] + while len(dummyDevices) < SimulatedControllers: + dummyDevices.append(dummyController(0, len(dummyDevices) + 1)) + + return dummyDevices + + +class ControllerConfig: + + def __init__(self, ctrl): + self.classVersion = CONFIG_CLASS_VERSION + self.ctrl = ctrl + self.id = ctrl.id + self.read() + + def read(self): + self.ctrl.reset_pwm_ctrl() + self.pwmRes = self.ctrl.get_max_pwm_value() + self.commonPWMRes = self.ctrl.get_common_max_pwm_value() + self.pwmFreq = self.ctrl.get_pwm_freq() + self.commonBright = self.ctrl.get_common_brightness() + self.numAreas = [ 0 ] * NumBaseAreas() + + eedata = self.ctrl.read_ee_data(1, 5 + len(self.numAreas) + NCHANNELS * 6) + #print "read eedata:", eedata + configValidId = eedata[0] + eedata[1] * 256 + if configValidId == CONFIG_VALID_ID: + configVersion = "{0:04X}".format(eedata[2] + eedata[3] * 256) + for p in range(len(self.numAreas)): + self.numAreas[p] = eedata[4 + p] + if self.numAreas[p] > MAX_AREAS[p]: self.numAreas = MAX_AREAS[p] + else: + configVersion = "" + self.version = "USB:{0} PWM:{1:04X} CONFIG:{2}".format(self.ctrl.version, self.ctrl.get_pwm_version(), configVersion) + pwmChannelMap = self.ctrl.get_channel_map(0, NCHANNELS) + #print "read pwmChannelMap", pwmChannelMap + self.channelMap = dict() + self.numReqChannels = 0 + for portName in PORT_NAME_MAP.keys(): + foundArea = None + port, pin = PORT_NAME_MAP[portName] + for channelRec in pwmChannelMap: + reqChannel = channelRec['channel'] + outPort = channelRec['port'] + outPins = channelRec['pins'] + if outPort == port and (outPins & pin): + if configValidId == CONFIG_VALID_ID: + p = 4 + len(self.numAreas) + self.numReqChannels = eedata[p] + if self.numReqChannels > NCHANNELS: self.numReqChannels = 0 + p = p + 1 + for i in range(self.numReqChannels): + if eedata[p] == reqChannel: + area = AreaName(eedata[p + 1], eedata[p + 2]) + if area: + foundArea = area + color = eedata[p + 1] & 0x03 + gamma = eedata[p + 3] + if gamma < MIN_GAMMA_VAL: gamma = MIN_GAMMA_VAL + if gamma > MAX_GAMMA_VAL: gamma = MAX_GAMMA_VAL + whiteCal = eedata[p + 4] + eedata[p + 5] * 256 + if whiteCal > self.pwmRes: whiteCal = self.pwmRes + break + p = p + 6 + break + if foundArea: + self.channelMap[portName] = dict(area=foundArea, color=color, gamma=gamma, whiteCal=whiteCal) + else: + self.channelMap[portName] = None + #print "read channelMap:", self.channelMap + + def write(self): + #print "write channelMap:", self.channelMap + eedata = list() + eedata.append(CONFIG_VALID_ID & 0x00FF) + eedata.append(CONFIG_VALID_ID >> 8) + eedata.append(CONFIG_VERSION & 0x00FF) + eedata.append(CONFIG_VERSION >> 8) + eedata.extend(self.numAreas) + eedata.append(0) + + reqChannelMap = dict() + pwmChannelMap = list() + n = 0 + for portName in self.channelMap.keys(): + port, pin = PORT_NAME_MAP[portName] + channelRec = self.channelMap[portName] + if channelRec: + area = channelRec['area'] + color = channelRec['color'] + whiteCal = channelRec['whiteCal'] + if whiteCal > self.pwmRes: whiteCal = self.pwmRes + gamma = channelRec['gamma'] + areaCode, areaNum = AreaCode(area, color) + key = '{0}{1}{2}{3}'.format(areaCode, areaNum, gamma, whiteCal) + if key in reqChannelMap: + reqChannel = reqChannelMap[key] + else: + reqChannel = n + n = n + 1 + reqChannelMap[key] = reqChannel + eedata.append(reqChannel) + eedata.append(areaCode) + eedata.append(areaNum) + eedata.append(gamma) + eedata.append(whiteCal & 0x00FF) + eedata.append(whiteCal >> 8) + pwmChannelMap.append(dict(channel=reqChannel, port=port, pins=pin)) + + eedata[4 + len(self.numAreas)] = n + + self.numReqChannels = 0 + while len(pwmChannelMap) < NCHANNELS: + pwmChannelMap.append(dict(channel=0, port=0, pins=0)) + + #print "write pwmChannelMap:", pwmChannelMap + #print "write eedata:", eedata + self.ctrl.write_ee_data(1, eedata) + self.ctrl.set_channel_map(0, pwmChannelMap) + self.ctrl.store_setup() + self.read() + + def reset(self): + self.ctrl.write_ee_data(1, [ 0xFF, 0xFF ]) + self.ctrl.reset_setup() + self.read() + + def setCommonBright(self, v): + if self.commonBright != v: + self.ctrl.set_common_brightness(v) + self.commonBright = v + + def setPWMFreq(self, v): + if self.pwmFreq != v: + self.ctrl.set_pwm_freq(v) + self.pwmRes = self.ctrl.get_max_pwm_value() + self.pwmFreq = v + + +DeviceList = list() + +def FindDevices(): + global DeviceList + + ReleaseDevices() + + if SimulatedControllers: + DeviceList = loadDummyDevices() + return + + busses = usb.busses() + busnum = 0 + for bus in busses: + devnum = 0 + for dev in bus.devices: + if dev.idProduct == PRODUCT_ID and dev.idVendor == VENDOR_ID: + try: + handle = dev.open() + if handle.getString(dev.iManufacturer, 64) == VENDOR_NAME and handle.getString(dev.iProduct, 64) == DEVICE_NAME: + handle.setConfiguration(1) + handle.claimInterface(0) + serial = handle.getString(dev.iSerialNumber, 64) + if os.name == "nt": + bn = bus.location + dn = dev.devnum + else: + bn = busnum + dn = devnum + ctrl = DF10CHController(handle, bn, dn, dev.deviceVersion, serial) + DeviceList.append(ctrl) + except usb.USBError: + pass + devnum = devnum + 1 + busnum = busnum + 1 + + +def ReleaseDevices(): + global DeviceList + for dev in DeviceList: + try: + dev.release() + except usb.USBError: + pass + DeviceList = list() + + +ConfigMap = dict() + +def LoadConfigs(): + global ConfigMap, DeviceList + ConfigMap = dict() + FindDevices() + for ctrl in DeviceList: + if not ctrl.bootloader_mode() and not ctrl.get_pwm_bootloader_mode(): + config = ControllerConfig(ctrl) + ConfigMap[ctrl.id] = config + + +if __name__ == "__main__": + def calc_gamma_tab(gamma, max_pwm): + result = list(); + for i in range (256): + v = pow (i / 255.0, gamma) + iv = int(round(v * max_pwm)) + result.append(iv) + return result + + FindDevices() + if len(DeviceList): + dev = DeviceList[0] + + #dev.set_reply_timeout(150, 10) + + if 0: +# data = range(128, 0, -1) +# print "write" +# dev.write_ee_data(0, data) + print "read" + for i in range(50): + eedata = dev.read_ee_data(0, 254) + print eedata + + #print "set reply timeout" + #dev.set_reply_timeout(950,5) + + if 1: + import firmware + fw = firmware.FlashMem("/home/andy/python_ws/df10ch/pwm_ctrl/10ch_pwm_ctrl.dff", 128, True) + if dev.bootloader_mode(): + dev.start_appl() + time.sleep(5) + if not dev.get_pwm_bootloader_mode(): + dev.start_pwm_ctrl_bootloader() + print dev.bootloader_mode(), dev.get_pwm_bootloader_mode() + try: + for i in range(50): + print i + for fp in fw.pageList: + #print fp.baseAddr + data = dev.read_pwm_flash(fp.baseAddr, fp.pageSize) + fp.verify(data) + except: + pass + print "get request error status" + stat = dev.get_bootloader_request_error_status() + print GetCommErrMsg(stat) + + if 0: + print 'get pwm version' + v = dev.get_pwm_version() + print "pwm version: ", v + + if 0: + print 'set brighness' + #dev.set_pwm_freq(100) + f = dev.get_pwm_freq() + print "freq: ", f + m = dev.get_max_pwm_value() + print "max pwm: ", m + gtab = calc_gamma_tab(2.2, m) + #while 1: + t = time.clock() + n = 0 + for i in range(9): + for v in range(NBRIGHTS): + data = [ gtab[v] ] + dev.set_brightness_synced(i, data) + n = n + 1 + for v in range(NBRIGHTS-1,-1,-1): + data = [ gtab[v] ] + dev.set_brightness_synced(i, data) + n = n + 1 + t1 = time.clock() + print "mean time ", (t1 - t) / n + print "get request error status" + stat = dev.get_request_error_status() + print GetCommErrMsg(stat) + print "get reply error status" + stat = dev.get_reply_error_status() + print GetCommErrMsg(stat) + + if 0: + print 'set brighness' + dev.set_pwm_freq(100) + f = dev.get_pwm_freq() + print "freq: ", f + m = dev.get_max_pwm_value() + print "max pwm: ", m + gtab = calc_gamma_tab(2.2, m) + rows = list() + for i in range(256): + data = list() + for c in range(30): + v = i + c + if v >= NBRIGHTS: + v = v - NBRIGHTS + data.append(gtab[v]) + rows.append(data) + data = list() + for c in range(30): + v = NBRIGHTS - i - c - 1 + if v < 0: + v = v + NBRIGHTS + data.append(gtab[v]) + rows.append(data) + while 1: + t = time.clock() + n = 0 + for data in rows: + dev.set_brightness(0, data) + n = n + 1 + t1 = time.clock() + print "mean time ", (t1 - t) / n + stat = dev.get_request_error_status() + if stat: + print "get request error status" + print GetCommErrMsg(stat) + stat = dev.get_reply_error_status() + if stat: + print "get reply error status" + print GetCommErrMsg(stat) + + if 0: + print 'get channel map' + data = dev.get_channel_map(0, NCHANNELS) + print data + + if 0: + print "common brightness test" + data = [ 255, 255, 255, 255, 255, 255, 255, 255, 255 ] + dev.set_brightness(0, data) + for v in range(NBRIGHTS-1,-1,-1): + print v + dev.set_common_brightness(v) + time.sleep(0.01) + time.sleep(1) + for v in range(NBRIGHTS): + dev.set_common_brightness(v) + time.sleep(0.01) + + if 0: + print "reset setup" + dev.reset_setup() + + if 0: + print "store setup" + dev.store_setup() + + if 0: + print "get request error status" + stat = dev.get_request_error_status() + print GetCommErrMsg(stat) + + if 1: + print "get reply error status" + stat = dev.get_reply_error_status() + print GetCommErrMsg(stat) + else: + print "No controller found!" + + + diff --git a/df10ch_setup_pkg/firmware.py b/df10ch_setup_pkg/firmware.py new file mode 100644 index 0000000..24dc577 --- /dev/null +++ b/df10ch_setup_pkg/firmware.py @@ -0,0 +1,135 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +import array +import fileinput +import string + +class FirmwareFlashError(Exception): + def __init__(self, msg = None): + self.message = msg + + def __str__(self): + return self.message + + +class FlashPage: + + def __init__(self, addr, pageSize): + self.pageSize = pageSize + self.baseAddr = addr - addr % pageSize + self.data = array.array('B', [ 0xFF ] * pageSize) + + def insert(self, addr, value): + self.data[addr % self.pageSize] = value + + def verify(self, data): + for i in range(self.pageSize): + if data[i] != self.data[i]: + raise FirmwareFlashError("verify of flash data against firmware fails at {0:04X}: {1:02X} <> {2:02X}".format(self.baseAddr + i, data[i], self.data[i])) + + def __str__(self): + s = "{0:04X}: ".format(self.baseAddr) + for v in self.data: + s = s + "{0:02X} ".format(v) + return s + + +class FlashMem: + + def __init__(self, fileName, pageSize, targetInfoMustExist = False): + self.pageSize = pageSize + self.pageList = list() + self.lastLookupPage = None + self.target = None + self.version = None + self.loadFromHexFile(fileName) + if targetInfoMustExist and (not self.target or not self.version): + raise FirmwareFlashError("no target and/or version information found!") + + def getPageForAddr(self, addr): + baseAddr = addr - addr % self.pageSize + if self.lastLookupPage and self.lastLookupPage.baseAddr == baseAddr: + return self.lastLookupPage; + for p in self.pageList: + if p.baseAddr == baseAddr: + self.lastLookupPage = p + return p + return None + + def insert(self, addr, value): + p = self.getPageForAddr(addr) + if not p: + p = FlashPage(addr, self.pageSize) + self.pageList.append(p) + p.insert(addr, value) + + def loadFromHexFile(self, fileName): + file = None + try: + file = fileinput.FileInput(fileName) + for line in file: + line = string.rstrip(line) + lineLen = len(line) + if not lineLen: + continue + lineType = line[0:1] + if lineType == "#": + continue + if lineType == "@": + try: + self.target, self.version = string.split(line[1:]) + except: + raise FirmwareFlashError() + continue + if lineLen < 9 or lineType != ":": + raise FirmwareFlashError() + try: + n = int(line[1:3], 16) + addr = int(line[3:7], 16) + type = int(line[7:9], 16) + except: + raise FirmwareFlashError() + if type != 0: + break + if n > 0: + if lineLen < (9 + 2 * n): + raise FirmwareFlashError() + for i in range(n): + try: + data = int(line[i * 2 + 9: i * 2 + 11], 16) + except: + raise FirmwareFlashError() + self.insert(addr + i, data) + except IOError as err: + raise FirmwareFlashError("could not read firmware file '{0}': {1}".format(fileName, err.__str__())) + except FirmwareFlashError: + raise FirmwareFlashError("could not read firmware file '{0}': syntax error at line {1}".format(fileName, file.lineno())) + finally: + if file: + file.close() + +if __name__ == "__main__": + m = FlashMem("10ch_usb_ctrl.hex", 16) + print "target:", m.target, "version:", m.version + for p in m.pageList: + print p \ No newline at end of file diff --git a/df10ch_setup_pkg/layout_dlg.py b/df10ch_setup_pkg/layout_dlg.py new file mode 100644 index 0000000..500c029 --- /dev/null +++ b/df10ch_setup_pkg/layout_dlg.py @@ -0,0 +1,111 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import tkFont + +import device_drv + + +class LayoutDialog: + def __init__(self, areasDlg, master=None, **args): + self.areasDlg = areasDlg + self.numAreas = [ 0 ] * device_drv.NumBaseAreas() + self.varNumAreas = list() + for i in range(device_drv.NumBaseAreas()): + self.varNumAreas.append(StringVar()) + + root = Frame(master, **args) + self.root = root + + Label(root, text="Configure RGB-Areas", font=tkFont.Font(weight="bold")).grid(row=0, column=0, columnspan=5, padx=5, pady=5) + Label(root, text="Top").grid(row=1, column=2) + Label(root, text="TopLeft").grid(row=1, column=0, sticky=E) + Label(root, text="Left").grid(row=3, column=0, sticky=E) + Label(root, text="BottomLeft").grid(row=5, column=0, sticky=E) + Label(root, text="TopRight").grid(row=1, column=4, sticky=W) + Label(root, text="Right").grid(row=3, column=4, sticky=W) + Label(root, text="BottomRight").grid(row=5, column=4, sticky=W) + Label(root, text="Bottom").grid(row=5, column=2) + + i = device_drv.AreaIndex("TopLeft") + self.sbTopLeft = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], width=2) + self.sbTopLeft.grid(row=2, column=1, padx=5, pady=5) + + i = device_drv.AreaIndex("TopRight") + self.sbTopRight = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], width=2) + self.sbTopRight.grid(row=2, column=3, padx=5, pady=5) + + i = device_drv.AreaIndex("BottomLeft") + self.sbBottomLeft = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], width=2) + self.sbBottomLeft.grid(row=4, column=1, padx=5, pady=5) + + i = device_drv.AreaIndex("BottomRight") + self.sbBottomRight = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], width=2) + self.sbBottomRight.grid(row=4, column=3, padx=5, pady=5) + + i = device_drv.AreaIndex("Center") + self.sbCenter = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], width=2) + self.sbCenter.grid(row=3, column=2, padx=20, pady=20) + + i = device_drv.AreaIndex("Top") + self.sbTop = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], increment=1, width=3) + self.sbTop.grid(row=2, column=2, padx=5, pady=5) + + i = device_drv.AreaIndex("Bottom") + self.sbBottom = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], increment=1, width=3) + self.sbBottom.grid(row=4, column=2, padx=5, pady=5) + + i = device_drv.AreaIndex("Left") + self.sbLeft = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], increment=1, width=3) + self.sbLeft.grid(row=3, column=1, padx=5, pady=5) + + i = device_drv.AreaIndex("Right") + self.sbRight = Spinbox(root, textvariable=self.varNumAreas[i], from_=0, to=device_drv.MAX_AREAS[i], increment=1, width=3) + self.sbRight.grid(row=3, column=3, padx=5, pady=5) + + self.btApply = Button(root, text="Apply", command=self.cbApply) + self.btApply.grid(row=6, column=4, padx=20, pady=20, ipadx=5) + + def cbApply(self): + for i in range(device_drv.NumBaseAreas()): + self.numAreas[i] = int(self.varNumAreas[i].get()) + self.areasDlg.configAreas(self.numAreas) + + def setLayoutFromConfig(self): + for i in range(device_drv.NumBaseAreas()): + self.numAreas[i] = 0 + for ctrlId in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[ctrlId] + for i in range(device_drv.NumBaseAreas()): + if config.numAreas[i] > self.numAreas[i]: + self.numAreas[i] = config.numAreas[i] + for i in range(device_drv.NumBaseAreas()): + self.varNumAreas[i].set(self.numAreas[i]) + self.areasDlg.configAreas(self.numAreas) + + def setConfigFromLayout(self): + for ctrlId in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[ctrlId] + for i in range(device_drv.NumBaseAreas()): + config.numAreas[i] = self.numAreas[i] + diff --git a/df10ch_setup_pkg/map_dlg.py b/df10ch_setup_pkg/map_dlg.py new file mode 100644 index 0000000..b8cd575 --- /dev/null +++ b/df10ch_setup_pkg/map_dlg.py @@ -0,0 +1,256 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import tkFont +import tkMessageBox +import device_drv +import string + +class ChannelMapDialog: + def __init__(self, areasDlg, master=None, **args): + self.areasDlg = areasDlg + areasDlg.selectCallbacks.append(self) + + self.actualIdx = None + self.actualCtrlId = None + self.actualPortName = None + self.actualArea = None + self.actualColor = None + self.channelList = None + + root = Frame(master, **args) + self.root = root + root.bind("", self.cbSheetSelected) + + Label(root, text="Channel Mapping", font=tkFont.Font(weight="bold")).grid(row=0, column=0, columnspan=4, padx=5, pady=5) + + self.yScroll = Scrollbar (root, orient=VERTICAL) + self.yScroll.grid(row=1, column=1, rowspan=4, sticky=N+S) + self.lbChannels = Listbox(root, selectmode=SINGLE, activestyle=NONE, width=35, height=30, yscrollcommand=self.yScroll.set) + self.lbChannels.grid(row=1, column=0, rowspan=4, padx=5, pady=5, sticky=N+S+W+E) + self.lbChannels.bind("", self.cbSelectChannel) + self.yScroll["command"] = self.lbChannels.yview + + frSingleColor = Frame(root, borderwidth=5, relief=RIDGE, padx=0, pady=0) + frSingleColor.grid(row=2, column = 2, columnspan = 2, padx=5, pady=5, sticky=W+E) + + Label(frSingleColor, text="Set color for channel:").grid(row=0, column=0, columnspan=3, padx=5, pady=5, sticky="W") + + self.btRed = Button(frSingleColor, text="Red", bg="red", command=lambda: self.cbSetColor("red")) + self.btRed.grid(row=1, column=0, padx=5, pady=5, ipadx=5, sticky=W+E) + + self.btGreen = Button(frSingleColor, text="Green", bg="green", command=lambda: self.cbSetColor("green")) + self.btGreen.grid(row=1, column=1, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btBlue = Button(frSingleColor, text="Blue", bg="blue", command=lambda: self.cbSetColor("blue")) + self.btBlue.grid(row=1, column=2, padx=5, pady=5, ipadx = 5, sticky=W+E) + + frGroupColor = Frame(root, borderwidth=5, relief=RIDGE, padx=0, pady=0) + frGroupColor.grid(row = 3, column = 2, columnspan = 2, padx=5, pady=5, sticky=W+E) + + Label(frGroupColor, text="Set color for channel group:").grid(row=0, column=0, columnspan=3, padx=5, pady=5, sticky="W") + + self.btRGB = Button(frGroupColor, text="RGB", command=lambda: self.cbSetGroupColor("red", "green", "blue")) + self.btRGB.grid(row=1, column=0, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btRBG = Button(frGroupColor, text="RBG", command=lambda: self.cbSetGroupColor("red", "blue", "green")) + self.btRBG.grid(row=1, column=1, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btBRG = Button(frGroupColor, text="GRB", command=lambda: self.cbSetGroupColor("green", "red", "blue")) + self.btBRG.grid(row=1, column=2, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btGRB = Button(frGroupColor, text="BRG", command=lambda: self.cbSetGroupColor("blue", "red", "green")) + self.btGRB.grid(row=2, column=0, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btBGR = Button(frGroupColor, text="GBR", command=lambda: self.cbSetGroupColor("green", "blue", "red")) + self.btBGR.grid(row=2, column=1, padx=5, pady=5, ipadx = 5, sticky=W+E) + + self.btGBR = Button(frGroupColor, text="BGR", command=lambda: self.cbSetGroupColor("blue", "green", "red")) + self.btGBR.grid(row=2, column=2, padx=5, pady=5, ipadx = 5, sticky=W) + + #frNavi = Frame(root, padx=0, pady=0) + #frNavi.grid(row = 1, column = 2, columnspan = 2, padx=5, pady=5, sticky=N+S+W+E) + + self.btNext = Button(root, text="Next Ch", command=self.cbNext) + self.btNext.grid(row=1, column=2, padx=5, pady=5, ipadx=5, sticky=W+E) + + self.btPrev = Button(root, text="Prev Ch", command=self.cbPrev) + self.btPrev.grid(row=1, column=3, padx=5, pady=5, ipadx=5, sticky=W+E) + + self.btDelete = Button(root, text="Delete mapping", command=self.cbDelete) + self.btDelete.grid(row=4, column=2, columnspan=2, padx=5, pady=5, ipadx = 5, sticky=W+E) + + + def cbSelectChannel(self, event): + s = self.lbChannels.curselection() + if len(s) == 1: + self.selectChannel(int(s[0])) + + def cbAreaSelected(self): + if self.root.winfo_ismapped() and self.actualIdx != None: + self.selectArea(self.areasDlg.selectedArea, self.actualColor) + self.storeActual() + + def cbSetGroupColor(self, *colors): + actIdx = self.actualIdx + if actIdx != None: + port, p = string.split(self.actualPortName, ".") + for pinNum in range(3): + portName = "{0}.{1}".format(port, pinNum + 1) + for i, mapRec in enumerate(self.channelList): + if mapRec['ctrlId'] == self.actualCtrlId and mapRec['portName'] == portName: + self.actualPortName = portName + self.actualColor = colors[pinNum] + self.actualIdx = i + self.storeActual() + break + self.selectChannel(actIdx) + + def cbSetColor(self, color): + if self.actualIdx != None: + self.selectArea(self.actualArea, color) + self.storeActual() + + def cbDelete(self): + if self.actualIdx != None: + self.storeActual(True) + + def cbNext(self): + if self.actualIdx != None: + i = self.actualIdx + 1 + if i == len(self.channelList): + i = 0 + self.selectChannel(i) + + def cbPrev(self): + if self.actualIdx != None: + i = self.actualIdx - 1 + if i < 0: + i = len(self.channelList) - 1 + self.selectChannel(i) + + def cbSheetSelected(self, event): + self.loadValues() + + def storeActual(self, delete=False): + config = device_drv.ConfigMap[self.actualCtrlId] + if delete: + config.channelMap[self.actualPortName] = None + self.channelList[self.actualIdx] = dict(ctrlId=self.actualCtrlId, portName=self.actualPortName, area=None, color=None) + else: + self.channelList[self.actualIdx] = dict(ctrlId=self.actualCtrlId, portName=self.actualPortName, area=self.actualArea, color=self.actualColor) + + if not delete and self.actualArea and self.actualColor: + configMapRec = config.channelMap[self.actualPortName] + if configMapRec: + configMapRec['area'] = self.actualArea + configMapRec['color'] = device_drv.ColorIndex(self.actualColor) + else: + configMapRec = dict(area=self.actualArea, color=device_drv.ColorIndex(self.actualColor), gamma=device_drv.DEFAULT_GAMMA_VAL, whiteCal=config.pwmRes) + config.channelMap[self.actualPortName] = configMapRec + text = "{0}: {1} -> {2}: {3}".format(self.actualCtrlId, self.actualPortName, self.actualArea, self.actualColor) + else: + text = "{0}: {1}".format(self.actualCtrlId, self.actualPortName) + + self.lbChannels.delete(self.actualIdx, self.actualIdx) + self.lbChannels.insert(self.actualIdx, text) + self.lbChannels.selection_set(self.actualIdx) + + def loadValues(self): + self.actualIdx = None + self.channelList = list() + self.lbChannels.delete(0, END) + for ctrlId in sorted(device_drv.ConfigMap.keys()): + config = device_drv.ConfigMap[ctrlId] + for portName in sorted(config.channelMap.keys()): + configMapRec = config.channelMap[portName] + if configMapRec: + area = configMapRec['area'] + color = device_drv.ColorName(configMapRec['color']) + text = "{0}: {1} -> {2}: {3}".format(ctrlId, portName, area, color) + else: + area = None + color = None + text = "{0}: {1}".format(ctrlId, portName) + self.channelList.append(dict(ctrlId=ctrlId, portName=portName, area=area, color=color)) + self.lbChannels.insert(END, text) + + pwmChannelMap = list() + while len(pwmChannelMap) < device_drv.NCHANNELS: + pwmChannelMap.append(dict(channel=0, port=0, pins=0)) + + try: + config.ctrl.set_channel_map(0, pwmChannelMap) + config.ctrl.set_brightness(0, [ config.pwmRes ] + [ 0 ] * (device_drv.NCHANNELS - 1)) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + self.areasDlg.initAreas("black") + self.actualCtrlId = None + self.actualPortName = None + if len(self.channelList): + self.selectChannel(0) + + def selectChannel(self, i): + self.actualIdx = i + + self.lbChannels.selection_clear(0, END) + self.lbChannels.selection_set(i) + self.lbChannels.see(i) + + mapRec = self.channelList[i] + self.selectLight(mapRec['ctrlId'], mapRec['portName']) + self.selectArea(mapRec['area'], mapRec['color']) + + def selectLight(self, ctrlId, portName): + if ctrlId != self.actualCtrlId or portName != self.actualPortName: + if self.actualCtrlId and ctrlId != self.actualCtrlId: + config = device_drv.ConfigMap[self.actualCtrlId] + try: + config.ctrl.set_channel_map(0, [ dict(channel=0, port=0, pins=0) ]) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + port, pin = device_drv.PORT_NAME_MAP[portName] + config = device_drv.ConfigMap[ctrlId] + try: + config.ctrl.set_channel_map(0, [ dict(channel=0, port=port, pins=pin) ]) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + self.actualCtrlId = ctrlId + self.actualPortName = portName + + def selectArea(self, area, color): + if self.actualArea: + self.areasDlg.setAreaColor(self.actualArea, "black") + self.actualColor = color + if area: + self.actualArea = area + if not color: + color = "black" + self.areasDlg.setAreaColor(area, color) + self.areasDlg.selectArea(area) diff --git a/df10ch_setup_pkg/setup_dlg.py b/df10ch_setup_pkg/setup_dlg.py new file mode 100644 index 0000000..23fc853 --- /dev/null +++ b/df10ch_setup_pkg/setup_dlg.py @@ -0,0 +1,58 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * + +class SetupDialog: + + def __init__(self, master, side=LEFT): + + self.activeSheet = None + self.count = 0 + self.choice = IntVar(0) + + if side in (TOP, BOTTOM): + self.side = LEFT + else: + self.side = TOP + + self.tabsMaster = Frame(master, borderwidth=2, relief=RIDGE) + self.tabsMaster.pack(side=side, fill=BOTH) + self.sheetMaster = Frame(master, borderwidth=2, relief=RIDGE) + self.sheetMaster.pack(fill=BOTH) + + + def addSheet(self, sheet, title): + b = Radiobutton(self.tabsMaster, text=title, padx=5, pady=10, indicatoron=0, \ + variable=self.choice, value=self.count, \ + command=lambda: self.displaySheet(sheet)) + b.pack(fill=BOTH, side=self.side) + if not self.activeSheet: + sheet.pack(fill=BOTH, expand=1) + self.activeSheet = sheet + self.count += 1 + + + def displaySheet(self, sheet): + self.activeSheet.forget() + sheet.pack(fill=BOTH, expand=1) + self.activeSheet = sheet diff --git a/df10ch_setup_pkg/white_cal_dlg.py b/df10ch_setup_pkg/white_cal_dlg.py new file mode 100644 index 0000000..3843e78 --- /dev/null +++ b/df10ch_setup_pkg/white_cal_dlg.py @@ -0,0 +1,172 @@ +# +# Copyright (C) 2010 Andreas Auras +# +# This file is part of the DF10CH Atmolight controller project. +# +# DF10CH Atmolight controller is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DF10CH Atmolight controller is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA +# +# This file is part of the DF10CH setup program +# + +from Tkinter import * +import tkFont +import tkMessageBox +import device_drv + + +class WhiteCalDialog: + def __init__(self, areasDlg, master=None, **args): + self.areasMap = None + self.selectedArea = None + + self.areasDlg = areasDlg + areasDlg.selectCallbacks.append(self) + + root = Frame(master, **args) + self.root = root + root.bind("", self.cbSheetSelected) + + Label(root, text="White Calibration", font=tkFont.Font(weight="bold")).grid(row=0, column=0, columnspan=5, padx=5, pady=5) + Label(root, text="Max. value:").grid(row=1, column=0, sticky=E) + Label(root, text="Gamma value:").grid(row=2, column=0, sticky=E) + + + self.varWhiteCal = DoubleVar(), DoubleVar(), DoubleVar() + self.scRed = Scale(root, label="Red", bg="red", length=200, from_=1.0, to=0.0, resolution=0.01, orient=VERTICAL, variable=self.varWhiteCal[device_drv.ColorIndex("red")], command=self.cbSetWhiteCal) + self.scRed.grid(row=1, column=1, rowspan=1, padx=5, pady=5) + self.scGreen = Scale(root, label="Green", bg="green", length=200, from_=1.0, to=0.0, resolution=0.01, orient=VERTICAL, variable=self.varWhiteCal[device_drv.ColorIndex("green")], command=self.cbSetWhiteCal) + self.scGreen.grid(row=1, column=2, rowspan=1, padx=5, pady=5) + self.scBlue = Scale(root, label="Blue", bg="blue", length=200, from_=1.0, to=0.0, resolution=0.01, orient=VERTICAL, variable=self.varWhiteCal[device_drv.ColorIndex("blue")], command=self.cbSetWhiteCal) + self.scBlue.grid(row=1, column=3, rowspan=1, padx=5, pady=5) + + self.varGamma = StringVar(), StringVar(), StringVar() + self.sbRed = Spinbox(root, textvariable=self.varGamma[device_drv.ColorIndex("red")], from_=device_drv.MIN_GAMMA_VAL/10.0, to=device_drv.MAX_GAMMA_VAL/10.0, format='%2.1f', increment=0.1, width=2, command=self.cbSetGamma) + self.sbRed.grid(row=2, column=1, padx=5, pady=5, sticky=W+E) + self.sbGreen = Spinbox(root, textvariable=self.varGamma[device_drv.ColorIndex("green")], from_=device_drv.MIN_GAMMA_VAL/10.0, to=device_drv.MAX_GAMMA_VAL/10.0, format='%2.1f', increment=0.1, width=2, command=self.cbSetGamma) + self.sbGreen.grid(row=2, column=2, padx=5, pady=5, sticky=W+E) + self.sbBlue = Spinbox(root, textvariable=self.varGamma[device_drv.ColorIndex("blue")], from_=device_drv.MIN_GAMMA_VAL/10.0, to=device_drv.MAX_GAMMA_VAL/10.0, format='%2.1f', increment=0.1, width=2, command=self.cbSetGamma) + self.sbBlue.grid(row=2, column=3, padx=5, pady=5, sticky=W+E) + + self.varBright = DoubleVar() + self.scBright = Scale(root, label="Brightness", length=200, from_=1.00, to=0.0, resolution=0.01, orient=VERTICAL, variable=self.varBright, command=self.cbSetBright) + self.scBright.grid(row=1, column=4, rowspan=1, padx=20, pady=5) + self.varBright.set(1.0) + + self.varSelectAll = IntVar() + self.btSelectAll = Checkbutton(root, text="Select All", command=self.cbSelectAll, variable=self.varSelectAll) + self.btSelectAll.grid(row=2, column=4, padx=20, pady=20, ipadx=5, sticky=W+E) + + + def cbSelectAll(self): + self.setBright() + + def cbSheetSelected(self, event): + self.loadValues() + + def cbSetWhiteCal(self, val): + self.update() + + def cbSetGamma(self): + self.update() + + def cbSetBright(self, val): + self.setBright() + + def cbAreaSelected(self): + if self.root.winfo_ismapped(): + self.selectArea(self.areasDlg.selectedArea) + + def loadValues(self): + self.areasMap = dict() + area = None + for ctrlId in device_drv.ConfigMap.keys(): + config = device_drv.ConfigMap[ctrlId] + pwmChannelMap = list() + brightList = list() + reqChannel = 0 + for portName in config.channelMap.keys(): + configMapRec = config.channelMap[portName] + if configMapRec: + port, pin = device_drv.PORT_NAME_MAP[portName] + pwmChannelMap.append(dict(channel=reqChannel, port=port, pins=pin)) + area = configMapRec['area'] + gamma = configMapRec['gamma'] / 10.0 + whiteCal = configMapRec['whiteCal'] + bright = int(round(pow(self.varBright.get(), gamma) * whiteCal)) + brightList.append(bright) + cRec = dict(ctrlId=ctrlId, portName=portName, configMapRec=configMapRec, reqChannel=reqChannel) + if not area in self.areasMap: self.areasMap[area] = list() + self.areasMap[area].append(cRec) + reqChannel = reqChannel + 1 + + while reqChannel < device_drv.NCHANNELS: + brightList.append(0) + pwmChannelMap.append(dict(channel=0, port=0, pins=0)) + reqChannel = reqChannel + 1 + + try: + config.ctrl.set_channel_map(0, pwmChannelMap) + config.ctrl.set_brightness(0, brightList) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return + + if area: + self.selectArea(area) + self.setBright() + + def setBright(self): + self.areasDlg.initAreas("#{0:02X}{0:02X}{0:02X}".format(int(round(self.varBright.get() * 255.0)))) + if not self.varSelectAll.get() and self.selectedArea: + self.areasDlg.selectArea(self.selectedArea) + self.update() + + def selectArea(self, area): + if area in self.areasMap: + self.varSelectAll.set(0) + self.selectedArea = area + self.areasDlg.selectArea(area) + for cRec in self.areasMap[area]: + ctrlId = cRec['ctrlId'] + config = device_drv.ConfigMap[ctrlId] + configMapRec = cRec['configMapRec'] + color = configMapRec['color'] + whiteCal = float(configMapRec['whiteCal']) / float(config.pwmRes) + gamma = float(configMapRec['gamma']) / 10.0 + self.varGamma[color].set(gamma) + self.varWhiteCal[color].set(whiteCal) + + def update(self): + for area in self.areasMap.keys(): + for cRec in self.areasMap[area]: + ctrlId = cRec['ctrlId'] + reqChannel = cRec['reqChannel'] + configMapRec = cRec['configMapRec'] + color = configMapRec['color'] + config = device_drv.ConfigMap[ctrlId] + if self.varSelectAll.get() or self.selectedArea == area: + gamma = float(self.varGamma[color].get()) + whiteCal = int(self.varWhiteCal[color].get() * config.pwmRes) + configMapRec['gamma'] = int(gamma * 10.0) + configMapRec['whiteCal'] = whiteCal + else: + gamma = float(configMapRec['gamma']) / 10.0 + whiteCal = configMapRec['whiteCal'] + bright = int(round(pow(self.varBright.get(), gamma) * whiteCal)) + try: + config.ctrl.set_brightness(reqChannel, [ bright ]) + except device_drv.AtmoControllerError as err: + tkMessageBox.showerror(self.root.winfo_toplevel().title(), err.__str__()) + return -- cgit v1.2.3