def main(): global device device.activate_ap() gc.collect() log.info("Free memory: {}".format(gc.mem_free())) http = Http() # Register handler for each path http.register_handler(HTTP_METHOD.GET, "/config", get_config) http.register_handler(HTTP_METHOD.POST, "/config", save_config) # Start the server @ port 80 log.info("IP: {}".format(device.get_ip())) log.info("Device ID: {}".format(device.id)) web = Webserver(http) web.start(80) gc.collect() device.indicate_ready() try: while True: # Let server process requests web.handle_client() time.sleep_ms(50) except Exception as err: log.severe("Unhandled exception: " + str(err)) finally: web.close()
class MainWnd(QtGui.QMainWindow): def __init__(self): super(MainWnd, self).__init__() self.tabs = [] # list of TabsType objects (namedtuples) self.folder_for_reveal = None self.filter = None self.treeView, self.treeViewModel = None,None self.text_change_mutex = Lock() self.paused = False self.tabCtrl = None self.proc_terminated = False self.taskManager = None self.threads = [] self.filter_model = None self.settings = QtCore.QSettings() self.openRecentMenu = None self.initUI() self.qtimer = QtCore.QTimer() self.qtimer.timeout.connect( self.make_screenshot ) self.qtimer.start( 5*1000 ) self.webserver = None RestoreGeom(self) def make_screenshot(self): if self.webserver: pngPath = self.webserver.pngPath if pngPath: widget = self.centralWidget() r = widget.rect() pixmap = QtGui.QPixmap( r.size() ) widget.render( pixmap, QtCore.QPoint(), QtGui.QRegion(r) ) file = QtCore.QFile(pngPath) file.open(QtCore.QIODevice.WriteOnly) pixmap.save(file, "PNG") def initUI(self): widget = QtGui.QWidget() total_layout = QtGui.QVBoxLayout() top = self.makeTopLayout() bottom = self.makeBottomLayout() self.initUI2(bottom) total_layout.addLayout(top) total_layout.addLayout(bottom) widget.setLayout(total_layout) self.setCentralWidget(widget) def makeTopLayout(self): """Make a QHBoxLayout containing clickable buttons like 'Pause', and filters ('errors', 'warnings'...)""" top = QtGui.QHBoxLayout() top.setAlignment( Qt.AlignLeft ) btn = QtGui.QPushButton("Pause") btn.setCheckable(True) btn.pressed.connect( self.toggle_paused ) top.addWidget( btn ) btn = QtGui.QPushButton("Run") btn.pressed.connect( self.run_yaml ) top.addWidget( btn ) btn = QtGui.QPushButton("Stop") btn.pressed.connect( self.stop_yaml ) top.addWidget( btn ) top.addWidget( QtGui.QLabel("Filters:") ) model = QStandardItemModel() combo = QtGui.QComboBox() combo.setModel(model) self.filter_model = model ix = 0 for label,str in config.FILTERS: item = QStandardItem(label) model.appendRow(item) combo.setItemData(ix, str) ix += 1 combo.currentIndexChanged.connect( lambda ix, combo=combo : self.filter_combo_changed(ix, combo) ) top.addWidget(combo) return top def makeBottomLeftLayout(self): bottom_left = QtGui.QVBoxLayout() self.treeView = QtGui.QTreeView() tv = self.treeView self.treeViewModel = QStandardItemModel() tvm = self.treeViewModel tvm.setHorizontalHeaderLabels (["Build Tasks"]) tv.setSizePolicy( QtGui.QSizePolicy( QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding ) ) tv.setModel(tvm) bottom_left.addWidget(tv) color_key = QtGui.QTextEdit() color_key.setFrameStyle( QtGui.QFrame.NoFrame ) color_key.setReadOnly(True) color_key.setHtml(''' <b>Key:</b> <p> <ul> <li><span style="color:{blue}">Blue Items - Task is completed.</span></li> <li><span style="color:{gray}">Gray Items - Optional task is disabled.</span></li> <li><span style="color:{green}">Green Items - Task is running.</span></li> <li><span style="color:{black}">Black Items - Task is still to be run.</span></li> </ul> </p> '''.format(red=Color().Red().htmlCol, black=Color().Black().htmlCol, gray=Color().Gray().htmlCol, green=Color().Green().htmlCol, blue=Color().Blue().htmlCol)) color_key.setSizePolicy( QtGui.QSizePolicy( QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed ) ) color_key.setFixedHeight(100) bottom_left.addWidget(color_key) return bottom_left def updateRecentFileList(self): menu = self.openRecentMenu menu.clear() paths = self.settings.value("recentPaths", []) if paths: for path in paths: action = QtGui.QAction(path, self) action.triggered.connect(lambda self=self, path=path : self.open_yaml_from_path(path)) menu.addAction(action) def makeBottomLayout(self): bottom = QtGui.QHBoxLayout() bottom.addLayout(self.makeBottomLeftLayout()) # self.enqueueTreeBuildTasks( [("Build CC Debug", "Build..."), ("Build CC Release", "Build...")] ) return bottom def enqueueTreeBuildTasks(self, listOfTasksAndDescriptions): ''' returns list( (taskLabel, parent QStandardItem) ) ''' tvm = self.treeViewModel tvm.clear() tvm.setHorizontalHeaderLabels (["Build Tasks"]) results = [] for label,description in listOfTasksAndDescriptions: parent = QStandardItem(label) parent.appendRow([QStandardItem(description)]) tvm.appendRow(parent) results.append( (label, parent) ) return results def enqueue_tab(self, label): ''' returns tab_ix - integer index of tab generated ''' def StyledTextEdit(): rv = QtGui.QTextEdit() rv.setStyleSheet('font-family: "Andale Mono", "Courier New", "Lucida Console", Monaco, monospace;' + 'font-size: 10px;') return rv tt = TabsType(edit=StyledTextEdit(), label=label, textread="", lastlines=-1, qprocess=None) self.tabs.append(tt) tab_ix = len(self.tabs)-1 self.tabCtrl.addTab( tt.edit, tt.label) self.tabCtrl.setCurrentIndex(tab_ix) return tab_ix def initUI2(self, hb): tabCtrl = QtGui.QTabWidget() self.tabCtrl = tabCtrl hb.addWidget(tabCtrl); actions = [] SEPARATOR = "separator" OPEN_RECENT_PLACEHOLDER = "OpenRecentPlaceholder" for x in ( ("Open YAML task file...", "Ctrl+O", self.open_yaml_task), OPEN_RECENT_PLACEHOLDER, SEPARATOR, ("Run YAML tasks", "Ctrl+R", self.run_yaml), ("Stop YAML tasks", "Ctrl+Shift+R", self.stop_yaml), SEPARATOR, ("Reveal in Finder / Explorer", "Ctrl+E", self.reveal_in_x), SEPARATOR, ("Start Webserver", "", self.start_webserver), ("Stop Webserver", "", self.stop_webserver), SEPARATOR, ("Exit", "Ctrl+Q", self.close) ): if x==SEPARATOR: action = QtGui.QAction("", self) action.setSeparator(True) actions.append(action) elif x==OPEN_RECENT_PLACEHOLDER: actions.append(OPEN_RECENT_PLACEHOLDER) else: label, cut, action_fn = x action = QtGui.QAction(label, self) action.setShortcut(cut) action.triggered.connect(action_fn) actions.append(action) sb = self.statusBar() menubar = self.menuBar() fileMenu = menubar.addMenu('&File') for act in actions: if act==OPEN_RECENT_PLACEHOLDER: self.openRecentMenu = QtGui.QMenu(title="Open Recent") fileMenu.addMenu(self.openRecentMenu) self.updateRecentFileList() else: fileMenu.addAction(act) self.setGeometry(300, 300, 350, 250) self.setWindowTitle('BuildChimp') self.show() def reveal_in_x(self): QtGui.QDesktopServices.openUrl(self.folder_for_reveal) def toggle_paused(self): self.paused = not self.paused if not self.paused: self.filter_change_filter( self.filter ) # Bump a change in texts def filter_combo_changed(self, ix, combo): data = combo.itemData(ix) self.filter_change_filter( data ) def filter_change_filter(self, filter): self.text_change_mutex.acquire() with ScopeGuard(self.text_change_mutex.release) as sg: self.filter = filter for x in self.tabs: edit = x.edit text = x.textread edit.setText( self.apply_filter(text, self.filter) ) def apply_filter(self, text, filter): def colorize(str, filter): # TODO: We can add <span...>...</span>... around text. return str if not filter: return text x = text.decode("utf-8").split("\n") return "\n".join( [colorize(y,filter) for y in x if filter in y.lower()] ) def write_process_output(self, process, finish, line_flush, tabs_ix): if self.proc_terminated: return self.text_change_mutex.acquire() with ScopeGuard(self.text_change_mutex.release) as sg: t = str( process.readAllStandardOutput() ) t += str( process.readAllStandardError() ) if finish: t = t + "\n" + "Process exit-code = {}".format(process.exitCode()) old = self.tabs[tabs_ix] newText = old.textread + t nLines = newText.count("\n") / line_flush self.tabs[tabs_ix] = old._replace(textread=newText, lastlines=nLines) if not self.paused and old.lastlines != nLines or finish: old.edit.setText( self.apply_filter(newText, self.filter) ) def warn_fn(self): ''' Returns True - user wants quit, False - user cancelled (doesn't want quit). ''' res = QtGui.QMessageBox.warning(self, "Closing", "Processes are still running - do you want to quit?", QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) if res==QtGui.QMessageBox.Ok: if self.taskmanager: self.taskmanager.stop() return True return False def add_load_path(self, path): path_list = self.settings.value("recentPaths", []) path_list = [path] + path_list def filter_dups_stable(seq): seen = set() seen_add = seen.add return [x for x in seq if not (x in seen or seen_add(x))] path_list = filter_dups_stable(path_list) path_list_items = set(path_list) self.settings.setValue("recentPaths", path_list) self.updateRecentFileList() def open_yaml_task(self): path, filter = QtGui.QFileDialog.getOpenFileName(filter="YAML buildchimp files (*.yaml)") if path: self.open_yaml_from_path(path) def open_yaml_from_path(self, path): self.folder_for_reveal = None path_user = None if path[-5:]==".yaml": path_user = path[:-5] + ".user.yaml" yaml_user = None try: with open(path_user, "rb") as user_yaml_file: if user_yaml_file: yaml_user = user_yaml_file.read() except: pass self.taskManager = taskmanager.TaskManager( open(path, "rb").read(), yaml_user, self ) self.add_load_path(path) qf = QtCore.QFileInfo(path) self.folder_for_reveal = "file://" + qf.absoluteDir().absolutePath() # print(self.folder_for_reveal) def run_yaml(self): if self.taskManager: self.taskManager.run_loop_init() self.tabs = [] self.tabCtrl.clear() self.taskManager.run_loop() def stop_yaml(self): if self.taskManager: self.taskManager.stop() def closeEvent(self, event): SaveGeom(self) self.settings.sync() stop_webserver = (lambda ws=self.webserver : ws.stop()) if self.webserver else lambda : None if not self.tabs or len(self.tabs)==0: stop_webserver() return self.close() for x in self.tabs: if x.qprocess: print(x.qprocess.state()) if x.qprocess and x.qprocess.state()!=QtCore.QProcess.NotRunning: if not self.warn_fn(): return stop_webserver() def start_webserver(self): if self.webserver: return if sys.platform=="darwin": try: self.webserver = Webserver() self.webserver.start() msg = self.webserver.wait_on_msg() if msg: m = QtGui.QMessageBox(self) m.setText(msg) m.setWindowModality(Qt.WindowModal) m.exec_() except: self.webserver = None def stop_webserver(self): if self.webserver: self.webserver.stop() self.webserver = None
class State(): def __init__(self, wlan_agent): self.dev_eui = None self.app_eui = None self.rf_mode = None self.measuring_mode = True self.debug_mode = False self._app_key = None self.onewire_interface = None self.web_server = None self.deepsleep = True self.chrono = Timer.Chrono() self.chrono.start() self.sensors = {} self.rgb_color = 0x007f00 #self.case_button = Button('P8', longms=500) self.wlan_agent = wlan_agent # Initialize Webserver self.web_server = Webserver(dev_state=self) self.web_server.init_webserver() # init pins for mode recognizion self.bin_switch_1 = Pin('P20', mode=Pin.IN, pull=Pin.PULL_UP) self.bin_switch_2 = Pin('P21', mode=Pin.IN, pull=Pin.PULL_UP) self.bin_switch_3 = Pin('P22', mode=Pin.IN, pull=Pin.PULL_UP) trigger = Pin.IRQ_FALLING | Pin.IRQ_RISING self.update_node_mode(None) self.bin_switch_1.callback(trigger, self.update_node_mode) self.bin_switch_2.callback(trigger, self.update_node_mode) self.bin_switch_3.callback(trigger, self.update_node_mode) #self.case_button.short = self.toggle_measuring #self.case_button.long = self.toggle_debug @property def app_key(self): return None # helper functions def set_rgb_color(self, color): """change local led colour""" self.rgb_color = color rgbled(color) def set_rgb_off(self): rgbled(RGBOFF) def update_node_mode(self, arg): # pin 1 and 2 0 0 0 1 1 0 1 1 # possible rf mode ["NO_MODE", "WLAN_AP", "WLAN_CLIENT", "LORA"] # measuring mode pin 3 # pins has to be inverted to fit to the labels on the pcb switch_1 = not self.bin_switch_1() switch_2 = not self.bin_switch_2() switch_3 = not self.bin_switch_3() log("Switch Positions: 1:{} 2:{} 3:{}".format(switch_1, switch_2, switch_3)) if switch_1 and switch_2: self.rf_mode = RF_MODES.LORA self.wlan_agent.stop() self.web_server.stop() elif switch_1 and not switch_2: self.rf_mode = RF_MODES.WLAN_CLIENT self.wlan_agent.activate_sta_mode() self.web_server.start() elif not switch_1 and switch_2: self.rf_mode = RF_MODES.WLAN_AP self.wlan_agent.activate_ap_mode() self.web_server.start() else: self.rf_mode = RF_MODES.NO_MODE log("Init Node Mode: {}".format(self.rf_mode)) # self.measuring_mode = switch_3 @app_key.setter def app_key(self, value): self._app_key = value def toggle_debug(): rgbled(TOGGLE_DEBUG_COLOR) self.debug_mode = not self.debug_mode if self.onewire_interface is not None: self.onewire_interface.debug = self.debug_mode sleep(0.5) rgbled(self.rgb_color) def toggle_measuring(self): rgbled(TOGGLE_MEASURING_COLOR) self.measuring_mode = not self.measuring_mode if self.measuring_mode: log('Measuring on') self.set_rgb_color(MEASURING_COLOR) else: log('Measuring off') self.set_rgb_color(NO_MEASURING_COLOR) def add_sensor(self, id, name, last_value=None): timestamp = self.chrono.read() counter = 0 self.sensors[id] = [ name, last_value, timestamp, counter, [None] * HISTORY_SIZE ] def update_sensor(self, id, value): counter = self.sensors[id][3] self.sensors[id][4][counter] = [ self.sensors[id][1], self.sensors[id][2] ] if counter == HISTORY_SIZE - 1: counter = 0 else: counter += 1 self.sensors[id][1] = value self.sensors[id][2] = self.chrono.read() self.sensors[id][3] = counter def remove_sensor(self, id): del self.sensors[id] def clear_sensors(self): self.sensors = {} def get_state(self): return { "dev_eui": self.dev_eui, "app_eui": self.app_eui, "sensors": self.sensors, "send_time": self.chrono.read() }