Skip to content

Commit 2717ab1

Browse files
committed
wip
1 parent 10725e4 commit 2717ab1

File tree

11 files changed

+710
-358
lines changed

11 files changed

+710
-358
lines changed

nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_hesai/hesai_cmd_response.hpp

+86-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
#include <boost/format.hpp>
2121

2222
#include <array>
23+
#include <bitset>
2324
#include <cstdint>
25+
#include <cstring>
26+
#include <iomanip>
2427
#include <ostream>
2528
#include <string>
29+
#include <vector>
2630

2731
using namespace boost::endian; // NOLINT(build/namespaces)
2832

@@ -217,7 +221,7 @@ struct HesaiInventory
217221
return os;
218222
}
219223

220-
std::string get_str_model()
224+
[[nodiscard]] std::string get_str_model() const
221225
{
222226
switch (model) {
223227
case 0:
@@ -399,7 +403,7 @@ struct HesaiLidarStatus
399403
return os;
400404
}
401405

402-
std::string get_str_gps_pps_lock()
406+
[[nodiscard]] std::string get_str_gps_pps_lock() const
403407
{
404408
switch (gps_pps_lock) {
405409
case 1:
@@ -410,7 +414,8 @@ struct HesaiLidarStatus
410414
return "Unknown";
411415
}
412416
}
413-
std::string get_str_gps_gprmc_status()
417+
418+
[[nodiscard]] std::string get_str_gps_gprmc_status() const
414419
{
415420
switch (gps_gprmc_status) {
416421
case 1:
@@ -421,7 +426,8 @@ struct HesaiLidarStatus
421426
return "Unknown";
422427
}
423428
}
424-
std::string get_str_ptp_clock_status()
429+
430+
[[nodiscard]] std::string get_str_ptp_clock_status() const
425431
{
426432
switch (ptp_clock_status) {
427433
case 0:
@@ -520,6 +526,82 @@ struct HesaiLidarMonitor
520526
}
521527
};
522528

529+
struct HesaiFaultModeInfo
530+
{
531+
char work_mode;
532+
char fault_code[4];
533+
534+
[[nodiscard]] std::string describeWorkMode() const
535+
{
536+
std::string prefix = std::string(&work_mode, 1) + " - ";
537+
switch (work_mode) {
538+
case '0':
539+
return prefix + "Energy-Saving";
540+
case '1':
541+
return prefix + "Standard";
542+
case '2':
543+
return prefix + "Standby";
544+
case '3':
545+
return prefix + "High-Temp-Shutdown";
546+
case '4':
547+
return prefix + "Other-Shutdown";
548+
}
549+
550+
return prefix + "Unknown";
551+
}
552+
553+
[[nodiscard]] std::string describeFaultCode() const
554+
{
555+
std::string hex = std::string(fault_code, sizeof(fault_code));
556+
uint8_t fault_code = std::stoul(hex, nullptr, 16);
557+
558+
std::bitset<8> bits{fault_code};
559+
std::vector<std::string> fault_messages;
560+
561+
if (bits[0]) {
562+
fault_messages.emplace_back("energy-saving fault");
563+
}
564+
565+
if (bits[4]) {
566+
fault_messages.emplace_back("high-temp shutdown fault");
567+
}
568+
569+
if (bits[5]) {
570+
fault_messages.emplace_back("other shutdown fault");
571+
}
572+
573+
// If any other fault bits are set, there are undocumented faults
574+
bits.reset(0);
575+
bits.reset(4);
576+
bits.reset(5);
577+
if (bits.any()) {
578+
fault_messages.emplace_back(std::to_string(bits.count()) + " unknown faults");
579+
}
580+
581+
if (fault_messages.empty()) {
582+
return "OK";
583+
}
584+
585+
return "Fault code " + hex + ": " + boost::join(fault_messages, ", ");
586+
}
587+
588+
bool ok()
589+
{
590+
bool work_mode_ok = work_mode != '3' && work_mode != '4';
591+
bool fault_code_ok = std::strncmp(fault_code, "0x00", sizeof(fault_code));
592+
return work_mode_ok && fault_code_ok;
593+
}
594+
595+
friend std::ostream & operator<<(std::ostream & os, nebula::HesaiFaultModeInfo const & arg)
596+
{
597+
os << "work_mode: " << arg.describeWorkMode();
598+
os << ", ";
599+
os << "fault_code: " << arg.describeFaultCode();
600+
601+
return os;
602+
}
603+
};
604+
523605
#pragma pack(pop)
524606

525607
} // namespace nebula

nebula_hw_interfaces/include/nebula_hw_interfaces/nebula_hw_interfaces_hesai/hesai_hw_interface.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const uint8_t PTC_COMMAND_GET_PTP_CONFIG = 0x26;
7575
const uint8_t PTC_COMMAND_RESET = 0x25;
7676
const uint8_t PTC_COMMAND_SET_ROTATE_DIRECTION = 0x2a;
7777
const uint8_t PTC_COMMAND_LIDAR_MONITOR = 0x27;
78+
const uint8_t PTC_COMMAND_GET_FAULT_MODE_INFO = 0x8d;
7879

7980
const uint8_t PTC_ERROR_CODE_NO_ERROR = 0x00;
8081
const uint8_t PTC_ERROR_CODE_INVALID_INPUT_PARAM = 0x01;
@@ -259,6 +260,9 @@ class HesaiHwInterface
259260
/// @brief Getting data with PTC_COMMAND_GET_LIDAR_STATUS
260261
/// @return Resulting status
261262
HesaiLidarStatus GetLidarStatus();
263+
/// @brief Getting data with PTC_COMMAND_GET_FAULT_MODE_INFO
264+
/// @return Resulting status
265+
HesaiFaultModeInfo GetFaultModeInfo();
262266
/// @brief Setting value with PTC_COMMAND_SET_SPIN_RATE
263267
/// @param rpm Spin rate
264268
/// @return Resulting status

nebula_hw_interfaces/src/nebula_hesai_hw_interfaces/hesai_hw_interface.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "nebula_hw_interfaces/nebula_hw_interfaces_hesai/hesai_hw_interface.hpp"
44

5+
#include "nebula_hw_interfaces/nebula_hw_interfaces_hesai/hesai_cmd_response.hpp"
6+
57
// #define WITH_DEBUG_STDOUT_HESAI_HW_INTERFACE
68

79
#ifdef WITH_DEBUG_STDOUT_HESAI_HW_INTERFACE
@@ -343,6 +345,13 @@ HesaiLidarStatus HesaiHwInterface::GetLidarStatus()
343345
return CheckSizeAndParse<HesaiLidarStatus>(response);
344346
}
345347

348+
HesaiFaultModeInfo HesaiHwInterface::GetFaultModeInfo()
349+
{
350+
auto response_or_err = SendReceive(PTC_COMMAND_GET_FAULT_MODE_INFO);
351+
auto response = response_or_err.value_or_throw(PrettyPrintPTCError(response_or_err.error_or({})));
352+
return CheckSizeAndParse<HesaiFaultModeInfo>(response);
353+
}
354+
346355
Status HesaiHwInterface::SetSpinRate(uint16_t rpm)
347356
{
348357
std::vector<unsigned char> request_payload;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2024 TIER IV, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <diagnostic_updater/diagnostic_status_wrapper.hpp>
18+
#include <nebula_hw_interfaces/nebula_hw_interfaces_hesai/hesai_hw_interface.hpp>
19+
#include <rclcpp/time.hpp>
20+
21+
#include <functional>
22+
#include <map>
23+
#include <string>
24+
25+
namespace nebula::ros::hw_monitor
26+
{
27+
28+
class DiagnosticProvider
29+
{
30+
public:
31+
using hook_t = std::function<void(diagnostic_updater::DiagnosticStatusWrapper &)>;
32+
33+
virtual std::map<std::string, hook_t> getHooks() = 0;
34+
virtual void fetch(drivers::HesaiHwInterface & hw_interface, rclcpp::Clock & clock) = 0;
35+
36+
[[nodiscard]] virtual std::optional<rclcpp::Time> getLastUpdate() const = 0;
37+
38+
[[nodiscard]] virtual std::string getName() const = 0;
39+
};
40+
41+
} // namespace nebula::ros::hw_monitor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2024 TIER IV, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "nebula_ros/hesai/hw_monitor/diagnostic_provider.hpp"
18+
19+
#include <diagnostic_updater/diagnostic_status_wrapper.hpp>
20+
21+
#include <diagnostic_msgs/msg/detail/diagnostic_status__struct.hpp>
22+
23+
#include <boost/lexical_cast/bad_lexical_cast.hpp>
24+
#include <boost/property_tree/ptree.hpp>
25+
26+
#include <map>
27+
#include <memory>
28+
#include <string>
29+
#include <vector>
30+
31+
namespace nebula::ros::hw_monitor
32+
{
33+
class HttpLidarMonitorProvider : public DiagnosticProvider
34+
{
35+
public:
36+
std::map<std::string, hook_t> getHooks() override
37+
{
38+
return {{"hesai_voltage", [this](auto & diagnostics) { updateVoltage(diagnostics); }}};
39+
}
40+
41+
void fetch(drivers::HesaiHwInterface & hw_interface, rclcpp::Clock & clock) override
42+
{
43+
hw_interface.GetLidarMonitorAsyncHttp([&](const std::string & str) {
44+
std::scoped_lock lock(mtx_lidar_monitor_);
45+
current_lidar_monitor_time_ = std::make_unique<rclcpp::Time>(clock.now());
46+
current_data_ = std::make_unique<boost::property_tree::ptree>(hw_interface.ParseJson(str));
47+
});
48+
}
49+
50+
[[nodiscard]] std::optional<rclcpp::Time> getLastUpdate() const override
51+
{
52+
if (!current_lidar_monitor_time_) return {};
53+
return *current_lidar_monitor_time_;
54+
}
55+
56+
[[nodiscard]] std::string getName() const override { return "LidarMonitor"; }
57+
58+
private:
59+
void updateVoltage(diagnostic_updater::DiagnosticStatusWrapper & diagnostics)
60+
{
61+
std::scoped_lock lock(mtx_lidar_monitor_);
62+
if (!current_data_) {
63+
diagnostics.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, "No data available");
64+
return;
65+
}
66+
67+
uint8_t level = diagnostic_msgs::msg::DiagnosticStatus::OK;
68+
std::vector<std::string> msgs;
69+
70+
std::vector<std::string> keys = {"lidarInCur", "lidarInVol"};
71+
72+
for (const auto & key : keys) {
73+
std::optional<std::string> value_opt{};
74+
std::string full_key = "Body." + key;
75+
76+
try {
77+
auto result = current_data_->get_optional<std::string>(full_key);
78+
if (result.has_value()) {
79+
value_opt.emplace(result.value());
80+
} else {
81+
level = diagnostic_msgs::msg::DiagnosticStatus::ERROR;
82+
msgs.emplace_back("field '" + full_key + "' not present");
83+
}
84+
} catch (boost::bad_lexical_cast & ex) {
85+
level = diagnostic_msgs::msg::DiagnosticStatus::ERROR;
86+
msgs.emplace_back("field '" + full_key + "' not parsed: " + std::string(ex.what()));
87+
}
88+
89+
if (value_opt.has_value()) {
90+
diagnostics.add(key, value_opt.value());
91+
}
92+
}
93+
94+
diagnostics.summary(level, boost::algorithm::join(msgs, ", "));
95+
}
96+
97+
std::unique_ptr<boost::property_tree::ptree> current_data_;
98+
std::unique_ptr<rclcpp::Time> current_lidar_monitor_time_;
99+
std::mutex mtx_lidar_monitor_;
100+
};
101+
} // namespace nebula::ros::hw_monitor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2024 TIER IV, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <iomanip>
18+
#include <sstream>
19+
#include <string>
20+
21+
namespace nebula::ros::hw_monitor
22+
{
23+
24+
inline std::string to_fixed(double value, int decimals)
25+
{
26+
std::stringstream ss;
27+
ss << std::fixed << std::setprecision(decimals) << value;
28+
return ss.str();
29+
}
30+
31+
} // namespace nebula::ros::hw_monitor

0 commit comments

Comments
 (0)