Skip to content

Commit a51dd4c

Browse files
committed
Merge branch 'master' into feature/quality-level-rep-2004
Signed-off-by: Nordmann Arne (CR/ADT3) <arne.nordmann@de.bosch.com>
2 parents d5b8dbb + 2dd9387 commit a51dd4c

28 files changed

Lines changed: 1080 additions & 47 deletions

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
[![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](https://github.com/micro-ROS/system_modes/blob/master/LICENSE)
44
[![Build status](https://build.ros2.org/job/Ddev__system_modes__ubuntu_bionic_amd64/badge/icon?subject=Build%20farm%3A%20Dashing)](https://build.ros2.org/job/Ddev__system_modes__ubuntu_bionic_amd64/)
5-
[![Build status](http://build.ros2.org/job/Edev__system_modes__ubuntu_bionic_amd64/badge/icon?subject=Build%20farm%3A%20Eloquent)](http://build.ros2.org/job/Edev__system_modes__ubuntu_bionic_amd64/)
65
[![Build status](http://build.ros2.org/job/Fdev__system_modes__ubuntu_focal_amd64/badge/icon?subject=Build%20farm%3A%20Foxy)](http://build.ros2.org/job/Fdev__system_modes__ubuntu_focal_amd64/)
76
[![Build status](http://build.ros2.org/job/Rdev__system_modes__ubuntu_focal_amd64/badge/icon?subject=Build%20farm%3A%20Rolling)](http://build.ros2.org/job/Rdev__system_modes__ubuntu_focal_amd64/)
87
[![Build status](https://github.com/micro-ROS/system_modes/workflows/Build%20action%3A%20Foxy%20%2B%20Rolling/badge.svg)](https://github.com/micro-ROS/system_modes/actions)

system_modes/CMakeLists.txt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ add_library(mode SHARED
4141
src/system_modes/mode.cpp
4242
src/system_modes/mode_impl.cpp
4343
src/system_modes/mode_handling.cpp
44-
src/system_modes/mode_inference.cpp)
44+
src/system_modes/mode_inference.cpp
45+
src/system_modes/mode_observer.cpp)
4546
target_include_directories(mode PUBLIC
4647
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
4748
$<INSTALL_INTERFACE:include>)
@@ -179,13 +180,32 @@ if(BUILD_TESTING)
179180
target_link_libraries(test_mode_monitor mode)
180181
endif()
181182

183+
ament_add_gtest(test_mode_observer test/test_mode_observer.cpp)
184+
if(TARGET test_mode_observer)
185+
target_include_directories(test_mode_observer PUBLIC
186+
${rclcpp_INCLUDE_DIRS}
187+
${CMAKE_CURRENT_BINARY_DIR}/system_modes/
188+
)
189+
target_link_libraries(test_mode_observer mode)
190+
endif()
191+
192+
# mode observer test node
193+
add_executable(modes_observer_test_node test/launchtest/modes_observer_test_node.cpp)
194+
target_include_directories(modes_observer_test_node PUBLIC
195+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
196+
$<INSTALL_INTERFACE:include>)
197+
target_link_libraries(modes_observer_test_node mode)
198+
install(TARGETS modes_observer_test_node DESTINATION lib/${PROJECT_NAME})
199+
182200
# Launch Tests
183201
find_package(launch_testing_ament_cmake REQUIRED)
184202
set(launch_tests
185203
"two_lifecycle_nodes" # Mode Manager with Lifecycle Nodes
186204
"two_mixed_nodes" # Mode Manager with Lifecycle and non-Lifecycle Nodes
187205
"two_independent_hierarchies" # Mode Manager for two independent hierarchies of nodes
188-
"manager_and_monitor") # Mode Manager and Mode Monitor
206+
"manager_and_monitor" # Mode Manager and Mode Monitor
207+
"redundant_mode_changes" # Ignore redundant mode changes
208+
"modes_observer") # Mode Observer
189209

190210
# Launch Test: Mode Manager with Lifecycle Nodes
191211
foreach(test_name ${launch_tests})

system_modes/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ Since the introduced (sub-)systems are not concrete software entities, their sta
8989
Before attempting a state or mode change for a system or node, the [mode manager](#mode_manager) publishes information about the request.
9090
The according topics might need to be *latched* in order to allow nodes to do the inference after joining a running system.
9191

92+
#### Mode Observer
93+
94+
Additionally, the library comprises a *Mode Observer* that serves as a local cache of states and modes of all observed system parts. The mode observer will try to obtain the current state and mode initially via sevrice calls (GetState, GetMode) and subsequently monitors according transitions events and mode events.
95+
96+
The mode observer is supposed to be instantiated within a ROS 2 node to have states and modes available locally for fast access, see this exemplary [ModeObserverNode](https://github.com/micro-ROS/system_modes/blob/master/system_modes/test/launchtest/modes_observer_test_node.cpp) used for testing.
97+
9298
### Mode Manager
9399

94100
The mode manager is a ROS node that accepts an SHM file (see [above](#system-modes)) as command line parameter. It parses the SHM file and creates the according services, publishers, and subscribers to manage the system and its modes.

system_modes/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
Changelog for package system_modes_examples
33
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44

5+
0.5.0 (2020-03-16)
6+
-----------
7+
* Atomic parameter setting https://github.com/micro-ROS/system_modes/issues/59
8+
* Bug fixing
9+
* More tests
10+
511
0.4.2 (2020-12-17)
612
-----------
713
* Error handling and rules feature no longer experimental

system_modes/include/system_modes/mode_impl.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ struct StateAndMode
9090
}
9191
return state_label_(state) + "." + mode;
9292
}
93+
94+
bool unknown()
95+
{
96+
return state == State::PRIMARY_STATE_UNKNOWN;
97+
}
9398
};
9499

95100
class ModeImpl
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) 2018 - for information on the respective copyright owner
2+
// see the NOTICE file and/or the repository https://github.com/microros/system_modes
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
#pragma once
16+
17+
#include <rclcpp/rclcpp.hpp>
18+
19+
#include <lifecycle_msgs/msg/transition_event.hpp>
20+
#include <lifecycle_msgs/srv/get_state.hpp>
21+
22+
#include <map>
23+
#include <memory>
24+
#include <mutex>
25+
#include <string>
26+
#include <utility>
27+
28+
#include "system_modes/mode.hpp"
29+
#include "system_modes/msg/mode_event.hpp"
30+
#include "system_modes/srv/get_mode.hpp"
31+
32+
namespace system_modes
33+
{
34+
35+
using std::map;
36+
using std::mutex;
37+
using std::string;
38+
39+
using lifecycle_msgs::msg::TransitionEvent;
40+
using lifecycle_msgs::srv::GetState;
41+
using rclcpp::node_interfaces::NodeBaseInterface;
42+
using system_modes::msg::ModeEvent;
43+
using system_modes::srv::GetMode;
44+
45+
/**
46+
* Mode observer provides a local system modes cache.
47+
*
48+
* The mode observer serves as a local system modes cache, instantiated by a node. The mode
49+
* observer will initially try to gain current states/modes from all observes entities and will
50+
* then continuously monitor transitions to keep up to date.
51+
*/
52+
class ModeObserver
53+
{
54+
public:
55+
explicit ModeObserver(std::weak_ptr<rclcpp::Node>);
56+
virtual ~ModeObserver() = default;
57+
58+
/**
59+
* Getter for observed state and mode.
60+
*
61+
* Returns cached observed state and mode for requested system entity (system or node).
62+
*/
63+
virtual StateAndMode
64+
get(const string & part_name);
65+
66+
/**
67+
* Add part to list of observed parts.
68+
*/
69+
virtual void
70+
observe(const string & part_name);
71+
72+
/**
73+
* Remove part from list of observed parts.
74+
*/
75+
virtual void
76+
stop_observing(const string & part_name);
77+
78+
protected:
79+
virtual void
80+
transition_callback(
81+
const TransitionEvent::SharedPtr msg,
82+
const string & part_name);
83+
84+
virtual void
85+
mode_event_callback(
86+
const ModeEvent::SharedPtr msg,
87+
const string & part_name);
88+
89+
private:
90+
std::weak_ptr<rclcpp::Node> node_handle_;
91+
map<string, StateAndMode> cache_;
92+
mutable std::shared_timed_mutex mutex_;
93+
94+
map<string, std::shared_ptr<rclcpp::Subscription<TransitionEvent>>> state_subs_;
95+
map<string, std::shared_ptr<rclcpp::Subscription<ModeEvent>>> mode_subs_;
96+
};
97+
98+
} // namespace system_modes

system_modes/package.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
33
<package format="3">
44
<name>system_modes</name>
5-
<version>0.4.2</version>
5+
<version>0.5.0</version>
66
<description>
77
The system modes concept assumes that a robotics system is built
88
from components with a lifecycle. It adds a notion of (sub-)systems,
@@ -34,6 +34,7 @@
3434
<test_depend>ament_lint_auto</test_depend>
3535
<test_depend>launch_testing_ament_cmake</test_depend>
3636
<test_depend>launch_testing_ros</test_depend>
37+
<test_depend>ros2run</test_depend>
3738

3839
<member_of_group>rosidl_interface_packages</member_of_group>
3940

system_modes/src/system_modes/mode_handling.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include <utility>
2929
#include <vector>
3030

31-
using std::endl;
3231
using std::pair;
3332
using std::mutex;
3433
using std::string;

system_modes/src/system_modes/mode_inference.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include <utility>
2929
#include <vector>
3030

31-
using std::endl;
3231
using std::mutex;
3332
using std::string;
3433
using std::make_pair;
@@ -223,6 +222,7 @@ ModeInference::infer_system(const string & part)
223222
// error-processing?
224223
if (stateAndMode.state == State::TRANSITION_STATE_ERRORPROCESSING) {
225224
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
225+
226226
return StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
227227
}
228228

@@ -236,6 +236,7 @@ ModeInference::infer_system(const string & part)
236236
"', inference failed.");
237237
}
238238
}
239+
239240
return StateAndMode(state, "");
240241
}
241242

@@ -245,15 +246,16 @@ ModeInference::infer_system(const string & part)
245246
string targetMode = stateAndModeTarget->second.mode;
246247
if (targetState == State::PRIMARY_STATE_INACTIVE) {
247248
// target: inactive
248-
for (auto part : default_mode->get_parts()) {
249-
auto stateAndMode = this->get_or_infer(part);
249+
for (auto partpart : default_mode->get_parts()) {
250+
auto stateAndMode = this->get_or_infer(partpart);
250251

251252
if (stateAndMode.state == State::TRANSITION_STATE_ERRORPROCESSING) {
252253
// Note: If current entity was in an active mode that allowed a part to
253254
// be in error-processing (by dont-care) and the current entity is requested
254255
// to switch to inactive, then the actual state of the current entity will
255256
// go to error-processing until the mentioned part recovers.
256257
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
258+
257259
return StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
258260
}
259261

@@ -262,9 +264,11 @@ ModeInference::infer_system(const string & part)
262264
stateAndMode.state != State::PRIMARY_STATE_UNCONFIGURED)
263265
{
264266
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_DEACTIVATING, "");
267+
265268
return StateAndMode(State::TRANSITION_STATE_DEACTIVATING, "");
266269
}
267270
}
271+
268272
return StateAndMode(State::PRIMARY_STATE_INACTIVE, "");
269273
} else if (targetState == State::PRIMARY_STATE_ACTIVE) {
270274
ModeConstPtr mode;
@@ -281,6 +285,7 @@ ModeInference::infer_system(const string & part)
281285
// TODO(anordman): consider DONT-CARE
282286
if (stateAndMode.state == State::TRANSITION_STATE_ERRORPROCESSING) {
283287
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
288+
284289
return StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
285290
}
286291

@@ -302,6 +307,7 @@ ModeInference::infer_system(const string & part)
302307
if (inTargetMode) {
303308
// Target state and target mode reached, all good!
304309
this->systems_[part] = StateAndMode(State::PRIMARY_STATE_ACTIVE, targetMode);
310+
305311
return StateAndMode(State::PRIMARY_STATE_ACTIVE, targetMode);
306312
}
307313
}
@@ -316,6 +322,7 @@ ModeInference::infer_system(const string & part)
316322
// TODO(anordman): consider DONT-CARE
317323
if (stateAndMode.state == State::TRANSITION_STATE_ERRORPROCESSING) {
318324
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
325+
319326
return StateAndMode(State::TRANSITION_STATE_ERRORPROCESSING, "");
320327
}
321328

@@ -331,11 +338,13 @@ ModeInference::infer_system(const string & part)
331338
if (foundMode) {
332339
// We are in a non-target mode, this means we are still activating
333340
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ACTIVATING, mode.first);
341+
334342
return StateAndMode(State::TRANSITION_STATE_ACTIVATING, mode.first);
335343
}
336344
}
337345

338346
this->systems_[part] = StateAndMode(State::TRANSITION_STATE_ACTIVATING, "");
347+
339348
return StateAndMode(State::TRANSITION_STATE_ACTIVATING, "");
340349
}
341350

@@ -616,8 +625,8 @@ const std::vector<string>
616625
ModeInference::get_nodes() const
617626
{
618627
std::vector<string> result;
619-
for (auto node : this->nodes_) {
620-
result.push_back(node.first);
628+
for (auto part : this->nodes_) {
629+
result.push_back(part.first);
621630
}
622631
return result;
623632
}
@@ -626,8 +635,8 @@ const std::vector<string>
626635
ModeInference::get_systems() const
627636
{
628637
std::vector<string> result;
629-
for (auto node : this->systems_) {
630-
result.push_back(node.first);
638+
for (auto part : this->systems_) {
639+
result.push_back(part.first);
631640
}
632641
return result;
633642
}

system_modes/src/system_modes/mode_manager.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,21 @@ ModeManager::change_mode(
432432
return false;
433433
}
434434

435+
// Mode change redundant?
436+
try {
437+
auto current = mode_inference_->get_or_infer(node_name);
438+
if (current.mode.compare(mode_name) == 0) {
439+
RCLCPP_ERROR(
440+
this->get_logger(),
441+
"Redundant mode change of %s to %s - ignored.",
442+
node_name.c_str(),
443+
mode_name.c_str());
444+
return true;
445+
}
446+
} catch (...) {
447+
// Okay, if we don't know anything about this part, yet
448+
}
449+
435450
// Publish info about this request
436451
auto info = std::make_shared<ModeEvent>();
437452
info->goal_mode.label = mode_name;
@@ -633,7 +648,6 @@ ModeManager::publish_transitions()
633648
info->start_state.label = state_label_(from.state);
634649
info->goal_state.id = to.state;
635650
info->goal_state.label = state_label_(to.state);
636-
this->transition_pub_[part]->publish(TransitionEvent());
637651
this->transition_pub_[part]->publish(*info);
638652
}
639653
if (from.mode.compare(to.mode) != 0) {

0 commit comments

Comments
 (0)