Exemplo n.º 1
0
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()
Exemplo n.º 2
0
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()
        }