Exemple #1
0
def startOfNextDay(t):
    """Given a time, returns time of next midnight."""
    s = ISO8601.epoch_seconds_to_ISO8601(t)  # e.g. "2007-10-23T23:32:10Z
    dt = ISO8601.epoch_seconds_to_datetime(t)
    dt += datetime.timedelta(days=1)
    s = dt.strftime("%Y-%m-%dT00:00:00")  # We've assumed timezone!
    return ISO8601.to_epoch_seconds(s)
Exemple #2
0
def in_simulated_time(self, secs=None):
    if g_get_sim_time:
        t = g_get_sim_time()
    else:
        t = 0
    return ISO8601.epoch_seconds_to_datetime(t).timetuple(
    )  # Logging might be emitted within sections where simLock is acquired, so we accept a small chance of duff time values in log messages, in order to allow diagnostics without deadlock
Exemple #3
0
def nextUsageTime(t, daySpec, hourSpec):
    """Given a daySpec e.g. ["Mon","Tue"] and an hourspec e.g. '08:00-10:00'
       work out a next time t which falls within that spec (picking randomly within the hour range)
       if given a time already within spec, move to the NEXT such time)"""
    (startHour, endHour) = toHours(hourSpec)
    (h, d) = (hourInDay(t), dayOfWeek(t))
    if (d in daySpec) and (h < endHour):  # If already in spec, move beyond
        t += 60 * 60 * endHour - h

    while True:  # Move to a valid day of the week
        (h, d) = (hourInDay(t), dayOfWeek(t))
        if (d in daySpec) and (h < endHour):
            break
        t = startOfNextDay(t)
    chosenHour = startHour + (endHour - startHour) * random.random()
    ts = ISO8601.epoch_seconds_to_ISO8601(t)
    ts = ts[:11] + hourToHHMMSS(chosenHour) + ts[19:]
    t = ISO8601.to_epoch_seconds(ts)
    return t
Exemple #4
0
def sun_angle(epochSecs, longitude, latitude):
    dateS = ISO8601.epoch_seconds_to_ISO8601(epochSecs)
    year = int(dateS[0:4])
    month = int(dateS[5:7])
    day = int(dateS[8:10])
    hour = int(dateS[11:13])
    minute = int(dateS[14:16])
    sec = int(dateS[17:19])
    (azimuthD, elevationD) = sunpos_2.sun_position(year, month, day, hour,
                                                   minute, sec, latitude,
                                                   longitude)
    return azimuthD, elevationD
Exemple #5
0
def jitter(t, X, amountS):
    """Return a random number (intended as a time offset, i.e. jitter) within the range +/-amountS
       The jitter is different (but constant) for any given day in t (epoch secs)
       and for any value X (which might be e.g. deviceID)"""
    dt = ISO8601.epoch_seconds_to_datetime(t)
    dayOfYear = int(dt.strftime("%j"))
    year = int(dt.strftime("%Y"))
    uniqueValue = year * 367 + dayOfYear + abs(
        hash(X)
    )  # Note that hash is implementation-dependent so may give different results on different platforms
    rand = utils.hashIt(uniqueValue, 100)
    sign = int(str(uniqueValue)[0]) < 5
    v = (rand / 100.0) * amountS
    if sign:
        v = -v
    return v
Exemple #6
0
    def PLUGIN_query(self, params):
        body = {}
        for p in params:
            if p in ["expect"]:
                pass
            elif p in ["start","end"]:
                body.update({p : ISO8601.to_epoch_seconds(params[p])*1000}),   # TODO: Needs * 1000 for ms?
            else:
                body.update({p : params[p]})
        logging.info("DevicePilot query:\n"+json.dumps(body, sort_keys=True, indent=4, separators=(',', ': ')))
        resp = self.session.post(self.url+"/query", verify=True, headers=set_headers(self.key), data=json.dumps(body))
        assert resp.ok, str(resp.reason) + str(resp.text)
        result = json.loads(resp.text)
        logging.info("Query returned:\n"+json.dumps(result, sort_keys=True, indent=4, separators=(',', ': ')))

        if "expect" in params:
            expected = params["expect"]
            if result == expected:
                logging.info("Query returned expected result - PASS")
            else:
                logging.info("Expected result:\n"+json.dumps(expected, sort_keys=True, indent=4, separators=(',', ': ')))
                assert False, "Query did not return expected result - FAIL"
Exemple #7
0
def second_of_day(epochSecs):
    s = ISO8601.epoch_seconds_to_ISO8601(epochSecs)
    s = "1970-01-01" + s[10:]
    return ISO8601.to_epoch_seconds(s)
Exemple #8
0
    def __init__(self, client, engine, instance_name, context, eventList):
        """<params> is a list of events. Note that our .event_count property is read from outside."""
        def update_callback(device_id, time, properties):
            for c in self.update_callbacks:
                properties.update(c(properties))  # We MERGE the results of the callback with the original message
            write_event_log(properties)
            client.update_device(device_id, time, properties)

        def query_action(params):
            events = evt2csv.read_evt_str("".join(self.logtext))
            query.do_query(params, events)
            
        def change_property_action(params):
            def set_it(d):
                if params.get("is_attribute", False):
                    d.__dict__[params["property_name"]] = params["property_value"]
                    logging.info("Set attribute "+str(params["property_name"])+" on device "+d.get_property("$id")+" to "+str(params["property_value"]))
                else:
                    d.set_property(params["property_name"], params["property_value"], timestamp=ts)
                    logging.info("Set property "+str(params["property_name"])+" on device "+d.get_property("$id")+" to "+str(params["property_value"]))

            d = device_factory.get_devices_by_property( params["identity_property"],
                                                       params["identity_value"])
            logging.info("change property acting on "+str(len(d))+" matching devices")

            if "$ts" in params:
                ts = conftime.richTime(params["$ts"])
            else:
                ts = None

            logging.info("change_property "+str(params))
            for the_d in d:
                set_it(the_d)

        def client_action(args):
            (name, params) = args
            if "PLUGIN_"+name in dir(client):
                logging.info("Plug-in client action "+str(name))
                getattr(client, "PLUGIN_"+name)(params)    # Programmatically call the method
            else:
                logging.error("Ignoring action '"+str(name)+"' as client "+str(client.__class__.__name__)+" does not support it")

        def write_event_log(properties):
            """Write .evt entry"""
            return
            s = pendulum.from_timestamp(properties["$ts"]).to_datetime_string()+" "

            for k in sorted(properties.keys()):
                s += str(k) + ","
                try:
                    s += json.dumps(properties[k])  # Use dumps not str so we preserve type in output
                    # s += properties[k].encode('ascii', 'ignore') # Python 2.x barfs if you try to write unicode into an ascii file
                except:
                    s += "<unicode encoding error>"
                s += ","
            s += "\n"
            self.logfile.write(s)
            # self.logtext.append(s)

            self.event_count += 1

        restart_log = context.get("restart_log",True)
        
        self.client = client
        self.event_count = 0
        self.update_callbacks = []

        mkdir_p(LOG_DIRECTORY)
        self.file_mode = "at"
        if restart_log:
            self.file_mode = "wt"
        #self.logfile = open(LOG_DIRECTORY+instance_name+".evt", self.file_mode)    # This was unbuffered, but Python3 now doesn't allow text files to be unbuffered
        #self.logfile.write("*** New simulation starting at real time "+datetime.now().ctime()+" (local)\n")

        self.logtext = []   # TODO: Probably a Bad Idea to store this in memory. Instead when we want this we should probably close the logfile, read it and then re-open it. We store as an array because appending to a large string gets very slow

        at_time = engine.get_now()
        for event in eventList:
            timespec = event.get("at", "PT0S")
            if timespec == "end":
                end_time = engine.get_end_time()    # This may not be a time
                assert type(end_time) in [int, float], "An event is defined at 'end', but simulation end time is not a definitive time"
                at_time = engine.get_end_time() - 0.001 # Ensure they happen BEFORE the end, as sim end time is non-inclusive
            elif timespec[0] in "-+P":    # Time relative to current sim time
                at_time = at_time + isodate.parse_duration(timespec).total_seconds()
            else:
                at_time = ISO8601.to_epoch_seconds(timespec)

            action = event.get("action", None)
            repeats = event.get("repeats", 1)    # MAY also specify a repeat and interval
            interval = event.get("interval","PT0S")

            while repeats > 0:
                # Built-in actions. TODO: Make these plug-in too?
                if action is None:
                    pass
                elif "create_device" in action:
                    engine.register_event_at(at_time,
                                             device_factory.create_device,
                                             (instance_name, client, engine, update_callback, context, action["create_device"]),
                                             None)
                elif "use_model" in action:
                    engine.register_event_at(at_time,
                            model.use_model,
                            (instance_name, client, engine, update_callback, context, action["use_model"]), None)
                elif "query" in action:
                    engine.register_event_at(at_time,
                                             query_action,
                                             action["query"],
                                             None)
                elif "change_property" in action:
                    engine.register_event_at(at_time,
                                             change_property_action,
                                             action["change_property"],
                                             None)
                elif "install_analyser" in action:
                    logging.info("Installing analyser")
                    self.analyser = analyse.Analyser()
                    self.update_callbacks.append(self.analyser.process)
                else:   # Plug-in actions
                    name = action.keys()[0]
                    if not name.startswith("client."):
                        logging.error("Ignoring unrecognised action "+name)
                    else:
                        engine.register_event_at(at_time,
                                                 client_action,
                                                 (name[7:], action[name]),
                                                 None)
##                    
##                elif "delete_demo_devices" in action:
##                    if "deleteDemoDevices" in dir(client):
##                        client.deleteDemoDevices()
##                else:
##                    logging.warning("Ignoring unknown event action type "+str(event["action"]))

                at_time += isodate.parse_duration(interval).total_seconds()
                repeats -= 1
Exemple #9
0
 def test(datestr):
     print(datestr, ":",
           get_intensity(ISO8601.to_epoch_seconds(datestr), False))
Exemple #10
0
    def __init__(self, client, engine, instance_name, context, eventList):
        """<params> is a list of events. Note that our .event_count property is read from outside."""
        def update_callback(device_id, time, properties):
            for c in self.update_callbacks:
                properties.update(c(properties))  # We MERGE the results of the callback with the original message
            if self.do_write_log:
                write_event_log(properties)

            if explode_factor is None:
                client.update_device(device_id, time, properties)
            else:
                new_props = properties.copy()
                for i in range(explode_factor):
                    eid = str(device_id) + "_" + str(i)    # Each exploded device is identical, except for trailing "-N" ID (and label, if exists)
                    new_props["$id"] = eid
                    if "label" in new_props:
                        new_props["label"] = properties["label"] + "_" + str(i) 
                    client.update_device(eid, time, new_props)

        def query_action(params):
            events = evt2csv.read_evt_str("".join(self.logtext))
            query.do_query(params, events)
            
        def change_property_action(params):
            def set_it(d):
                if params.get("is_attribute", False):
                    d.__dict__[params["property_name"]] = params["property_value"]
                    logging.info("Set attribute "+str(params["property_name"])+" on device "+d.get_property("$id")+" to "+str(params["property_value"]))
                else:
                    d.set_property(params["property_name"], params["property_value"], timestamp=ts)
                    logging.info("Set property "+str(params["property_name"])+" on device "+d.get_property("$id")+" to "+str(params["property_value"]))

            d = device_factory.get_devices_by_property( params["identity_property"], params["identity_value"])
            if "identity_property2" in params:
                d2 = device_factory.get_devices_by_property( params["identity_property2"], params["identity_value2"])
                d = list(set(d) & set(d2))
            logging.info("change property acting on "+str(len(d))+" matching devices")

            if "$ts" in params:
                ts = conftime.richTime(params["$ts"])
            else:
                ts = None

            logging.info("change_property "+str(params))
            for the_d in d:
                set_it(the_d)

        def dump_periodic_metadata(params):
            interval = isodate.parse_duration(params["interval"]).total_seconds()
            metadata_list = set(params["metadata"])
            metadata = []
            for d in device_factory.get_devices():
                p = {}
                for k,v in d.get_properties().items():
                    if k in metadata_list:
                        p[k] = v
                if len(p) > 0: 
                    # logging.info("For device " + d.get_property("$id") + " setting properties "+str(p))
                    # d.set_properties(p)
                    p["$id"] = d.get_property("$id")
                    metadata.append(p)
            fname_stem = METADATA_DIRECTORY + instance_name
            open(fname_stem + ".tmp","wt").write(json.dumps(metadata))
            os.rename(fname_stem + ".tmp", fname_stem + ".json")    # So change is atomic (don't want ^C to leave partially-written file)
            engine.register_event_at(engine.get_now() + interval, dump_periodic_metadata, params, None)

        def client_action(args):
            (name, params) = args
            if "PLUGIN_"+name in dir(client):
                logging.info("Plug-in client action "+str(name))
                getattr(client, "PLUGIN_"+name)(params)    # Programmatically call the method
            else:
                logging.error("Ignoring action '"+str(name)+"' as client "+str(client.__class__.__name__)+" does not support it")

        @functools.lru_cache(maxsize=128)
        def dt_string(t):
            return pendulum.from_timestamp(t).to_datetime_string() + " "    # For some reason, this is very slow

        def write_event_log(properties):
            """Write .evt entry"""
            s = dt_string(properties["$ts"])

            for k in sorted(properties.keys()):
                s += str(k) + ","
                try:
                    s += json.dumps(properties[k])  # Use dumps not str so we preserve type in output
                    # s += properties[k].encode('ascii', 'ignore') # Python 2.x barfs if you try to write unicode into an ascii file
                except:
                    logging.error("Encoding error in events.py::write_event_log")
                    s += "<unicode encoding error>"
                s += ","
            s += "\n"
            self.logfile.write(s)

            self.event_count += 1

        restart_log = context.get("restart_log", True)
        self.do_write_log = context.get("write_log", True)
        shard_size = context.get("shard_size", None)
        shard_start = context.get("shard_start", 0) # If not specified, we are shard 0, so get to create the other shards
        explode_factor = context.get("explode_factor", None)
        if explode_factor is not None:
            logging.info("Running with explode_factor="+str(explode_factor))

        self.event_count = 0
        self.update_callbacks = []
        device_count = 0   # Used to shard device creation

        if self.do_write_log:
            mkdir_p(LOG_DIRECTORY)
            self.file_mode = "at"
            if restart_log:
                self.file_mode = "wt"
            self.logfile = open(LOG_DIRECTORY+instance_name+".evt", self.file_mode)    # This was unbuffered, but Python3 now doesn't allow text files to be unbuffered
            self.logfile.write("*** New simulation starting at real time "+datetime.now().ctime()+" (local)\n")
        else:
            self.logfile = None
            logging.info("Not writing an event log")

        self.logtext = []   # TODO: Probably a Bad Idea to store this in memory. Instead when we want this we should probably close the logfile, read it and then re-open it. We store as an array because appending to a large string gets very slow

        at_time = engine.get_now()
        for event in eventList:
            timespec = event.get("at", "PT0S")
            if timespec.startswith("now"):
                dt = datetime.fromtimestamp(time.time()) # Take time in whole seconds (so if we ever want to repeat the run, we can start it at the exact same time)
                dt.microsecond = 0
                realtime = dt.timestamp()
                delta = isodate.parse_duration(timespec[3:]).total_seconds()
                at_time = realtime + delta
            elif timespec == "end":
                end_time = engine.get_end_time()    # This may not be a time
                assert type(end_time) in [int, float], "An event is defined at 'end', but simulation end time is not a definitive time"
                at_time = engine.get_end_time() - 0.001 # Ensure they happen BEFORE the end, as sim end time is non-inclusive
            elif timespec[0] in "-+P":    # Time relative to current 'at' time
                at_time = at_time + isodate.parse_duration(timespec).total_seconds()
            else:
                at_time = ISO8601.to_epoch_seconds(timespec)

            action = event.get("action", None)
            repeats = event.get("repeats", 1)    # MAY also specify a repeat and interval
            interval = event.get("interval","PT0S")
            time_advance = event.get("time_advance", True)

            insert_time = at_time
            while repeats > 0:
                # Built-in actions. TODO: Make these plug-in too?
                if action is None:
                    pass
                elif "create_device" in action:
                    do_create = True                 # Is this device in our shard?
                    if shard_size is not None:
                        if device_count < shard_start:
                            do_create = False
                        if device_count >= shard_start + shard_size:
                            do_create = False

                        if shard_start == 0:    # We're the first shard
                            if device_count > 0:
                                if (device_count % shard_size) == 0:
                                    logging.info("CREATE NEW SHARD for devices starting at "+str(device_count))
                                    args = sys.argv.copy()
                                    args = ["python3"] + args + ["{ shard_start : " + str(device_count) + " } "]
                                    logging.info(str(args))

                    if do_create:
                        engine.register_event_at(insert_time, device_factory.create_device,
                                             (instance_name, client, engine, update_callback, context, action["create_device"]),
                                             None)

                    device_count += 1
                elif "use_model" in action:
                    engine.register_event_at(insert_time,
                            model.use_model,
                            (instance_name, client, engine, update_callback, context, action["use_model"]), None)
                elif "query" in action:
                    engine.register_event_at(insert_time,
                                             query_action,
                                             action["query"],
                                             None)
                elif "change_property" in action:
                    engine.register_event_at(insert_time,
                                             change_property_action,
                                             action["change_property"],
                                             None)
                elif "install_analyser" in action:
                    logging.info("Installing analyser")
                    self.analyser = analyse.Analyser()
                    self.update_callbacks.append(self.analyser.process)
                elif "periodic_metadata" in action:
                    engine.register_event_at(insert_time, dump_periodic_metadata, action["periodic_metadata"], None)
                else:   # Plug-in actions
                    name = action.keys()[0]
                    if not name.startswith("client."):
                        logging.error("Ignoring unrecognised action "+name)
                    else:
                        engine.register_event_at(insert_time,
                                                 client_action,
                                                 (name[7:], action[name]),
                                                 None)
##                    
##                elif "delete_demo_devices" in action:
##                    if "deleteDemoDevices" in dir(client):
##                        client.deleteDemoDevices()
##                else:
##                    logging.warning("Ignoring unknown event action type "+str(event["action"]))

                insert_time += isodate.parse_duration(interval).total_seconds()
                repeats -= 1
            if time_advance:
                at_time = insert_time
Exemple #11
0
def dayOfWeek(t):
    """Returns e.g. 'Mon'"""
    dt = ISO8601.epoch_seconds_to_datetime(t)
    return dt.strftime("%a")
Exemple #12
0
def hourInDay(t):
    """Returns hour in day (as a float, to nearest second, e.g. 07:30 is 7.5)"""
    dt = ISO8601.epoch_seconds_to_datetime(t)
    return int(dt.strftime("%H")) + int(dt.strftime("%M")) / 60.0 + int(
        dt.strftime("%S")) / 3600.0
Exemple #13
0
 def get_now_str(self):
     return str(ISO8601.epoch_seconds_to_ISO8601(self.get_now()))