123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- # Copyright (c) 2016 Anki, Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License in the file LICENSE.txt or at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
-
- '''
- Animation related classes, functions, events and values.
- '''
-
- # __all__ should order by constants, event classes, other classes, functions.
- __all__ = ['EvtAnimationsLoaded', 'EvtAnimationCompleted',
- 'Animation', 'AnimationTrigger', 'AnimationNames', 'Triggers',
- 'animation_completed_filter']
-
- import collections
-
- from . import logger
-
- from . import action
- from . import exceptions
- from . import event
-
- from ._clad import _clad_to_engine_iface, _clad_to_engine_cozmo
-
-
- class EvtAnimationsLoaded(event.Event):
- '''Triggered when animations names have been received from the engine'''
-
-
- class EvtAnimationCompleted(action.EvtActionCompleted):
- '''Triggered when an animation completes.'''
- animation_name = "The name of the animation or trigger that completed"
-
-
- class Animation(action.Action):
- '''An Animation describes an actively-playing animation on a robot.'''
- def __init__(self, anim_name, loop_count, ignore_body_track=False,
- ignore_head_track=False, ignore_lift_track=False, **kw):
- super().__init__(**kw)
-
- #: The name of the animation that was dispatched
- self.anim_name = anim_name
-
- #: The number of iterations the animation was requested for
- self.loop_count = loop_count
-
- #: bool: True to ignore the body track (i.e. the wheels / treads)
- self.ignore_body_track = ignore_body_track
-
- #: bool: True to ignore the head track
- self.ignore_head_track = ignore_head_track
-
- #: bool: True to ignore the lift track
- self.ignore_lift_track = ignore_lift_track
-
-
- def _repr_values(self):
- all_tracks = {"body":self.ignore_body_track,
- "head":self.ignore_head_track,
- "lift":self.ignore_lift_track}
- ignore_tracks = [k for k, v in all_tracks.items() if v]
-
- return "anim_name=%s loop_count=%s ignore_tracks=%s" % (self.anim_name, self.loop_count, str(ignore_tracks))
-
- def _encode(self):
- return _clad_to_engine_iface.PlayAnimation(animationName=self.anim_name, numLoops=self.loop_count, ignoreBodyTrack=self.ignore_body_track,
- ignoreHeadTrack=self.ignore_head_track, ignoreLiftTrack=self.ignore_lift_track)
-
- def _dispatch_completed_event(self, msg):
- self._completed_event = EvtAnimationCompleted(
- action=self, state=self._state,
- animation_name=self.anim_name)
- self.dispatch_event(self._completed_event)
-
-
- class AnimationTrigger(action.Action):
- '''An AnimationTrigger represents a playing animation trigger.
-
- Asking Cozmo to play an AnimationTrigger causes him to pick one of the
- animations represented by the group.
- '''
- def __init__(self, trigger, loop_count, use_lift_safe, ignore_body_track,
- ignore_head_track, ignore_lift_track, **kw):
- super().__init__(**kw)
-
- #: An attribute of :class:`cozmo.anim.Triggers`: The animation trigger dispatched.
- self.trigger = trigger
-
- #: int: The number of iterations the animation was requested for
- self.loop_count = loop_count
-
- #: bool: True to automatically ignore the lift track if Cozmo is carrying a cube.
- self.use_lift_safe = use_lift_safe
-
- #: bool: True to ignore the body track (i.e. the wheels / treads)
- self.ignore_body_track = ignore_body_track
-
- #: bool: True to ignore the head track
- self.ignore_head_track = ignore_head_track
-
- #: bool: True to ignore the lift track
- self.ignore_lift_track = ignore_lift_track
-
- def _repr_values(self):
- all_tracks = {"body":self.ignore_body_track,
- "head":self.ignore_head_track,
- "lift":self.ignore_lift_track}
- ignore_tracks = [k for k, v in all_tracks.items() if v]
-
- return "trigger=%s loop_count=%s ignore_tracks=%s use_lift_safe=%s" % (
- self.trigger.name, self.loop_count, str(ignore_tracks), self.use_lift_safe)
-
- def _encode(self):
- return _clad_to_engine_iface.PlayAnimationTrigger(
- trigger=self.trigger.id, numLoops=self.loop_count,
- useLiftSafe=self.use_lift_safe, ignoreBodyTrack=self.ignore_body_track,
- ignoreHeadTrack=self.ignore_head_track, ignoreLiftTrack=self.ignore_lift_track)
-
- def _dispatch_completed_event(self, msg):
- self._completed_event = EvtAnimationCompleted(
- action=self, state=self._state,
- animation_name=self.trigger.name)
- self.dispatch_event(self._completed_event)
-
-
- class AnimationNames(event.Dispatcher, set):
- '''Holds the set of animation names (strings) returned from the Engine.
-
- Animation names are dynamically retrieved from the engine when the SDK
- connects to it, unlike :class:`Triggers` which are defined at runtime.
- '''
- def __init__(self, conn, **kw):
- super().__init__(self, **kw)
- self._conn = conn
- self._loaded = False
-
- def __contains__(self, key):
- if not self._loaded:
- raise exceptions.AnimationsNotLoaded("Animations not yet received from engine")
- return super().__contains__(key)
-
- def __hash__(self):
- # We want to compare AnimationName instances rather than the
- # names they contain
- return id(self)
-
- def refresh(self):
- '''Causes the list of animation names to be re-requested from the engine.
-
- Attempting to play an animation while the list is refreshing will result
- in an AnimationsNotLoaded exception being raised.
-
- Generates an EvtAnimationsLoaded event once completed.
- '''
- self._loaded = False
- self.clear()
- self._conn.send_msg(_clad_to_engine_iface.RequestAvailableAnimations())
-
- @property
- def is_loaded(self):
- '''bool: True if the animation names have been received from the engine.'''
- return self._loaded != False
-
- async def wait_for_loaded(self, timeout=None):
- '''Wait for the animation names to be loaded from the engine.
-
- Returns:
- The :class:`EvtAnimationsLoaded` instance once loaded
- Raises:
- :class:`asyncio.TimeoutError`
- '''
- if self._loaded:
- return self._loaded
- return await self.wait_for(EvtAnimationsLoaded, timeout=timeout)
-
- def _recv_msg_animation_available(self, evt, msg):
- name = msg.animName
- self.add(name)
-
- def _recv_msg_end_of_message(self, evt, msg):
- if not self._loaded:
- logger.debug("%d animations loaded", len(self))
- self._loaded = evt
- self.dispatch_event(EvtAnimationsLoaded)
-
-
- # generate names for each CLAD defined trigger
-
- _AnimTrigger = collections.namedtuple('_AnimTrigger', 'name id')
- class Triggers:
- """Playing an animation trigger causes the game engine play an animation of a particular type.
-
- The engine may pick one of a number of actual animations to play based on
- Cozmo's mood or emotion, or with random weighting. Thus playing the same
- trigger twice may not result in the exact same underlying animation playing
- twice.
-
- To play an exact animation, use play_anim with a named animation.
-
- This class holds the set of defined animations triggers to pass to play_anim_trigger.
- """
- trigger_list = []
-
- for (_name, _id) in _clad_to_engine_cozmo.AnimationTrigger.__dict__.items():
- if not _name.startswith('_'):
- trigger = _AnimTrigger(_name, _id)
- setattr(Triggers, _name, trigger)
- Triggers.trigger_list.append(trigger)
-
-
- def animation_completed_filter():
- '''Creates an :class:`cozmo.event.Filter` to wait specifically for an animation completed event.'''
- return event.Filter(action.EvtActionCompleted,
- action=lambda action: isinstance(action, Animation))
|