Skip to content

Commit aab5c92

Browse files
committed
feat: update
1 parent 634ee0d commit aab5c92

File tree

13 files changed

+507
-0
lines changed

13 files changed

+507
-0
lines changed

modules/micropython.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ if(ESP_PLATFORM)
99
include(${CMAKE_CURRENT_LIST_DIR}/esp-lib-utils/micropython.cmake)
1010
include(${CMAKE_CURRENT_LIST_DIR}/ESP32_IO_Expander/micropython.cmake)
1111
include(${CMAKE_CURRENT_LIST_DIR}/ESP32_Display_Panel/micropython.cmake)
12+
include(/home/lzw/Arduino/libraries/esp-boost/micropython.cmake)
1213
endif()

ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.base

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,6 @@ CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n
137137
# Further limit total sockets in TIME-WAIT when there are many short-lived
138138
# connections.
139139
CONFIG_LWIP_MAX_ACTIVE_TCP=12
140+
141+
CONFIG_COMPILER_CXX_EXCEPTIONS=y
142+
CONFIG_COMPILER_CXX_RTTI=y
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#pragma once
2+
#include "IService.h"
3+
#include <iostream>
4+
#include <string>
5+
#include <thread>
6+
#include "cJSON.h"
7+
#include <boost/asio.hpp>
8+
#include <map>
9+
#include <mutex>
10+
#include <sstream>
11+
12+
using boost::asio::ip::tcp;
13+
14+
// Thread-safe logger class
15+
class Logger {
16+
public:
17+
enum LogLevel {
18+
DEBUG,
19+
INFO,
20+
WARNING,
21+
ERROR
22+
};
23+
24+
static Logger& getInstance() {
25+
static Logger instance;
26+
return instance;
27+
}
28+
29+
void log(LogLevel level, const std::string& service_name, const std::string& message) {
30+
std::lock_guard<std::mutex> lock(mutex_);
31+
const char* level_str = "";
32+
switch (level) {
33+
case DEBUG: level_str = "DEBUG"; break;
34+
case INFO: level_str = "INFO"; break;
35+
case WARNING: level_str = "WARN"; break;
36+
case ERROR: level_str = "ERROR"; break;
37+
}
38+
39+
std::ostream& out = (level == ERROR || level == WARNING) ? std::cerr : std::cout;
40+
out << "[" << level_str << "][" << service_name << "] " << message << std::endl;
41+
}
42+
43+
private:
44+
Logger() = default;
45+
std::mutex mutex_;
46+
};
47+
48+
// Helper macros for logging
49+
#define LOG_DEBUG(service_name, message) Logger::getInstance().log(Logger::DEBUG, service_name, message)
50+
#define LOG_INFO(service_name, message) Logger::getInstance().log(Logger::INFO, service_name, message)
51+
#define LOG_WARNING(service_name, message) Logger::getInstance().log(Logger::WARNING, service_name, message)
52+
#define LOG_ERROR(service_name, message) Logger::getInstance().log(Logger::ERROR, service_name, message)
53+
54+
class BaseService : public IService {
55+
public:
56+
BaseService(const std::string& address, uint16_t port)
57+
: address_(address), port_(port), acceptor_(io_context_) {
58+
}
59+
60+
virtual ~BaseService() {
61+
stop();
62+
}
63+
64+
void start() override {
65+
LOG_INFO(name(), "Starting service...");
66+
tcp::endpoint endpoint(boost::asio::ip::make_address(address_), port_);
67+
acceptor_.open(endpoint.protocol());
68+
acceptor_.set_option(boost::asio::socket_base::reuse_address(true));
69+
acceptor_.bind(endpoint);
70+
acceptor_.listen();
71+
LOG_INFO(name(), "Listening on " + address_ + ":" + std::to_string(port_));
72+
doAccept();
73+
thread_ = std::thread([this]() {
74+
LOG_INFO(name(), "IO context thread started");
75+
io_context_.run();
76+
LOG_INFO(name(), "IO context thread finished");
77+
});
78+
LOG_INFO(name(), "Service started successfully");
79+
}
80+
81+
void stop() override {
82+
LOG_INFO(name(), "Stopping service...");
83+
io_context_.stop();
84+
if (thread_.joinable()) {
85+
LOG_INFO(name(), "Waiting for IO context thread to join");
86+
thread_.join();
87+
}
88+
LOG_INFO(name(), "Service stopped");
89+
}
90+
91+
protected:
92+
virtual void handleRequest(int client_socket, const cJSON* req) = 0;
93+
94+
void sendResponse(int client_socket, const cJSON* response) {
95+
char* response_str = cJSON_PrintUnformatted(response);
96+
std::string response_str_with_newline = std::string(response_str) + "\n";
97+
98+
LOG_INFO(name(), "Sending response to socket " + std::to_string(client_socket) +
99+
": " + response_str);
100+
101+
auto socket_ptr = findSocket(client_socket);
102+
if (socket_ptr) {
103+
LOG_INFO(name(), "Found socket, sending data...");
104+
try {
105+
// 使用同步写入替代异步写入,确保数据完整发送
106+
boost::system::error_code ec;
107+
boost::asio::write(*socket_ptr, boost::asio::buffer(response_str_with_newline), ec);
108+
109+
if (ec) {
110+
LOG_ERROR(name(), "Error sending response to socket " +
111+
std::to_string(client_socket) + ": " + ec.message());
112+
} else {
113+
LOG_INFO(name(), "Successfully sent " + std::to_string(response_str_with_newline.length()) +
114+
" bytes to socket " + std::to_string(client_socket));
115+
}
116+
cJSON_free(response_str);
117+
118+
// 添加一个短暂延迟,确保数据到达客户端
119+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
120+
} catch (const std::exception& e) {
121+
LOG_ERROR(name(), "Exception while sending response: " + std::string(e.what()));
122+
cJSON_free(response_str);
123+
}
124+
} else {
125+
LOG_ERROR(name(), "Socket " + std::to_string(client_socket) +
126+
" not found, response not sent");
127+
cJSON_free(response_str);
128+
}
129+
}
130+
131+
private:
132+
void doAccept() {
133+
LOG_INFO(name(), "Setting up async accept for new connections");
134+
auto socket = std::make_shared<tcp::socket>(io_context_);
135+
acceptor_.async_accept(*socket, [this, socket](boost::system::error_code ec) {
136+
if (!ec) {
137+
int socket_fd = socket->native_handle();
138+
socket_map_[socket_fd] = socket;
139+
std::stringstream ss;
140+
ss << "Accepted new connection, socket_fd: " << socket_fd
141+
<< ", remote endpoint: " << socket->remote_endpoint();
142+
LOG_INFO(name(), ss.str());
143+
handleClient(socket_fd, socket);
144+
} else {
145+
LOG_ERROR(name(), "Error accepting connection: " + ec.message());
146+
}
147+
doAccept(); // Continue accepting the next client
148+
});
149+
}
150+
151+
void handleClient(int socket_fd, std::shared_ptr<tcp::socket> socket) {
152+
LOG_INFO(name(), "Setting up async read for socket " + std::to_string(socket_fd));
153+
auto buf = std::make_shared<boost::asio::streambuf>();
154+
boost::asio::async_read_until(*socket, *buf, "\n",
155+
[this, socket_fd, socket, buf](boost::system::error_code ec, std::size_t bytes_transferred) {
156+
if (ec) {
157+
if (ec == boost::asio::error::eof ||
158+
ec == boost::asio::error::connection_reset ||
159+
ec == boost::asio::error::connection_aborted) {
160+
LOG_INFO(name(), "Client closed connection on socket " + std::to_string(socket_fd));
161+
} else {
162+
LOG_ERROR(name(), "Error while reading from socket " + std::to_string(socket_fd) +
163+
": " + ec.message());
164+
}
165+
166+
// Close socket properly
167+
boost::system::error_code close_ec;
168+
socket->close(close_ec);
169+
if (close_ec) {
170+
LOG_ERROR(name(), "Error closing socket " + std::to_string(socket_fd) +
171+
": " + close_ec.message());
172+
}
173+
174+
LOG_INFO(name(), "Removing socket " + std::to_string(socket_fd) + " from map");
175+
socket_map_.erase(socket_fd);
176+
return;
177+
}
178+
179+
LOG_INFO(name(), "Received " + std::to_string(bytes_transferred) +
180+
" bytes from socket " + std::to_string(socket_fd));
181+
182+
std::istream is(buf.get());
183+
std::string line;
184+
std::getline(is, line);
185+
LOG_INFO(name(), "Received data: " + line);
186+
187+
cJSON* req = cJSON_Parse(line.c_str());
188+
if (req) {
189+
LOG_INFO(name(), "Successfully parsed JSON request");
190+
handleRequest(socket_fd, req);
191+
cJSON_Delete(req);
192+
} else {
193+
LOG_ERROR(name(), "Error parsing JSON request: " + line);
194+
const char* error_ptr = cJSON_GetErrorPtr();
195+
if (error_ptr) {
196+
LOG_ERROR(name(), "Error before: " + std::string(error_ptr));
197+
}
198+
cJSON* error_response = cJSON_CreateObject();
199+
cJSON_AddStringToObject(error_response, "error", "Invalid JSON");
200+
sendResponse(socket_fd, error_response);
201+
cJSON_Delete(error_response);
202+
}
203+
204+
// Continue reading from this client
205+
LOG_INFO(name(), "Setting up next async read for socket " + std::to_string(socket_fd));
206+
handleClient(socket_fd, socket);
207+
});
208+
}
209+
210+
std::shared_ptr<tcp::socket> findSocket(int socket_fd) {
211+
LOG_INFO(name(), "Looking for socket " + std::to_string(socket_fd) + " in map");
212+
auto it = socket_map_.find(socket_fd);
213+
if (it != socket_map_.end()) {
214+
LOG_INFO(name(), "Socket " + std::to_string(socket_fd) + " found in map");
215+
return it->second;
216+
}
217+
LOG_INFO(name(), "Socket " + std::to_string(socket_fd) + " not found in map");
218+
return nullptr;
219+
}
220+
221+
protected:
222+
std::string address_;
223+
uint16_t port_;
224+
boost::asio::io_context io_context_;
225+
tcp::acceptor acceptor_;
226+
std::thread thread_;
227+
std::map<int, std::shared_ptr<tcp::socket>> socket_map_;
228+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
class IService {
6+
public:
7+
virtual ~IService() = default;
8+
virtual void start() = 0;
9+
virtual void stop() = 0;
10+
virtual std::string name() const = 0;
11+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
#include "BaseService.h"
3+
4+
class MediaPlayerService : public BaseService {
5+
public:
6+
MediaPlayerService(const std::string& address, uint16_t port)
7+
: BaseService(address, port) {}
8+
9+
std::string name() const override { return "MediaPlayer"; }
10+
11+
protected:
12+
void handleRequest(int client_socket, const cJSON* req) override {
13+
cJSON* method = cJSON_GetObjectItem(req, "method");
14+
if (method && cJSON_IsString(method)) {
15+
std::string method_str = method->valuestring;
16+
char* req_str = cJSON_Print(req);
17+
LOG_INFO(name(), "Received request: " + std::string(req_str));
18+
cJSON_free(req_str);
19+
20+
cJSON* response = cJSON_CreateObject();
21+
if (method_str == "media.play") {
22+
cJSON_AddStringToObject(response, "status", "playing");
23+
} else if (method_str == "media.pause") {
24+
cJSON_AddStringToObject(response, "status", "paused");
25+
} else if (method_str == "media.stop") {
26+
cJSON_AddStringToObject(response, "status", "stopped");
27+
} else {
28+
cJSON_AddStringToObject(response, "error", "unknown method");
29+
}
30+
sendResponse(client_socket, response);
31+
cJSON_Delete(response);
32+
} else {
33+
LOG_ERROR(name(), "Missing or invalid method in request");
34+
cJSON* error_response = cJSON_CreateObject();
35+
cJSON_AddStringToObject(error_response, "error", "missing or invalid method");
36+
sendResponse(client_socket, error_response);
37+
cJSON_Delete(error_response);
38+
}
39+
}
40+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// ServiceEntry.h
2+
#pragma once
3+
#include <functional>
4+
#include <memory>
5+
#include <string>
6+
#include "IService.h"
7+
8+
struct ServiceEntry {
9+
std::string name;
10+
std::shared_ptr<IService> service;
11+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#pragma once
2+
#include <map>
3+
#include <memory>
4+
#include <string>
5+
#include <functional>
6+
#include <vector>
7+
#include "IService.h"
8+
#include "ServiceEntry.h"
9+
#include "BaseService.h" // Include for Logger
10+
11+
class ServiceManager {
12+
private:
13+
static constexpr const char* TAG = "ServiceManager";
14+
15+
public:
16+
void registerService(const std::shared_ptr<IService>& service) {
17+
LOG_INFO(TAG, "Registering service: " + service->name());
18+
services_[service->name()] = { service->name(), service };
19+
}
20+
21+
void startAll() {
22+
LOG_INFO(TAG, "Starting all services...");
23+
for (auto& [name, entry] : services_) {
24+
entry.service->start();
25+
}
26+
LOG_INFO(TAG, "All services started");
27+
}
28+
29+
void stopAll() {
30+
LOG_INFO(TAG, "Stopping all services...");
31+
for (auto& [name, entry] : services_) {
32+
entry.service->stop();
33+
}
34+
LOG_INFO(TAG, "All services stopped");
35+
}
36+
37+
std::shared_ptr<IService> getService(const std::string& name) {
38+
auto it = services_.find(name);
39+
if (it != services_.end()) {
40+
LOG_INFO(TAG, "Service found: " + name);
41+
return it->second.service;
42+
}
43+
LOG_WARNING(TAG, "Service not found: " + name);
44+
return nullptr;
45+
}
46+
47+
std::vector<std::string> listServices() const {
48+
std::vector<std::string> names;
49+
for (const auto& [name, entry] : services_) {
50+
names.push_back(name);
51+
}
52+
LOG_INFO(TAG, "Listed " + std::to_string(names.size()) + " services");
53+
return names;
54+
}
55+
56+
private:
57+
std::map<std::string, ServiceEntry> services_;
58+
};

0 commit comments

Comments
 (0)