def main(argc, argv): '''Main Function.''' # Check and parse program options from arguments options = parse_options() # Serial Port serial_port = "" if options["port"] is None: show_help() sys_exit(RC.OK) serial_port = options["port"][0] # Serial Bauds serial_bauds = 0 if options["bauds"] is None: print_log(LOG.INFO, "BaudRate not provided, detecting...") serial_bauds = auto_detect_serial_bauds(serial_port) if serial_bauds == 0: print_log(LOG.INFO, "BaudRate detection fail.") sys_exit(RC.OK) else: serial_bauds = options["bauds"][0] # Serial log file serial_log = "" if options["log"] is not None: serial_log = options["log"][0] # Serial Terminal rc = serial_terminal(serial_port, serial_bauds) # Program end if not rc: program_exit(RC.FAIL) program_exit(RC.OK)
def cmd_script_parse(cmdscript): '''Parse cmdscript and check for unexpected or usupported keywords.''' # DOS to UNIX EOLs cmdscript = cmdscript.replace("\r\n", "\n") # Split the text in lines cmdscript = cmdscript.split("\n") # Iterate over each line detecting wich ones would be ignored lines_to_rm_index = [] for i in range(len(cmdscript) - 1): line = cmdscript[i] # Check for empty and comment lines if (len(line) == 0) or (line[0] == "#"): lines_to_rm_index.append(i) continue # Check for unsupported/unkown command if line.split()[0] not in CONST.CMDSCRIPT_COMMANDS: print_log(LOG.WARNING, TEXT.UNEXPECTED_CMDSCRIPT_CMD.format(line)) lines_to_rm_index.append(i) continue # Remove lines to ignore from cmdscript removed = 0 for i in lines_to_rm_index: del cmdscript[i - removed] removed = removed + 1 return cmdscript
def cmd_eol(args): '''Command EOL (send an End Of Line character/s to Serial CLI)''' # Send the EOL rc = serial_write(ser, eol) if rc is None: print_log(LOG.ERROR, TEXT.EOL_FAIL) return False print_log(LOG.INFO, TEXT.EOL_SEND) return True
def serial_read_str(ser=None, num_bytes=1024, timeout=None): '''Try to read from a Serial Port and return read data as string.''' raw_read = serial_read(ser, num_bytes, timeout) str_read = "" try: str_read = raw_read.decode() except Exception as e: print_log(LOG.ERROR, str(e)) print_log(LOG.DEBUG, "Serial read (str):\n{}".format(str_read)) return str_read
def create_parents_dirs(file_path): '''Create all parents directories from provided file path (mkdir -p $file_path).''' try: parentdirpath = os_path.dirname(file_path) if not os_path.exists(parentdirpath): os_makedirs(parentdirpath, 0o775) except Exception as e: print_log( LOG.ERROR, "Can't create parents directories of {}. {}".format( file_path, str(e)))
def file_clear(file_path): '''Remove and recreate a file to empty file content.''' create_parents_dirs(file_path) try: if os_path.exists(file_path): os_remove(file_path) with open(file_path, 'a'): os_utime(file_path, None) except Exception as e: print_log(LOG.ERROR, "Can't clear file {}. {}".format(file_path, str(e)))
def program_exit(return_code): '''Finish function.''' global ser # Check if unexpected exit code provided and use RC.FAIL in that case if (return_code != RC.OK) and (return_code != RC.FAIL): return_code = RC.FAIL # Close and free any pending memory if (ser is not None) and ser.isOpen(): serial_close(ser) # Exit print_log(LOG.DEBUG, "Program exit ({}).\n".format(return_code)) sys_exit(return_code)
def cmd_disconnect(args): '''Command DISCONNECT (make a Serial Connection)''' global ser # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("DISCONNECT")) return False serial_port = args[0] # Just close current porresponset (multiple ports support unimplemnted) if (ser is not None) and ser.isOpen(): serial_close(ser) print_log(LOG.INFO, TEXT.DISCONNECT_CLOSE) return True
def serial_read_until(ser=None, expected=LF, num_bytes=1024, timeout=None): '''Try to read from a Serial Port until an expected bytes/substring.''' # Check if no port provided if ser is None: return "" # Setup read timeout backup_timeout = ser.timeout if timeout is not None: ser.timeout = timeout try: raw_read = ser.read_until(expected, num_bytes) except Exception as e: print_log(LOG.ERROR, str(e)) return "" ser.timeout = backup_timeout print_log(LOG.DEBUG, "Serial read (bytes):\n{}".format(raw_read)) # Parse to string str_read = "" try: str_read = str(raw_read) # str_read = raw_read.decode() except Exception as e: print_log(LOG.ERROR, str(e)) return "" print_log(LOG.DEBUG, "Serial read (str):\n{}".format(str_read)) return str_read
def file_read_all_bin(file_path): '''Read all file content as binary and return it.''' # Check if no path provided or file doesnt exists if file_path is None: return None if not os_path.exists(file_path): print_log(LOG.ERROR, "File {} not found.".format(file_path)) return None # File exists, so open and read it read = None try: with open(file_path, "rb") as f: read = f.read() except Exception as e: print_log(LOG.ERROR, "Can't open and read file {}. {}".format(file_path, str(e))) return read
def serial_read(ser=None, num_bytes=1024, timeout=None): '''Try to read from a Serial Port.''' # Check if no port provided if ser is None: return b'' # Setup read timeout backup_timeout = ser.timeout if timeout is not None: ser.timeout = timeout # Read and restore default read timeout try: raw_read = ser.read(num_bytes) except Exception as e: print_log(LOG.ERROR, str(e)) ser.timeout = backup_timeout print_log(LOG.DEBUG, "Serial read (bytes):\n{}".format(raw_read)) if not raw_read: return b'' return raw_read
def file_read_all_text(file_path): '''Read all text file content and return it in a string.''' read = "" # Check if file doesnt exists if not os_path.exists(file_path): print_log(LOG.ERROR, "File {} not found.".format(file_path)) # File exists, so open and read it else: try: if is_running_with_py3(): with open(file_path, "r", encoding="utf-8") as f: read = f.read() else: with open(file_path, "r") as f: read = f.read() except Exception as e: print_log( LOG.ERROR, "Can't open and read file {}. {}".format(file_path, str(e))) return read
def cmdscript_interpreter(cmdscript): '''cmdscript interpreter''' for line in cmdscript: command = line.split() operation = command[0] args = command[1:] # Ignore unsupported operations if operation not in CONST.CMDSCRIPT_COMMANDS: continue # Handle Commands if operation == "CONNECT": if not cmd_connect(args): return False elif operation == "DISCONNECT": if not cmd_disconnect(args): return False elif operation == "CFGRESTIMEOUT": if not cmd_cfrestimeout(args): return False elif operation == "CFGEOL": if not cmd_cfgeol(args): return False elif operation == "DELAY": if not cmd_delay(args): return False elif operation == "DELAYMS": if not cmd_delayms(args): return False elif operation == "EOL": if not cmd_eol(args): return False elif operation == "CMD": if not cmd_command(args): return False elif operation == "RES": if not cmd_response(args): return False else: print_log(LOG.WARNING, TEXT.INVALID_CMDSCRIPT_CMD.format(command)) return True
def auto_detect_serial_bauds(serial_port): '''Automatic Serial baudrate detection (check for ascii text in commons bauds).''' bauds = 0 ser = None for common_baud in CONST.SERIAL_COMMON_BAUDS: # Try to open Serial port at current common baud rate # If port can't be open, continue to next common bauds print("\nChecking {} bauds...".format(common_baud)) ser = serial_open(serial_port, common_baud, 1.0, 1.0) if ser is None: continue time.sleep(2) # Send some strange string with end of line and read response serial_write(ser, CONST.SERIAL_AUTODETECT_BAUDS_SEND) readed_bytes = serial_read_str(ser) if len(readed_bytes) == 0: serial_close(ser) continue # Remove end of line bytes readed_bytes = readed_bytes.replace("\r", "") readed_bytes = readed_bytes.replace("\n", "") # Ignore if readed bytes are less than 10 characters if len(readed_bytes) < 5: serial_close(ser) continue # Check how many characters are commons in human texts num_human_chars = 0 for character in readed_bytes: if character in CONST.SERIAL_AUTODETECT_ASCII_LIST: num_human_chars = num_human_chars + 1 # If >=70% of the read string are common human text characters # Assume that this is the correct baud rate if ((num_human_chars / len(readed_bytes)) *100) >= 70: bauds = common_baud print_log(LOG.INFO, "Seems that BuadRate is {}".format(bauds)) serial_close(ser) break serial_close(ser) return bauds
def cmd_connect(args): '''Command CONNECT (make a Serial Connection)''' global ser # Check for expected number of arguments if len(args) < 2: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("CONNECT")) return False # Get and check arguments serial_port = args[0] serial_bauds = args[1] if not is_int(serial_bauds): print_log(LOG.ERROR, TEXT.CONNECT_INVALID_BAUDS) return False serial_bauds = int(serial_bauds) # Try to open the serial port print_log(LOG.INFO, TEXT.CONNECT_OPENING.format(serial_port, serial_bauds)) ser = serial_open(serial_port, serial_bauds, res_timeout, 1.0) if (ser is None) or (not ser.isOpen()): print_log(LOG.INFO, TEXT.CONNECT_OPEN_FAIL) return False print_log(LOG.INFO, TEXT.CONNECT_OPEN) return True
def serial_terminal(port, bauds): '''Handle a Serial Terminal.''' global ser global th_read, th_write # Opening Serial Port print("\nOpening port {} at {} bauds...".format(port, bauds)) ser = serial_open(port, bauds, 1.0, 1.0) if (ser is None) or (not ser.isOpen()): print_log(LOG.INFO, "Can't open Serial port.") return False time.sleep(2) # Launching Serial read and write threads th_read = Thread(target=th_serial_read, args=(ser,)) th_write = Thread(target=th_serial_write, args=(ser,)) print("\nSerial Terminal Start") th_read.start() th_write.start() th_read.join() th_write.join() # Close Serial Port if (ser is not None) and ser.isOpen(): serial_close(ser) return True
def serial_close(ser=None): '''Try to close a Serial Port.''' # Check if no port provided if ser is None: return port = ser.port print_log(LOG.INFO, "Closing Serial port {}...".format(port)) try: ser.close() print_log(LOG.INFO, "Serial port {} closed.".format(port)) except SerialException as e: print_log(LOG.ERROR, str(e))
def serial_write(ser=None, to_write=None): '''Basic Serial write function managed for Py2 and Py3 support.''' if ser is None: return None print_log(LOG.DEBUG, "Serial write (str):\n{}".format(to_write)) if is_running_with_py3(): to_write = to_write.encode() try: ser.write(to_write) except Exception as e: print_log(LOG.ERROR, str(e)) print_log(LOG.DEBUG, "Serial write (bytes):\n{}".format(to_write)) ser.flush()
def serial_open(port, baudrate, read_timeout=None, write_timeout=None): '''Try to open a Serial Port.''' ser = None print_log(LOG.INFO, "Opening Serial port {} at {}bps...".format(port, str(baudrate))) try: ser = Serial(port=None) ser.port = port ser.baudrate = baudrate ser.timeout = read_timeout ser.write_timeout = write_timeout if not ser.isOpen(): ser.open() ser.flush() print_log(LOG.INFO, "Port successfully open.") except Exception as e: print_log(LOG.ERROR, str(e)) if (ser is None) or (not ser.isOpen()): ser = None print_log(LOG.ERROR, "Can't open serial port.") return ser
def cmd_delayms(args): '''Command DELAYMS (wait for some milli-seconds)''' # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("DELAYMS")) return False # Check for valid time value if not is_int(args[0]): print_log(LOG.ERROR, TEXT.DELAYMS_INVALID) return False delay_time_ms = int(args[0]) print_log(LOG.INFO, TEXT.DELAYMS.format(delay_time_ms)) sleep(delay_time_ms / 1000) return True
def cmd_command(args): '''Command CMD (send a command to Serial CLI)''' # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("CMD")) return False # Compose command string and add EOL character/s cmd_str = " ".join(args) cmd_str_eol = "{}{}".format(cmd_str, eol) # Send the command if not serial_write(ser, cmd_str_eol): print_log(LOG.ERROR, TEXT.CMD_SEND_FAIL.format(cmd_str)) return False print_log(LOG.INFO, TEXT.CMD_SEND.format(cmd_str)) return True
def cmd_response(args): '''Command RES (read for a response from Serial CLI)''' # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("RES")) return False # Compose response string to expect res_str = " ".join(args) # Receive and read response res = serial_read_until(ser, res_str, timeout=res_timeout) if res == "": print_log(LOG.ERROR, TEXT.RES_FAIL.format(res_str, res)) return False print_log(LOG.INFO, TEXT.RES_OK.format(res_str)) return True
def cmd_cfrestimeout(args): '''Command CFGRESTIMEOUT (set Serial read timeout)''' global res_timeout # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("CFGRESTIMEOUT")) return False # Check for valid timeout value if not is_int(args[0]): print_log(LOG.ERROR, TEXT.CFGRESTIMEOUT_INVALID) return False res_timeout = int(args[0]) / 1000 if ser is not None: ser.timeout = res_timeout print_log(LOG.INFO, TEXT.CFGRESTIMEOUT_SET.format(res_timeout)) return True
def cmd_cfgeol(args): '''Command CFGEOL (set Serial End Of Line character)''' global eol # Check for expected number of arguments if len(args) < 1: print_log(LOG.ERROR, TEXT.CMD_NO_ARGS.format("CFGEOL")) return False # Check for valid EOL value if args[0] not in ["CR", "LF", "CRLF"]: print_log(LOG.ERROR, TEXT.CFGEOL_INVALID) return False # Set EOL if args[0] == "CR": eol = EOL.CR elif args[0] == "LF": eol = EOL.LF elif args[0] == "CRLF": eol = EOL.CRLF print_log(LOG.INFO, TEXT.CFGEOL_SET.format(args[0])) return True
def file_write(file_path, text=""): '''Write text to provided file.''' create_parents_dirs(file_path) # Determine if file exists and set open mode to write or append if not os_path.exists(file_path): print_log(LOG.INFO, "File {} not found, creating it...".format(file_path)) # Try to Open and write to file if is_running_with_py3(): try: with open(file_path, 'a', encoding="utf-8") as f: f.write(text) except Exception as e: print_log(LOG.ERROR, "Can't write to file {}. {}".format(file_path, str(e))) else: try: with open(file_path, 'a') as f: f.write(text) except Exception as e: print_log(LOG.ERROR, "Can't write to file {}. {}".format(file_path, str(e)))