Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions rm_msgs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ add_service_files(
ShooterSpeed.srv
)

add_service_files(
DIRECTORY srv/mvs_driver
FILES
GetFrameRate.srv
ExposureStatusSwitch.srv
)

add_message_files(
DIRECTORY msg/detection
FILES
Expand Down
34 changes: 34 additions & 0 deletions rm_msgs/srv/mvs_driver/ExposureStatusSwitch.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Manual-exposure preset switch of one instance (see docs_ai/usage.md 5.10).
#
# Why a service and not dynamic_reconfigure: the windmill case has to switch the exposure of a
# running camera in one call, without every caller having to know the target value, and the switch
# has to survive a reconnect. The presets themselves stay in the yaml block of the instance
# (exposure_value / exposure_windmill_value), so a field site stays configurable per camera.
# All this service writes is ExposureTime - one field of ExposureParams per preset. ExposureAuto and
# the auto range stay as configured, so under exposure_auto=true the written value has no effect and
# the read-back in the response is what counts.
#
# Request: exactly one field, the preset to activate.
# NORMAL (0) = ExposureTime = exposure_value of the yaml block
# WINDMILL (1) = ExposureTime = exposure_windmill_value
# Extending the table is one yaml key plus one 'case' in MvsDriverNode::OnExposureStatusSwitch() plus
# one constant here: the field stays a single int32, so existing callers keep working.
#
# Contrary to get_frame_rate the request HAS a field, so 'rosservice call' needs it (the terminal
# client is what demands it - docs_ai/usage.md 5.1.1):
# rosservice call /<instance>/exposure_status_switch "target: 1"
#
# The write runs on the device lock (CameraDevice::WithHandle()) and the response carries the value
# the camera really holds afterwards: the camera clamps out-of-range requests instead of failing
# them, so 'requested_us' alone would not tell what is in effect. Without an open camera the call
# still answers (success=false) instead of throwing or hanging, which is how a caller tells "preset
# applied" from "there is no camera".

int32 target # preset to activate: NORMAL (0) or WINDMILL (1)
int32 NORMAL = 0 # ExposureTime = exposure_value of the yaml block
int32 WINDMILL = 1 # ExposureTime = exposure_windmill_value
---
bool success # false = no camera handle (not open / not grabbing) or the write failed
string message # human readable status: preset name plus what the camera holds now
float64 requested_us # exposure (us) this call asked for (-1 = unknown target, nothing written)
float64 exposure_us # ExposureTime read back from the camera (-1 = no camera / unreadable)
34 changes: 34 additions & 0 deletions rm_msgs/srv/mvs_driver/GetFrameRate.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Live frame-rate read-out of one instance (see docs_ai/usage.md 5.1).
#
# Why a service and not 'rostopic hz': hz averages over the whole subscription, so it needs seconds
# to converge and keeps reporting the old value for a while after a change, and it makes the caller
# deserialize every frame. This service reads counters the driver already keeps, so one call answers:
# fps = raw frames in the newest <= 0.3 s window (follows a change within ~0.2 s)
# fps_1s = raw frames of the last finished 1 Hz interval (the number in the 1 Hz log)
# fps_compressed_1s = compressed frames published in that same interval
# Calling it while nothing streams is a valid answer too (success=false / growing frame_age): that is
# how a stalled camera is told apart from a stalled subscriber.
#
# Request: empty. This service takes no arguments, so 'rosservice call /<instance>/get_frame_rate'
# works as it is written: there is no 'count' to pass and nothing a client could leave unset.
# Besides answering, every call prints exactly one line with the same numbers to the node log /
# rosout, so one call leaves one line behind and ten calls leave ten ("one call = one line", see
# docs_ai/usage.md 5.1.1). That line names its instance in the text ('hk_camera: frame rate report ...'),
# which is what tells two instances of the same manager apart in a shared log (rosconsole only knows
# the manager process - docs_ai/known_issues.md D4). The call itself stays non-blocking: it reads only
# counters the driver already keeps and never touches the camera device (the one lock it can take is
# a microsecond-sized read of the compressed queue length). The line adds no thread, no timer tick and
# no lock either - it is printed from this very callback, from exactly the values answered below.

---
bool success # false = no frame at all since startup (camera not opened / not grabbing)
string message # human readable status, e.g. "no frame for 2.4s (stream stalled?)"
float64 fps # raw frames/s over the newest 'window' seconds
float64 window # length of that window in seconds (0 = time for one sample only)
float64 fps_1s # raw frames/s of the last finished 1 Hz interval
float64 fps_compressed_1s # image_raw/compressed frames/s of that same interval
float64 frame_age # seconds since the newest frame (-1 = no frame since startup)
uint64 frames # raw frames received since startup
uint32 queue # frames waiting for the JPEG worker right now
uint32 dropped_1s # compressed frames given up (queue full) in the last finished interval
uint32 skipped_1s # frames not compressed because of compress_fps, same interval
Loading