Events in CRAM
This is a short tutorial to showcase how to trigger and handle custom-defined events. CRAM has an event protocol that makes the above-mentioned very simple.
Code-wise, the event callback mechanism is based on the notion of hooks of the cram_utitlities package from the CRAM core.
The protocol itself is defined in the cram_occasions_events
ROS package. The actual event types are defined in cram_plan_occasions_events
. Event handlers are mostly defined in cram_bullet_reasoning_belief_state
package or you can define your own handlers.
In the nutshell, the protocol is pretty trivial: there is a generic function called cram-occasions-events:on-event
which is supposed to be overloaded with new handlers and called to emit new events.
So, let's first load the plan knowledge system and switch to its corresponding Lisp package:
CL-USER> (ros-load:load-system "cram_plan_occasions_events" :cram-plan-occasions-events) ;; or ,r-l-s RET cram_plan_occasions_events RET RET CL-USER> (in-package :cram-plan-occasions-events)
Next, we define a custom event class that inherits from cram-occasions-events:event
and has one data slot:
CPOE> (defclass cat-appeared-event (event) ((color-of-cat :initarg :cat-color :reader cat-color :initform (error 'simple-error :format-control "CAT-APPEARED-EVENT requires CAT-COLOR")))) #<STANDARD-CLASS CAT-APPEARED-EVENT> CPOE> (describe 'cat-appeared-event) CRAM-PLAN-OCCASIONS-EVENTS::CAT-APPEARED-EVENT [symbol] CAT-APPEARED-EVENT names the standard-class #<STANDARD-CLASS CAT-APPEARED-EVENT>: Direct superclasses: EVENT No subclasses. Not yet finalized. Direct slots: COLOR-OF-CAT Initargs: :CAT-COLOR Initform: (ERROR 'SIMPLE-ERROR :FORMAT-CONTROL "CAT-APPEARED-EVENT requires CAT-COLOR") Readers: CAT-COLOR
Now that we have our custom event type we define an event handler for it:
CPOE> (defmethod on-event ((event cat-appeared-event)) (format t "OMG! I just saw a ~a cat!~%" (cat-color event)))
To trigger the event we simply call the cram-occasions-events:on-event
method:
CPOE> (on-event (make-instance 'cat-appeared-event :cat-color "black")) OMG! I just saw a black cat! (NIL)
Now, imagine that we would like to have multiple handlers for the same event. E.g., in addition to screaming excitedly each time we see a cat, we would also like to count the number of cats seen so far:
CPOE> (let ((saw-cats 0)) (defmethod on-event cat-counter ((event cat-appeared-event)) (incf saw-cats) (format t "number of cats seen so far: ~a~%" saw-cats) saw-cats)) #<STANDARD-METHOD ON-EVENT CAT-COUNTER (CAT-APPEARED-EVENT) {100F5D3C43}> CPOE> (on-event (make-instance 'cat-appeared-event :cat-color "black")) number of cats seen so far: 1 OMG! I just saw a black cat! (1 NIL) CPOE> (on-event (make-instance 'cat-appeared-event :cat-color "black")) number of cats seen so far: 2 OMG! I just saw a black cat! (2 NIL)
Let's break this down.
- We define a lexical variable
saw-cats
which will be our counter (ifsaw-cats
confuses you read up on closures). - Then we overload our
on-event
method while specifyingcram-plan-occasions-events::cat-counter
as a method combination qualifier. The default method combination of generic functionon-event
iscram-utilities:hooks
which simply combines the results of all the suitable methods into a list. That is clearly illustrated in the result of the laston-event
call in the code snippet above:(2 NIL)
, where2
is the result of the method qualified withcat-counter
andNIL
results from theformat
statement of the other method. Combining the results of all the suitable methods means that the qualifier itself (in our casecat-counter
) is of no importance. So, as long as we can come up with new unique qualifiers we can have new handlers for our event (if this paragraph is hard to understand look into the definition ofcram-utilities:define-hook
and read up more on method combinations).