def __init__(self, instance_name, time, engine, update_callback, context, params): super(Expect, self).__init__(instance_name, time, engine, update_callback, context, params) tf = params["expect"]["timefunction"] self.expected_timefunction = importer.get_class( "timefunction", tf.keys()[0])(engine, self, tf[tf.keys()[0]]) self.expected_event_name = params["expect"]["event_name"] self.expected_instance_name = context["instance_name"] self.expected_ignore_start = isodate.parse_duration( params["expect"].get("ignore_start", "PT0S")).total_seconds() self.expected_required_score_percent = params["expect"].get( "required_score_percent", None) if not Expect.initialised: self.engine.register_event_in(REPORT_PERIOD_S, self.tick_send_report, self, self) Expect.initialised = True self.seen_event_in_this_window = False t = engine.get_now() t_next = self.expected_timefunction.next_change(t) if self.expected_timefunction.state(t): self.engine.register_event_at(t_next, self.tick_window_end, self, self) else: self.engine.register_event_at(t_next, self.tick_window_start, self, self)
def compose_class(class_names): """Create a composite class from a list of class names.""" classes = [] classes.append(Basic) # Class is the root of inheritance for class_name in class_names: if class_name != "basic": # Normally this is not explicitly specified, so is implicit, but even it is explicit we want to ensure that it's the last class added classes.append(importer.get_class('device', class_name)) classes.reverse() # In each device class constructor, the first thing we do is to call super(). This means that (in terms of the order of execution of all the init code AFTER that call to super()), the last shall be first return type("compositeDeviceClass", tuple(classes), {})
def render_smart_properties(self, elem): new_props = {} if "properties" in elem: for n, v in elem["properties"].copy().items(): if type(v) == dict: model = importer.get_class('model', n) model(self.context, v, new_props) del elem["properties"][n] # Delete smart property elem["properties"].update(new_props)
def __init__(self, instance_name, time, engine, update_callback, context, params): """A button which gets pressed according to some time function""" super(Button, self).__init__(instance_name, time, engine, update_callback, context, params) tf = params["button"]["timefunction"] self.button_timefunction = importer.get_class( "timefunction", tf.keys()[0])(engine, self, tf[tf.keys()[0]]) engine.register_event_at(self.button_timefunction.next_change(), self.tick_button, self, self)
def __init__(self, instance_name, time, engine, update_callback, context, params): """Take Comms up and down according to some time function""" tf = params["commswave"]["timefunction"] self.comms_timefunction = importer.get_class( "timefunction", list(tf.keys())[0])(engine, self, tf[list(tf.keys())[0]]) self.comms_tf_threshold = params["commswave"].get("threshold", None) self.messages_sent = 0 self.messages_attempted = 0 super(Commswave, self).__init__(instance_name, time, engine, update_callback, context, params)
def create_var(params): self.my_random = random.Random( ) # We use our own random-number generator, one per variable per device rp = params.get("randomness_property", None) if rp is None: self.my_random.seed( self.get_property("$id")) # Seed each device uniquely else: self.my_random.seed(hash(self.get_property(rp))) var_name = params["name"] if "value" in params: var_value = params["value"] if type(var_value) == list: var_value = self.my_random.choice(var_value) variables[var_name] = var_value elif "timefunction" in params: tf_name = list( params["timefunction"].keys())[0] # We know there's only 1 var_value = importer.get_class("timefunction", tf_name)( engine, self, params["timefunction"][tf_name]) variables[var_name] = var_value.state() next_change = var_value.next_change() engine.register_event_at(next_change, self.tick_variable, (var_name, var_value), self) elif "random_lower" in params: lower = float(params["random_lower"]) upper = float(params["random_upper"]) precision = params.get("precision", 1) var_value = lower + self.my_random.random() * (upper - lower) var_value = int(var_value * precision) / float(precision) variables[var_name] = var_value elif "randstruct" in params: var_value = randstruct.evaluate(params["randstruct"], self.my_random) variables[var_name] = var_value elif "series" in params: series = params["series"] if var_name not in Variable.device_indices: Variable.device_indices[var_name] = 0 idx = Variable.device_indices[var_name] var_value = series[idx % len(series)] variables[var_name] = var_value else: assert False, "variable " + var_name + " must have either value, timefunction, random_lower/upper, randstruct or series"
def __init__(self, engine, device, params): self.engine = engine self.device = device self.mix_operator = params["operator"] self.mix_timefunctions = [] for f in params["timefunctions"]: tf = importer.get_class("timefunction", f.keys()[0])(engine, device, f[f.keys()[0]]) self.mix_timefunctions.append(tf) self.operators = { "add": self.operator_add, "and": self.operator_and, "mul": self.operator_mul } self.initial_state = {"add": 0.0, "and": 1.0, "mul": 1.0}
def main(): global g_get_sim_time global g_instance_name def postWebEvent(webParams): # CAUTION: Called asynchronously from the web server thread if "action" in webParams: if webParams["action"] == "event": if webParams["headers"]["Instancename"] == g_instance_name: engine.register_event_in(0, device_factory.external_event, webParams, None) elif webParams["action"] == "announce": logging.log(webParams["severity"], "[broadcast message] "+webParams["message"]) def event_count_callback(): return events.event_count logging.getLogger().setLevel(logging.INFO) os.makedirs("../synth_logs", exist_ok = True) os.makedirs("../synth_accounts", exist_ok = True) params = get_params() assert g_instance_name is not None, "Instance name has not been defined, but this is required for logfile naming" init_logging(params) logging.info("*** Synth starting at real time "+str(datetime.now())+" ***") logging.info("Parameters:\n"+json.dumps(params, sort_keys=True, indent=4, separators=(',', ': '))) post_to_slack("Started") Tstart = time.time() random.seed(12345) # Ensure reproduceability if not "client" in params: logging.error("No client defined to receive simulation results") return client = importer.get_class('client', params['client']['type'])(g_instance_name, params, params['client']) if not "engine" in params: logging.error("No simulation engine defined") return engine = importer.get_class('engine', params['engine']['type'])(params['engine'], client.enter_interactive, event_count_callback) g_get_sim_time = engine.get_now_no_lock if not "events" in params: logging.warning("No events defined") events = Events(client, engine, g_instance_name, params, params["events"]) zeromq_rx.init(postWebEvent) logging.info("Simulation starts") err_str = "" try: while engine.events_to_come(): engine.next_event() client.tick() device_factory.close() except: err_str = traceback.format_exc() # Report any exception, but continue to clean-up anyway logging.error("Error at real time "+str(datetime.now())+" (local)") logging.error(err_str) logging.info("Simulation ends") logging.info("Ending device logging ("+str(len(device_factory.g_devices))+" devices were emulated)") events.flush() client.close() logging.info("Elapsed real time: "+str(int(time.time()-Tstart))+" seconds") if err_str=="": post_to_slack("Finished OK") exit(0) post_to_slack(err_str) exit(-1)
def main(): global g_get_sim_time global g_instance_name global g_asked_to_pause def incomingAsyncEvent( packet ): # CAUTION: Called asynchronously from the ZeroMQ rx thread logging.info("incoming async event " + str(packet)) if "action" in packet: if packet["action"] == "event": if packet["headers"]["Instancename"] == g_instance_name: logging.info("Matches this instance name, so dispatching") engine.register_event_in(0, device_factory.external_event, packet, None) elif packet["action"] == "announce": logging.log(packet["severity"], "[broadcast message] " + packet["message"]) elif packet["action"] == "command": if packet["headers"]["Instancename"] == g_instance_name: argv = packet["argv"] logging.info("Received async command " + str(argv)) if len(argv) > 0: client.async_command(argv) def event_count_callback(): return events.event_count logging.getLogger().setLevel(logging.INFO) os.makedirs("../synth_logs", exist_ok=True) os.makedirs("../synth_accounts", exist_ok=True) params = get_params() assert g_instance_name is not None, "Instance name has not been defined, but this is required for logfile naming" init_logging(params) logging.info("*** Synth starting at real time " + str(datetime.now()) + " ***") logging.info( "Parameters:\n" + json.dumps(params, sort_keys=True, indent=4, separators=(',', ': '))) post_to_slack("Started") install_signal_catcher() Tstart = time.time() # Human time Tstart_process = time.process_time() # Time CPU usage random.seed(12345) # Ensure reproduceability if not "client" in params: logging.error("No client defined to receive simulation results") return client = importer.get_class('client', params['client']['type'])(g_instance_name, params, params['client']) if not "engine" in params: logging.error("No simulation engine defined") return engine = importer.get_class('engine', params['engine']['type'])( params['engine'], client.enter_interactive, event_count_callback) g_get_sim_time = engine.get_now_no_lock if not "events" in params: logging.warning("No events defined") events = Events(client, engine, g_instance_name, params, params["events"]) zeromq_rx.init(incomingAsyncEvent, emit_logging=True) zeromq_tx.init(emit_logging=True) logging.info("Simulation starts") faulthandler.dump_traceback_later( 600, repeat=True ) # TEMP - every 10 minutes emit a stack trace for every thread - to diagnose hanging issue err_str = "" try: while engine.events_to_come(): engine.next_event() client.tick() if g_asked_to_pause: g_asked_to_pause = False logging.info("Paused") signal.pause( ) # Suspend this process. Receiving any signal will then cause us to resume logging.info("Resuming") device_factory.close() except: err_str = traceback.format_exc( ) # Report any exception, but continue to clean-up anyway logging.error("Error at real time " + str(datetime.now()) + " (local)") logging.error(err_str) logging.info("Simulation ends") logging.info("Ending device logging (" + str(len(device_factory.g_devices)) + " devices were emulated)") events.flush() client.close() logging.info("Elapsed real time: " + str(int(time.time() - Tstart)) + " seconds.") logging.info("CPU time used: " + str(int(time.process_time() - Tstart_process)) + " seconds.") if err_str == "": post_to_slack("Finished OK") exit(0) post_to_slack(err_str) exit(-1)