221 lines
7.0 KiB
C++
221 lines
7.0 KiB
C++
#include "HTTPDataSource.h"
|
||
#include "utils/Logger.h"
|
||
#include <nlohmann/json.hpp>
|
||
#include <regex>
|
||
|
||
using json = nlohmann::json;
|
||
|
||
HTTPDataSource::HTTPDataSource(const ConnectionConfig& config)
|
||
: host_(config.host)
|
||
, port_(std::to_string(config.port)) {
|
||
// 设置坐标转换器的参考点(浦东机场坐标)
|
||
coordinateConverter_.setReferencePoint(31.143494, 121.805214);
|
||
}
|
||
|
||
HTTPDataSource::~HTTPDataSource() {
|
||
disconnect();
|
||
}
|
||
|
||
std::optional<VehicleData> HTTPDataSource::getData() {
|
||
if (dataCache_.empty()) {
|
||
Logger::debug("Data cache is empty, attempting to fetch new data");
|
||
if (!fetchData()) {
|
||
Logger::error("Failed to fetch new data");
|
||
return std::nullopt;
|
||
}
|
||
}
|
||
|
||
if (dataCache_.empty()) {
|
||
Logger::debug("No data available after fetch");
|
||
return std::nullopt;
|
||
}
|
||
|
||
auto data = std::move(dataCache_.front());
|
||
dataCache_.pop();
|
||
Logger::debug("Returning vehicle: ", data.id);
|
||
return data;
|
||
}
|
||
|
||
bool HTTPDataSource::connect() {
|
||
try {
|
||
Logger::debug("Connecting to ", host_, ":", port_);
|
||
|
||
auto resolver = asio::ip::tcp::resolver(io_context_);
|
||
auto endpoints = resolver.resolve(host_, port_);
|
||
|
||
socket_ = std::make_unique<asio::ip::tcp::socket>(io_context_);
|
||
asio::connect(*socket_, endpoints);
|
||
|
||
Logger::info("Connected to server");
|
||
return true;
|
||
}
|
||
catch (const std::exception& e) {
|
||
Logger::error("Connection error: ", e.what());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void HTTPDataSource::disconnect() {
|
||
if (socket_ && socket_->is_open()) {
|
||
boost::system::error_code ec;
|
||
socket_->close(ec);
|
||
}
|
||
socket_.reset();
|
||
}
|
||
|
||
bool HTTPDataSource::fetchData() {
|
||
if (!connect()) {
|
||
return false;
|
||
}
|
||
|
||
// 构造 HTTP 请求
|
||
std::stringstream request;
|
||
request << "GET /api/vehicles HTTP/1.1\r\n"
|
||
<< "Host: " << host_ << "\r\n"
|
||
<< "Connection: close\r\n"
|
||
<< "Accept: application/json\r\n"
|
||
<< "\r\n";
|
||
|
||
// 发送请求
|
||
boost::system::error_code ec;
|
||
asio::write(*socket_, asio::buffer(request.str()), ec);
|
||
if (ec) {
|
||
Logger::error("Failed to send request: ", ec.message());
|
||
disconnect();
|
||
return false;
|
||
}
|
||
|
||
// <20><>取响应
|
||
std::string response;
|
||
if (!readResponse(response)) {
|
||
disconnect();
|
||
return false;
|
||
}
|
||
|
||
// 解析数据
|
||
std::vector<VehicleData> vehicles;
|
||
if (parseResponse(response, vehicles)) {
|
||
std::lock_guard<std::mutex> lock(cacheMutex_);
|
||
for (auto& vehicle : vehicles) {
|
||
vehicle.updateLocalPosition(coordinateConverter_);
|
||
dataCache_.push(vehicle);
|
||
}
|
||
Logger::info("Cached ", vehicles.size(), " vehicles");
|
||
}
|
||
|
||
disconnect();
|
||
return !vehicles.empty();
|
||
}
|
||
|
||
bool HTTPDataSource::readResponse(std::string& response) {
|
||
try {
|
||
Logger::debug("Reading response");
|
||
|
||
asio::streambuf response_buf;
|
||
boost::system::error_code ec;
|
||
|
||
// 读取响应头
|
||
size_t header_length = asio::read_until(*socket_, response_buf, "\r\n\r\n", ec);
|
||
if (ec) {
|
||
Logger::error("Failed to read headers: ", ec.message());
|
||
return false;
|
||
}
|
||
|
||
// 提取响应头
|
||
std::string header{asio::buffers_begin(response_buf.data()),
|
||
asio::buffers_begin(response_buf.data()) + header_length};
|
||
Logger::debug("Header received:\n", header);
|
||
|
||
// 解析 Content-Length
|
||
std::regex content_length_regex("Content-Length: (\\d+)");
|
||
std::smatch matches;
|
||
if (!std::regex_search(header, matches, content_length_regex)) {
|
||
Logger::error("No Content-Length found");
|
||
return false;
|
||
}
|
||
|
||
size_t content_length = std::stoul(matches[1]);
|
||
Logger::debug("Content-Length: ", content_length);
|
||
|
||
// 读取响应体
|
||
std::string body;
|
||
size_t total_read = 0;
|
||
while (total_read < content_length) {
|
||
std::vector<char> buf(1024);
|
||
size_t to_read = std::min(buf.size(), content_length - total_read);
|
||
size_t bytes_read = socket_->read_some(asio::buffer(buf, to_read), ec);
|
||
|
||
if (ec) {
|
||
Logger::error("Error reading body: ", ec.message());
|
||
return false;
|
||
}
|
||
|
||
body.append(buf.data(), bytes_read);
|
||
total_read += bytes_read;
|
||
Logger::debug("Read ", total_read, " of ", content_length, " bytes");
|
||
}
|
||
|
||
response = body;
|
||
Logger::info("Successfully read complete response");
|
||
return true;
|
||
}
|
||
catch (const std::exception& e) {
|
||
Logger::error("Exception in readResponse: ", e.what());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool HTTPDataSource::parseResponse(const std::string& response, std::vector<VehicleData>& vehicles) {
|
||
try {
|
||
Logger::debug("Parsing response data, length: ", response.length());
|
||
auto j = json::parse(response);
|
||
|
||
if (!j.contains("vehicles") || !j["vehicles"].is_array()) {
|
||
Logger::error("Invalid response format: missing or invalid 'vehicles' array");
|
||
return false;
|
||
}
|
||
|
||
for (const auto& vehicle : j["vehicles"]) {
|
||
try {
|
||
VehicleData data;
|
||
|
||
// 解析基本信息
|
||
data.id = vehicle["id"].get<std::string>();
|
||
std::string typeStr = vehicle["type"].get<std::string>();
|
||
data.type = (typeStr == "AIRCRAFT") ? VehicleType::AIRCRAFT : VehicleType::VEHICLE;
|
||
|
||
// 解析地理坐标
|
||
data.geo.latitude = vehicle["position"]["latitude"].get<double>();
|
||
data.geo.longitude = vehicle["position"]["longitude"].get<double>();
|
||
|
||
// 解析其他属性
|
||
data.heading = vehicle["heading"].get<double>();
|
||
data.timestamp = vehicle["timestamp"].get<uint64_t>();
|
||
|
||
if (data.type == VehicleType::AIRCRAFT) {
|
||
data.altitude = vehicle["altitude"].get<double>();
|
||
}
|
||
|
||
vehicles.push_back(data);
|
||
Logger::debug("Parsed vehicle: ", data.id,
|
||
", type: ", typeStr,
|
||
", pos: (", data.geo.latitude, ",", data.geo.longitude, ")");
|
||
}
|
||
catch (const std::exception& e) {
|
||
Logger::error("Error parsing vehicle data: ", e.what());
|
||
continue;
|
||
}
|
||
}
|
||
|
||
Logger::info("Successfully parsed ", vehicles.size(), " vehicles");
|
||
return !vehicles.empty();
|
||
}
|
||
catch (const std::exception& e) {
|
||
Logger::error("Error parsing response: ", e.what());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool HTTPDataSource::isAvailable() const {
|
||
return socket_ && socket_->is_open();
|
||
} |