def angle_heapmaps(description, zextractor): configuration_names = Configuration.names() configurations = [Configuration.create_specific(name, 11, 4.5, "topology") for name in Configuration.names()] #Get rid of configurations that aren't grids configurations = filter(lambda c: isinstance(c.topology, Grid), configurations) # Get rid of configurations that do not have 2 sources configurations = filter(lambda c: len(c.source_ids) == 2, configurations) grapher = configuration_heatmap.Grapher("results/Configurations", description, zextractor) grapher.nokey = True grapher.xaxis_label = "X Coordinate" grapher.yaxis_label = "Y Coordinate" grapher.cb_label = "Source Angle" grapher.dgrid3d_dimensions = "11,11" grapher.cbrange = (0, 180) grapher.create(configurations) summary_grapher = summary.GraphSummary( os.path.join("results/Configurations", description), '{}-{}'.format("results/Configurations", description) ) summary_grapher.width_factor = None summary_grapher.height = "7cm" summary_grapher.run()
def build_arguments(a): build_args = a.build_arguments() configuration = Configuration.create(a.args.configuration, a.args) build_args.update(configuration.build_arguments()) return build_args
def build_arguments(self, a): build_args = a.build_arguments() configuration = Configuration.create(a.args.configuration, a.args) build_args.update(configuration.build_arguments()) try: build_args.update(LOG_MODES[self.log_mode]) except KeyError: raise RuntimeError( f"Unknown testbed log mode {self.log_mode}. Available: {LOG_MODES.keys()}" ) return build_args
def _get_configuration(self, **kwargs): args = ('network size', 'distance', 'node id order', 'seed') arg_converters = { 'network size': int, 'distance': float, 'seed': int, } kwargs_copy = {k.replace("_", " "): v for (k,v) in kwargs.items()} arg_values = { name: arg_converters.get(name, lambda x: x)(kwargs_copy[name]) for name in args if name in kwargs_copy } return Configuration.create(kwargs['configuration'], arg_values)
def build_arguments(self, a): build_args = a.build_arguments() configuration = Configuration.create(a.args.configuration, a.args) build_args.update(configuration.build_arguments()) build_args.update(self.testbed.build_arguments()) log_mode = self.testbed.log_mode() modes = { "printf": { "USE_SERIAL_PRINTF": 1, "SERIAL_PRINTF_BUFFERED": 1 }, "unbuffered_printf": { "USE_SERIAL_PRINTF": 1, "SERIAL_PRINTF_UNBUFFERED": 1 }, "uart_printf": { "USE_SERIAL_PRINTF": 1, "SERIAL_PRINTF_UART": 1 }, "serial": { "USE_SERIAL_MESSAGES": 1 }, "disabled": { "NO_SERIAL_OUTPUT": 1 }, "avrora": { "AVRORA_OUTPUT": 1 }, "cooja": { "COOJA_OUTPUT": 1 }, } try: build_args.update(modes[log_mode]) except KeyError: raise RuntimeError( f"Unknown testbed log mode {log_mode}. Available: {modes.keys()}" ) return build_args
def post_build_actions(target_directory, a): import os.path import shutil from simulator import Configuration # Create main.elf shutil.copy(os.path.join(target_directory, "main.exe"), os.path.join(target_directory, "main.elf")) # Output topology file configuration = Configuration.create(a.args.configuration, a.args) with open(os.path.join(target_directory, "topology.txt"), "w") as topo_file: for (nid, (x, y)) in sorted(configuration.topology.nodes.items(), key=lambda k: k[0]): z = 0 print(f"node{nid} {x} {y} {z}", file=topo_file)
def _submit_job(self, a, target_directory): # Remove some things to get the name to be shorter name = target_directory[len("testbed-"):-len("-real")] name = name.replace("ReliableFaultModel__-", "") name = name.replace("topology-", "") configuration = Configuration.create(a.args.configuration, a.args) exe_path = os.path.join(target_directory, "main.exe") config_path = os.path.join(target_directory, "flocklab.xml") with open(config_path, "w") as config_file: self.generate_configuration_xml( configuration, config_file, exe_path, name=name, duration=self.duration, ) # Check that everything is okay command = ["./scripts/flocklab.sh", "-v", config_path] print("Checking xml validity:", " ".join(command)) validator_output = subprocess.check_output( " ".join(command), shell=True).decode("utf-8").strip() if validator_output != "The file validated correctly.": raise RuntimeError(validator_output) # Submit the job command = ["./scripts/flocklab.sh", "-c", config_path] print("Submitting xml job:", " ".join(command), "for a duration of", self.duration) if self.dry_run: print("Dry run complete!") return print(f"Submitting {name}...") subprocess.check_call(" ".join(command), shell=True)
def _get_configuration(self): arg_converters = { 'network_size': int, 'distance': float, } arg_values = { name.replace("_", " "): converter(self.opts[name]) for (name, converter) in arg_converters.items() if name in self.opts } # Will never have a seed because opts to too early to get the # per simulation seed arg_values['seed'] = None # As we don't have a seed the node_id_order must always be topology arg_values['node id order'] = "topology" return Configuration.create(self.opts['configuration'], arg_values)
def add_job(self, options, name, estimated_time=None): if not self.quiet: print(name) if not self._progress.has_started(): self._progress.start(self.total_job_size) # Create the target directory target_directory = name[:-len(".txt")] data.util.create_dirtree(target_directory) # Get the job arguments # If options is a tuple then we have just been given the # module name and the parsed arguments. if isinstance(options, tuple): module, a = options else: options = shlex.split(options) module, argv = options[0], options[1:] a = self.parse_arguments(module, argv) module_path = module.replace(".", "/") # Check that the topology supports the chosen platform # Some topologies only support one platform type configuration = Configuration.create(a.args.configuration, a.args) if hasattr(configuration.topology, "platform"): if configuration.topology.platform != self.platform: raise RuntimeError( "The topology's platform ({}) does not match the chosen platform ({})" .format(configuration.topology.platform, self.platform)) # Build the binary # These are the arguments that will be passed to the compiler build_args = self.build_arguments(a) build_args["PLATFORM"] = self.platform if not self.quiet: print(f"Building for {build_args}") build_result = Builder.build_actual(module_path, self.platform, enable_fast_serial=False, **build_args) if not self.quiet: print( f"Build finished with result {build_result}, waiting for a bit..." ) # For some reason, we seemed to be copying files before # they had finished being written. So wait a bit here. time.sleep(1) if not self.quiet: print(f"Copying files to '{target_directory}'") # Detect the OS from the presence of one of these files: os_of_build = self._detect_os_of_build(module_path) files_to_copy = { "tinyos": ( "app.c", "ident_flags.txt", "main.exe", "main.ihex", "main.srec", "tos_image.xml", "wiring-check.xml", ), "contiki": ( "main.exe", "symbols.h", "symbols.c", ), } for name in files_to_copy[os_of_build]: try: src = os.path.join(module_path, "build", self.platform, name) dest = target_directory shutil.copy(src, dest) #print("Copying {} -> {}".format(src, dest)) except IOError as ex: # Ignore expected fails if name not in {"main.srec", "wiring-check.xml"}: print(f"Not copying {name} due to {ex}") # Copy any generated class files for class_file in glob.glob(os.path.join(module_path, "*.class")): try: shutil.copy(class_file, target_directory) except shutil.Error as ex: if str(ex).endswith("are the same file"): continue else: raise if self.pool is not None: target_ihex = os.path.join(target_directory, "main.ihex") if not self.quiet: print( f"Creating per node id binaries using '{target_ihex}'...") def fn(node_id): output_ihex = os.path.join(target_directory, f"main-{node_id}.ihex") self.create_tos_node_id_ihex(target_ihex, output_ihex, node_id) self.pool.map(fn, configuration.topology.nodes) if not self.quiet: print("All Done!") self._progress.print_progress(self._jobs_executed) self._jobs_executed += 1 return a, module, module_path, target_directory
def run_simulation(module, a, count=1, print_warnings=False): import shlex import sys import subprocess from simulator import Configuration if a.args.node_id_order != "topology": raise RuntimeError("COOJA does not support a nido other than topology") configuration = Configuration.create(a.args.configuration, a.args) command = avrora_command(module, a, configuration) print("@command:{}".format(command)) sys.stdout.flush() command = shlex.split(command) if a.args.mode == "RAW": if count != 1: raise RuntimeError("Cannot run avrora multiple times in RAW mode") with subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) as proc: proc_iter = iter(proc.stdout.readline, '') for line in avrora_iter(proc_iter): print(line) proc.stdout.close() try: return_code = proc.wait(timeout=1) if return_code: raise subprocess.CalledProcessError(return_code, command) except subprocess.TimeoutExpired: proc.terminate() else: if a.args.mode == "SINGLE": from simulator.Simulation import OfflineSimulation elif a.args.mode == "GUI": from simulator.TosVis import GuiOfflineSimulation as OfflineSimulation else: raise RuntimeError("Unknown mode {}".format(a.args.mode)) for n in range(count): with subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) as proc: proc_iter = iter(proc.stdout.readline, '') with OfflineSimulation(module, configuration, a.args, event_log=avrora_iter(proc_iter)) as sim: a.args.attacker_model.setup(sim) try: sim.run() except Exception as ex: import traceback all_args = "\n".join("{}={}".format(k, v) for (k, v) in vars(a.args).items()) print("Killing run due to {}".format(ex), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) print("For parameters:", file=sys.stderr) print("With seed:", sim.seed, file=sys.stderr) print(all_args, file=sys.stderr) # Make sure to kill the avrora java process proc.kill() return 51 proc.stdout.close() try: return_code = proc.wait(timeout=1) if return_code: raise subprocess.CalledProcessError(return_code, command) except subprocess.TimeoutExpired: proc.terminate() try: sim.metrics.print_results() if print_warnings: sim.metrics.print_warnings() except Exception as ex: import traceback all_args = "\n".join("{}={}".format(k, v) for (k, v) in vars(a.args).items()) print("Failed to print metrics due to: {}".format(ex), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) print("For parameters:", file=sys.stderr) print("With seed:", sim.seed, file=sys.stderr) print(all_args, file=sys.stderr) # Make sure to kill the avrora java process proc.kill() return 52 return 0
def run_one_file(log_file, module, a, count=1, print_warnings=False): import copy import simulator.Configuration as Configuration import simulator.OfflineLogConverter as OfflineLogConverter args = copy.deepcopy(a.args) # Get the correct Simulation constructor if args.mode == "SINGLE": from simulator.Simulation import OfflineSimulation elif args.mode == "GUI": from simulator.TosVis import GuiOfflineSimulation as OfflineSimulation else: raise RuntimeError(f"Unknown mode {args.mode}") configuration = Configuration.create(args.configuration, args) print(f"Analysing log file {log_file}", file=sys.stderr) with OfflineLogConverter.create_specific(args.log_converter, log_file) as converted_event_log: with OfflineSimulation(module, configuration, args, event_log=converted_event_log) as sim: args.attacker_model.setup(sim) try: sim.run() except Exception as ex: import traceback all_args = "\n".join(f"{k}={v}" for (k, v) in vars(args).items()) print("Killing run due to {}".format(ex), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) print("For parameters:", file=sys.stderr) print("With seed:", sim.seed, file=sys.stderr) print(all_args, file=sys.stderr) print("In file: {}".format(log_file), file=sys.stderr) return 51 try: sim.metrics.print_results() if print_warnings: sim.metrics.print_warnings(sys.stderr) except Exception as ex: import traceback all_args = "\n".join(f"{k}={v}" for (k, v) in vars(args).items()) print("Failed to print metrics due to: {}".format(ex), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) print("For parameters:", file=sys.stderr) print("With seed:", sim.seed, file=sys.stderr) print(all_args, file=sys.stderr) print("In file: {}".format(log_file), file=sys.stderr) return 52 return 0
import simulator.Configuration as Configuration from simulator.Topology import Grid from data.graph import summary, configuration_heatmap parser = argparse.ArgumentParser(description="Misc. Grapher", add_help=True) parser.add_argument("--min-source-distance-heatmap", action='store_true', default=False) parser.add_argument("--source-angle-estimate-heatmap", action='store_true', default=False) parser.add_argument("--source-angle-meters-heatmap", action='store_true', default=False) parser.add_argument("--source-angle-actual-heatmap", action='store_true', default=False) parser.add_argument("--source-angle-det-heatmap", action='store_true', default=False) parser.add_argument("--source-angle-tan-heatmap", action='store_true', default=False) args = parser.parse_args(sys.argv[1:]) if args.min_source_distance_heatmap: configuration_names = Configuration.names() configurations = [Configuration.create_specific(name, 11, 4.5, "topology") for name in Configuration.names()] #Get rid of configurations that aren't grids configurations = filter(lambda c: isinstance(c.topology, Grid), configurations) def zextractor(configuration, nid): return min(configuration.node_source_distance(nid, src_id) for src_id in configuration.source_ids) grapher = configuration_heatmap.Grapher("results/Configurations", "min-src-distance", zextractor) grapher.nokey = True grapher.xaxis_label = "X Coordinate" grapher.yaxis_label = "Y Coordinate" grapher.cb_label = "Minimum Source Distance (m)"
parser.add_argument( "-d", "--distance", type=ArgumentsCommon.type_positive_float, default=4.5, help= "The distance between nodes. How this is used depends on the configuration specified." ), parser.add_argument( "-nido", "--node-id-order", choices=("topology", "randomised"), default="topology", help= "With 'topology' node id orders are the same as the topology defines. 'randomised' causes the node ids to be randomised." ), parser.add_argument("--show", default=False, action="store_true") args = parser.parse_args() configuration = Configuration.create_specific(args.configuration, args.network_size, args.distance, args.node_id_order, seed=args.seed) main(configuration, show=args.show)
def _create_plot(self, global_params, src_period, params, results): def chunks(l, n): """ Yield successive n-sized chunks from l.""" for i in range(0, len(l), n): yield l[i:i + n] global_parameter_names = self._key_names_base # Pop the source period off the end of the parameters global_params_dict = dict( zip(global_parameter_names[:-1], global_params)) config = global_params_dict["configuration"] size = int(global_params_dict["network size"]) distance = float(global_params_dict["distance"]) nido = global_params_dict["node id order"] dat = results[self.result_index] # The dat is a (mean, var) pair, so take the mean if isinstance(dat, np.ndarray): dat = dat[0] if not isinstance(dat, dict): raise RuntimeError( "The data is not a dict. It is a {} with value {}".format( type(dat), dat)) dir_name = os.path.join( self.output_directory, self.result_name, *(global_params + (str(src_period), ) + tuple(map(str, params)))) print(dir_name) # Ensure that the dir we want to put the files in actually exists data.util.create_dirtree(dir_name) configuration = Configuration.create_specific(config, size, distance, nido) (minx, miny) = configuration.minxy_coordinates() (maxx, maxy) = configuration.maxxy_coordinates() self._write_plot_data(dir_name, configuration, dat) with open(os.path.join(dir_name, 'graph.gp'), 'w') as graph_p: graph_p.write('#!/usr/bin/gnuplot\n') graph_p.write('set terminal pdf enhanced\n') graph_p.write('set output "graph.pdf" \n') if self.palette is not None: graph_p.write('set palette {}\n'.format(self.palette)) #graph_p.write('set title "Heat Map of Messages Sent"\n') graph_p.write('unset key\n') #graph_p.write('set size ratio 0.5\n') #graph_p.write('set tic scale 0\n') graph_p.write('set xlabel "X Coordinate"\n') graph_p.write('set ylabel "Y Coordinate"\n') graph_p.write('set size square\n') # To top left to be (0, 0) graph_p.write('set xrange [{}:{}]\n'.format(minx, maxx)) graph_p.write('set xtics auto\n') #graph_p.write('set yrange [{}:{}] reverse\n'.format(maxy, miny)) graph_p.write('set yrange [:] reverse\n') graph_p.write('set ytics auto\n') graph_p.write('set cbrange [:]\n') graph_p.write('set cblabel "{}"\n'.format( self.result_name.title())) graph_p.write('set dgrid3d {0},{0}\n'.format(size)) graph_p.write('set pm3d map interpolate 3,3\n') graph_p.write('splot "graph.dat" using 1:2:3 with pm3d\n') with open(os.path.join(dir_name, 'graph.caption'), 'w') as graph_caption: graph_caption.write('Parameters:\\newline\n') for (name, value) in global_params_dict.items(): graph_caption.write('{}: {}\\newline\n'.format( latex.escape(name.title()), latex.escape(value))) for (name, value) in zip(self.results.parameter_names, params): graph_caption.write('{}: {}\\newline\n'.format( latex.escape(str(name)), latex.escape(str(value))))
def create_csc(csc, target_directory, a): """Output simulation csc""" import os.path from xml.sax.saxutils import escape from simulator import Configuration def pcsc(*args, **kwargs): print(*args, file=csc, **kwargs) firmware_path = os.path.abspath(os.path.join(target_directory, "main.exe")) if not os.path.exists(firmware_path): raise RuntimeError(f"The firmware at {firmware_path} is missing") pcsc('<?xml version="1.0" encoding="UTF-8"?>') pcsc('<!-- Generated at: {} -->'.format(datetime.now())) pcsc('<simconf>') pcsc(' <project EXPORT="discard">[APPS_DIR]/mrm</project>') pcsc(' <project EXPORT="discard">[APPS_DIR]/mspsim</project>') pcsc(' <project EXPORT="discard">[APPS_DIR]/avrora</project>') #pcsc(' <project EXPORT="discard">[APPS_DIR]/serial_socket</project>') #pcsc(' <project EXPORT="discard">[APPS_DIR]/collect-view</project>') pcsc(' <project EXPORT="discard">[APPS_DIR]/powertracker</project>') pcsc(' <simulation>') pcsc(' <title>My simulation</title>') pcsc(' <speedlimit>null</speedlimit>') pcsc(' <randomseed>{}</randomseed>'.format(a.args.seed)) pcsc(' <motedelay_us>{}</motedelay_us>'.format( int(a.args.latest_node_start_time * 1000000))) pcsc(' <radiomedium>') pcsc(a.args.radio_model.cooja_csc()) pcsc(' </radiomedium>') pcsc(' <events>') pcsc(' <logoutput>40000</logoutput>') pcsc(' </events>') pcsc(' <motetype>') pcsc(a.args.platform.cooja_csc(firmware_path)) pcsc(' </motetype>') # Output topology file configuration = Configuration.create(a.args.configuration, a.args) for (nid, (x, y)) in sorted(configuration.topology.nodes.items(), key=lambda k: k[0].nid): pcsc(' <mote>') pcsc(' <breakpoints />') pcsc(' <interface_config>') pcsc(' org.contikios.cooja.interfaces.Position') pcsc(' <x>{}</x>'.format(x)) pcsc(' <y>{}</y>'.format(y)) pcsc(' <z>0.0</z>') pcsc(' </interface_config>') pcsc(a.args.platform.node_interface_configs(nid=nid)) pcsc(' <motetype_identifier>{}</motetype_identifier>'.format( a.args.platform.platform())) pcsc(' </mote>') pcsc(' </simulation>') try: seconds_to_run = a.args.safety_period except AttributeError: slowest_source_period = a.args.source_period if isinstance( a.args.source_period, float) else a.args.source_period.slowest() seconds_to_run = configuration.size() * 4.0 * slowest_source_period # Many algorithms have some sort of setup period, so it is important to allow cooja to consider some time for this # Try getting this value from the algorithm itself, otherwise guess 15 seconds seconds_to_run += getattr(a, "cycle_accurate_setup_period", 15.0) # See: https://github.com/contiki-os/contiki/wiki/Using-Cooja-Test-Scripts-to-Automate-Simulations # for documentation on this scripting language pcsc(' <plugin>') pcsc(' org.contikios.cooja.plugins.ScriptRunner') pcsc(' <plugin_config>') pcsc(' <script>') pcsc( escape(f""" /* Make test automatically timeout after the safety period (milliseconds) */ TIMEOUT({int(seconds_to_run * 1000)}, log.testOK()); /* milliseconds. print last msg at timeout */ // Detect Leds changing on the motes // Doing this in Cooja is cheaper than doing it on the motes function LedObserver(node) {{ this.node = node; this.green = null; this.yellow = null; this.red = null; this.update = function(o, arg) {{ var mmled = o; var g = mmled.isGreenOn() ? 1 : 0; // Led1 var y = mmled.isRedOn() ? 1 : 0; // Led0 var r = mmled.isYellowOn() ? 1 : 0; // Led2 if (this.green != g || this.red != r || this.yellow != y) {{ this.green = g; this.red = r; this.yellow = y; java.lang.System.err.println(sim.getSimulationTime() + "|LedsCooja:D:" + this.node.getID() + ":None:" + y + "," + g + "," + r); }} }}; }} allMotes = sim.getMotes(); for (var i = 0; i < allMotes.length; i++) {{ ledObserver = new java.util.Observer(new LedObserver(allMotes[i])); allMotes[i].getInterfaces().getLED().addObserver(ledObserver); }} while (true) {{ java.lang.System.err.println(time + "|" + msg); YIELD(); }} log.testOK(); /* Report test success and quit */ """)) pcsc(' </script>') pcsc(' <active>true</active>') pcsc(' </plugin_config>') pcsc(' </plugin>') pcsc('</simconf>')
def _submit_job(self, a, target_directory): name = target_directory[len("testbed-"):-len("-real")] name = name.replace("/", "-") configuration = Configuration.create(a.args.configuration, a.args) duration_min = int(math.ceil(self.duration.total_seconds() / 60)) testbed_name = type(configuration.topology).__name__.lower() platform = self._get_platform(configuration.topology.platform) profile = "wsn430_with_power" if configuration.topology.platform == "wsn430v13": print( "*********************************************************************" ) print( "* WARNING: The CC1101 interrupt line from the radio *" ) print( "* to the MSP430 CPU is not connected on the wsn430v13 *" ) print( "* hardware on the FIT IoT-Lab. This will prevent it from *" ) print( "* sending messages. *" ) print( "* See: https://github.com/iot-lab/iot-lab/wiki/Hardware_Wsn430-node *" ) print( "* This website says that the CC2420 is affected, but I have only *" ) print( "* observed this with wsn430v13 and the wsn430v14 seems to work. *" ) print( "*********************************************************************" ) # Need to get a shorter name experiment_name = name.replace("ReliableFaultModel__-", "").replace("topology-", "") command = [ "iotlab-experiment", "submit", f"--name \"{experiment_name}\"", f"--duration {duration_min}", ] # Send this script to be executed # It will start the serial aggregator automatically and # gather the output from the nodes aggregator_script = "data/testbed/info/fitiotlab/aggregator.sh" # If the site supports executing the aggregator script, then just have it run it. # Otherwise, we need to find another site to run the script on. if configuration.topology.support_script_execution: command.append( f"--site-association \"{testbed_name},script={aggregator_script}\"" ) else: raise RuntimeError( f"The site {testbed_name} does not support script execution") for node in configuration.topology.nodes: executable = os.path.join(target_directory, f"main-{node}.ihex") if not os.path.isfile(executable): raise RuntimeError( f"Could not find '{executable}'. Did you forget to build the binaries with the --generate-per-node-id-binary options?" ) command.append( f"--list {testbed_name},{platform},{node},{executable}") print(" ".join(command)) if self.dry_run: print("Dry run complete!") return print(f"Submitting {name} to {testbed_name}...") subprocess.check_call(" ".join(command), shell=True)