Skip to content

Commit a821ca8

Browse files
authored
feat: subscribe topics fetched from coScout (#23)
* feat: subscribe topics fetched from coScout * nit * version * fix: bugs in humble
1 parent fc7cd62 commit a821ca8

File tree

18 files changed

+608
-176
lines changed

18 files changed

+608
-176
lines changed

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ if(NOT CMAKE_CXX_STANDARD)
2525
endif()
2626

2727
if(NOT CMAKE_BUILD_TYPE)
28-
set(CMAKE_BUILD_TYPE Debug)
28+
set(CMAKE_BUILD_TYPE RelWithDebInfo)
29+
endif()
30+
31+
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
32+
add_definitions(-DDEBUG_BUILD)
2933
endif()
3034

3135
macro(enable_strict_compiler_warnings target)
@@ -67,6 +71,7 @@ add_library(colistener_base SHARED
6771
listener_base/src/actions/agi_action.cpp
6872
listener_base/src/persistence/database_manager.cpp
6973
listener_base/src/utils/logger.cpp
74+
listener_base/src/utils/curl_client.cpp
7075

7176
${CMAKE_CURRENT_BINARY_DIR}/colistener_base/src/version.cpp
7277
)

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ Install using apt
101101

102102
This project uses GitHub Actions to automatically build and publish Debian packages. When a new tag is pushed or a new release is created, the workflow automatically updates the version number and builds the corresponding Debian package.
103103

104+
## Caution
105+
106+
coListener will record all the messages it subscribes to and parses in its log file, in order to locate and troubleshoot the problem, so please don't use colistener to subscribe to topics with large amount of data (e.g. sensor_msgs/Image, etc.) to prevent colistener from taking up a lot of disk IO and storage space.
107+
104108
## Development Guide
105109

106110
* Adding a new action type

README_CN.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ ROS 环境。
103103
此项目使用 GitHub Actions 自动构建和发布 Debian 包。当推送新的标签或创建新的发布时,工作流会自动更新版本号并构建对应的
104104
Debian 包。
105105

106+
## 注意事项
107+
108+
coListener 会将其订阅并解析的所有 message 记录在其 log 文件中,以便进行问题定位及排查,所以,请勿使用 colistener 订阅大数据量的 topic (例如 sensor_msgs/Image 等),以防 colistener 占用大量磁盘 IO 和存储空间
109+
106110
## 开发指南
107111

108112
* 添加新的动作类型

listener_base/include/actions/common_action.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#define ACTIONS__COMMON_ACTION_HPP_
1717

1818
#include "actions/action.hpp"
19-
#include <curl/curl.h>
19+
#include "utils/curl_client.hpp"
2020
#include <string>
2121
#include <vector>
2222

@@ -29,10 +29,9 @@ class CommonAction final : public Action {
2929
bool execute(const std::vector<MessageCache>& messages) override;
3030

3131
private:
32-
CURL* curl_;
32+
CurlClient curl_client_;
33+
std::map<std::string, std::string> headers_;
3334
std::string endpoint_;
34-
struct curl_slist* headers_;
35-
FILE* dev_null_;
3635
};
3736

3837
} // namespace colistener

listener_base/include/colistener.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ extern const char GIT_HASH[];
2626

2727
constexpr char DEFAULT_URL[] = "http://localhost";
2828
constexpr char DEFAULT_PORT[] = "22524";
29-
constexpr char DEFAULT_ROUTE[] = "/ruleEngine/messages";
29+
constexpr char SEND_MESSAGES[] = "/ruleEngine/messages";
30+
constexpr char ACTIVE_TOPICS[] = "/ruleEngine/activeTopics";
3031

3132
enum class RosDataType
3233
{
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2025 coScene
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+
#ifndef UTILS__CURL_CLIENT_HPP_
16+
#define UTILS__CURL_CLIENT_HPP_
17+
18+
#include <string>
19+
#include <map>
20+
#include <curl/curl.h>
21+
#include <utils/json.hpp>
22+
23+
namespace colistener {
24+
25+
struct HttpResponse {
26+
int status_code;
27+
std::string body;
28+
std::map<std::string, std::string> headers;
29+
bool success;
30+
std::string error_message;
31+
};
32+
33+
class CurlClient {
34+
public:
35+
CurlClient();
36+
~CurlClient();
37+
38+
CurlClient(const CurlClient&) = delete;
39+
CurlClient& operator=(const CurlClient&) = delete;
40+
41+
HttpResponse get(const std::string& url,
42+
const std::map<std::string, std::string>& headers = {});
43+
44+
HttpResponse post(const std::string& url,
45+
const nlohmann::json& data,
46+
const std::map<std::string, std::string>& headers = {});
47+
48+
HttpResponse post(const std::string& url,
49+
const std::string& data,
50+
const std::map<std::string, std::string>& headers = {});
51+
52+
void setTimeout(int timeout_seconds);
53+
54+
void setVerifySSL(bool verify);
55+
56+
private:
57+
CURL* curl_;
58+
struct curl_slist* header_list_;
59+
int timeout_seconds_;
60+
bool verify_ssl_;
61+
62+
bool initCurl();
63+
64+
HttpResponse executeRequest();
65+
66+
void cleanup();
67+
68+
void cleanupHeaders();
69+
};
70+
71+
} // namespace colistener
72+
73+
#endif // UTILS__CURL_CLIENT_HPP_
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2025 coScene
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+
#ifndef UTILS__VECTOR_UTILS_HPP_
16+
#define UTILS__VECTOR_UTILS_HPP_
17+
18+
#include <vector>
19+
#include <unordered_set>
20+
#include <algorithm>
21+
22+
namespace colistener {
23+
24+
template<typename T>
25+
struct SetDiff {
26+
std::vector<T> missing;
27+
std::vector<T> added;
28+
29+
bool isIdentical() const {
30+
return missing.empty() && added.empty();
31+
}
32+
};
33+
34+
template<typename T>
35+
SetDiff<T> findSetsDifference(const std::set<T>& A, const std::set<T>& B) {
36+
SetDiff<T> result;
37+
38+
std::set_difference(A.begin(), A.end(), B.begin(), B.end(),
39+
std::inserter(result.missing, result.missing.begin()));
40+
41+
std::set_difference(B.begin(), B.end(), A.begin(), A.end(),
42+
std::inserter(result.added, result.added.begin()));
43+
44+
return result;
45+
}
46+
} // namespace colistener
47+
48+
#endif // UTILS__VECTOR_UTILS_HPP_

listener_base/src/actions/agi_action.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ AgiAction::AgiAction() : curl_(nullptr), headers_(nullptr), dev_null_(nullptr) {
3333
endpoint_ = std::string(DEFAULT_URL) +
3434
":" +
3535
std::string(DEFAULT_PORT) +
36-
std::string(DEFAULT_ROUTE);
36+
std::string(SEND_MESSAGES);
3737

3838
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, dev_null_);
3939
}

listener_base/src/actions/common_action.cpp

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,17 @@
1818
#include "utils/logger.hpp"
1919

2020
namespace colistener {
21-
CommonAction::CommonAction() : curl_(nullptr), headers_(nullptr), dev_null_(nullptr) {
22-
curl_ = curl_easy_init();
23-
if (!curl_) {
24-
throw std::runtime_error("Failed to initialize CURL");
25-
}
26-
27-
dev_null_ = fopen("/dev/null", "w");
28-
if (!dev_null_) {
29-
curl_easy_cleanup(curl_);
30-
throw std::runtime_error("Failed to open /dev/null");
31-
}
32-
33-
headers_ = curl_slist_append(nullptr, "Content-Type: application/json");
21+
CommonAction::CommonAction() {
22+
headers_["Content-Type"] = "application/json";
23+
headers_["User-Agent"] = "coListener/1.0";
24+
3425
endpoint_ = std::string(DEFAULT_URL) +
3526
":" +
3627
std::string(DEFAULT_PORT) +
37-
std::string(DEFAULT_ROUTE);
38-
39-
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, dev_null_);
28+
std::string(SEND_MESSAGES);
4029
}
4130

42-
CommonAction::~CommonAction() {
43-
if (headers_) {
44-
curl_slist_free_all(headers_);
45-
}
46-
if (curl_) {
47-
curl_easy_cleanup(curl_);
48-
}
49-
if (dev_null_) {
50-
fclose(dev_null_);
51-
}
52-
}
31+
CommonAction::~CommonAction() = default;
5332

5433
bool CommonAction::execute(const std::vector<MessageCache>& messages) {
5534
if (messages.empty()) return true;
@@ -82,31 +61,16 @@ bool CommonAction::execute(const std::vector<MessageCache>& messages) {
8261

8362
// Log request details
8463
COLOG_DEBUG("Sending request to endpoint: %s", endpoint_.c_str());
85-
COLOG_DEBUG("Request payload size: %zu bytes", json_str.length());
8664
// Log detailed request payload for debugging
87-
COLOG_DEBUG("Request payload: %s", json_str.c_str());
65+
COLOG_INFO("Request payload: %s", json_str.c_str());
8866

89-
curl_easy_setopt(curl_, CURLOPT_URL, endpoint_.c_str());
90-
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_);
91-
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, json_str.c_str());
92-
curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, json_str.length());
9367

94-
const CURLcode res = curl_easy_perform(curl_);
95-
if (res != CURLE_OK) {
96-
// Log CURL errors
97-
COLOG_ERROR("CURL request failed: %s", curl_easy_strerror(res));
98-
return false;
99-
}
68+
HttpResponse post_response = curl_client_.post(endpoint_, root, headers_);
10069

101-
int64_t http_code = 0;
102-
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
103-
if (http_code >= 200 && http_code < 300) {
104-
// Log successful response
105-
COLOG_INFO("Request successful, HTTP code: %ld", http_code);
70+
if (post_response.success) {
10671
return true;
10772
} else {
108-
// Log failed response
109-
COLOG_ERROR("Request failed with HTTP code: %ld", http_code);
73+
COLOG_ERROR("POST request failed: %s", post_response.error_message.c_str());
11074
return false;
11175
}
11276
}

0 commit comments

Comments
 (0)