def secureInterruptHandlerScheduler(timer=None): try: # Execute CBF LIST from local cached config with timirqseq in sec scheduler(CFG_TIMER_IRQ[0], CFG_TIMER_IRQ[1]) except Exception as e: console_write("[IRQ] TIMIRQ (cron) callback: {} error: {}".format( CFG_TIMER_IRQ[0], e))
def profiling_info(label=""): """ Runtime memory measurements """ if cfgget('dbg'): console_write("{} [PROFILING INFO] - {} {}".format('~'*5, label, '~'*5)) mem_info() console_write("~"*30)
def secureInterruptHandlerSimple(timer=None): try: # Execute CBF from cached config state = execute_LM_function_Core(CFG_TIMER_IRQ[0].split(' ')) if not state: console_write("[IRQ] TIMIRQ execute_LM_function_Core error: {}".format(CFG_TIMER_IRQ[0])) except Exception as e: console_write("[IRQ] TIMIRQ callback: {} error: {}".format(CFG_TIMER_IRQ[0], e))
def set_emergency_buffer(): emergency_buff_kb = cfgget('irqmembuf') if cfgget('extirq') or cfgget("timirq"): from micropython import alloc_emergency_exception_buf console_write("[IRQ] Interrupts was enabled, alloc_emergency_exception_buf={}".format(emergency_buff_kb)) alloc_emergency_exception_buf(emergency_buff_kb) else: console_write("[IRQ] Interrupts disabled, skip alloc_emergency_exception_buf configuration.")
def secureEventInterruptHandler(pin=None): """ EVENT INTERRUPT CALLBACK FUNCTION WRAPPER """ try: state = execute_LM_function_Core(CFG_EVIRQCBF.split(' ')) if not state: console_write("[IRQ] EXTIRQ execute_LM_function_Core error: {}".format(CFG_EVIRQCBF)) except Exception as e: console_write("[IRQ] EVENTIRQ callback: {} error: {}".format(CFG_EVIRQCBF, e))
def deserialize_raw_input(raw_cron_input): datastruct = [] try: datastruct = [ tuple(cron.split('!')) for cron in raw_cron_input.split(';') ] except Exception as e: console_write( "deserialize_raw_input: input syntax error: {}".format(e)) return datastruct
def set_emergency_buffer(): from micropython import alloc_emergency_exception_buf irqmembuf = cfgget('irqmembuf') emergency_buff_kb = irqmembuf if irqmembuf is not None and isinstance( irqmembuf, int) else 1000 if cfgget('extirq') or cfgget("timirq"): console_write( "[IRQ] Interrupts was enabled, alloc_emergency_exception_buf={}". format(emergency_buff_kb)) alloc_emergency_exception_buf(emergency_buff_kb) else: console_write( "[IRQ] Interrupts disabled, skip alloc_emergency_exception_buf configuration." )
def __enableInterruptSimple(): """ SIMPLE TIMER INTERRUPT CONFIGURATION """ # LOAD DATA FOR TIMER IRQ: cfgget("timirq") # CACHE TASK FOR CBF CFG_TIMER_IRQ[0] = cfgget('timirqcbf') if CFG_TIMER_IRQ[0].lower() != 'n/a': from machine import Timer # INIT TIMER IRQ with callback function wrapper timer = Timer(0) timer.init(period=int(cfgget("timirqseq")), mode=Timer.PERIODIC, callback=secureInterruptHandlerSimple) else: console_write("[IRQ] TIMIRQ: isenable: {} callback: {}".format(cfgget("timirq"), cfgget('timirqcbf')))
def __recovery(self, is_critic=False): """ Handle memory errors here """ self.reply_message("[HA] system recovery ...") collect() self.reply_message("[HA] gc-collect-memfree: {}".format(mem_free())) if is_critic: try: self.reply_message( "[HA] Critical error - disconnect & hard reset") self.__safe_reboot_system() except Exception as e: console_write("==> [!!!][HA] Recovery error: {}".format(e))
def setNTP_RTC(): if WLAN(STA_IF).isconnected(): for _ in range(4): try: # Sync with NTP server settime() # Get localtime + GMT (year, month, mday, hour, minute, second, weekday, yearday) = localtime(time() + int(cfgget('gmttime')) * 3600) # Create RealTimeClock + Set RTC with time (+timezone) RTC().datetime((year, month, mday, 0, hour, minute, second, 0)) # Print time console_write("NTP setup DONE: {}".format(localtime())) return True except Exception as e: console_write("NTP setup errer.:{}".format(e)) sleep(0.5) else: console_write("NTP setup errer: STA not connected!") # Recursion to get actual time for cron execution if cfgget('cron'): console_write("[!] NTP setup retry due to cron is {}".format( cfgget('cron'))) return setNTP_RTC() return False
def __scheduler_trigger(cron_time_now, check_time_now_sec_tuple, scheduler_fragment, sec_tolerance=2): """ SchedulerCore logic cron time now format: WD, H, M, S """ check_time = tuple( int(t.strip()) if t.strip() != '*' else t.strip() for t in scheduler_fragment[0].split(':')) # Cron actual time (now) parts summary in sec check_time_now_sec = check_time_now_sec_tuple[ 0] + check_time_now_sec_tuple[1] + check_time_now_sec_tuple[2] # Cron overall requested time in sec - hour in sec, minute in sec, sec check_time_scheduler_sec = int(check_time_now_sec_tuple[0] if check_time[1] == '*' else check_time[1] * 3600) \ + int(check_time_now_sec_tuple[1] if check_time[2] == '*' else check_time[2] * 60) \ + int(check_time_now_sec_tuple[2] if check_time[3] == '*' else check_time[3]) # Time frame +/- corrections tolerance_min_sec = 0 if check_time_now_sec - sec_tolerance < 0 else check_time_now_sec - sec_tolerance tolerance_max_sec = check_time_now_sec + sec_tolerance task_id = "{}:{}|{}".format(check_time[0], check_time_scheduler_sec, scheduler_fragment[1].replace(' ', '')) # Check WD - WEEK DAY if check_time[0] == '*' or check_time[0] == cron_time_now[0]: # Check H, M, S in sec format between tolerance range if tolerance_min_sec <= check_time_scheduler_sec <= tolerance_max_sec: __cron_task_cache_manager(check_time_now_sec, sec_tolerance) if check_time[3] == '*' or task_id not in LAST_CRON_TASKS: lm_state = execute_LM_function_Core( scheduler_fragment[1].split()) if not lm_state: console_write( "[CRON ERROR]NOW[{}] {} <-> {} CONF[{}] EXECUTE[{}] LM: {}" .format(cron_time_now, __convert_sec_to_time(tolerance_min_sec), __convert_sec_to_time(tolerance_max_sec), scheduler_fragment[0], lm_state, scheduler_fragment[1])) # SAVE TASK TO CACHE if check_time[3] != '*': # SAVE WHEN SEC not * LAST_CRON_TASKS.append(task_id) return True return False
def init_eventPIN(): """ EVENT INTERRUPT CONFIGURATION """ global CFG_EVIRQCBF if cfgget('extirq') and cfgget('extirqcbf').lower() != 'n/a': CFG_EVIRQCBF = cfgget('extirqcbf') pin = get_pin_on_platform_by_key('pwm_4') console_write("[IRQ] EVENTIRQ ENABLED PIN: {} CBF: {}".format(pin, CFG_EVIRQCBF)) # Init event irq with callback function wrapper from machine import Pin pin_obj = Pin(pin, Pin.IN, Pin.PULL_UP) pin_obj.irq(trigger=Pin.IRQ_RISING, handler=secureEventInterruptHandler) else: console_write("[IRQ] EVENTIRQ: isenable: {} callback: {}".format(cfgget('extirq'), CFG_EVIRQCBF))
def secureInterruptHandler(timer=None): """ TIMER INTERRUPT CALLBACK FUNCTION WRAPPER """ try: if CFG_TIMIRQCBF.lower() != 'n/a': # Execute CBF from config state = execute_LM_function_Core(CFG_TIMIRQCBF.split(' ')) if not state: console_write( "[IRQ] TIMIRQ execute_LM_function_Core error: {}".format( CFG_TIMIRQCBF)) except Exception as e: console_write("[IRQ] TIMIRQ callback: {} error: {}".format( CFG_TIMIRQCBF, e))
def __recovery(self, errlvl=0): """ Handle memory errors here """ self.reply_message("[HA] system recovery ...") if 'esp' in platform: collect() self.reply_message("[HA] gc-ollect-memfree: {}".format(mem_free())) if errlvl == 1: try: self.reply_message( "[HA] Critical error - disconnect & hard reset") self.__safe_reboot_system() except Exception as e: console_write("==> [!!!][HA] Recovery error: {}".format(e)) else: console_write("[HA] recovery only available on esp - nodemcu")
def __select_available_wifi_nw(sta_if, raw_essid, raw_pwd): """ raw_essid: essid parameter, in case of multiple values separator is ; raw_pwd: essid pwd parameter, in case of multiple values separator is ; return detected essid with corresponding password """ for idx, essid in enumerate(raw_essid.split(';')): essid = essid.strip() # Scan wifi network - retry workaround for _ in range(0, 2): if essid in (wifispot[0].decode('utf-8') for wifispot in sta_if.scan()): console_write( '\t| - [NW: STA] ESSID WAS FOUND: {}'.format(essid)) return essid, str(raw_pwd.split(';')[idx]).strip() sleep(1) return None, ''
def enableInterrupt(): """ TIMER INTERRUPT CALLBACK FUNCTION CONFIG. WRAPPER - FIRST PRIORITY: SCHEDULER - SECOND PRIORITY: SIMPLE PERIODIC CALLBACK """ console_write("[IRQ] TIMIRQ SETUP - TIMIRQ: {} SEQ: {}".format(cfgget("timirq"), cfgget("timirqseq"))) console_write("|- [IRQ] CRON:{} CBF:{}".format(cfgget('cron'), cfgget('crontasks'))) console_write("|- [IRQ] SIMPLE CBF:{}".format(cfgget('timirqcbf'))) if cfgget("timirq"): # Configure advanced scheduler OR simple repeater if cfgget('cron') and cfgget('crontasks').lower() != 'n/a': console_write("|-- TIMER IRQ MODE: SCHEDULER") # ENABLE ADVANCED SCHEDULER (BASED ON SIMPLE TIMIRQ) __enableInterruptScheduler() return # ENABLE SIMPLE PERIODIC INTERRUPT console_write("|-- TIMER IRQ MODE: SIMPLE") __enableInterruptSimple()
def run(self): self.server_console( "[ socket server ] SERVER ADDR: telnet {} {}".format( cfgget("devip"), self.__port)) try: cfgput('version', self.__socket_interpreter_version) except Exception as e: console_write( "Export system version to config failed: {}".format(e)) self.__init_socket() self.__bind_and_accept() while True: try: # Evaluate incoming msg via InterpreterShell -> InterpreterCore "Console prompt" is_healthy = InterpreterShell_shell(self.__wait_for_message(), SocketServerObj=self) if not is_healthy: console_write( "[EXEC-WARNING] InterpreterShell internal error.") self.__recovery(is_critic=False) except OSError: # BrokenPipeError self.__reconnect() except Exception as e: console_write( "[EXEC-ERROR] InterpreterShell error: {}".format(e)) self.__recovery(is_critic=True) # Memory dimensioning dump self.server_console( '[X] AFTER INTERPRETER EXECUTION FREE MEM [byte]: {}'.format( mem_free()))
def profiling_info(label=""): """ Runtime memory measurements """ if cfgget('dbg'): console_write("{} [PROFILING INFO] - {} {}".format( '~' * 5, label, '~' * 5)) try: mem_info() except Exception as e: console_write("MEM INFO QUERY ERROR: {}".format(e)) console_write("~" * 30) else: console_write("[PROFILING INFO] SKIP dbg:{}".format(cfgget('dbg')))
def bootup_hook(): """ Executes when system boots up. """ console_write("[BOOT HOOKS] EXECUTION...") if cfgget('boothook') is not None and cfgget('boothook').lower() != 'n/a': for shell_cmd in (cmd.strip() for cmd in tuple(cfgget('boothook').split(';')) if len(cmd.split()) > 1): console_write("|-[BOOT HOOKS] SHELL EXEC: {}".format(shell_cmd)) try: state = execute_LM_function_Core(shell_cmd.split()) console_write("|-[BOOT HOOKS] state: {}".format(state)) except Exception as e: console_write("|--[BOOT HOOKS] error: {}".format(e))
def __set_wifi_dev_static_ip(sta_if): console_write("[NW: STA] Set device static IP.") stored_ip = cfgget('devip') if 'n/a' not in stored_ip.lower() and '.' in stored_ip: conn_ips = list(sta_if.ifconfig()) # Check ip type before change, conn_ip structure: 10.0.1.X if conn_ips[0] != stored_ip and conn_ips[-1].split( '.')[0:3] == stored_ip.split('.')[0:3]: conn_ips[0] = stored_ip console_write("\t| [NW: STA] DEV. StaticIP: {}".format(stored_ip)) try: # IP address, subnet mask, gateway and DNS server sta_if.ifconfig(tuple(conn_ips)) return True # was reconfigured except Exception as e: console_write( "\t\t| [NW: STA] StaticIP conf. failed: {}".format(e)) else: console_write("[NW: STA][SKIP] StaticIP conf.: {} ? {}".format( stored_ip, conn_ips[0])) else: console_write("[NW: STA] IP was not stored: {}".format(stored_ip)) return False # was not reconfigured
def set_access_point(_essid, _pwd, _authmode=3): console_write("[NW: AP] SET AP MODE: {} - {} - auth mode: {}".format( _essid, _pwd, _authmode)) sta_if = WLAN(STA_IF) if sta_if.isconnected(): sta_if.active(False) ap_if = WLAN(AP_IF) ap_if.active(True) # Set WiFi access point name (formally known as ESSID) and WiFi authmode (2): WPA2 try: console_write("[NW: AP] Configure") ap_if.config(essid=_essid, password=_pwd, authmode=_authmode) except Exception as e: console_write("[NW: AP] Config Error: {}".format(e)) if ap_if.active() and str(ap_if.config('essid')) == str( _essid) and ap_if.config('authmode') == _authmode: cfgput("devip", ap_if.ifconfig()[0]) console_write("\t|\t| [NW: AP] network config: " + str(ap_if.ifconfig())) set_uid_macaddr_hex(ap_if) return ap_if.active()
def run(self): if "esp" in platform: self.server_console("[ socket server ] SERVER ADDR: telnet " + str(cfgget("devip")) + " " + str(self.port)) else: self.server_console( "[ socket server ] SERVER ADDR: telnet 127.0.0.1 " + str(self.port)) try: cfgput('version', self.__socket_interpreter_version) except Exception as e: console_write( "Export system version to config failed: {}".format(e)) self.__init_socket() self.__bind_and_accept() while True: try: # Evaluate incoming msg via InterpreterShell -> InterpreterCore "Console prompt" is_healthy, msg = InterpreterShell_shell( self.__wait_for_message(), SocketServerObj=self) if not is_healthy: console_write( "[EXEC-WARNING] InterpreterShell internal error: {}". format(msg)) self.__recovery(errlvl=0) except OSError: # BrokenPipeError self.__reconnect() except Exception as e: console_write( "[EXEC-ERROR] InterpreterShell error: {}".format(e)) self.__recovery(errlvl=1) # Memory dimensioning dump if mem_free is not None: self.server_console( '[X] AFTER INTERPRETER EXECUTION FREE MEM [byte]: {}'. format(mem_free()))
def enableInterrupt(): """ TIMER INTERRUPT CONFIGURATION """ global CFG_TIMIRQCBF CFG_TIMIRQCBF = cfgget('timirqcbf') if cfgget("timirq") and CFG_TIMIRQCBF.lower() != 'n/a': try: period_ms_usr = int(cfgget("timirqseq")) except Exception as e: console_write("[IRQ] TIMIRQ period query error: {}".format(e)) period_ms_usr = 3000 console_write("[IRQ] TIMIRQ ENABLED: SEQ: {} CBF: {}".format( period_ms_usr, CFG_TIMIRQCBF)) from machine import Timer # Init timer irq with callback function wrapper timer = Timer(0) timer.init(period=period_ms_usr, mode=Timer.PERIODIC, callback=secureInterruptHandler) else: console_write("[IRQ] TIMIRQ: isenable: {} callback: {}".format( cfgget("timirq"), cfgget('timirqcbf')))
Designed by Marcell Ban aka BxNxM """ ######################################################### # IMPORTS # ######################################################### from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR from time import sleep from ConfigHandler import console_write, cfgget, cfgput from InterpreterShell import shell as InterpreterShell_shell try: from gc import collect, mem_free except: console_write("[SIMULATOR MODE GC IMPORT]") from simgc import collect, mem_free ######################################################### # SOCKET SERVER CLASS # ######################################################### class SocketServer: """ Socket message data packet layer - send and receive Embedded command interpretation: - hello - version - exit - reboot
def set_wifi(essid, pwd, timeout=60): console_write('[NW: STA] SET WIFI STA NW {}'.format(essid)) # Disable AP mode ap_if = WLAN(AP_IF) if ap_if.active(): ap_if.active(False) del ap_if # Set STA and Connect sta_if = WLAN(STA_IF) sta_if.active(True) if not sta_if.isconnected(): # Multiple essid and pwd handling with retry mechanism essid, pwd = __select_available_wifi_nw(sta_if, essid, pwd) # Connect to the located wifi network if essid is not None: console_write('\t| [NW: STA] CONNECT TO NETWORK {}'.format(essid)) # connect to network sta_if.connect(essid, pwd) # wait for connection, with timeout set while not sta_if.isconnected() and timeout > 0: console_write("\t| [NW: STA] Waiting for connection... " + str(timeout) + "/60") timeout -= 1 sleep(0.5) # Set static IP - here because some data comes from connection. if sta_if.isconnected() and __set_wifi_dev_static_ip(sta_if): sta_if.disconnect() del sta_if return set_wifi(essid, pwd) else: console_write( "\t| [NW: STA] Wifi network was NOT found: {}".format(essid)) return False console_write("\t|\t| [NW: STA] network config: " + str(sta_if.ifconfig())) console_write("\t|\t| [NW: STA] CONNECTED: " + str(sta_if.isconnected())) else: console_write("\t| [NW: STA] ALREADY CONNECTED TO {}".format(essid)) cfgput("devip", str(sta_if.ifconfig()[0])) set_uid_macaddr_hex(sta_if) return sta_if.isconnected()
def bootup_hook(): """ Executes when system boots up. """ # Execute LMs from boothook config parameter console_write("[BOOT HOOKS] EXECUTION...") if cfgget('boothook') is not None and cfgget('boothook').lower() != 'n/a': for shell_cmd in (cmd.strip() for cmd in tuple(cfgget('boothook').split(';')) if len(cmd.split()) > 1): console_write("|-[BOOT HOOKS] SHELL EXEC: {}".format(shell_cmd)) try: state = execute_LM_function_Core(shell_cmd.split()) console_write("|-[BOOT HOOKS] state: {}".format(state)) except Exception as e: console_write("|--[BOOT HOOKS] error: {}".format(e)) # Set boostmd (boost mode) if cfgget('boostmd') is True: console_write("[BOOT HOOKS] Set up CPU 16MHz/24MHz - boostmd: {}".format(cfgget('boostmd'))) if platform == 'esp8266': freq(160000000) if platform == 'esp32': freq(240000000) else: console_write("[BOOT HOOKS] Set up CPU 8MHz - boostmd: {}".format(cfgget('boostmd'))) freq(80000000)
def server_console(self, msg): console_write("|" + "-" * self.server_console_indent + msg) if self.server_console_indent < 50: # if less then max indent self.server_console_indent += 1
def __del__(self): console_write("[ socket server ] <<destructor>>") self.__deinit_socket()
def set_wifi(essid, pwd, timeout=60): console_write('[NW: STA] SET WIFI: {}'.format(essid)) essid_found = False # Disable AP mode ap_if = WLAN(AP_IF) if ap_if.active(): ap_if.active(False) del ap_if # Set STA and Connect sta_if = WLAN(STA_IF) sta_if.active(True) if not sta_if.isconnected(): console_write('\t| [NW: STA] CONNECT TO NETWORK {}'.format(essid)) # Scan wifi network - retry workaround for _ in range(0, 2): if essid in (wifispot[0].decode('utf-8') for wifispot in sta_if.scan()): essid_found = True console_write( '\t| - [NW: STA] ESSID WAS FOUND {}'.format(essid_found)) break sleep(1) # Connect to the located wifi network if essid_found: # connect to network sta_if.connect(essid, pwd) # wait for connection, with timeout set while not sta_if.isconnected() and timeout > 0: console_write("\t| [NW: STA] Waiting for connection... " + str(timeout) + "/60") timeout -= 1 sleep(0.5) # Set static IP - here because some data comes from connection. if sta_if.isconnected() and __set_wifi_dev_static_ip(sta_if): sta_if.disconnect() del sta_if return set_wifi(essid, pwd) else: console_write( "\t| [NW: STA] Wifi network was NOT found: {}".format(essid)) return False console_write("\t|\t| [NW: STA] network config: " + str(sta_if.ifconfig())) console_write("\t|\t| [NW: STA] CONNECTED: " + str(sta_if.isconnected())) else: console_write("\t| [NW: STA] ALREADY CONNECTED TO {}".format(essid)) cfgput("devip", str(sta_if.ifconfig()[0])) set_uid_macaddr_hex(sta_if) return sta_if.isconnected()
try: self.reply_message( "[HA] Critical error - disconnect & hard reset") self.__safe_reboot_system() except Exception as e: console_write("==> [!!!][HA] Recovery error: {}".format(e)) else: console_write("[HA] recovery only available on esp - nodemcu") def server_console(self, msg): console_write("|" + "-" * self.server_console_indent + msg) if self.server_console_indent < 50: # if less then max indent self.server_console_indent += 1 def __del__(self): console_write("[ socket server ] <<destructor>>") self.__deinit_socket() ######################################################### # MAIN (FOR TEST REASONS) # ######################################################### if __name__ == "__main__": try: server = SocketServer() server.run() except KeyboardInterrupt: console_write("Keyboard interrupt in SocketServer.")