示例#1
0
def test_BASESETTINGS_getset_defaultsdosomething():
    a = BaseSettings(
        {"test_default1": {
            "type": "string",
            "default": "value1"
        }})
    ov = "value1"
    nv = a.get("test_default1")
    assert nv == ov, "defaults are populating correctly in absence of a set"
示例#2
0
def test_BASESETTINGS_getset_defaultsdontoverride():
    a = BaseSettings(
        {"test_default2": {
            "type": "string",
            "default": "value2"
        }})
    ov = "value1"
    a.set("test_default2", ov)
    nv = a.get("test_default2")
    assert nv == ov, "set is overriding default arguments when supplied"
示例#3
0
class BaseFloor:
    def __init__(self, attributes=None):
        """init() with no parameters or init(dict) can specify a dictionary of attributes"""
        self.settings = BaseSettings({
            "id": {
                "type":
                "string",
                "validation":
                "",
                "default":
                "",
                "comment":
                "A unique string identifying the object. Generally not human friendly.",
            },
            "height": {
                "type": "float",
                "validation": "gt_zero",
                "default": 3.00,
                "comment": "The height of the floor.",
            },
            "elevation": {
                "type":
                "float",
                "validation":
                "",
                "default":
                0.00,
                "comment":
                "The distance of the bottom-most point of the object from the ground.",
            },
            "elevation_top": {
                "type":
                "float",
                "validation":
                "",
                "default":
                0.00,
                "comment":
                "The distance of the top-most point of the object from the ground.",
            },
            "label": {
                "type": "string",
                "validation": "",
                "default": "",
                "comment": "A human friendly descriptor of the object.",
            },
            "carrying": {
                "type":
                "list",
                "validation":
                "",
                "default": [],
                "comment":
                "A list of the people contained within the floor object.",
            },
            "building": {
                "type": "building",
                "validation": "",
                "default": None,
                "comment": "A reference to the parent building object.",
            },
            "is_floor": {
                "type":
                "boolean",
                "validation":
                "",
                "default":
                True,
                "comment":
                "A boolean indicating whether we are a floor object or not.",
            },
        })
        if attributes is not None:
            for key in attributes:
                self.set(key, attributes[key])
        self.set("id", SOLE.new_id("BaseFloor"))

    def __str__(self):
        """allow print() to function in some intelligible way"""
        return "{}".format(self.settings)

    def set(self, name, value):
        """set() will set the given attribute for the object. Will perform basic sanity checks on the attribute itself."""
        self.settings.set(name, value)
        return self

    def get(self, name):
        """get(attr) will return attribute attr for the object or empty string if not"""
        return self.settings.get(name)

    def tick(self):
        """tick() will advance one step for this object and any/all objects contained by it"""
        SOLE.log("[{}] BaseFloor->tick()".format(self.get("id")),
                 SOLE.LOG_INFO)
        for p in self.get("carrying"):
            p.tick()
        return

    def add_to_request_queue(self, floor_id):
        """add_to_request_queue() will pass on the request for floor_id to associated elevator for floor"""
        self.get("building").get("elevators")[0].add_to_request_queue(floor_id)
示例#4
0
class BaseElevator:
    def __init__(self, attributes=None):
        """init() with no parameters or init(dict) can specify a dictionary of attributes"""
        self.settings = BaseSettings(
            {
                "height": {
                    "type": "float",
                    "validation": "gt_zero",
                    "default": 2.44,
                    "comment": "The height of the elevator itself.",
                },
                "elevation": {
                    "type": "float",
                    "validation": "",
                    "default": 0.00,
                    "comment": "The elevation of the top-most point of the elevator.",
                },
                "destination": {
                    "type": "floor",
                    "validation": "",
                    "default": None,
                    "comment": "The current destination floor that we are moving towards. Set to None when not in motion.",
                },
                "velocity": {
                    "type": "float",
                    "validation": "",
                    "default": 0.00,
                    "comment": "Positive velocity means upwards, negative means downward, 0 is at rest",
                },
                "label": {
                    "type": "string",
                    "validation": "",
                    "default": "",
                    "comment": "A friendly identifier for the elevator",
                },
                "maximum_up_speed": {
                    "type": "float",
                    "validation": "gt_zero",
                    "default": 1.00,
                    "comment": "A maximum upward velocity for the elevator, must be >0",
                },
                "maximum_down_speed": {
                    "type": "float",
                    "validation": "lt_zero",
                    "default": -1.00,
                    "comment": "A maximum downward velocity for the elevator, must be <0",
                },
                "carrying": {
                    "type": "list",
                    "validation": "",
                    "default": [],
                    "comment": "A list of Person objects presently within the elevator",
                },
                "building": {
                    "type": "building",
                    "validation": "",
                    "default": None,
                    "comment": "A reference to the current building object the elevator is contained within.",
                },
                "floor_requests": {
                    "type": "list",
                    "validation": "",
                    "default": [],
                    "comment": "A list of floor requests in sequential order.",
                },
                "status": {
                    "type": "string",
                    "validation": "",
                    "default": "idle",
                    "comment": "The current status of the elevator.",
                },
                "status_percent": {
                    "type": "float",
                    "validation": "is_percent",
                    "default": 1.00,
                    "comment": "A float inside [0,1] indicating percentage completion of status.",
                },
                "unloading_time_needed": {
                    "type": "float",
                    "validation": "gt_zero",
                    "default": 5.00,
                    "comment": "Real world seconds that it takes to unload the elevator.",
                },
                "loading_time_needed": {
                    "type": "float",
                    "validation": "gt_zero",
                    "default": 5.00,
                    "comment": "Real world seconds that it takes to load the elevator.",
                },
                "id": {
                    "type": "string",
                    "validation": "",
                    "default": "",
                    "comment": "A unique string identifying the object. Generally not human friendly.",
                },
            }
        )
        if attributes is not None:
            for key in attributes:
                self.set(key, attributes[key])
        self.set("id", SOLE.new_id("BaseElevator"))

    def __str__(self):
        """allow print() to function in some intelligible way"""
        return str(self.__class__) + ": " + str(self.__dict__)

    def set(self, name, value):
        """set() will set the given attribute for the object. Will perform basic sanity checks on the attribute itself."""
        self.settings.set(name, value)
        return self

    def get(self, name):
        """get(attr) will return attribute attr for the object or empty string if not"""
        return self.settings.get(name)

    def change_velocity(self, velocity):
        """change_velocity(velocity) changes elevator velocity. Pass positive float for up, negative for down, and 0 for stop."""
        if velocity == 0:
            self.set("velocity", velocity)
        elif velocity > 0:
            self.set("velocity", self.get("maximum_up_speed"))
        elif velocity < 0:
            self.set("velocity", self.get("maximum_down_speed"))
        else:
            assert (False, "unmatched velocity")

    def move(self):
        """move() moves the elevator by one unit of velocity."""
        self.set(
            "elevation",
            (
                self.get("elevation")
                + (self.get("velocity") / SOLE.tick_ratio_to_real_time)
            ),
        )

    def unload(self):
        """unload() iterates through the elevators carrying attribute and dumps out passengers"""
        SOLE.log("[{}] BaseElevator->unload()".format(self.get("id")), SOLE.LOG_INFO)
        elevation = self.get("elevation")
        b = self.get("building")
        floor_id = b.at_elevation(elevation)
        floor = b.ref_to(floor_id)
        carrying = self.get("carrying")
        if type(carrying) == list:
            for p in carrying:
                if p.get("destination") == floor:
                    SOLE.log(
                        "Unloaded {} from the elevator at the {}".format(
                            p.get("label"), floor.get("label")
                        )
                    )
                    p.unload(self, floor)

    def load(self):
        """load() iterates through the floors carrying attribute and loads up passenges"""
        SOLE.log("[{}] BaseElevator->load()".format(self.get("id")), SOLE.LOG_INFO)
        elevation = self.get("elevation")
        b = self.get("building")
        floor_id = b.at_elevation(elevation)
        floor = b.ref_to(floor_id)
        carrying = floor.get("carrying")
        if type(carrying) == list:
            for p in carrying:
                SOLE.log(
                    "Loaded {} into the elevator from the {}".format(
                        p.get("label"), floor.get("label")
                    )
                )
                p.load(self, floor)

    def add_to_request_queue(self, floor):
        """add_to_request_queue(floor_id) will add floor_id to the list of floors to travel to"""
        SOLE.log(
            "[{}] BaseElevator->add_to_request_queue({})".format(self.get("id"), floor),
            SOLE.LOG_INFO,
        )

        self.get("floor_requests").append(floor)

    def queue(self):
        """queue() will iterate through the queue of floor_requests if there is no current desination_floor"""
        SOLE.log("[{}] BaseElevator->queue()".format(self.get("id")), SOLE.LOG_INFO)
        if self.get("destination") is None:
            if type(self.get("floor_requests")) == list:
                if len(self.get("floor_requests")) > 0:
                    floor_requests = self.get("floor_requests")
                    destination = floor_requests.pop(0)
                    if destination is None:
                        pass
                    else:
                        self.set("destination", destination)
                        SOLE.log(
                            "{} moving to {}".format(
                                self.get("label"), destination.get("label")
                            ),
                            SOLE.LOG_NOTICE,
                        )

    def tick(self):
        """tick() will advance one step for this object and any/all objects contained by it"""
        SOLE.log("[{}] BaseElevator->tick()".format(self.get("id")), SOLE.LOG_INFO)
        SOLE.log(
            "[{}] BaseElevator->tick() elevation={:.2f} status={} status_percent={:.0%}".format(
                self.get("id"),
                self.get("elevation"),
                self.get("status"),
                self.get("status_percent"),
            ),
            SOLE.LOG_DEBUG,
        )

        # iterate through each of the people we are carrying and let them tick()
        for p in self.get("carrying"):
            p.tick()

        valid_statuses = (
            "idle",
            "unloading",
            "loading",
            "moving",
            "doors_opening",
            "doors_open",
            "doors_closing"
        )

        b = self.get("building")
        elevation = self.get("elevation")
        destination = self.get("destination")
        status = self.get("status")
        status_percent = self.get("status_percent")

        # from unloading state, we can either continue unloading or start loading
        if status == "unloading":
            self.unload()
            if status_percent >= 1.00:
                self.set("status", "loading")
                self.set("status_percent", 0)
                return

            self.set("status", "unloading")
            self.set(
                "status_percent",
                min(
                    status_percent
                    + 1
                    / (
                        self.get("unloading_time_needed") * SOLE.tick_ratio_to_real_time
                    ),
                    1,
                ),
            )
            return

        # from loading state, we can either continue loading or start idle
        if status == "loading":
            self.load()
            if status_percent >= 1.00:
                self.set("status", "idle")
                self.set("status_percent", 0)
                return

            self.set("status", "loading")
            self.set(
                "status_percent",
                min(
                    status_percent
                    + 1
                    / (self.get("loading_time_needed") * SOLE.tick_ratio_to_real_time),
                    1,
                ),
            )
            return

        # from idle state, we can either continue idle or start moving
        if status == "idle":
            if destination is None:
                # check if there is any new destinations to move to
                self.queue()
                destination = self.get("destination")

            if destination is None:
                # continue to wait if there is still no destination floor
                self.set(status, "idle")
                self.set(status_percent, 1.00)
                return

            if destination is not None:
                # there is a destination floor so start moving next tick
                self.set("status", "moving")
                self.set("status_percent", 0.00)
                return

        if status == "moving":
            destination_elevation = destination.get("elevation")
            distance = destination_elevation - elevation
            velocity = self.get("velocity")

            SOLE.log(
                "BaseElevator (moving) my_elevation={:.2f} destination={} destination_elevation={:.2f} distance={:.2f} velocity={:.2f}".format(
                    self.get("elevation"),
                    self.get("destination").get("id"),
                    destination_elevation,
                    distance,
                    velocity,
                ),
                SOLE.LOG_DEBUG,
            )

            # if we've arrived then stop and unload next tick
            if distance == 0:
                SOLE.log(
                    "{} has arrived at {}".format(
                        self.get("label"), destination.get("label")
                    ),
                    SOLE.LOG_NOTICE,
                )
                self.set("destination", None)
                self.change_velocity(0)
                self.set("status", "unloading")
                self.set("status_percent", 0)
                return

            if (distance > 0) and (
                distance < (velocity / SOLE.tick_ratio_to_real_time)
            ):
                SOLE.log(
                    "{} has arrived at {}".format(
                        self.get("label"), destination.get("label")
                    ),
                    SOLE.LOG_NOTICE,
                )
                self.set("destination", None)
                self.set("elevation", destination_elevation)
                self.change_velocity(0)
                self.set("status", "unloading")
                self.set("status_percent", 0)
                return

            elif (distance < 0) and (
                distance > (velocity / SOLE.tick_ratio_to_real_time)
            ):
                SOLE.log(
                    "{} has arrived at {}".format(
                        self.get("label"), destination.get("label")
                    ),
                    SOLE.LOG_NOTICE,
                )
                self.set("destination", None)
                self.set("elevation", destination_elevation)
                self.change_velocity(0)
                self.set("status", "unloading")
                self.set("status_percent", 0)
                return

            else:

                self.change_velocity(distance)
                velocity = self.get("velocity")
                self.move()
                self.set("status", "moving")
                self.set(
                    "status_percent", 1.00
                )  # TOFIX: NEED TO KEEP TRACK OF RELATIVE DISTANCE TRAVELLED FOR % TO WORK
                return

        return
示例#5
0
def test_BASESETTINGS_getset_iscopy():
    a = BaseSettings({"test_iscopy": {"type": "string"}})
    ov = "foo"
    a.set("test_iscopy", ov)
    nv = a.get("test_iscopy")
    assert nv is ov, "set/get are returning a copy rather than the original object"
示例#6
0
def test_BASESETTINGS_getset_list():
    a = BaseSettings({"test_list": {"type": "list"}})
    v = [1, 2, 3]
    a.set("test_list", v)
    assert a.get("test_list") == v, "set/get don't match for list type"
示例#7
0
def test_BASESETTINGS_getset_dict():
    a = BaseSettings({"test_dict": {"type": "dict"}})
    v = {"a": "b", "c": "d"}
    a.set("test_dict", v)
    assert a.get("test_dict") == v, "set/get don't match for dict type"
示例#8
0
def test_BASESETTINGS_getset_float_intok():
    a = BaseSettings({"test_float": {"type": "float"}})
    v = 3
    a.set("test_float", v)
    assert a.get("test_float") == v, "set/get doesn't cast int type"
示例#9
0
def test_BASESETTINGS_getset_float():
    a = BaseSettings({"test_float": {"type": "float"}})
    v = 3.14159
    a.set("test_float", v)
    assert a.get("test_float") == v, "set/get don't match for float type"
示例#10
0
def test_BASESETTINGS_getset_string():
    a = BaseSettings({"test_string": {"type": "string"}})
    v = "var"
    a.set("test_string", v)
    assert a.get("test_string") == v, "set/get don't match for string type"
示例#11
0
class BaseBuilding:
    def __init__(self, attributes=None):
        """init() with no parameters or init(dict) can specify a dictionary of attributes"""
        self.settings = BaseSettings({
            "id": {
                "type":
                "string",
                "validation":
                "",
                "default":
                "",
                "comment":
                "A unique string identifying the object. Generally not human friendly.",
            },
            "label": {
                "type": "string",
                "validation": "",
                "default": "",
                "comment": "A human friendly descriptor of the object.",
            },
            "height": {
                "type": "float",
                "validation": "",
                "default": 0.00,
                "comment": "The overall height of the building.",
            },
            "floors": {
                "type": "list",
                "validation": "",
                "default": [],
                "comment": "The floors contained in the building.",
            },
            "elevators": {
                "type": "list",
                "validation": "",
                "default": [],
                "comment": "The elevators contained in the building.",
            },
            "_elevation_of": {
                "type": "dict",
                "validation": "",
                "default": {},
                "comment": "A mapping of objects->elevations.",
            },
            "_at_elevation": {
                "type": "dict",
                "validation": "",
                "default": {},
                "comment": "A mapping of elevations->objects.",
            },
            "_ref_to": {
                "type": "dict",
                "validation": "",
                "default": {},
                "comment": "A mapping of ids->objects.",
            },
        })
        if attributes is not None:
            for key in attributes:
                self.set(key, attributes[key])
        self.set("id", SOLE.new_id("BaseBuilding"))

        running_height = 0
        floors = self.get("floors")
        if type(floors) == list:
            for f in floors:
                floor_id = f.get("id")
                f.set("elevation", running_height)
                running_height += f.get("height")
                f.set("elevation_top", running_height)
                f.set("building", self)
            self.set("height", running_height)

        elevators = self.get("elevators")
        if type(elevators) == list:
            for e in elevators:
                e.set("building", self)

    def __str__(self):
        """allow print() to function in some intelligible way"""
        return "{}".format(self.settings)

    def set(self, name, value):
        """set() will set the given attribute for the object. Will perform basic sanity checks on the attribute itself."""
        self.settings.set(name, value)
        return self

    def get(self, name):
        """get(attr) will return attribute attr for the object or empty string if not"""
        return self.settings.get(name)

    def elevation_of(self, object_id):
        eo = self.get("_elevation_of")
        if object_id is None:
            return 0

        if object_id in eo:
            return eo[object_id]
        else:
            return None

    def at_elevation(self, height):
        ae = self.get("_at_elevation")
        if height in ae:
            return ae[height]
        else:
            return None

    def ref_to(self, object_id):
        rt = self.get("_ref_to")
        if object_id in rt:
            return rt[object_id]
        else:
            return None

    def rebuild_internal_tracking(self):
        """rebuild_internal_tracking() maintains height tables. it is called every tick()"""
        _elevation_of = dict()
        _at_elevation = dict()
        _ref_to = dict()
        running_height = 0

        floors = self.get("floors")
        if type(floors) == list and len(floors) > 0:
            for f in floors:
                floor_id = f.get("id")
                floor_elevation = f.get("elevation")
                _ref_to[floor_id] = f
                _elevation_of[floor_id] = floor_elevation
                _at_elevation[floor_elevation] = floor_id
        #                carrying = f.get("carrying")

        #                if type(carrying) == list and len(carrying) > 0:
        #                    for p in carrying:
        #                        person_id = p.get("id")
        #                        person_elevation = floor_elevation
        #                        p.set("elevation", person_elevation)
        #                        _ref_to[person_id] = p
        #                        _elevation_of[person_id] = person_elevation

        self.set("_elevation_of", _elevation_of)
        self.set("_at_elevation", _at_elevation)

        elevators = self.get("elevators")
        if type(elevators) == list:
            for e in elevators:
                elevator_id = e.get("id")
                elevator_elevation = e.get("elevation")
                _ref_to[elevator_id] = e
                _elevation_of[elevator_id] = elevator_elevation
                #               carrying = e.get("carrying")
                #               if type(carrying) == list and len(carrying) > 0:
                #                   for p in carrying:
                #                       person_id = p.get("id")
                #                       person_elevation = elevator_elevation
                #                       p.set("elevation", person_elevation)
                #                       _ref_to[person_id] = p
                #                       _elevation_of[person_id] = person_elevation

                e.set("building", self)

        self.set("_ref_to", _ref_to)

    def tick(self):
        """tick() will advance one step for this object and any/all objects contained by it"""
        SOLE.log("[{}] BaseBuilding->tick()".format(self.get("id")),
                 SOLE.LOG_INFO)
        self.rebuild_internal_tracking()
        for f in self.get("floors"):
            f.tick()
        for e in self.get("elevators"):
            e.tick()
        # for e in elevator: e.tick()
        # for p in people: p.tick()
        # for f in floors: f.tick()
        return
示例#12
0
class BasePerson:
    def __init__(self, attributes=None):
        """init() with no parameters or init(dict) can specify a dictionary of attributes"""
        self.settings = BaseSettings(
            {
                "id": {
                    "type": "string",
                    "validation": "",
                    "default": "",
                    "comment": "A unique string identifying the object. Generally not human friendly.",
                },
                "height": {
                    "type": "float",
                    "validation": "gt_zero",
                    "default": 1.77,
                    "comment": "The height of the person.",
                },
                "location": {
                    "type": "reference",
                    "validation": "",
                    "default": None,
                    "comment": "A reference to the object the person is contained in, whether floor or elevator.",
                },
                "building": {
                    "type": "building",
                    "validation": "",
                    "default": None,
                    "comment": "A reference to the parent building.",
                },
                "destination": {
                    "type": "reference",
                    "validation": "",
                    "default": None,
                    "comment": "A reference to the floor/elevator that we are destined to.",
                },
                "label": {
                    "type": "string",
                    "validation": "",
                    "default": sole_baseperson_random_name(),
                    "comment": "A human friendly descriptor of the object.",
                },
                "status": {
                    "type": "string",
                    "validation": "in_list",
                    "default": "idle",
                    "valid_list": [
                        "idle",
                        "requesting",
                        "entering",
                        "aboard",
                        "leaving",
                    ],
                    "comment": "The current status of the person.",
                },
                "status_percent": {
                    "type": "float",
                    "validation": "is_percent",
                    "default": 1.00,
                    "comment": "A float inside [0,1] indicating percentage completion of the status.",
                },
            }
        )
        if attributes is not None:
            for key in attributes:
                self.set(key, attributes[key])
        self.set("id", SOLE.new_id("BasePerson"))

    def __str__(self):
        """allow print() to function in some intelligible way"""
        return "{}".format(self.settings)

    def __del__(self):
        """track destruction of object"""
        SOLE.log("[{}] BasePerson->destroyed".format(self.get("id")), SOLE.LOG_INFO)

    def set(self, name, value):
        """set() will set the given attribute for the object. Will perform basic sanity checks on the attribute itself."""
        self.settings.set(name, value)
        return self

    def get(self, name):
        """get(attr) will return attribute attr for the object or empty string if not"""
        return self.settings.get(name)

    def unload(self, elevator, floor):
        """unload() to remove person from elevator."""

        # Remove person from elevator's carrying list
        carrying = elevator.get("carrying")
        carrying.remove(self)

        # Check the destination for the person
        destination_floor = self.get("destination_floor")

        if floor == destination_floor:
            SOLE.log(
                "[{}] BasePerson reached destination={}".format(
                    self.get("id"), destination_floor.get("id")
                ),
                SOLE.LOG_INFO,
            )

            SOLE.log(
                "{} reached destination {}".format(
                    self.get("label"), destination_floor.get("label")
                ),
                SOLE.LOG_NOTICE,
            )

            self.set("location", None)
            del self
        else:
            # Set person's location attribute to the current floor.
            self.set("location", floor)

    def load(self, elevator, floor):
        """load() will add person to an elevator."""

        # Remove person from floors carrying list
        floor.get("carrying").remove(self)

        # Get the destination for the person
        destination_floor = self.get("destination_floor")

        # Set person's location attribute to the elevator
        self.set("location", elevator)
        elevator.add_to_request_queue(destination_floor)

    def tick(self):
        """tick() will advance one step for this object and any/all objects contained by it"""
        SOLE.log("[{}] BasePerson->tick()".format(self.get("id")), SOLE.LOG_INFO)
        carrying = self.get("carrying")
        if carrying is not None:
            self.set("elevation", self.get("carrying").get("elevation"))