def construct_dicts(self, path, ch_name_dict): """ Constructs and returns python dictionaries keyed on id and name This function should not be called directly, instead, use get_id_dict(path) and get_name_dict(path) Args: path (string): Path to the packet definition xml file to parse into a dictionary. ch_name_dict (dict()): Channel dictionary with names as keys and ChTemplate objects as values. Returns: A tuple with two packet dictionaries (type==dict()): (id_dict, name_dict). The keys should be the packets' id and name fields respectively and the values should be PktTemplate objects. """ packet_list = self.get_xml_tree(path) if packet_list.tag != self.PKT_LIST_TAG: raise exceptions.GseControllerParsingException( "expected packet list to have tag %s, but found %s" % (self.PKT_LIST_TAG, packet_list.tag)) id_dict = dict() name_dict = dict() for packet in packet_list: # check if this is actually a packet, and not something to ignore if packet.tag != self.PKT_TAG: continue pkt_name = packet.attrib[self.NAME_FIELD] pkt_id = int(packet.attrib[self.ID_FIELD]) ch_list = [] for ch in packet: ch_name = ch.attrib[self.CH_NAME_FIELD] if ch_name not in ch_name_dict: raise exceptions.GseControllerParsingException( "Channel %s in pkt %s, but cannot be found in channel dictionary" % (ch_name, pkt_name)) ch_list.append(ch_name_dict[ch_name]) pkt_temp = PktTemplate(pkt_id, pkt_name, ch_list) id_dict[pkt_id] = pkt_temp name_dict[pkt_name] = pkt_temp return (id_dict, name_dict)
def parse_type(self, type_name, xml_item, xml_tree): ''' Parses the given type string and returns a type object. Args: type_name (string): Name of the type in the xml xml_item (lxml etree root): Parsed xml object for the item containing the type name being parsed. This is used to get meta data such as string lenght. xml_tree (lxml etree root): Parsed Xml object containing enum and serializable type info (may not be used) Returns: Object of a class derived from the TypeBase class if successful, Raises an exception if the parsing fails. The caller will hold the only reference to the object. ''' if (type_name == "I8"): return I8Type() elif (type_name == "I16"): return I16Type() elif (type_name == "I32"): return I32Type() elif (type_name == "I64"): return I64Type() elif (type_name == "U8"): return U8Type() elif (type_name == "U16"): return U16Type() elif (type_name == "U32"): return U32Type() elif (type_name == "U64"): return U64Type() elif (type_name == "F32"): return F32Type() elif (type_name == "F64"): return F64Type() elif (type_name == "bool"): return BoolType() elif (type_name == "string"): if self.STR_LEN_TAG not in xml_item.attrib: print("Trying to parse string type, but found %s field" % self.STR_LEN_TAG) return None return StringType(max_string_len=xml_item.get(self.STR_LEN_TAG)) else: # First try Serialized types: result = self.get_serializable_type(type_name, xml_tree) if result != None: return result # Now try enums: result = self.get_enum_type(type_name, xml_tree) if result != None: return result # Abandon all hope raise exceptions.GseControllerParsingException( "Could not find type %s" % type_name)
def construct_dicts(self, path): """ Constructs and returns python dictionaries keyed on id and name This function should not be called directly, instead, use get_id_dict(path) and get_name_dict(path) Args: path: Path to the xml dictionary file containing event information Returns: A tuple with two event dictionaries (python type dict): (id_idct, name_dict). The keys are the events' id and name fields respectively and the values are ChTemplate objects """ xml_tree = self.get_xml_tree(path) # Check if xml dict has events section event_section = self.get_xml_section(self.EVENT_SECT, xml_tree) if event_section is None: raise exceptions.GseControllerParsingException( "Xml dict did not have a %s section" % self.EVENT_SECT ) id_dict = dict() name_dict = dict() for event in event_section: event_dict = event.attrib event_comp = event_dict[self.COMP_TAG] event_name = event_dict[self.NAME_TAG] event_id = int(event_dict[self.ID_TAG], base=16) event_severity = EventSeverity[event_dict[self.SEVERITY_TAG]] event_fmt_str = event_dict[self.FMT_STR_TAG] event_desc = None if self.DESC_TAG in event_dict: event_desc = event_dict[self.DESC_TAG] # Parse arguments args = self.get_args_list(event, xml_tree) event_temp = EventTemplate( event_id, event_name, event_comp, args, event_severity, event_fmt_str, event_desc, ) id_dict[event_id] = event_temp name_dict[event_name] = event_temp return (id_dict, name_dict)
def construct_dicts(self, path): ''' Constructs and returns python dictionaries keyed on id and name This function should not be called directly, instead, use get_id_dict(path) and get_name_dict(path) Args: path: Path to the xml dictionary file containing command information Returns: A tuple with two command dictionaries (python type dict): (id_dict, name_dict). The keys are the events' id and name fields respectively and the values are ChTemplate objects ''' xml_tree = self.get_xml_tree(path) # Check if xml dict has commands section cmd_section = self.get_xml_section(self.CMD_SECT, xml_tree) if (cmd_section == None): # TODO make this its own error (XML section err or something) raise exceptions.GseControllerParsingException( "Xml dict did not have a %s section"%self.EVENT_SECT) id_dict = dict() name_dict = dict() for cmd in cmd_section: cmd_dict = cmd.attrib cmd_comp = cmd_dict[self.COMP_TAG] cmd_mnemonic = cmd_dict[self.MNEMONIC_TAG] cmd_opcode = int(cmd_dict[self.OPCODE_TAG], base=16) cmd_desc = None if (self.DESC_TAG in cmd_dict): cmd_desc = cmd_dict[self.DESC_TAG] # Parse Arguments args = self.get_args_list(cmd, xml_tree) cmd_temp = CmdTemplate(cmd_opcode, cmd_mnemonic, cmd_comp, args, cmd_desc) id_dict[cmd_opcode] = cmd_temp name_dict[cmd_mnemonic] = cmd_temp return (id_dict, name_dict)
def parse(self, filename): """ Generator that parses an input sequence file and returns a tuple for each valid line of the sequence file. @param seqfile: A sequence file name (usually a .seq extension) @return A list of tuples: (lineNumber, descriptor, seconds, useconds, mnemonic, arguments) """ def subQuoted(f, string): """ Run a substitution function on only substrings within a string that are surrounded by single or double quotes @param f: a string substitution acting on matchobjs @param string: the string to perform the substitution on @return the substituted string """ s = re.sub(r'"[^"]*"', f, string) return re.sub(r"'[^']*'", f, s) def removeTrailingComments(string): """ Remove any trailing comments (proceded by ';') in a string @param string: the string to perform comment removal on @return the string without trailing comments """ def replaceSemis(matchobj): return matchobj.group(0).replace(";", " ") # ignore all semicolons in quotes: s = subQuoted(replaceSemis, string) s = subQuoted(replaceSemis, s) # get index of first semicolon, and return everything before it: if ";" in s: index = s.index(";") return string[:index] # return original string if no semicolon found: return string def splitString(string): """ Split a string with ' ' or ',' as a delimiter. Ignore any delimiters found within quoted substrings. @param string: the string to perform the split on @return a list representing the split string """ def replaceSpacesAndCommas(matchobj): s = re.sub(r"\s", "_", matchobj.group(0)) s = re.sub(r"\,", "_", s) return s # ignore all spaces in quotes: s = subQuoted(replaceSpacesAndCommas, string) # replace all commas with spaces, since either can be a delimiter: s = s.replace(",", " ") # get the split indices of the modified string: indices = [(m.start(), m.end()) for m in re.finditer(r"\S+", s)] toReturn = [] for start, end in indices: toReturn.append(string[start:end]) return toReturn def parseArgs(args): """ Turn .seq command argument list into their appropriate python types @param args: a list of parsed arguments @return a list of arguments as native python types """ def parseArg(arg): # See if argument is a string, if so remove the quotes and return it: if (arg[0] == '"' and arg[-1] == '"') or (arg[0] == "'" and arg[-1] == "'"): return arg[1:-1] # If the string contains a "." assume that it is a float: elif "." in arg: return float(arg) elif arg == "True" or arg == "true" or arg == "TRUE": return True elif arg == "False" or arg == "false" or arg == "FALSE": return False else: try: # See if it translates to an integer: return int(arg, 0) except ValueError: try: # See if it translates to a float: return float(arg) except ValueError: # Otherwise it is an enum type: return str(arg) return list(map(parseArg, args)) def parseTime(lineNumber, time): """ Parse a time string and return the command descriptor, seconds, and useconds of the time string @param lineNumber: the current line number where the time string was parsed @param time: the time string to parse @return a tuple (descriptor, seconds, useconds) """ def parseTimeStringOption(timeStr, timeFmts): """ Parse a time string by trying to use different time formats, until one succeeds @param timeStr: the time string @param timeFmts: the time format used to parse the string @return the datetime object containing the parsed string """ def parseTimeString(timeFmt): try: return datetime.strptime(timeStr, timeFmt) except: return None for fmt in timeFmts: dt = parseTimeString(fmt) if dt: return dt raise BaseException def parseRelative(timeStr): """ Parse a relative time string @param timeStr: the time string @return the datetime object containing the parsed string """ options = ["%H:%M:%S.%f", "%H:%M:%S"] return parseTimeStringOption(timeStr, options) def parseAbsolute(timeStr): """ Parse an absolute time string @param timeStr: the time string @return the datetime object containing the parsed string """ options = ["%Y-%jT%H:%M:%S.%f", "%Y-%jT%H:%M:%S"] return parseTimeStringOption(timeStr, options) descriptor = None d = time[0] t = time[1:] if d == "R": descriptor = Descriptor.RELATIVE dt = parseRelative(t) delta = timedelta( hours=dt.hour, minutes=dt.minute, seconds=dt.second, microseconds=dt.microsecond, ).total_seconds() elif d == "A": descriptor = Descriptor.ABSOLUTE dt = parseAbsolute(t) # See if timezone was specified. If not, use UTC if dt.tzinfo is not None: print("Using timezone %s" % dt.tzinfo.tzname()) epoch = datetime.fromtimestamp(0, dt.tzinfo) else: print("Using UTC timezone") epoch = datetime.utcfromtimestamp(0) delta = (dt - epoch).total_seconds() else: raise gseExceptions.GseControllerParsingException("Line %d: %s" % ( lineNumber + 1, "Invalid time descriptor '" + d + "' found. Descriptor should either be 'A' for absolute times or 'R' for relative times", )) seconds = int(delta) useconds = int((delta - seconds) * 1000000) return descriptor, seconds, useconds # Open the sequence file and parse each line: with open(filename) as inputFile: for i, line in enumerate(inputFile): line = line.strip() # ignore blank lines and comments if line and line[0] != ";": line = removeTrailingComments(line) line = splitString(line) length = len(line) if length < 2: raise gseExceptions.GseControllerParsingException( "Line %d: %s" % ( i + 1, "Each line must contain a minimum of two fields, time and command mnemonic\n", )) else: try: descriptor, seconds, useconds = parseTime( i, line[0]) except: raise gseExceptions.GseControllerParsingException( "Line %d: %s" % (i + 1, "Encountered syntax error parsing timestamp")) mnemonic = line[1] args = [] if length > 2: args = line[2:] try: args = parseArgs(args) except: raise gseExceptions.GseControllerParsingException( "Line %d: %s" % ( i + 1, "Encountered sytax error parsing arguments", )) yield i, descriptor, seconds, useconds, mnemonic, args
def parseTime(lineNumber, time): """ Parse a time string and return the command descriptor, seconds, and useconds of the time string @param lineNumber: the current line number where the time string was parsed @param time: the time string to parse @return a tuple (descriptor, seconds, useconds) """ def parseTimeStringOption(timeStr, timeFmts): """ Parse a time string by trying to use different time formats, until one succeeds @param timeStr: the time string @param timeFmts: the time format used to parse the string @return the datetime object containing the parsed string """ def parseTimeString(timeFmt): try: return datetime.strptime(timeStr, timeFmt) except: return None for fmt in timeFmts: dt = parseTimeString(fmt) if dt: return dt raise BaseException def parseRelative(timeStr): """ Parse a relative time string @param timeStr: the time string @return the datetime object containing the parsed string """ options = ["%H:%M:%S.%f", "%H:%M:%S"] return parseTimeStringOption(timeStr, options) def parseAbsolute(timeStr): """ Parse an absolute time string @param timeStr: the time string @return the datetime object containing the parsed string """ options = ["%Y-%jT%H:%M:%S.%f", "%Y-%jT%H:%M:%S"] return parseTimeStringOption(timeStr, options) descriptor = None d = time[0] t = time[1:] if d == "R": descriptor = Descriptor.RELATIVE dt = parseRelative(t) delta = timedelta( hours=dt.hour, minutes=dt.minute, seconds=dt.second, microseconds=dt.microsecond, ).total_seconds() elif d == "A": descriptor = Descriptor.ABSOLUTE dt = parseAbsolute(t) # See if timezone was specified. If not, use UTC if dt.tzinfo is not None: print("Using timezone %s" % dt.tzinfo.tzname()) epoch = datetime.fromtimestamp(0, dt.tzinfo) else: print("Using UTC timezone") epoch = datetime.utcfromtimestamp(0) delta = (dt - epoch).total_seconds() else: raise gseExceptions.GseControllerParsingException("Line %d: %s" % ( lineNumber + 1, "Invalid time descriptor '" + d + "' found. Descriptor should either be 'A' for absolute times or 'R' for relative times", )) seconds = int(delta) useconds = int((delta - seconds) * 1000000) return descriptor, seconds, useconds
def construct_dicts(self, path): """ Constructs and returns python dictionaries keyed on id and name This function should not be called directly, instead, use get_id_dict(path) and get_name_dict(path) Args: path: Path to the xml dictionary file containing channel information Returns: A tuple with two channel dictionaries (python type dict): (id_idct, name_dict). The keys are the channels' id and name fields respectively and the values are ChTemplate objects """ xml_tree = self.get_xml_tree(path) # Check if xml dict has channels section ch_section = self.get_xml_section(self.CH_SECT, xml_tree) if ch_section is None: raise exceptions.GseControllerParsingException( "Xml dict did not have a %s section" % self.CH_SECT ) id_dict = dict() name_dict = dict() for ch in ch_section: ch_dict = ch.attrib # Assume the required fields are present (component, name, id, # description, type). Check for all others ch_comp = ch_dict[self.COMP_TAG] ch_name = ch_dict[self.NAME_TAG] ch_id = int(ch_dict[self.ID_TAG], base=16) ch_type_obj = self.parse_type(ch_dict[self.TYPE_TAG], ch, xml_tree) ch_desc = None ch_fmt_str = None ch_low_red = None ch_low_orange = None ch_low_yellow = None ch_high_yellow = None ch_high_orange = None ch_high_red = None if self.DESC_TAG in ch_dict: ch_desc = ch_dict[self.DESC_TAG] if self.FMT_STR_TAG in ch_dict: ch_fmt_str = ch_dict[self.FMT_STR_TAG] # TODO we need to convert these into numbers, is this the best # way to do it? if self.LOW_R_TAG in ch_dict: ch_low_red = float(ch_dict[self.LOW_R_TAG]) if self.LOW_O_TAG in ch_dict: ch_low_orange = float(ch_dict[self.LOW_O_TAG]) if self.LOW_Y_TAG in ch_dict: ch_low_yellow = float(ch_dict[self.LOW_Y_TAG]) if self.HIGH_Y_TAG in ch_dict: ch_high_yellow = float(ch_dict[self.HIGH_Y_TAG]) if self.HIGH_O_TAG in ch_dict: ch_high_orange = float(ch_dict[self.HIGH_O_TAG]) if self.HIGH_R_TAG in ch_dict: ch_high_red = float(ch_dict[self.HIGH_R_TAG]) ch_temp = ChTemplate( ch_id, ch_name, ch_comp, ch_type_obj, ch_fmt_str, ch_desc, ch_low_red, ch_low_orange, ch_low_yellow, ch_high_yellow, ch_high_orange, ch_high_red, ) id_dict[ch_id] = ch_temp name_dict[ch_name] = ch_temp return (id_dict, name_dict)