import yaml, os import numpy as np class UnitsParser: def range(r,limit): if r == "all": return(range(0,limit)) elt=r.replace("@",str(limit-1)).split("-") if len(elt) == 2: min = int(elt[0]) max = int(elt[1]) # TODO: Check min/max return(range(min,max)) else: return([int(elt[0])]) def bandwidth(bw): for i,c in enumerate(bw): if not c.isdigit() and c != ".": break number=float(bw[:i]) unit=bw[i:] number=number*1000 if unit == "Mbps" else number number=number*1000*8 if unit == "MBps" else number number=number*100 if unit == "kbps" else number number=number*100*8 if unit == "kBps" else number number=number*8 if unit == "Bps" else number return(number) def latency(lat): for i,c in enumerate(lat): if not c.isdigit() and c != ".": break number=float(lat[:i]) unit=lat[i:] number=number*60 if unit in ["m","M"] else number number=number*3600 if unit in ["h","H"] else number number=number/1000 if unit in ["ms","MS"] else number return(number) class YAMLPlatformFile: def __init__(self, file_path): self.file_path=file_path self.default={ "breakpoints": [], "breakpoints_every": None, "debug": False, "interferences": True, "node_count": 0, "implementations": [], "interfaces": dict() } with open(file_path) as f: self.platform = yaml.load(f, Loader=yaml.FullLoader) ##### General if "general" in self.platform: self.parse_general() ##### Nodes if "nodes" in self.platform: self.parse_nodes() else: self.parsing_error("platform file has no nodes section") ##### Interfaces if "interfaces" in self.platform: self.parse_interfaces() else: self.parsing_error("platform file has no interfaces section") def parsing_error(self,msg): raise Exception("Fail to parse platform file \""+self.file_path+"\": "+msg) def parse_link(self,link): words=link.split() if len(words) == 4: return(( UnitsParser.range(words[0],self.default["node_count"]), UnitsParser.bandwidth(words[1]), UnitsParser.latency(words[2]), UnitsParser.range(words[3],self.default["node_count"]))) elif len(words) == 2: return(( range(0,self.default["node_count"]), UnitsParser.bandwidth(words[0]), UnitsParser.latency(words[1]), range(0,self.default["node_count"]))) return(None) def parse_txperf(self,txperf): elts=txperf.split() return((UnitsParser.bandwidth(elts[0]),UnitsParser.latency(elts[1]))) def parse_interfaces(self): interfaces=self.platform["interfaces"] node_count=self.default["node_count"] for i in interfaces: is_wired=not interfaces[i]["wireless"] links=list() if type(interfaces[i]["links"]) == list: for link in interfaces[i]["links"]: links.append(self.parse_link(link)) else: links.append(self.parse_link(interfaces[i]["links"])) ##### Create network matrix BW=np.full((node_count,node_count),0) LAT=np.full((node_count,node_count),0) for link in links: for n1 in link[0]: for n2 in link[3]: BW[n1][n2]=link[1] LAT[n1][n2]=link[2] ##### Set txperfs for wireless interfaces if not is_wired: txperfs=interfaces[i]["txperfs"] for node in range(0,node_count): txperf=txperfs[0] if len(txperfs) == 1 else txperfs[node] bw,lat=self.parse_txperf(txperf) BW[node][node]=bw LAT[node][node]=lat self.default["interfaces"][i]={ "is_wired": is_wired, "bandwidth": BW, "latency": LAT } def parse_nodes(self): nodes=self.platform["nodes"] if "count" in nodes: if not str(nodes["count"]).isnumeric(): self.parsing_error("node count should be a number") self.default["node_count"]=nodes["count"] if "implementations" in nodes: if type(nodes["implementations"]) != list: self.parsing_error("nodes implementations should be a list of file path") for file in nodes["implementations"]: if not os.path.exists(file): self.parsing_error("File "+file+ " not found") path, extension = os.path.splitext(file) if extension != ".py": self.parsing_error("File "+file+" must be a python file") self.default["implementations"].append(path) count = len(nodes["implementations"]) if count > 1 and count != self.default["node_count"]: self.parsing_error("If more than one implementation is specified, each node implementation should be provided ("+str(self.default["node_count"])+" in total)") def parse_general(self): general=self.platform["general"] if "breakpoints" in general: if type(general["breakpoints"]) != list: self.parsing_error("breakpoints should be a list of number") self.default["breakpoints"]=general["breakpoints"] if "breakpoints_every" in general: if not str(general["breakpoints_every"]).isnumeric(): self.parsing_error("breakpoints_every should be a number") self.default["breakpoints_every"]=general["breakpoints_every"] if "debug" in general: if type(general["debug"]) != bool: self.parsing_error("debug should be on or off") self.default["debug"]=general["debug"] if "interferences" in general: if type(general["interferences"]) != bool: self.parsing_error("interferences should be on or off") self.default["interferences"]=general["interferences"]