Example #1
0
 def test_getDSHandle_valid(self):
     """
     getDSHandle must return a valid datastore handle. If no such handle
     exists yet it must populate the datastore handles.
     """
     # Request a valid datastore name. This must not raise any errors.
     datastore.dbHandles = {}
     datastore.getDSHandle('ObjInstances')
Example #2
0
 def test_getDSHandle_valid(self):
     """
     getDSHandle must return a valid datastore handle. If no such handle
     exists yet it must populate the datastore handles.
     """
     # Request a valid datastore name. This must not raise any errors.
     datastore.dbHandles = {}
     datastore.getDSHandle('ObjInstances')
Example #3
0
def addCmdBoosterForce(objID: str, force: list, torque: list):
    """
    Orient ``torque`` and ``force`` according to the ``objID`` and then apply
    them to the object.

    The only difference between this command and ``addCmdDirectForce`` is that
    the ``force`` and ``torque`` vector are specified in the object coordinate
    system and Leonard will rotate them to world coordinates before actually
    applying the force.

    Other services, most notably Leonard, will periodically check for new
    announcements and incorporate them into the simulation as necessary.

    :param str objID: the object
    :param list force: apply this central ``force`` to ``objID``.
    :param list torque: apply this ``torque`` to ``objID``.
    :return bool: Success
    """
    # Sanity check.
    if objID == '':
        msg = 'Invalid Object ID'
        logit.warning(msg)
        return RetVal(False, msg, None)
    if not (len(force) == len(torque) == 3):
        return RetVal(False, 'force or torque has invalid length', None)

    # Compile datastore ops.
    db = datastore.getDSHandle('Commands')
    data = {'force': force, 'torque': torque}
    key = 'booster_force:{}'.format(objID)
    ops = {key: {'data': data}}
    db.put(ops)

    return RetVal(True, None, None)
Example #4
0
def addCmdDirectForce(objID: str, force: list, torque: list):
    """
    Apply ``torque`` and central ``force`` to ``objID``.

    Other services, most notably Leonard, will periodically check for new
    announcements and incorporate them into the simulation as necessary.

    :param str objID: the object
    :param list force: apply this central ``force`` to ``objID``.
    :param list torque: apply this ``torque`` to ``objID``.
    :return bool: Success
    """
    # Sanity check.
    if objID == '':
        msg = 'Invalid Object ID'
        logit.warning(msg)
        return RetVal(False, msg, None)
    if not (len(force) == len(torque) == 3):
        return RetVal(False, 'force or torque has invalid length', None)

    # Compile datastore ops.
    db = datastore.getDSHandle('Commands')
    data = {'force': force, 'torque': torque}
    key = 'direct_force:{}'.format(objID)
    ops = {key: {'data': data}}
    db.put(ops)

    return RetVal(True, None, None)
Example #5
0
def addCmdSpawn(objData: (tuple, list)):
    """
    Announce that the elements in ``objData`` were created.

    The ``objData`` variables comprises a list of (objID, body) tuples.

    Returns **False** if ``objID`` already exists, is already scheduled to
    spawn, or if any of the parameters are invalid.

    Other services, most notably Leonard, will periodically check for new
    announcements and incorporate them into the simulation as necessary.

    :param tuple[(int, _RigidBodyData)]: the new objects created in Azrael.
    :return: success.
    """
    # Sanity check all bodies.
    for objID, body in objData:
        try:
            assert isinstance(objID, str)
            assert isinstance(body, _RigidBodyData)
        except AssertionError:
            msg = '<addCmdQueue> received invalid argument type'
            return RetVal(False, msg, None)

        if objID == '':
            msg = 'Invalid Object ID'
            logit.warning(msg)
            return RetVal(False, msg, None)

    # Compile the datastore ops.
    ops = {}
    for objID, body in objData:
        # Compile the AABBs. Return immediately if an error occurs.
        aabbs = computeAABBs(body.cshapes)
        if not aabbs.ok:
            return RetVal(False, 'Could not compile all AABBs', None)

        # Insert this document.
        data = {'rbs': body._asdict(), 'AABBs': aabbs.data}
        key = 'spawn:{}'.format(objID)
        ops[key] = {'data': data}

    # Store the spawn commands.
    db = datastore.getDSHandle('Commands')
    ret = db.put(ops)
    if not ret.ok:
        return ret

    # Notify the user if not all spawn commands could be written. This should
    # not happen because all object IDs must be unique. If this error occurs
    # then something is wrong with the atomic object count.
    if False in ret.data.values():
        msg = ('At least one spawn command for the same objID already '
               'exists --> serious bug')
        logit.error(msg)
        return RetVal(False, msg, None)
    else:
        # All objIDs were unique --> success.
        return RetVal(True, None, None)
Example #6
0
    def __init__(self):
        # Create a Class-specific logger.
        name = '.'.join([__name__, self.__class__.__name__])
        self.logit = logging.getLogger(name)

        # Create the database handle and local constraint cache.
        self.db = datastore.getDSHandle('Constraints')
        self._cache = {}
Example #7
0
    def test_getDSHandle_invalid(self, m_init):
        """
        getDSHandle must call the 'init' method if it cannot find a particular
        data store name. If the requested datastore name still does not exist
        after the init function was called then it must raise an error.
        """
        # Delete any existing handles and replace it with a dummy.
        datastore.dbHandles = {'foo': None}

        # Request the dummy name. This must not trigger the 'init' method
        # because the name 'foo' exists in the list.
        assert m_init.call_count == 0
        datastore.getDSHandle('foo')
        assert m_init.call_count == 0

        # Attempt to fetch a non-existing handle. This must raise a KeyError.
        datastore.dbHandles = {}
        m_init.mock_reset()
        assert m_init.call_count == 0
        with pytest.raises(KeyError):
            datastore.getDSHandle('foo')
        assert m_init.call_count == 1
Example #8
0
    def test_getDSHandle_invalid(self, m_init):
        """
        getDSHandle must call the 'init' method if it cannot find a particular
        data store name. If the requested datastore name still does not exist
        after the init function was called then it must raise an error.
        """
        # Delete any existing handles and replace it with a dummy.
        datastore.dbHandles = {'foo': None}

        # Request the dummy name. This must not trigger the 'init' method
        # because the name 'foo' exists in the list.
        assert m_init.call_count == 0
        datastore.getDSHandle('foo')
        assert m_init.call_count == 0

        # Attempt to fetch a non-existing handle. This must raise a KeyError.
        datastore.dbHandles = {}
        m_init.mock_reset()
        assert m_init.call_count == 0
        with pytest.raises(KeyError):
            datastore.getDSHandle('foo')
        assert m_init.call_count == 1
Example #9
0
def addCmdModifyBodyState(objID: str, body: dict):
    """
    Queue request to override the Body State of ``objID`` with ``body``.

    Other services, most notably Leonard, will periodically check for new
    announcements and incorporate them into the simulation as necessary.

    :param int objID: object to update.
    :param dict body: new object attributes.
    :return bool: Success
    """
    # Sanity check.
    if objID == '':
        msg = 'Invalid Object ID'
        logit.warning(msg)
        return RetVal(False, msg, None)

    # Make sure that ``body`` is really valid by constructing a new
    # DefaultRigidBody from it.
    body_sane = aztypes.DefaultRigidBody(**body)
    if body_sane is None:
        return RetVal(False, 'Invalid override data', None)

    # Recompute the AABBs if new collision shapes were provided.
    aabbs = None
    if 'cshapes' in body:
        if body_sane.cshapes is not None:
            ret = computeAABBs(body_sane.cshapes)
            if ret.ok:
                aabbs = ret.data

    # Build the original 'body' but from the sanitised version - just to be
    # sure.
    body = {k: v for (k, v) in body_sane._asdict().items() if k in body}
    del body_sane

    # Add the new body state and AABBs to the 'command' database from where
    # clients can read it at their leisure. Note that this will overwrite
    # already pending update commands for the same object - tough luck.
    db = datastore.getDSHandle('Commands')

    data = {'rbs': body, 'AABBs': aabbs}
    key = 'modify:{}'.format(objID)
    ops = {key: {'data': data}}
    db.put(ops)

    # This function was successful if exactly one document was updated.
    return RetVal(True, None, None)
Example #10
0
def addCmdRemoveObject(objID: str):
    """
    Remove ``objID`` from the physics simulation.

    Other services, most notably Leonard, will periodically check for new
    announcements and incorporate them into the simulation as necessary.

    .. note:: This function always succeeds.

    :param str objID: ID of object to delete.
    :return: Success.
    """
    db = datastore.getDSHandle('Commands')
    key = 'remove:{}'.format(objID)
    ops = {key: {'data': {}}}
    db.put(ops)

    return RetVal(True, None, None)
Example #11
0
def dequeueCommands():
    """
    Return and de-queue all commands currently in the command queue.

    :return QueuedCommands: a tuple with lists for each command.
    """
    # Convenience.
    db = datastore.getDSHandle('Commands')

    # Fetch all pending commands.
    ret = db.getAll()
    if not ret.ok:
        return ret
    docs = ret.data

    # Delete all the commands we have just fetched.
    db.remove(list(docs.keys()))

    # Decompose the command:objID key into its constituents and add them to the
    # document. This will allow us to compile all spawn/remove/etc commands
    # easily below.
    for key in docs:
        cmd, objID = key.split(':')
        docs[key]['cmd'] = cmd
        docs[key]['objID'] = objID
        del cmd, objID
    docs = docs.values()

    # Split the commands into categories.
    spawn = [_ for _ in docs if _['cmd'] == 'spawn']
    remove = [_ for _ in docs if _['cmd'] == 'remove']
    modify = [_ for _ in docs if _['cmd'] == 'modify']
    direct_force = [_ for _ in docs if _['cmd'] == 'direct_force']
    booster_force = [_ for _ in docs if _['cmd'] == 'booster_force']

    # Compile the output dictionary.
    out = {
        'spawn': spawn,
        'remove': remove,
        'modify': modify,
        'direct_force': direct_force,
        'booster_force': booster_force
    }
    return RetVal(True, None, out)