Skip to content
Draft
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
70 changes: 70 additions & 0 deletions src/ros2_medkit_fault_detection/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2026 mfaferek93
#
# 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 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.

cmake_minimum_required(VERSION 3.8)
project(ros2_medkit_fault_detection)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(ros2_medkit_cmake REQUIRED)
include(ROS2MedkitWarnings)

find_package(ament_cmake REQUIRED)

# Header-only INTERFACE library: the detection logic is compiled directly into
# each consuming plugin (see header rationale).
add_library(ros2_medkit_fault_detection INTERFACE)
target_include_directories(ros2_medkit_fault_detection INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(ros2_medkit_fault_detection INTERFACE cxx_std_17)

install(TARGETS ros2_medkit_fault_detection
EXPORT export_${PROJECT_NAME}
)
install(DIRECTORY include/
DESTINATION include
)

ament_export_targets(export_${PROJECT_NAME})
ament_export_include_directories(include)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# clang_format is registered explicitly below; excluding it here prevents
# ament_lint_auto from registering a second clang_format test.
list(APPEND AMENT_LINT_AUTO_EXCLUDE
ament_cmake_uncrustify
ament_cmake_cpplint
ament_cmake_clang_tidy
ament_cmake_clang_format)
ament_lint_auto_find_test_dependencies()

find_package(ament_cmake_clang_format QUIET)
if(ament_cmake_clang_format_FOUND)
file(GLOB_RECURSE _format_files "include/*.hpp" "test/*.cpp")
ament_clang_format(${_format_files}
CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../../.clang-format")
endif()

find_package(ament_cmake_gtest REQUIRED)
ament_add_gtest(test_fault_detection test/test_fault_detection.cpp)
target_link_libraries(test_fault_detection ros2_medkit_fault_detection)

ros2_medkit_relax_vendor_warnings()
endif()

ament_package()
55 changes: 55 additions & 0 deletions src/ros2_medkit_fault_detection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ros2_medkit_fault_detection

Shared, protocol-agnostic fault-detection model for medkit gateway plugins.
A single evaluator maps a raw value read from any source (OPC UA, S7, Modbus,
ADS, ...) to the set of faults it implies, so every protocol plugin detects
faults the same way.

## Detection modes

Composable per mapped point in a plugin's node map:

- **threshold** (`ThresholdRule`) - numeric above/below a setpoint. A boolean
point is alarm-on-true.
- **status word** (`StatusWordRule`) - decode named bits of an integer status
register; one fault per configured bit.
- **fault enum** (`EnumMapRule`) - map a fault-code register value to a fault
code + text; one fault per configured code, with an `ok_value` meaning
"no fault".

## API

```cpp
#include "ros2_medkit_fault_detection/fault_detection.hpp"
namespace fd = ros2_medkit::fault_detection;

// Pure evaluator: value + rule -> set of (active/inactive) fault signals.
std::vector<fd::FaultSignal> signals = fd::evaluate(value, rule);

// Stateful raise/clear edge detector over successive evaluations.
fd::FaultTransitionTracker tracker;
std::vector<fd::FaultSignal> transitions = tracker.apply(signals); // raises + clears
```

`evaluate` reports the full set of faults a rule governs (each flagged active
or inactive). `FaultTransitionTracker` keeps the last-known state per
`fault_code` and emits only the raise/clear edges, which a plugin forwards to
the fault manager as report/clear.

## Placement and packaging

This is a **header-only INTERFACE package**: the detection logic is compiled
directly into each consuming plugin. Protocol plugins are built as MODULE
libraries that resolve gateway symbols from the host process at load time
(`-Wl,--unresolved-symbols=ignore-all`); a separately linked shared library
would not be present in that host, so the logic is compiled in instead. The
header has no ROS or protocol dependencies, which keeps the evaluator a pure,
trivially unit-testable function.

It lives in the open `ros2_medkit` repo so both the open OPC UA plugin
(`ros2_medkit_opcua`) and future protocol plugins can depend on it.

## Consumers

`ros2_medkit_opcua` lowers its `alarm:`, `status_bits:` and `fault_enum:`
node-map blocks onto `DetectionRule` and evaluates them through this module.
Loading
Loading