Generated ROS Action Plugin
Source code generated by cx_ros_comm_gen.
Given the package (e.g., example_interfaces) and action_name (e.g., Fibonacci), this document uses the following abbreviations:
PkgCamel: CamelCase of package (e.g.,
ExampleInterfaces)SrvCamel: CamelCase of action_name (e.g.,
Fibonacci)pkg-kebab: kebab-case of package (e.g.,
example-interfaces)action-kebab: kebab-case of action_name (e.g.,
fibonacci)
Plugin Class
cx::CXPkgCamelACtionCamelPlugin
(e.g., cx::CXExampleInterfacesFibonacciPlugin)
Configuration
This plugin has no specific configuration.
Features
Facts
; Asserted by the respective create-client function.
; Retracted by the respective destroy-client function.
(<package-kebab>-<action-kebab>-client (server ?server-name-string))
; Asserted by the respective create-server function.
; Retracted by the respective destroy-server function.
(<package-kebab>-<action-kebab>-server (name ?server-name-string))
; Asserted by the goal response callback of a client.
; Process the response and then destroy feedback.
; The client-goal-handle should persist until the processing of the request is fully done.
(<package-kebab>-<action-kebab>-goal-response (server ?server-name-string) (client-goal-handle-ptr ?cgh-ptr))
; Asserted by the goal feedback callback of a client.
; Process the response and then destroy feedback (it is read-only, do not modify it).
; The client-goal-handle should persist (and not be destroyed) until the processing of the goal is fully done and the action is terminated.
; In particular, the client goal handle pointer here will match the one from the goal-response fact.
(<package-kebab>-<action-kebab>-goal-feedback (server ?server-name-string) (client-goal-handle-ptr ?cgh-ptr) (feedback-ptr ?f-ptr))
; Asserted by the goal result callback of a client.
; Process the result and then destroy the result pointer before retracting this fact.
; "code" is either UNKNOWN SUCCEEDED CANCELED or ABORTED.
; "goal-id" is the string representation of a uuid according to rclcpp_action::to_string().
(<package-kebab>-<action-kebab>-wrapped-result (server ?server-name-string) (goal-id ?uuid-str) (code ?code-symbol) (result-ptr ?f-ptr))
; Asserted by the goal result callback of a server.
; Process the server goal handle to retrieve important information such as the goal sent by the client.
; The server-goal-handle should persist (and not be destroyed) until the processing of the action is fully done and the action is terminated.
(<package-kebab>-<action-kebab>-accepted-goal (server ?server-name-string) (server-goal-handle-ptr ?f-ptr))
; Asserted by the cancel callback of the client.
; The return code is one of ERROR_NONE, ERROR_REJECTED, ERROR_UNKNOWN_GOAL_ID, ERROR_GOAL_TERMINATED.
; The num-goals slot indicates the amount of goals canceling.
(<package-kebab>-<action-kebab>-cancel-goal-response (server ?server-name-string) (return-code ?code-symbol) (num-goals ?n-int))
; Asserted by the cancel callback of the client.
; Provides information about which goals are actually canceling.
(<package-kebab>-<action-kebab>-cancel-goal-entry (server ?server-name-string) (goal-id ?uuid-string) (stamp ?stamp-float))
Functions
; Create and destroy clients and servers.
; After creating a server, user-defined function is called whenever a goal request arrives.
(<package-kebab>-<action-kebab>-create-server ?server-name)
(<package-kebab>-<action-kebab>-destroy-server ?server-name)
(<package-kebab>-<action-kebab>-create-client ?server-name)
(<package-kebab>-<action-kebab>-destroy-client ?server-name)
; Send a goal to an action server
; Requires the client to be created first using create-client.
; Callbacks will assert goal-response feedback and wrapped-result facts.
; Do not destroy the ?msg-ptr immediately, keep it until the goal is fully processed.
(<package-kebab>-<action-kebab>-send-goal ?msg-ptr ?server-name)
; Cancel goals
(<package-kebab>-<action-kebab>-client-cancel-goal ?server-name ?goal-handle-ptr)
(<package-kebab>-<action-kebab>-client-cancel-all-goals ?server-name)
(<package-kebab>-<action-kebab>-client-cancel-goals-before ?server-name ?stamp-sec-float)
; Creating, destroying and processing of goals
(bind ?msg-ptr (<package-kebab>-<action-kebab>-goal-create))
(<package-kebab>-<action-kebab>-goal-destroy ?msg-ptr)
(<package-kebab>-<action-kebab>-goal-set-field ?msg-ptr ?field-name ?field-value)
(<package-kebab>-<action-kebab>-goal-get-field ?msg-ptr ?field-name)
; Creating, destroying and processing of feedback
(bind ?msg-ptr (<package-kebab>-<action-kebab>-feedback-create))
(<package-kebab>-<action-kebab>-feedback-destroy ?msg-ptr)
(<package-kebab>-<action-kebab>-feedback-set-field ?msg-ptr ?field-name ?field-value)
(<package-kebab>-<action-kebab>-feedback-get-field ?msg-ptr ?field-name)
; Creating, destroying and processing of results
(bind ?msg-ptr (<package-kebab>-<action-kebab>-result-create))
(<package-kebab>-<action-kebab>-result-destroy ?msg-ptr)
(<package-kebab>-<action-kebab>-result-set-field ?msg-ptr ?field-name ?field-value)
(<package-kebab>-<action-kebab>-result-get-field ?msg-ptr ?field-name)
; Destroy server goal handle pointer.
(<package-kebab>-<action-kebab>-server-goal-handle-destroy ?handle-ptr)
; server goal handle members (see rclcpp_action documentation)
(<package-kebab>-<action-kebab>-server-goal-handle-abort ?handle-ptr ?result-ptr)
(<package-kebab>-<action-kebab>-server-goal-handle-succeed ?handle-ptr ?result-ptr)
(<package-kebab>-<action-kebab>-server-goal-handle-canceled ?handle-ptr ?result-ptr)
(bind ?goal-ptr (<package-kebab>-<action-kebab>-server-goal-handle-get-goal ?handle-ptr))
; the goal id is returned as string representation via rclcpp_action::to_string()
(bind ?uuid-str (<package-kebab>-<action-kebab>-server-goal-handle-get-goal-id ?handle-ptr))
(bind ?bool-sym (<package-kebab>-<action-kebab>-server-goal-handle-is-canceling ?handle-ptr))
(bind ?bool-sym (<package-kebab>-<action-kebab>-server-goal-handle-is-active ?handle-ptr))
(bind ?bool-sym (<package-kebab>-<action-kebab>-server-goal-handle-is-executing ?handle-ptr))
(<package-kebab>-<action-kebab>-server-goal-handle-execute ?handle-ptr)
(<package-kebab>-<action-kebab>-server-goal-handle-publish-feedback ?handle-ptr ?feedback-ptr)
; Destroy client goal handle pointer.
(<package-kebab>-<action-kebab>-client-goal-handle-destroy ?handle-ptr)
; client goal handle members (see rclcpp_action documentation)
(bind ?bool-sym (<package-kebab>-<action-kebab>-client-goal-handle-is-feedback-aware ?handle-ptr))
(bind ?bool-sym (<package-kebab>-<action-kebab>-client-goal-handle-is-result-aware ?handle-ptr))
; return codes:
; 0 = STATUS_UNKNOWN
; 1 = STATUS_ACCEPTED
; 2 = STATUS_EXECUTING
; 3 = STATUS_CANCELING
; 4 = STATUS_SUCCEEDED
; 5 = STATUS_CANCELED
; 6 = STATUS_ABORTED
(bind ?status-int (<package-kebab>-<action-kebab>-client-goal-handle-get-status ?handle-ptr))
; the goal id is returned as string representation via rclcpp_action::to_string()
(bind ?uuid-str (<package-kebab>-<action-kebab>-client-goal-handle-get-goal-id ?handle-ptr))
(bind ?time-seconds-float (<package-kebab>-<action-kebab>-client-goal-handle-get-goal-stamp ?handle-ptr))
(<package-kebab>-<action-kebab>-client-goal-handle-stop-callbacks ?server-name ?handle-ptr)
Functions Defined by User
The following functions are called at the appropriate places if they are defined by the user.
; Gets called for each server receiving a goal, needs to return one of these integers:
; (return 1) ; REJECT
; (return 2) ; ACCEPT_AND_EXECUTE
; (return 3) ; ACCEPT_AND_DEFER
; If the function does not exist, every goal is automatically accepted (ACCEPT_AND_EXECUTE)
(bind ?response-int (<package-kebab>-<action-kebab>-handle-goal-callback ?server-name-str ?goal-ptr ?uuid-str))
; Gets called for each server receiving a cancellation request, needs to return one of these integers: ; (return 1) ; REJECT ; (return 2) ; ACCEPT ; If the function does not exist, every request is automatically accepted (ACCEPT) (bind ?response-int (<package-kebab>-<action-kebab>-cancel-goal-callback ?server-name-str ?server-goal-handle-ptr))
Usage Example
A minimal working example is provided by the cx_bringup package. Run it via:
ros2 launch cx_bringup cx_launch.py manager_config:=plugin_examples/fibonacci_action.yaml
- The launch file starts two CLIPS environments:
One environment provides an action server that computes Fibonacci sequences.
The second environment acts as an action client. It first requests a Fibonacci sequence of order 5. After the goal completes, it sends a second request for a sequence of order 10. The second goal is canceled after the sixth element has been computed.
Configuration
File cx_bringup/params/plugin_examples/fibonacci_action.yaml.
/**:
ros__parameters:
environments: ["cx_fibonacci_server", "cx_fibonacci_client"]
cx_fibonacci_client:
plugins: ["executive", "fibonacci", "files_client"]
log_clips_to_file: true
watch: ["facts", "rules"]
redirect_stdout_to_debug: true
cx_fibonacci_server:
plugins: ["executive", "fibonacci", "files_server"]
log_clips_to_file: true
watch: ["facts", "rules"]
redirect_stdout_to_debug: true
executive:
plugin: "cx::ExecutivePlugin"
publish_on_refresh: false
assert_time: true
refresh_rate: 10
fibonacci:
plugin: "cx::CXExampleInterfacesFibonacciPlugin"
files_client:
plugin: "cx::FileLoadPlugin"
pkg_share_dirs: ["cx_bringup"]
load: [
"clips/plugin_examples/fibonacci-action-client.clp"]
files_server:
plugin: "cx::FileLoadPlugin"
pkg_share_dirs: ["cx_bringup"]
load: [
"clips/plugin_examples/fibonacci-action-server.clp"]
Code
File cx_bringup/clips/plugin_examples/fibonacci-action-server.clp.
(deftemplate fibonacci
(slot uuid (type STRING))
(slot order (type INTEGER))
(slot progress (type INTEGER))
(multislot sequence (type INTEGER))
(slot result (type INTEGER))
(slot canceled (type SYMBOL) (allowed-values FALSE TRUE))
(slot last-computed (type FLOAT))
)
(deffunction example-interfaces-fibonacci-handle-goal-callback (?server ?goal ?uuid)
" Overrides default callback, printing a message before accepting. "
(printout blue "Accepting goal, uuid: " ?uuid crlf)
; (return 1) ; REJECT
(return 2) ; ACCEPT_AND_EXECUTE
; (return 3) ; ACCEPT_AND_DEFER
)
(deffunction example-interfaces-fibonacci-cancel-goal-callback (?server ?goal ?goal-handle)
" Overrides default callback, marking the associated fibonacci fact for cancelation. "
(do-for-fact ((?f fibonacci)) (eq ?f:uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?goal-handle))
(printout blue "Accepting cancelation of goal with uuid " ?f:uuid crlf)
(modify ?f (canceled TRUE))
(return 2) ; ACCEPT
)
(return 1) ; REJECT
)
(defrule fibonacci-action-server-init
" Create a simple server using the generated bindings. "
(not (example-interfaces-fibonacci-server (name "ros_cx_fibonacci")))
(not (executive-finalize))
=>
(example-interfaces-fibonacci-create-server "ros_cx_fibonacci")
(printout blue "Created server for /ros_cx_fibonacci" crlf)
)
(defrule fibonacci-goal-accepted-start-compute
" Create an initial fibonacci fact for an accepted goal. "
(example-interfaces-fibonacci-accepted-goal (server ?server) (server-goal-handle-ptr ?ptr))
(not (fibonacci (uuid ?uuid&:(eq ?uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?ptr)))))
=>
(if (not (example-interfaces-fibonacci-server-goal-handle-is-canceling ?ptr)) then
(bind ?goal (example-interfaces-fibonacci-server-goal-handle-get-goal ?ptr))
(bind ?order (example-interfaces-fibonacci-goal-get-field ?goal "order"))
(bind ?uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?ptr))
(assert (fibonacci (uuid ?uuid) (order ?order) (progress 2) (result 0) (sequence (create$ 0 1)) (last-computed (now))))
else
(printout error "Somehow the goal is canceling already" crlf)
)
; do not destroy the server goal handle here, only do it once the goal is fully processed and finished
; (example-interfaces-fibonacci-server-goal-handle-destroy ?ptr)
)
(defrule fibonacci-compute-next
" Compute the next number in the sequence and send out feedback accordingly. "
(example-interfaces-fibonacci-accepted-goal (server ?server) (server-goal-handle-ptr ?ptr))
?f <- (fibonacci (order ?order) (progress ?remaining&:(>= ?order ?remaining))
(last-computed ?computed) (result ?old-res) (sequence $?seq) (uuid ?uuid) (canceled FALSE))
(time ?now&:(> (- ?now ?computed) 1))
(test (eq ?uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?ptr)))
=>
(bind ?step (+ ?remaining 1))
(bind ?res (+ (nth$ ?remaining ?seq) (nth$ (- ?remaining 1) ?seq)))
(printout blue "Computing partial result fibonacci(" ?remaining ") = " ?res crlf)
(bind ?seq (create$ ?seq ?res))
(modify ?f (progress ?step) (result (+ ?old-res ?res)) (sequence ?seq))
(bind ?feedback (example-interfaces-fibonacci-feedback-create))
(example-interfaces-fibonacci-feedback-set-field ?feedback "sequence" ?seq)
(example-interfaces-fibonacci-server-goal-handle-publish-feedback ?ptr ?feedback)
(example-interfaces-fibonacci-feedback-destroy ?feedback)
(modify ?f (last-computed ?now))
)
(defrule fibonacci-compute-done
" Send the final goal result once the computation is finished. "
?ag <- (example-interfaces-fibonacci-accepted-goal (server ?server) (server-goal-handle-ptr ?ptr))
?f <- (fibonacci (order ?order) (progress ?remaining&:(< ?order ?remaining)) (result ?old-res) (sequence $?seq) (uuid ?uuid&:(eq ?uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?ptr))))
=>
(printout blue "Final fibonacci sequence: " ?seq crlf)
(bind ?result (example-interfaces-fibonacci-result-create))
(example-interfaces-fibonacci-result-set-field ?result "sequence" ?seq)
(example-interfaces-fibonacci-server-goal-handle-succeed ?ptr ?result)
(example-interfaces-fibonacci-result-destroy ?result)
(example-interfaces-fibonacci-server-goal-handle-destroy ?ptr)
(retract ?f)
(retract ?ag)
)
(defrule fibonacci-compute-canceled
" Stop computation and send the partial result in case the goal is canceled. "
?ag <- (example-interfaces-fibonacci-accepted-goal (server ?server) (server-goal-handle-ptr ?ptr))
?f <- (fibonacci (sequence $?seq) (canceled TRUE) (uuid ?uuid&:(eq ?uuid (example-interfaces-fibonacci-server-goal-handle-get-goal-id ?ptr))))
=>
(printout blue "Canceling fibonacci sequence: " ?seq crlf)
(bind ?result (example-interfaces-fibonacci-result-create))
(example-interfaces-fibonacci-result-set-field ?result "sequence" ?seq)
(example-interfaces-fibonacci-server-goal-handle-canceled ?ptr ?result)
(example-interfaces-fibonacci-result-destroy ?result)
(example-interfaces-fibonacci-server-goal-handle-destroy ?ptr)
(retract ?f)
(retract ?ag)
)
(defrule fibonacci-server-cleanup
(executive-finalize)
(example-interfaces-fibonacci-server (name ?server))
=>
(example-interfaces-fibonacci-destroy-server ?server)
)
(defrule fibonacci-goal-response-cleanup
(executive-finalize)
?f <- (example-interfaces-fibonacci-goal-response (client-goal-handle-ptr ?p))
=>
(example-interfaces-fibonacci-client-goal-handle-destroy ?p)
(retract ?f)
)
(defrule fibonacci-accepted-goal-cleanup
(executive-finalize)
?f <- (example-interfaces-fibonacci-accepted-goal (server-goal-handle-ptr ?p))
=>
(example-interfaces-fibonacci-server-goal-handle-destroy ?p)
(retract ?f)
)
File cx_bringup/clips/plugin_examples/fibonacci-action-client.clp.
(defrule fibonacci-action-client-init
" Create a simple client using the generated bindings. "
(not (example-interfaces-fibonacci-client (server "ros_cx_fibonacci")))
(not (executive-finalize))
=>
(example-interfaces-fibonacci-create-client "ros_cx_fibonacci")
(printout green "Created client for /ros_cx_fibonacci" crlf)
)
(defrule fibonacci-client-send-goal
" Request computation of the fibonacci sequence of order 5. "
(example-interfaces-fibonacci-client (server ?server))
(not (send-request))
=>
(assert (send-request))
(bind ?goal (example-interfaces-fibonacci-goal-create))
(assert (fibonacci-goal ?goal))
(example-interfaces-fibonacci-goal-set-field ?goal "order" 5)
(example-interfaces-fibonacci-send-goal ?goal ?server)
(printout green "Sending goal fibonacci(5)" crlf)
; do not destroy the goal here, only do it once the goal is fully processed and finished
; (example-interfaces-fibonacci-goal-destroy ?goal)
)
(defrule fibonacci-client-get-feedback
" Print any partial compuation result received so far and perpare cancelation once the received sequence is of length 7. "
(declare (salience 100))
?f <- (example-interfaces-fibonacci-goal-feedback (server ?server) (client-goal-handle-ptr ?ghp) (feedback-ptr ?fp))
=>
(bind ?g-id (example-interfaces-fibonacci-client-goal-handle-get-goal-id ?ghp))
(bind ?g-stamp (example-interfaces-fibonacci-client-goal-handle-get-goal-stamp ?ghp))
(bind ?g-status (example-interfaces-fibonacci-client-goal-handle-get-status ?ghp))
(bind ?g-is-f-aware (example-interfaces-fibonacci-client-goal-handle-is-feedback-aware ?ghp))
(bind ?g-is-r-aware (example-interfaces-fibonacci-client-goal-handle-is-result-aware ?ghp))
; the stamp seems to be broken (looks like a rclcpp_action issue)
(printout debug "[" (- (now) ?g-stamp) "] " ?g-status " " ?g-id " f " ?g-is-f-aware " r " ?g-is-r-aware crlf)
(bind ?part-seq (example-interfaces-fibonacci-feedback-get-field ?fp "sequence"))
(printout green "partial sequence: " ?part-seq crlf)
(if (= (length$ ?part-seq) 7) then
(assert (cancel-goal))
)
(example-interfaces-fibonacci-feedback-destroy ?fp)
(retract ?f)
)
(defrule fibonacci-client-cleanup-after-wrapped-result
" Process result of finished compuatation, printing the final sequence. "
(declare (salience 10))
?f <- (example-interfaces-fibonacci-goal-response (server ?server) (client-goal-handle-ptr ?ghp))
?g <- (example-interfaces-fibonacci-wrapped-result (server ?server) (goal-id ?uuid) (code ?code) (result-ptr ?rp))
(test (eq ?uuid (example-interfaces-fibonacci-client-goal-handle-get-goal-id ?ghp)))
?request-goal <- (fibonacci-goal ?goal)
(time ?now)
=>
(bind ?g-status (example-interfaces-fibonacci-client-goal-handle-get-status ?ghp))
(if (> ?g-status 3) then ; status is final in one way or another
(bind ?seq (example-interfaces-fibonacci-result-get-field ?rp "sequence"))
(if (= ?g-status 4) then
(printout green "Final fibonacci sequence: " ?seq crlf)
)
(if (= ?g-status 5) then
(printout green "Canceled fibonacci sequence: " ?seq crlf)
)
(example-interfaces-fibonacci-result-destroy ?rp)
(retract ?g)
(bind ?g-id (example-interfaces-fibonacci-client-goal-handle-get-goal-id ?ghp))
(bind ?g-stamp (example-interfaces-fibonacci-client-goal-handle-get-goal-stamp ?ghp))
(bind ?g-is-f-aware (example-interfaces-fibonacci-client-goal-handle-is-feedback-aware ?ghp))
(bind ?g-is-r-aware (example-interfaces-fibonacci-client-goal-handle-is-result-aware ?ghp))
(printout debug "Final goal response [" (- (now) ?g-stamp) "] " ?uuid " " ?g-status " " ?g-id " f " ?g-is-f-aware " r " ?g-is-r-aware crlf)
(example-interfaces-fibonacci-client-goal-handle-destroy ?ghp)
(retract ?f)
(example-interfaces-fibonacci-goal-destroy ?goal)
(retract ?request-goal)
)
)
(defrule fibonacci-client-send-goal-that-cancels-later
" Send out a second goal for the fibonacci sequence of order 10 once the previous goal is completed. "
(example-interfaces-fibonacci-client (server ?server))
(send-request)
(not (fibonacci-goal ?))
(not (send-request-again))
=>
(printout green "Request fibonacci(10), will cancel before finish" crlf)
(assert (send-request-again))
(bind ?goal (example-interfaces-fibonacci-goal-create))
(assert (fibonacci-goal ?goal))
(example-interfaces-fibonacci-goal-set-field ?goal "order" 10)
(example-interfaces-fibonacci-send-goal ?goal ?server)
)
(defrule fibonacci-request-cancel
" Send out a cancel request after the partial feedback provides a sequence of length 7. "
(cancel-goal)
(example-interfaces-fibonacci-client (server ?server))
(example-interfaces-fibonacci-goal-response (server ?server) (client-goal-handle-ptr ?ghp))
=>
(example-interfaces-fibonacci-client-cancel-goal ?server ?ghp)
(printout green "Canceling current goal" crlf)
)
(defrule fibonacci-client-cleanup
(executive-finalize)
(example-interfaces-fibonacci-client (server ?client))
=>
(example-interfaces-fibonacci-destroy-client ?client)
)
(defrule fibonacci-accepted-goal-cleanup
(executive-finalize)
?f <- (fibonacci-goal ?p)
=>
(example-interfaces-fibonacci-goal-destroy ?p)
(retract ?f)
)