def __send(_host, _cmd): try: out = send_cmd(_host, _cmd) except Exception as e: out = [] errlog_add('[intercon] sendcmd: {}'.format(e)) return out
def __inject_default_conf(): # Load config and template liveconf = Data.read_cfg_file(nosafe=True) # Remove obsolete keys from conf try: remove( 'cleanup.pds' ) # Try to remove cleanup.pds (cleanup indicator by micrOSloader) console_write("[CONFIGHANDLER] Purge obsolete keys") for key in (key for key in liveconf.keys() if key not in Data.CONFIG_CACHE.keys()): liveconf.pop(key, None) except Exception: console_write( "[CONFIGHANDLER] SKIP obsolete keys check (no cleanup.pds)") # Merge template to live conf Data.CONFIG_CACHE.update(liveconf) # Run conf injection and store console_write( "[CONFIGHANDLER] Inject user config ...") # Data.CONFIG_CACHE try: # [LOOP] Only returns True Data.write_cfg_file() console_write("[CONFIGHANDLER] Save conf struct successful") except Exception as e: console_write( "[CONFIGHANDLER] Save conf struct failed: {}".format(e)) errlog_add('__inject_default_conf error: {}'.format(e)) finally: del liveconf
def scheduler(scheduler_input, irqperiod): """ irqperiod - in sec RAW INPUT SYNTAX: '{cron time}!COMD;{cron time2}!COMD2;...' ! - execute ; - cron task separator """ state = False time_now = localtime()[0:8] # time_now = GEN.__next__() # TODO: remove after test # Actual time - WD, H, M, S cron_time_now = (time_now[-2], time_now[-5], time_now[-4], time_now[-3]) # Cron overall time now in sec - hour in sec, minute in sec, sec now_sec_tuple = (cron_time_now[1] * 3600, cron_time_now[2] * 60, cron_time_now[3]) try: for cron in deserialize_raw_input(scheduler_input): state |= __scheduler_trigger(cron_time_now, now_sec_tuple, cron, deltasec=irqperiod) return state except Exception as e: console_write("scheduler callback error: {}".format(e)) errlog_add('scheduler error: {}'.format(e)) return False
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]: console_write( "\t| [NW: STA] micrOS dev. StaticIP request: {}".format( stored_ip)) conn_ips[0] = 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)) errlog_add("__set_wifi_dev_static_ip error: {}".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 __wait_for_msg(cls): # Check for open connection if cls.__conn is None: return '' # Reply on open connection cls.__send_prompt() cls.__conn.settimeout(cls.__timeout_user) # Receive msg and handle timeout try: # message size 1024 byte data_byte = cls.__conn.recv(1024) except Exception as e: data_byte = b'' if 'ETIMEDOUT' in str(e).upper(): cls.server_console( "[ socket server ] socket recv - connection with user - timeout {} sec" .format(cls.__timeout_user)) cls.reply_message( "\n__@_/' Session timeout {} sec\nBye!".format( cls.__timeout_user)) cls.__reconnect() else: errlog_add('__wait_for_msg unexpected error: {}'.format(e)) cls.__reconnect() # Convert msg to str try: data_str = data_byte.decode("utf-8").strip() except Exception: data_str = 'ctrl-c' cls.server_console("[ socket server ] RAW INPUT |{}|".format(data_str)) # CALL LOW LEVEL COMMANDS - server built-ins return cls.__server_level_cmds(data_str)
def run(cls): """ Main method, runs socket server with interpreter shell """ cls.server_console( "[ socket server ] SERVER ADDR: telnet {} {}".format( cfgget("devip"), cls.__port)) try: cfgput('version', cls.__socket_interpreter_version) except Exception as e: console_write( "Export system version to config failed: {}".format(e)) errlog_add('socket run system version export error: {}'.format(e)) cls.__init_socket() while True and cls.__isconn: try: # Evaluate incoming msg via InterpreterShell -> InterpreterCore "Console prompt" is_healthy = shell(cls.__wait_for_msg(), sso=cls) if not is_healthy: console_write( "[EXEC-WARNING] InterpreterShell internal error.") cls.__recovery(is_critic=False) except OSError: # Broken pipe error handling cls.__reconnect() except Exception as e: console_write( "[EXEC-ERROR] InterpreterShell error: {}".format(e)) errlog_add("Socket-InterpreterShell error: {}".format(e)) cls.__recovery(is_critic=True) # Memory dimensioning dump cls.server_console( '[X] AFTER INTERPRETER EXECUTION FREE MEM [byte]: {}'.format( mem_free()))
def interrupt_handler(): try: enableInterrupt() enableCron() except Exception as e: print("[micrOS main] InterruptHandler.enableInterrupt/CronInterrupt error: {}".format(e)) errlog_add("interrupt_handler error: {}".format(e))
def start_micropython_webrepl(cls, update=False): cls.reply_message( " Start micropython WEBREPL for interpreter web access and file transferring." ) cls.reply_message( " [!] micrOS socket shell will be available again after reboot.") cls.reply_message(" \trestart machine shortcut: import reset") cls.reply_message( " Connect over http://micropython.org/webrepl/#{}:8266/".format( cfgget("devip"))) cls.reply_message(" \t[!] webrepl password: {}".format( cfgget('appwd'))) if update: cls.reply_message(' Restart node then start webrepl...') cls.reply_message(" Bye!") if update: from machine import reset with open('.if_mode', 'w') as f: f.write('webrepl') reset() try: import webrepl cls.reply_message(webrepl.start(password=cfgget('appwd'))) # Deinit socket obj to make webrepl available cls.__del__() except Exception as e: cls.reply_message("Error while starting webrepl: {}".format(e)) errlog_add('Start Webrepl error: {}'.format(e))
def alarms(clean=False, test=False, msgobj=None): if test: errlog_add('TeSt ErRoR') if clean: errlog_clean() errcnt = errlog_get(msgobj=msgobj) return {'NOK alarm': errcnt} if errcnt > 0 else {'OK alarm': errcnt}
def ntptime(utc_shift=0): """ Set NTP time with utc shift :param utc_shift: +/- hour (int) :return: None """ if not WLAN(STA_IF).isconnected(): errlog_add("STA not connected: ntptime") return False import struct def getntp(): host = "pool.ntp.org" # (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60 NTP_DELTA = 3155673600 NTP_QUERY = bytearray(48) NTP_QUERY[0] = 0x1B addr = getaddrinfo(host, 123)[0][-1] s = socket(AF_INET, SOCK_DGRAM) try: s.settimeout(2) res = s.sendto(NTP_QUERY, addr) msg = s.recv(48) finally: s.close() val = struct.unpack("!I", msg[40:44])[0] return val - NTP_DELTA t = getntp() tm = localtime(t + utc_shift * 3600) # Get localtime + GMT shift RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0)) return True
def deserialize_raw_input(raw_cron_input): try: return tuple( tuple(cron.split('!')) for cron in raw_cron_input.split(';')) except Exception as e: console_write( "deserialize_raw_input: input syntax error: {}".format(e)) errlog_add('deserialize_raw_input input syntax error: {}'.format(e)) return tuple()
def exec_lm_pipe_schedule(taskstr): """ Wrapper for exec_lm_pipe - fix IRQ execution limit - magic """ try: schedule(exec_lm_pipe, taskstr) return True except Exception as e: errlog_add("exec_lm_pipe_schedule error: {}".format(e)) return False
def exec_lm_core_schedule(arg_list): """ Wrapper for exec_lm_core - scheduling - exec protection for [IRQ callbacks] - fix IRQ execution limitation magic """ try: schedule(exec_lm_core, arg_list) return True except Exception as e: errlog_add("schedule_lm_exec {} error: {}".format(arg_list, e)) return False
def set_ntp_rtc(): err = '' for _ in range(4 if cfgget('cron') else 2): try: ntptime(utc_shift=int(cfgget('gmttime'))) return True except Exception as e: console_write("set_ntp_rtc errer.:{}".format(e)) err = e sleep_ms(100) errlog_add("set_ntp_rtc error: {}".format(err)) return False
def cfgget(key=None): if key is None: return Data.CONFIG_CACHE try: val = Data.CONFIG_CACHE.get(key, None) if val == '...': # Handle special "offloaded" keys return Data.disk_keys(key) return val except Exception as e: console_write("[CONFIGHANDLER] Get config value error: {}".format(e)) errlog_add('cfgget {} error: {}'.format(key, e)) return None
def write_cfg_file(): while True: try: # WRITE JSON CONFIG with open(Data.CONFIG_PATH, 'w') as f: dump(Data.CONFIG_CACHE, f) break except Exception as e: console_write( "[CONFIGHANDLER] __write_cfg_file error {} (json): {}". format(Data.CONFIG_PATH, e)) errlog_add('write_cfg_file error: {}'.format(e)) sleep(0.2) return True
def exec_lm_pipe(taskstr): """ Input: taskstr contains LM calls separated by ; Used for execute config callback parameters (IRQs and BootHook) """ try: # Handle config default empty value (do nothing) if taskstr.startswith('n/a'): return True # Execute individual commands - msgobj->"/dev/null" for cmd in (cmd.strip().split() for cmd in taskstr.split(';')): if not exec_lm_core_schedule(cmd): console_write("|-[LM-PIPE] task error: {}".format(cmd)) except Exception as e: console_write("[IRQ-PIPE] error: {}\n{}".format(taskstr, e)) errlog_add('exec_lm_pipe error: {}'.format(e)) return False return True
def read_cfg_file(nosafe=False): conf = {} while True: try: with open(Data.CONFIG_PATH, 'r') as f: conf = load(f) break except Exception as e: console_write( "[CONFIGHANDLER] read_cfg_file error {} (json): {}".format( conf, e)) # Write out initial config, if no config exists. if nosafe: break sleep(0.2) errlog_add('read_cfg_file error: {}'.format(e)) # Return config cache return conf
def send_cmd(host, cmd): port = cfgget('socport') hostname = None if not validate_ipv4(host): hostname = host host = socket.getaddrinfo(host, port)[-1][4][0] if validate_ipv4(host): # Socket reply msg SocketServer().reply_message("[intercon] {} -> {}:{}:{}".format(cmd, hostname, host, port)) # Send CMD conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.settimeout(5) conn.connect((host, port)) output = __run_command(conn, cmd) __close_connection(conn) return output else: errlog_add("[INTERCON] Invalid host: {}".format(host)) return None
def cfgput(key, value, type_check=False): # Handle special "offloaded" keys if str(Data.CONFIG_CACHE.get(key, None)) == '...': return Data.disk_keys(key, value) # Handle regular keys if Data.CONFIG_CACHE[key] == value: return True try: if type_check: value = Data.type_handler(key, value) # value type error or deny "offloaded" key's value ... if value is None or str(value) == '...': return False Data.CONFIG_CACHE[key] = value Data.write_cfg_file() del value return True except Exception as e: errlog_add('cfgput {} error: {}'.format(key, e)) return False
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 (3): WPA2-PSK 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)) errlog_add("set_access_point 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_dev_uid() return ap_if.active()
def __init_socket(cls): """ Socket init: - socket create + setup as reusable (for rebind) - listen on socket connections """ # Create and Configure socket instance cls.__s = socket(AF_INET, SOCK_STREAM) cls.__s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # Listen up clients while True: try: cls.__s.bind((cls.__host, cls.__port)) break except Exception as msg: cls.server_console( '[ socket server ] Bind failed. Error Code : {}'.format( msg)) errlog_add('__init_socket bind failed: {}'.format(msg)) sleep(1) cls.server_console('[ socket server ] Socket bind complete') cls.__s.listen(5) cls.server_console('[ socket server ] Socket now listening') cls.__accept()
def micrOS(): profiling_info(label='[memUsage] MAIN LOAD') # BOOT HOOKs execution safe_boot_hook() # SET external interrupt with extirqcbf from nodeconfig external_interrupt_handler() # NETWORK setup auto_network_configuration() # LOAD Singleton SocketServer [1] SocketServer() # SET interrupt with timirqcbf from nodeconfig interrupt_handler() profiling_info(label='[memUsage] SYSTEM IS UP') # RUN Singleton SocketServer - main loop [2] SocketServer().run() # UNEXPECTED RESTART ??? errlog_add("Unexpected micrOS restart")
def suntime(lat=51.509865, lng=-0.118092): """ :param lat: latitude :param lng: longitude :return: raw string / query output """ if not WLAN(STA_IF).isconnected(): errlog_add("STA not connected: suntime") return '', '' url = 'https://api.sunrise-sunset.org/json?lat={lat}&lng={lng}&date=today&formatted=0'.format( lat=lat, lng=lng) _, _, host, path = url.split('/', 3) try: addr = getaddrinfo(host, 80)[0][-1] except Exception as e: errlog_add('suntime: resolve failed: {}'.format(e)) return '', '' # HTTP GET s = socket() try: s.settimeout(3) s.connect(addr) s.send( bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8')) data = s.recv(1024) finally: s.close() # BYTE CONVERT AND PARSE try: data = str(data, 'utf8').splitlines() data = data[2], data[-1] except Exception as e: errlog_add('suntime: query failed: {}'.format(e)) return '', '' return data
def safe_boot_hook(): try: bootup_hook() except Exception as e: print("[micrOS main] Hooks.bootup_hook() error: {}".format(e)) errlog_add("safe_boot_hook error: {}".format(e))
def external_interrupt_handler(): try: initEventIRQs() except Exception as e: print("[micrOS main] InterruptHandler.initEventIRQs error: {}".format(e)) errlog_add("external_interrupt_handler error: {}".format(e))
def set_dev_uid(): try: cfgput('hwuid', 'micr{}OS'.format(hexlify(unique_id()).decode('utf-8'))) except Exception as e: errlog_add("set_dev_uid error: {}".format(e))