def test_two_branch_cascading_schedule_installation_4(motes): # even tree with random pick params = { 'withJoin': False, 'topology': 'twoBranch', 'cascadingScheduling': True, 'schedulingMode': 'random-pick' } settings = SimSettings.SimSettings(**params) engine = SimEngine.SimEngine() motes1 = engine.motes engine.destroy() settings.destroy() settings = SimSettings.SimSettings(**params) engine = SimEngine.SimEngine() motes2 = engine.motes engine.destroy() settings.destroy() assert len(motes1) == len(motes2) prev_ret = True for i, v in enumerate(motes1): assert len(motes1[i].schedule) == len(motes2[i].schedule) for j, cell in enumerate(motes1[i].schedule): if j not in motes1[i].schedule: continue try: cell1 = motes1[i].schedule[j] cell2 = motes2[i].schedule[j] except KeyError: ret = False break if type(cell1['neighbor']) is list: ret = (cell1['ch'] == cell2['ch'] and cell1['dir'] == Mote.DIR_TXRX_SHARED and cell1['dir'] == cell2['dir'] and (sorted(map(lambda x: x.id, cell1['neighbor'])) == sorted(map(lambda x: x.id, cell2['neighbor'])))) else: ret = (cell1['ch'] == cell2['ch'] and cell1['dir'] == cell2['dir'] and cell1['neighbor'].id == cell2['neighbor'].id) ret = prev_ret and ret if ret is False: break # all schedules must not be the same assert ret is False
def runSimsSequentially(params): (cpuID, numRuns, options, verbose) = params # record simulation start time simStartTime = time.time() # compute all the simulation parameter combinations combinationKeys = sorted( [k for (k, v) in options.items() if type(v) == list]) simParams = [] for p in itertools.product(*[options[k] for k in combinationKeys]): simParam = {} for (k, v) in zip(combinationKeys, p): simParam[k] = v for (k, v) in options.items(): if k not in simParam: simParam[k] = v simParams += [simParam] # run a simulation for each set of simParams for (simParamNum, simParam) in enumerate(simParams): # record run start time runStartTime = time.time() # run the simulation runs for runNum in xrange(numRuns): # print output = 'parameters {0}/{1}, run {2}/{3}'.format( simParamNum + 1, len(simParams), runNum + 1, numRuns) printOrLog(cpuID, output, verbose) # create singletons settings = SimSettings.SimSettings(cpuID=cpuID, runNum=runNum, **simParam) settings.setStartTime(runStartTime) settings.setCombinationKeys(combinationKeys) simengine = SimEngine.SimEngine(cpuID=cpuID, runNum=runNum) simstats = SimStats.SimStats(cpuID=cpuID, runNum=runNum, verbose=verbose) # start simulation run simengine.start() # wait for simulation run to end simengine.join() # destroy singletons simstats.destroy() simengine.destroy() settings.destroy() # print output = 'simulation ended after {0:.0f}s.'.format(time.time() - simStartTime) printOrLog(cpuID, output, verbose)
def create_motes(num_motes, **kwargs): # Mote needs SimEngine has already instantiated when its constructor is # called params = {'numMotes': num_motes, 'topology': 'linear'} params['linearTopologyStaticScheduling'] = True # there are prerequisite parameters for Mote.Mote() params['pkPeriod'] = 0 params['minRssi'] = 0 params['withJoin'] = False params['slotframeLength'] = 101 params['slotDuration'] = 0.010 params['bayesianBroadcast'] = False # disable interference model params['noInterference'] = True # override by kwargs is any specified if kwargs: params.update(kwargs) settings = SimSettings.SimSettings(**params) engine = SimEngine.SimEngine() def fin(): engine.destroy() settings.destroy() request.addfinalizer(fin) return engine.motes
def create_sim_engine( diff_config = {}, force_initial_routing_and_scheduling_state = False, run_id = None ): engine = None sim_log = None sim_settings = None # add a finalizer def fin(): if engine: engine.connectivity.destroy() engine.destroy() if sim_log: sim_log.destroy() if sim_settings: sim_settings.destroy() request.addfinalizer(fin) # get default configuration sim_config = SimConfig.SimConfig(u.CONFIG_FILE_PATH) config = sim_config.settings['regular'] assert 'exec_numMotes' not in config config['exec_numMotes'] = sim_config.settings['combination']['exec_numMotes'][0] # update default configuration with parameters for (k,v) in list(diff_config.items()): assert k in config config.update(**diff_config) # create sim settings sim_settings = SimSettings.SimSettings(**config) sim_settings.setLogDirectory( '{0}-{1:03d}'.format( time.strftime('%Y%m%d-%H%M%S'), int(round(time.time() * 1000))%1000 ) ) sim_settings.setCombinationKeys([]) # create sim log sim_log = SimEngine.SimLog.SimLog() sim_log.set_log_filters('all') # do not log # create sim engine engine = SimEngine.SimEngine(run_id=run_id) # force initial routing and schedule, if appropriate if force_initial_routing_and_scheduling_state: set_initial_routing_and_scheduling_state(engine) return engine
def runSims(options): # compute all the simulation parameter combinations combinationKeys = sorted( [k for (k, v) in options.items() if type(v) == list]) print options.items() simParams = [] for p in itertools.product(*[options[k] for k in combinationKeys]): simParam = {} for (k, v) in zip(combinationKeys, p): simParam[k] = v for (k, v) in options.items(): if k not in simParam: simParam[k] = v simParams += [simParam] # run a simulation for each set of simParams for (simParamNum, simParam) in enumerate(simParams): # print if simParam['processID'] == None: print('parameters {0}/{1}'.format(simParamNum + 1, len(simParams))) else: print('parameters {0}/{1}, processID {2}'.format( simParamNum + 1, len(simParams), simParam['processID'])) # record run start time runStartTime = time.time() # run the simulation runs for runNum in xrange(simParam['numRuns']): # print if simParam['processID'] == None: print(' run {0}/{1}'.format(runNum + 1, simParam['numRuns'])) # create singletons settings = SimSettings.SimSettings(**simParam) settings.setStartTime(runStartTime) settings.setCombinationKeys(combinationKeys) simengine = SimEngine.SimEngine(runNum) simstats = SimStats.SimStats(runNum, simParam['numRuns']) #passing the number of runs to enable the display of them while the code is runnig # start simulation run simengine.start() # wait for simulation run to end simengine.join() # destroy singletons simstats.destroy() simengine.destroy() settings.destroy()
def test_two_branch_cascading_schedule_installation(motes): # even tree *without* random pick params = { 'withJoin': False, 'topology': 'twoBranch', 'cascadingScheduling': True } settings = SimSettings.SimSettings(**params) engine = SimEngine.SimEngine() motes1 = engine.motes engine.destroy() settings.destroy() settings = SimSettings.SimSettings(**params) engine = SimEngine.SimEngine() motes2 = engine.motes engine.destroy() settings.destroy() assert len(motes1) == len(motes2) for i, v in enumerate(motes1): assert len(motes1[i].schedule) == len(motes2[i].schedule) for j, cell in enumerate(motes1[i].schedule): if j not in motes1[i].schedule: continue cell1 = motes1[i].schedule[j] cell2 = motes2[i].schedule[j] if type(cell1['neighbor']) is list: ret = (cell1['ch'] == cell2['ch'] and cell1['dir'] == Mote.DIR_TXRX_SHARED and cell1['dir'] == cell2['dir'] and (sorted(map(lambda x: x.id, cell1['neighbor'])) == sorted(map(lambda x: x.id, cell2['neighbor'])))) else: ret = (cell1['ch'] == cell2['ch'] and cell1['dir'] == cell2['dir'] and cell1['neighbor'].id == cell2['neighbor'].id) assert ret is True
def runSimCombinations(params): """ Runs simulations for all combinations of simulation settings. This function may run independently on different CPUs. """ cpuID = params['cpuID'] pid = params['pid'] numRuns = params['numRuns'] first_run = params['first_run'] verbose = params['verbose'] config_data = params['config_data'] simconfig = SimConfig.SimConfig(configdata=config_data) # record simulation start time simStartTime = time.time() # compute all the simulation parameter combinations combinationKeys = simconfig.settings.combination.keys() simParams = [] for p in itertools.product( *[simconfig.settings.combination[k] for k in combinationKeys]): simParam = {} for (k, v) in zip(combinationKeys, p): simParam[k] = v for (k, v) in simconfig.settings.regular.items(): if k not in simParam: simParam[k] = v simParams += [simParam] # run a simulation for each set of simParams for (simParamNum, simParam) in enumerate(simParams): # run the simulation runs for run_id in range(first_run, first_run + numRuns): # printOrLog output = 'parameters {0}/{1}, run {2}/{3}'.format( simParamNum + 1, len(simParams), run_id + 1 - first_run, numRuns) printOrLog(cpuID, pid, output, verbose) # create singletons settings = SimSettings.SimSettings(cpuID=cpuID, run_id=run_id, **simParam) settings.setLogDirectory(simconfig.get_log_directory_name()) settings.setCombinationKeys(combinationKeys) simlog = SimLog.SimLog() simlog.set_log_filters(simconfig.logging) parentlog = ParentLogs.ParentLogs() parentlog.set_log_filters(simconfig.logging) simengine = SimEngine.SimEngine(run_id=run_id, verbose=verbose) # start simulation run simengine.start() # wait for simulation run to end simengine.join() # destroy singletons simlog.destroy() #parentlog.destroy() simengine.destroy() Connectivity.Connectivity().destroy() settings.destroy() # destroy last, Connectivity needs it # printOrLog output = 'simulation ended after {0:.0f}s ({1} runs).'.format( time.time() - simStartTime, numRuns * len(simParams)) printOrLog(cpuID, pid, output, verbose)
def test_avg_hops(sim_engine, fragmentation, app_pkLength, pkt_loss_mode): sim_engine = sim_engine( diff_config={ 'exec_numSlotframesPerRun': 40, 'exec_numMotes': 10, 'fragmentation': fragmentation, 'app': 'AppPeriodic', 'app_pkPeriod': 0, 'app_pkLength': app_pkLength, 'tsch_probBcast_ebProb': 0, 'roupe_daoPeriod': 0, 'conn_class': 'Linear' }, force_initial_routing_and_scheduling_state=True, ) # in this test, the leaf sends two application packets. when pkt_loss_mode # is 'with_pkt_loss', the second application packet will be lost at the # root. # shorthands root = sim_engine.motes[0] leaf = sim_engine.motes[-1] sim_settings = SimSettings.SimSettings() # make the app send an application packet leaf.app._send_a_single_packet() # wait for some time u.run_until_asn(sim_engine, 2020) # make sure SimEngine datalock is disengaged with pytest.raises(RuntimeError): assert sim_engine.dataLock.release() # the root should receive the first application packet logs = u.read_log_file([SimLog.LOG_APP_RX['type']]) assert len(logs) == 1 assert logs[0]['_mote_id'] == root.id assert logs[0]['packet']['net']['srcIp'] == leaf.get_ipv6_global_addr() assert logs[0]['packet']['net']['dstIp'] == root.get_ipv6_global_addr() assert logs[0]['packet']['type'] == d.PKT_TYPE_DATA # make the root not receive at 6LoWPAN layer anything if pkt_loss_mode is # 'with_pkt_loss' if pkt_loss_mode == 'with_pkt_loss': assert root.dagRoot is True def recvPacket(self, packet): # do nothing; ignore the incoming packet pass root.sixlowpan.recvPacket = types.MethodType(recvPacket, root.sixlowpan) elif pkt_loss_mode == 'without_pkt_loss': # do nothing pass else: raise NotImplemented() # make the app send another application packet leaf.app._send_a_single_packet() # run the simulator until it ends u.run_until_end(sim_engine) # confirm the leaf sent two application packets logs = u.read_log_file([SimLog.LOG_APP_TX['type']]) assert len(logs) == 2 for i in range(2): assert logs[i]['_mote_id'] == leaf.id assert logs[i]['packet']['net']['srcIp'] == leaf.get_ipv6_global_addr() assert logs[i]['packet']['net']['dstIp'] == root.get_ipv6_global_addr() assert logs[i]['packet']['type'] == d.PKT_TYPE_DATA # run compute_kpis.py against the log file output = run_compute_kpis_py() # remove blank lines output = [line for line in output if not re.match(r'^\s*$', line)] # confirm if compute_kpis.py referred the right log file # the first line of output has the log directory name assert (re.search(os.path.basename(sim_settings.getOutputFile()), output[0]) is not None) # convert the body of the output, which is a JSON string, to an object json_string = '\n'.join(output[1:-1]) kpis = json.loads(json_string) # the avg_hops should be the same number as leaf.id since we use a linear # topology here. assert kpis['null'][str(leaf.id)]['avg_hops'] == leaf.id
def settings(self): return SimSettings.SimSettings(failIfNotInit=True)
def test_random_seed(sim_engine, fixture_random_seed): diff_config = { 'exec_randomSeed': fixture_random_seed, 'exec_numSlotframesPerRun': 100 } log_file_hashes = [] # compare logs out of 10 runs results = [] for i in range(10): sha2 = hashlib.sha256() if fixture_random_seed == 'range': # ues i for 'exec_randomSeed': diff_config['exec_randomSeed'] = i engine = sim_engine(diff_config=diff_config) log = SimLog.SimLog() settings = SimSettings.SimSettings() # run the simulator u.run_until_end(engine) # save the log file name and a random seed for later use log_file_name = settings.getOutputFile() logs = u.read_log_file( filter=[SimLog.LOG_SIMULATOR_RANDOM_SEED['type']]) assert len(logs) == 1 random_seed = logs[0]['value'] # destroy singletons for the next run engine.connectivity.destroy() engine.destroy() log.destroy() settings.destroy() # this is for test purpose only; reset SimConfig._startTime SimConfig.SimConfig._startTime = None # compute the file hash with open(log_file_name, 'r') as f: # skip the very first line in the log file, which has # 'logDirectory' etnry and it should have a unique value even if a # certain integer is specified for 'exec_randSeed' f.readline() sha2.update(f.read().encode('utf-8')) results.append({ 'log_file_name': log_file_name, 'sha256': sha2.hexdigest(), 'random_seed': random_seed }) # collect hash values and print debug messages seed_list = [] hash_list = [] for entry in results: seed_list.append(entry['random_seed']) hash_list.append(entry['sha256']) # print hash values, which will be printed out when this test fails print('{0} {1}'.format(entry['sha256'], entry['log_file_name'])) # compare hash values and random seeds if fixture_random_seed in ['random', 'range', 'context']: # different seed values should have been used assert (sum([i == j for i, j in zip(seed_list[:-1], seed_list[1:])]) == 0) # hash values shouldn't be the same assert (sum([i == j for i, j in zip(hash_list[:-1], hash_list[1:])]) == 0) else: # the random seed values are found in the log files should be identical assert (sum([i != j for i, j in zip(seed_list[:-1], seed_list[1:])]) == 0) # hash values should be identical as well assert (sum([i != j for i, j in zip(hash_list[:-1], hash_list[1:])]) == 0)
def start(settings, log_notification_filter='all', stderr_redirect=True): global _sim_engine global _elapsed_minutes sim_settings = None sim_log = None ret_val = {} if _sim_engine is not None: return { 'status': RETURN_STATUS_FAILURE, 'message': 'SimEngine has been started already', 'trace': None } try: sim_settings = SimSettings.SimSettings( cpuID=0, run_id=0, log_root_dir=backend.SIM_DATA_PATH, **settings) start_time = time.time() sim_settings.setLogDirectory('{0}-{1:03d}'.format( time.strftime("%Y%m%d-%H%M%S", time.localtime(start_time)), int(round(start_time * 1000)) % 1000)) sim_settings.setCombinationKeys(DUMMY_COMBINATION_KEYS) sim_log = SimLog.SimLog() sim_log.set_log_filters(SIM_LOG_FILTERS) _overwrite_sim_log_log(log_notification_filter) _save_config_json(sim_settings, saving_settings={ 'combination': {}, 'regular': settings.copy() }) crash_report_path = os.path.join(sim_settings.logRootDirectoryPath, sim_settings.logDirectory, 'crash_report.log') if stderr_redirect is True: _redirect_stderr(redirect_to=open(crash_report_path, 'w')) _sim_engine = SimEngine.SimEngine() _elapsed_minutes = 0 _overwrite_sim_engine_actionEndSlotframe() # start and wait until the simulation ends _sim_engine.start() _sim_engine.join() except Exception as e: ret_val['status'] = RETURN_STATUS_FAILURE ret_val['message'] = str(e) ret_val['trace'] = traceback.format_exc() else: if _sim_engine.getAsn() == (sim_settings.exec_numSlotframesPerRun * sim_settings.tsch_slotframeLength): ret_val['status'] = RETURN_STATUS_SUCCESS # rename .dat file and remove the subdir dat_file_path = sim_settings.getOutputFile() subdir_path = os.path.dirname(dat_file_path) new_file_name = subdir_path + '.dat' os.rename(dat_file_path, new_file_name) os.rmdir(subdir_path) else: # simulation is aborted ret_val['status'] = RETURN_STATUS_ABORTED finally: # housekeeping for crash_report and stderr if stderr_redirect is True: crash_report = _restore_stderr() crash_report.close() if os.stat(crash_report.name).st_size == 0: os.remove(crash_report.name) else: ret_val['crash_report_path'] = crash_report.name # cleanup if _sim_engine is None: if sim_settings is not None: sim_settings.destroy() if sim_log is not None: sim_log.destroy() else: _destroy_sim() return ret_val
def collect_setting_keys_in_use(): sim_engine_dir = os.path.join( os.path.abspath(os.path.dirname(__file__)), '../SimEngine' ) # collect core files core_files = [] for root, dirs, files in os.walk(sim_engine_dir): for file in files: if file in ['SimSettings.py', 'SimConfig.py']: # skip SimSettings.py and SimConfig.py continue if re.match(r'^.+\.py$', file): core_files.append( os.path.join( root, file ) ) # identify SimSettings methods sim_settings_methods = [ name for name, _ in inspect.getmembers( SimSettings.SimSettings(), predicate=inspect.ismethod ) ] # collect settings which are referred in core files settings_variables = set([]) setting_keys = set([]) for file_path in core_files: with open(file_path, 'r') as f: for line in f: # remote all the white spaces and the new line character line = line.rstrip() if re.match(r'^import', line): # skip import line continue elif re.match(r'^\s*#', line): # skip comment line continue elif re.search(r'SimSettings', line): if ( re.search(r'SimSettings\(\)\.__dict__', line) and re.search(r' for ', line) ): # this line looks like a 'for' statement; skip this line continue settings = re.sub(r'^(.+)=.+SimSettings.+$', r'\1', line) settings = settings.replace(' ', '') settings_variables.add(settings) elif ( (len(settings_variables) > 0) and re.search('|'.join(settings_variables), line) ): # identify a setting key in in this line pattern = re.compile( '(' + '|'.join(settings_variables) + ')' + '\.(\w+)' ) m = re.search(pattern, line) if m is not None: key = m.group(2) if key in sim_settings_methods: # it's class/instance method; skip it continue elif key == '__dict__': # this is not a key; skip it continue elif key == 'run_id': # run_id is not a setting key; ignore this continue else: # add the key referred from a core file setting_keys.add(m.group(2)) # add exec_minutesPerRun which is found only in SimSettings, which # is not processed in the loop above setting_keys.add('exec_minutesPerRun') return setting_keys
def test_config(sim_engine): sim_engine = sim_engine() # identify the root directory of the simulator root_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) # identify SimEngine directory sim_engine_dir = os.path.join(root_dir, 'SimEngine') # collect file paths to the core files core_files = [] for root, dirs, files in os.walk(sim_engine_dir): for file in files: if file in ['SimSettings.py', 'SimConfig.py']: # skip SimSettings.py and SimConfig.py continue if re.match(r'^.+\.py$', file): core_files.append(os.path.join(root, file)) # collect setting keys in the default config.json; each key is converted # from a unicode string ('u' prefixed string) to a normal string keys_found_in_config_file = [ '{0}'.format(key) for key in sim_engine.settings.__dict__.keys() ] # remove elements which are not configuration keys keys_found_in_config_file = [ key for key in keys_found_in_config_file if (key not in ['cpuID', 'run_id', 'combinationKeys', 'logDirectory']) ] # convert keys_found_in_config_file to a set keys_found_in_config_file = set(keys_found_in_config_file) # collect setting keys referred in the core files keys_found_in_core_files = set() sim_settings_methods = [ name for name, _ in inspect.getmembers(SimSettings.SimSettings(), predicate=inspect.ismethod) ] for file_path in core_files: # put self.settings as the default setting variable settings_variables = set(['self.settings']) with open(file_path, 'r') as f: for line in f: # remote all the white spaces and the new line character line = line.rstrip() if re.match(r'^import', line): # skip import line continue elif re.search(r'SimSettings', line): if (re.search(r'SimSettings\(\)\.__dict__', line) and re.search(r' for ', line)): # this line looks like a 'for' statement; skip this line continue settings_variable = re.sub(r'^(.+)=.+SimSettings.+$', r'\1', line) settings_variable = settings_variable.replace(' ', '') settings_variables.add(settings_variable) elif ((len(settings_variables) > 0) and re.search('|'.join(settings_variables), line)): # identify a setting key in in this line pattern = re.compile('(' + '|'.join(settings_variables) + ')' + '\.(\w+)') m = re.search(pattern, line) key = m.group(2) if key in sim_settings_methods: # it's class/instance method; skip it continue elif key == '__dict__': # this is not a key; skip it continue else: # add the key referred from a core file keys_found_in_core_files.add(m.group(2)) # ready to test; two sets should be identical assert keys_found_in_config_file == keys_found_in_core_files