增加红绿灯的两个方向的信号状态处理和前端页面显示
This commit is contained in:
parent
d645dbc486
commit
5f2c0e4d86
@ -9,6 +9,8 @@
|
||||
#include "network/TrafficLightHttpServer.h"
|
||||
#include "config/SystemConfig.h"
|
||||
#include "types/TrafficLightTypes.h"
|
||||
#include "network/WebSocketServer.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
System* System::instance_ = nullptr;
|
||||
|
||||
@ -494,6 +496,16 @@ void System::broadcastPositionUpdate(const MovingObject& obj) {
|
||||
" timestamp=", msg.timestamp);
|
||||
}
|
||||
|
||||
// 新增辅助函数:将 SignalStatus 转换为字符串
|
||||
std::string signalStatusToString(SignalStatus status) {
|
||||
switch (status) {
|
||||
case SignalStatus::RED: return "RED";
|
||||
case SignalStatus::GREEN: return "GREEN";
|
||||
case SignalStatus::YELLOW: return "YELLOW";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
@ -505,8 +517,13 @@ void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
||||
nlohmann::json j = {
|
||||
{"type", "traffic_light_status"},
|
||||
{"id", signal.trafficLightId},
|
||||
{"status", static_cast<int>(signal.status)},
|
||||
{"timestamp", signal.timestamp} };
|
||||
// 修改: 将 status 改为一个包含 ns 和 ew 状态的对象
|
||||
{"status", {
|
||||
{"ns", signalStatusToString(signal.ns_status)},
|
||||
{"ew", signalStatusToString(signal.ew_status)}
|
||||
}},
|
||||
{"timestamp", signal.timestamp}
|
||||
};
|
||||
|
||||
// 添加路口信息
|
||||
if (intersection) {
|
||||
@ -517,12 +534,13 @@ void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
||||
}
|
||||
|
||||
ws_server_->broadcast(j.dump());
|
||||
|
||||
// 修改日志记录
|
||||
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
|
||||
" status=", static_cast<int>(signal.status),
|
||||
" intersection=", intersection ? intersection->id : "",
|
||||
" position=(", intersection ? intersection->position.longitude : 0.0, ",",
|
||||
intersection ? intersection->position.latitude : 0.0, ")",
|
||||
" timestamp=", signal.timestamp);
|
||||
", NS_Status=", signalStatusToString(signal.ns_status),
|
||||
", EW_Status=", signalStatusToString(signal.ew_status),
|
||||
", intersection=", intersection ? intersection->id : "",
|
||||
", timestamp=", signal.timestamp);
|
||||
}
|
||||
|
||||
std::string getRiskLevelString(RiskLevel level) {
|
||||
@ -774,61 +792,65 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
||||
}
|
||||
|
||||
void System::processPushedTrafficLightData(const nlohmann::json& di_data) {
|
||||
const auto& config = SystemConfig::instance();
|
||||
const std::string target_id = config.simulated_mobile_light_target_intersection_id;
|
||||
|
||||
if (target_id.empty()) {
|
||||
Logger::warning("Target intersection ID for pushed traffic light data is not configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用配置的 ID 查找路口信息
|
||||
const Intersection* intersection = intersection_config_.findById(target_id);
|
||||
if (!intersection) {
|
||||
Logger::warning("Configured target intersection ID ", target_id, " not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析 DI 数据获取当前状态 (简化: 只处理南北向)
|
||||
SignalStatus current_status = SignalStatus::UNKNOWN;
|
||||
try {
|
||||
// DI-13: 北绿 (NS Green)
|
||||
const auto& config = SystemConfig::instance();
|
||||
const std::string targetIntersectionId = config.simulated_mobile_light_target_intersection_id;
|
||||
const Intersection* targetIntersection = intersection_config_.findById(targetIntersectionId);
|
||||
|
||||
if (!targetIntersection) {
|
||||
Logger::warning("Configured target intersection ID not found in intersection config: ", targetIntersectionId);
|
||||
return;
|
||||
}
|
||||
|
||||
SignalStatus ns_status = SignalStatus::UNKNOWN;
|
||||
SignalStatus ew_status = SignalStatus::UNKNOWN;
|
||||
|
||||
if (di_data.contains("DI-13") && di_data.at("DI-13").get<int>() == 1) {
|
||||
current_status = SignalStatus::GREEN;
|
||||
}
|
||||
// DI-12: 北黄 (NS Yellow)
|
||||
else if (di_data.contains("DI-12") && di_data.at("DI-12").get<int>() == 1) {
|
||||
current_status = SignalStatus::YELLOW;
|
||||
}
|
||||
// DI-11: 北红 (NS Red)
|
||||
else if (di_data.contains("DI-11") && di_data.at("DI-11").get<int>() == 1) {
|
||||
current_status = SignalStatus::RED;
|
||||
}
|
||||
// 可以添加东西向 DI-14 (E/W Red), DI-15 (E/W Yellow), DI-16 (E/W Green) 的解析逻辑
|
||||
else {
|
||||
ns_status = SignalStatus::GREEN;
|
||||
} else if (di_data.contains("DI-12") && di_data.at("DI-12").get<int>() == 1) {
|
||||
ns_status = SignalStatus::YELLOW;
|
||||
} else if (di_data.contains("DI-11") && di_data.at("DI-11").get<int>() == 1) {
|
||||
ns_status = SignalStatus::RED;
|
||||
} else {
|
||||
Logger::warning("Received DI data has unknown or invalid N/S state: ", di_data.dump());
|
||||
// 可以选择不广播或广播 UNKNOWN
|
||||
// return; // 如果状态无效则不广播
|
||||
}
|
||||
} catch (const json::exception& e) {
|
||||
Logger::error("Error parsing DI data JSON: ", e.what(), ", Data: ", di_data.dump());
|
||||
return;
|
||||
|
||||
if (di_data.contains("DI-16") && di_data.at("DI-16").get<int>() == 1) {
|
||||
ew_status = SignalStatus::GREEN;
|
||||
} else if (di_data.contains("DI-15") && di_data.at("DI-15").get<int>() == 1) {
|
||||
ew_status = SignalStatus::YELLOW;
|
||||
} else if (di_data.contains("DI-14") && di_data.at("DI-14").get<int>() == 1) {
|
||||
ew_status = SignalStatus::RED;
|
||||
} else {
|
||||
Logger::warning("Received DI data has unknown or invalid E/W state: ", di_data.dump());
|
||||
}
|
||||
|
||||
TrafficLightSignal signal;
|
||||
signal.trafficLightId = "DI_PUSHED_" + targetIntersectionId;
|
||||
signal.ns_status = ns_status;
|
||||
signal.ew_status = ew_status;
|
||||
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
if (ws_server_) {
|
||||
nlohmann::json messageJson;
|
||||
messageJson["type"] = "intersection_traffic_light_status";
|
||||
messageJson["intersection_id"] = targetIntersection->id;
|
||||
messageJson["position"] = {
|
||||
{"latitude", targetIntersection->position.latitude},
|
||||
{"longitude", targetIntersection->position.longitude}
|
||||
};
|
||||
messageJson["ns_status"] = static_cast<int>(signal.ns_status);
|
||||
messageJson["ew_status"] = static_cast<int>(signal.ew_status);
|
||||
messageJson["timestamp"] = signal.timestamp;
|
||||
|
||||
ws_server_->broadcast(messageJson.dump());
|
||||
Logger::debug("广播路口交通灯状态: ", messageJson.dump());
|
||||
} else {
|
||||
Logger::warning("WebSocket server is not available, cannot broadcast intersection status.");
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("处理推送的交通信号灯数据时出错: ", e.what());
|
||||
}
|
||||
|
||||
// 创建 TrafficLightSignal 结构体
|
||||
TrafficLightSignal signal;
|
||||
signal.trafficLightId = intersection->trafficLightId; // 使用找到的路口的红绿灯 ID
|
||||
signal.status = current_status;
|
||||
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
signal.intersectionId = intersection->id;
|
||||
signal.latitude = intersection->position.latitude;
|
||||
signal.longitude = intersection->position.longitude;
|
||||
|
||||
Logger::debug("Processing pushed traffic light data for intersection: ", intersection->id,
|
||||
", status: ", static_cast<int>(signal.status));
|
||||
|
||||
// 广播信号状态
|
||||
broadcastTrafficLightStatus(signal);
|
||||
}
|
||||
@ -16,6 +16,7 @@
|
||||
#include "network/MessageTypes.h"
|
||||
#include "config/IntersectionConfig.h"
|
||||
#include "network/TrafficLightHttpServer.h"
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
// 前向声明
|
||||
class DataCollector;
|
||||
@ -43,9 +44,12 @@ public:
|
||||
const SystemConfig& getSystemConfig() const { return SystemConfig::instance(); }
|
||||
const IntersectionConfig& getIntersectionConfig() const { return intersection_config_; }
|
||||
|
||||
// 新增: 处理推送的红绿灯数据
|
||||
// 新增: 处理推送的红绿灯数据 (改回接收 json 对象)
|
||||
void processPushedTrafficLightData(const nlohmann::json& di_data);
|
||||
|
||||
// 处理来自数据源的交通信号灯状态
|
||||
void processTrafficLightStatus(const TrafficLightSignal& signal);
|
||||
|
||||
private:
|
||||
void processLoop();
|
||||
void processCollisions(const std::vector<CollisionRisk>& detectedRisks,
|
||||
|
||||
@ -27,7 +27,9 @@ void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
|
||||
for (const auto& vehicle : vehicles) {
|
||||
if (controllable_vehicles_.isControllable(vehicle.vehicleNo)) {
|
||||
// 根据信号灯状态发送指令
|
||||
switch (signal.status) {
|
||||
// FIXME: 临时修改 - 仅根据南北向状态发送指令,未考虑车辆方向和东西向状态。
|
||||
// 需要根据车辆行驶方向判断应该参考 ns_status 还是 ew_status。
|
||||
switch (signal.ns_status) {
|
||||
case SignalStatus::RED:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::RED);
|
||||
break;
|
||||
|
||||
@ -582,8 +582,8 @@ bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std:
|
||||
for (const auto& item : data) {
|
||||
TrafficLightSignal signal;
|
||||
signal.trafficLightId = item["id"].get<std::string>();
|
||||
signal.status = [&]() {
|
||||
int state = item["state"].get<int>();
|
||||
signal.ns_status = [&]() {
|
||||
int state = item["ns_status"].get<int>();
|
||||
switch (state) {
|
||||
case 0: return SignalStatus::RED;
|
||||
case 1: return SignalStatus::GREEN;
|
||||
|
||||
@ -12,16 +12,10 @@ enum class SignalStatus {
|
||||
|
||||
struct TrafficLightSignal {
|
||||
std::string trafficLightId; // 红绿灯设备 ID
|
||||
SignalStatus status; // 信号灯状态
|
||||
SignalStatus ns_status = SignalStatus::UNKNOWN; // 南北向状态
|
||||
SignalStatus ew_status = SignalStatus::UNKNOWN; // 东西向状态
|
||||
uint64_t timestamp; // 时间戳
|
||||
std::string intersectionId; // 路口编号
|
||||
double latitude; // 路口纬度
|
||||
double longitude; // 路口经度
|
||||
|
||||
static SignalStatus parseStatus(const std::string& status) {
|
||||
if (status == "red") return SignalStatus::RED;
|
||||
if (status == "green") return SignalStatus::GREEN;
|
||||
if (status == "yellow") return SignalStatus::YELLOW;
|
||||
return SignalStatus::UNKNOWN;
|
||||
}
|
||||
};
|
||||
@ -118,7 +118,8 @@ protected:
|
||||
TrafficLightSignal createTestTrafficLight(const std::string& id, int state) {
|
||||
TrafficLightSignal signal;
|
||||
signal.trafficLightId = id;
|
||||
signal.status = static_cast<SignalStatus>(state);
|
||||
signal.ns_status = static_cast<SignalStatus>(state);
|
||||
signal.ew_status = static_cast<SignalStatus>(state);
|
||||
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
return signal;
|
||||
@ -379,9 +380,9 @@ TEST_F(DataCollectorTest, TrafficLightSignalsTest) {
|
||||
EXPECT_EQ(signals.size(), 3);
|
||||
if (signals.size() >= 3) {
|
||||
EXPECT_EQ(signals[0].trafficLightId, "TL001");
|
||||
EXPECT_EQ(signals[0].status, SignalStatus::RED); // RED = 0
|
||||
EXPECT_EQ(signals[0].ns_status, SignalStatus::RED); // RED = 0
|
||||
EXPECT_EQ(signals[1].trafficLightId, "TL002");
|
||||
EXPECT_EQ(signals[1].status, SignalStatus::GREEN); // GREEN = 1
|
||||
EXPECT_EQ(signals[1].ns_status, SignalStatus::GREEN); // GREEN = 1
|
||||
}
|
||||
|
||||
// 停止数据采集
|
||||
|
||||
@ -76,7 +76,8 @@ protected:
|
||||
TrafficLightSignal createTestSignal(const std::string& id, SignalStatus status) {
|
||||
TrafficLightSignal signal;
|
||||
signal.trafficLightId = id;
|
||||
signal.status = status;
|
||||
signal.ns_status = status;
|
||||
signal.ew_status = status;
|
||||
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
return signal;
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>机场车辆监控</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/leaflet-rotatedmarker/leaflet.rotatedMarker.js"></script>
|
||||
<style>
|
||||
@ -11,15 +12,18 @@
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 800px;
|
||||
width: 60%;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#messages {
|
||||
width: 40%;
|
||||
height: 800px;
|
||||
@ -28,15 +32,35 @@
|
||||
padding: 10px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.error { color: red; }
|
||||
.success { color: green; }
|
||||
.info { color: blue; }
|
||||
.position { color: #666; }
|
||||
.warning { color: #f90; }
|
||||
.command { color: #800080; }
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.position {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #f90;
|
||||
}
|
||||
|
||||
.command {
|
||||
color: #800080;
|
||||
}
|
||||
|
||||
.vehicle-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@ -44,6 +68,7 @@
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.aircraft-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
@ -52,6 +77,7 @@
|
||||
border: 2px solid white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aircraft-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@ -63,6 +89,7 @@
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.special-vehicle-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@ -70,6 +97,7 @@
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.intersection-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
@ -77,6 +105,7 @@
|
||||
clip-path: polygon(40% 0%, 60% 0%, 60% 40%, 100% 40%, 100% 60%, 60% 60%, 60% 100%, 40% 100%, 40% 60%, 0% 60%, 0% 40%, 40% 40%);
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.traffic-light {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
@ -84,15 +113,19 @@
|
||||
border: 2px solid white;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.traffic-light-red {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.traffic-light-green {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.traffic-light-yellow {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.countdown-label {
|
||||
background: #000;
|
||||
border: 1px solid #666;
|
||||
@ -105,21 +138,25 @@
|
||||
padding: 1px 2px;
|
||||
line-height: 14px;
|
||||
min-width: 28px;
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.5);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.countdown-red {
|
||||
color: #ff3333;
|
||||
}
|
||||
|
||||
.countdown-green {
|
||||
color: #33ff33;
|
||||
}
|
||||
|
||||
.countdown-yellow {
|
||||
color: #ffff33;
|
||||
}
|
||||
|
||||
.distance-label {
|
||||
background: none;
|
||||
border: none;
|
||||
@ -128,6 +165,7 @@
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.command-text {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -143,23 +181,28 @@
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.safety-border {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.emergency-border {
|
||||
border-color: rgba(255, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.core-border {
|
||||
border-color: rgba(255, 165, 0, 0.6);
|
||||
}
|
||||
|
||||
.warning-border {
|
||||
border-color: rgba(255, 255, 0, 0.4);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>机场车辆监控系统</h2>
|
||||
<div class="container">
|
||||
@ -175,7 +218,7 @@
|
||||
<script>
|
||||
let ws = null;
|
||||
const messagesDiv = document.getElementById('messages');
|
||||
|
||||
|
||||
// 初始化地图
|
||||
const map = L.map('map').setView([36.35305878, 120.08558121], 17);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
@ -231,7 +274,7 @@
|
||||
|
||||
// 存储所有标记
|
||||
const markers = new Map();
|
||||
const trafficLights = new Map();
|
||||
const intersectionMarkers = new Map();
|
||||
const intersections = new Map();
|
||||
|
||||
// 创建自定义图标
|
||||
@ -246,7 +289,7 @@
|
||||
} else {
|
||||
size = [20, 20]; // 其他图标保持原样
|
||||
}
|
||||
|
||||
|
||||
// 如果有指令,创建带指令的图标
|
||||
if (command && className === 'vehicle-icon') {
|
||||
const html = `
|
||||
@ -258,15 +301,15 @@
|
||||
html: html,
|
||||
className: '',
|
||||
iconSize: size,
|
||||
iconAnchor: [size[0]/2, size[1]/2]
|
||||
iconAnchor: [size[0] / 2, size[1] / 2]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 没有指令时,创建普通图标
|
||||
return L.divIcon({
|
||||
className: className,
|
||||
iconSize: size,
|
||||
iconAnchor: [size[0]/2, size[1]/2]
|
||||
iconAnchor: [size[0] / 2, size[1] / 2]
|
||||
});
|
||||
}
|
||||
|
||||
@ -287,29 +330,29 @@
|
||||
const id = data.object_id;
|
||||
const position = [data.position.latitude, data.position.longitude];
|
||||
let iconClass;
|
||||
|
||||
|
||||
// 根据ID前缀确定图标类型
|
||||
if (data.object_type === 'aircraft') {
|
||||
iconClass = 'aircraft-icon';
|
||||
|
||||
|
||||
// 创建或更新安全边框
|
||||
const borders = [
|
||||
{size: 250, class: 'warning-border'}, // 外围预警区
|
||||
{size: 150, class: 'core-border'}, // 核心安全区
|
||||
{size: 100, class: 'emergency-border'} // 紧急制动区
|
||||
{ size: 250, class: 'warning-border' }, // 外围预警区
|
||||
{ size: 150, class: 'core-border' }, // 核心安全区
|
||||
{ size: 100, class: 'emergency-border' } // 紧急制动区
|
||||
];
|
||||
|
||||
|
||||
borders.forEach(border => {
|
||||
const borderId = id + '_' + border.class;
|
||||
let borderMarker = markers.get(borderId);
|
||||
|
||||
|
||||
if (!borderMarker) {
|
||||
// 创建边框标记
|
||||
borderMarker = L.marker(position, {
|
||||
icon: L.divIcon({
|
||||
className: 'safety-border ' + border.class,
|
||||
iconSize: [border.size, border.size],
|
||||
iconAnchor: [border.size/2, border.size/2]
|
||||
iconAnchor: [border.size / 2, border.size / 2]
|
||||
}),
|
||||
rotationAngle: data.heading || 0,
|
||||
rotationOrigin: 'center center'
|
||||
@ -349,90 +392,52 @@
|
||||
}
|
||||
|
||||
// 添加红绿灯状态和倒计时变量
|
||||
let lastTrafficLightState = null;
|
||||
let countdownInterval = null;
|
||||
let countdown = 10; // 10秒倒计时
|
||||
// let lastTrafficLightState = null;
|
||||
// let countdownInterval = null;
|
||||
// let countdown = 10;
|
||||
|
||||
function updateTrafficLight(data) {
|
||||
const id = data.id;
|
||||
// New function to update intersection traffic lights
|
||||
function updateIntersectionTrafficLights(data) {
|
||||
const intersectionId = data.intersection_id;
|
||||
const position = [data.position.latitude, data.position.longitude];
|
||||
const state = data.status === 0 ? 'red' : data.status === 1 ? 'green' : 'yellow';
|
||||
const nsStatus = data.ns_status; // 0: red, 1: green, 2: yellow
|
||||
const ewStatus = data.ew_status; // 0: red, 1: green, 2: yellow
|
||||
|
||||
// 检查是否是西路口(TL001)的红绿灯状态变化
|
||||
if (id === 'TL001' && lastTrafficLightState !== state) {
|
||||
lastTrafficLightState = state;
|
||||
// 重置倒计时
|
||||
countdown = 10;
|
||||
// 清除现有的倒计时
|
||||
if (countdownInterval) {
|
||||
clearInterval(countdownInterval);
|
||||
}
|
||||
// 启动新的倒计时
|
||||
countdownInterval = setInterval(() => {
|
||||
countdown = Math.max(0, countdown - 1);
|
||||
// 更新红绿灯标签显示
|
||||
const light = trafficLights.get(id);
|
||||
if (light) {
|
||||
// 格式化倒计时为 0:SS 格式
|
||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
||||
const label = L.divIcon({
|
||||
className: `countdown-label countdown-${state}`,
|
||||
html: countdownStr,
|
||||
iconSize: [32, 16],
|
||||
iconAnchor: [16, 30]
|
||||
});
|
||||
// 更新或创建倒计时标签
|
||||
if (!light.countdownMarker) {
|
||||
light.countdownMarker = L.marker(position, {
|
||||
icon: label,
|
||||
interactive: false
|
||||
}).addTo(map);
|
||||
} else {
|
||||
light.countdownMarker.setIcon(label);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
const nsColor = nsStatus === 0 ? 'red' : nsStatus === 1 ? 'green' : 'yellow';
|
||||
const ewColor = ewStatus === 0 ? 'red' : ewStatus === 1 ? 'green' : 'yellow';
|
||||
|
||||
let light = trafficLights.get(id);
|
||||
if (!light) {
|
||||
light = L.marker(position, {
|
||||
icon: createIcon(`traffic-light traffic-light-${state}`)
|
||||
}).addTo(map);
|
||||
// 为西路口添加倒计时显示
|
||||
if (id === 'TL001') {
|
||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
||||
const label = L.divIcon({
|
||||
className: `countdown-label countdown-${state}`,
|
||||
html: countdownStr,
|
||||
iconSize: [32, 16],
|
||||
iconAnchor: [16, 30]
|
||||
});
|
||||
light.countdownMarker = L.marker(position, {
|
||||
icon: label,
|
||||
interactive: false
|
||||
}).addTo(map);
|
||||
}
|
||||
trafficLights.set(id, light);
|
||||
// Calculate offset positions for NS and EW lights
|
||||
const nsPositionOffset = [position[0] + 0.00003, position[1]]; // Slightly north
|
||||
const ewPositionOffset = [position[0], position[1] + 0.00003]; // Slightly east
|
||||
|
||||
let currentMarkers = intersectionMarkers.get(intersectionId);
|
||||
if (!currentMarkers) {
|
||||
// Create new marker group
|
||||
currentMarkers = {};
|
||||
currentMarkers.nsMarker = L.marker(nsPositionOffset, {
|
||||
icon: createIcon(`traffic-light traffic-light-${nsColor}`),
|
||||
pane: 'markerPane' // Ensure lights are above roads
|
||||
}).addTo(map).bindTooltip(`Intersection ${intersectionId} (NS)`);
|
||||
|
||||
currentMarkers.ewMarker = L.marker(ewPositionOffset, {
|
||||
icon: createIcon(`traffic-light traffic-light-${ewColor}`),
|
||||
pane: 'markerPane'
|
||||
}).addTo(map).bindTooltip(`Intersection ${intersectionId} (EW)`);
|
||||
|
||||
intersectionMarkers.set(intersectionId, currentMarkers);
|
||||
} else {
|
||||
light.setIcon(createIcon(`traffic-light traffic-light-${state}`));
|
||||
// 更新西路口的倒计时显示
|
||||
if (id === 'TL001' && light.countdownMarker) {
|
||||
const countdownStr = `0:${countdown.toString().padStart(2, '0')}`;
|
||||
const label = L.divIcon({
|
||||
className: `countdown-label countdown-${state}`,
|
||||
html: countdownStr,
|
||||
iconSize: [32, 16],
|
||||
iconAnchor: [16, 30]
|
||||
});
|
||||
light.countdownMarker.setIcon(label);
|
||||
}
|
||||
// Update existing markers
|
||||
currentMarkers.nsMarker.setLatLng(nsPositionOffset);
|
||||
currentMarkers.nsMarker.setIcon(createIcon(`traffic-light traffic-light-${nsColor}`));
|
||||
|
||||
currentMarkers.ewMarker.setLatLng(ewPositionOffset);
|
||||
currentMarkers.ewMarker.setIcon(createIcon(`traffic-light traffic-light-${ewColor}`));
|
||||
}
|
||||
}
|
||||
|
||||
function updateVehicleCommand(vehicleId, commandType) {
|
||||
console.log('更新车辆指令:', vehicleId, commandType);
|
||||
|
||||
|
||||
// 只处理无人车
|
||||
if (!vehicleId.startsWith('QN')) {
|
||||
return;
|
||||
@ -446,7 +451,7 @@
|
||||
|
||||
// 获取指令字母
|
||||
let commandText = '';
|
||||
switch(commandType) {
|
||||
switch (commandType) {
|
||||
case 'ALERT':
|
||||
commandText = 'A';
|
||||
break;
|
||||
@ -501,23 +506,26 @@
|
||||
if (data.type === 'heartbeat') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let type = 'info'; // 默认类型
|
||||
let message = '';
|
||||
|
||||
|
||||
switch (data.type) {
|
||||
case 'position_update':
|
||||
type = 'position';
|
||||
updatePosition(data);
|
||||
message = `位置更新: ${data.object_id} (${data.object_type})\n` +
|
||||
`位置: (${data.position.longitude.toFixed(6)}, ${data.position.latitude.toFixed(6)})\n` +
|
||||
`航向: ${data.heading !== undefined ? data.heading.toFixed(2) + '°' : 'N/A'}\n` +
|
||||
`速度: ${data.speed !== undefined ? data.speed.toFixed(2) + ' m/s' : 'N/A'}`;
|
||||
`位置: (${data.position.longitude.toFixed(6)}, ${data.position.latitude.toFixed(6)})\n` +
|
||||
`航向: ${data.heading !== undefined ? data.heading.toFixed(2) + '°' : 'N/A'}\n` +
|
||||
`速度: ${data.speed !== undefined ? data.speed.toFixed(2) + ' m/s' : 'N/A'}`;
|
||||
break;
|
||||
case 'traffic_light_status':
|
||||
case 'intersection_traffic_light_status':
|
||||
type = 'info';
|
||||
updateTrafficLight(data);
|
||||
message = `红绿灯状态更新:\n信号灯: ${data.id}\n状态: ${data.status === 0 ? '红灯' : data.status === 1 ? '绿灯' : '黄灯'}`;
|
||||
updateIntersectionTrafficLights(data);
|
||||
message = `路口交通灯状态更新:\n` +
|
||||
`路口 ID: ${data.intersection_id}\n` +
|
||||
`南北状态: ${data.ns_status === 0 ? '红' : data.ns_status === 1 ? '绿' : '黄'}\n` +
|
||||
`东西状态: ${data.ew_status === 0 ? '红' : data.ew_status === 1 ? '绿' : '黄'}`;
|
||||
break;
|
||||
case 'collision_warning':
|
||||
type = 'warning';
|
||||
@ -544,17 +552,17 @@
|
||||
'PARKING_SIDE': '安全停靠'
|
||||
};
|
||||
message = `收到车辆控制指令:\n车辆: ${data.vehicleId}\n` +
|
||||
`指令类型: ${commandTypes[data.commandType] || data.commandType}\n` +
|
||||
`原因: ${reasons[data.reason] || data.reason}\n` +
|
||||
(data.targetLatitude !== undefined ? `目标位置: (${data.targetLatitude}, ${data.targetLongitude})\n` : '') +
|
||||
(data.signalState ? `信号灯状态: ${data.signalState}\n` : '') +
|
||||
(data.intersectionId ? `路口ID: ${data.intersectionId}\n` : '') +
|
||||
`时间戳: ${new Date(data.timestamp/1000000).toLocaleString()}`;
|
||||
`指令类型: ${commandTypes[data.commandType] || data.commandType}\n` +
|
||||
`原因: ${reasons[data.reason] || data.reason}\n` +
|
||||
(data.targetLatitude !== undefined ? `目标位置: (${data.targetLatitude}, ${data.targetLongitude})\n` : '') +
|
||||
(data.signalState ? `信号灯状态: ${data.signalState}\n` : '') +
|
||||
(data.intersectionId ? `路口ID: ${data.intersectionId}\n` : '') +
|
||||
`时间戳: ${new Date(data.timestamp / 1000000).toLocaleString()}`;
|
||||
break;
|
||||
default:
|
||||
message = '收到未知类型消息:\n' + JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
|
||||
log(message, type);
|
||||
} catch (e) {
|
||||
log('消息解析错误: ' + e.message, 'error');
|
||||
@ -572,32 +580,40 @@
|
||||
}
|
||||
|
||||
// 清除倒计时
|
||||
if (countdownInterval) {
|
||||
clearInterval(countdownInterval);
|
||||
countdownInterval = null;
|
||||
}
|
||||
lastTrafficLightState = null;
|
||||
countdown = 10;
|
||||
// if (countdownInterval) {
|
||||
// clearInterval(countdownInterval);
|
||||
// countdownInterval = null;
|
||||
// }
|
||||
// lastTrafficLightState = null;
|
||||
// countdown = 10;
|
||||
|
||||
// 清除倒计时标记
|
||||
trafficLights.forEach(light => {
|
||||
if (light.countdownMarker) {
|
||||
map.removeLayer(light.countdownMarker);
|
||||
light.countdownMarker = null;
|
||||
}
|
||||
});
|
||||
// trafficLights.forEach(light => {
|
||||
// if (light.countdownMarker) {
|
||||
// map.removeLayer(light.countdownMarker);
|
||||
// light.countdownMarker = null;
|
||||
// }
|
||||
// });
|
||||
|
||||
ws.close();
|
||||
ws = null;
|
||||
|
||||
// 清除所有标记(包括边框标记)
|
||||
|
||||
// 清除所有车辆和安全边框标记
|
||||
markers.forEach((marker, key) => {
|
||||
map.removeLayer(marker);
|
||||
});
|
||||
markers.clear();
|
||||
|
||||
trafficLights.forEach(light => map.removeLayer(light));
|
||||
trafficLights.clear();
|
||||
|
||||
// 清除旧的红绿灯标记
|
||||
// trafficLights.forEach(light => map.removeLayer(light));
|
||||
// trafficLights.clear();
|
||||
|
||||
// 清除路口红绿灯标记
|
||||
intersectionMarkers.forEach((markers, id) => {
|
||||
if (markers.nsMarker) map.removeLayer(markers.nsMarker);
|
||||
if (markers.ewMarker) map.removeLayer(markers.ewMarker);
|
||||
});
|
||||
intersectionMarkers.clear();
|
||||
}
|
||||
|
||||
// 添加道路刻度标记函数
|
||||
@ -607,26 +623,26 @@
|
||||
const lon1 = startPoint[1];
|
||||
const lat2 = endPoint[0];
|
||||
const lon2 = endPoint[1];
|
||||
|
||||
|
||||
// 计算道路角度(考虑经纬度投影)
|
||||
const latMid = (lat1 + lat2) / 2; // 使用中点纬度来算经度缩放
|
||||
const lonScale = Math.cos(latMid * Math.PI / 180); // 经度缩放因子
|
||||
const dx = (lon2 - lon1) * lonScale;
|
||||
const dy = lat2 - lat1;
|
||||
const angle = Math.atan2(dy, dx);
|
||||
|
||||
|
||||
// 计算垂直于道路的方向(只在右侧显示刻度)
|
||||
const perpAngle = angle + Math.PI / 2;
|
||||
const markLength = 0.00005; // 保持您设置的较短刻度线长度
|
||||
const offset = 0.00004; // 向右偏移一点,避免与道路重叠
|
||||
|
||||
|
||||
// 计算总距离
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
|
||||
// 每50米一个刻度
|
||||
const step = 0.0005; // 约50米
|
||||
const steps = Math.floor(dist / step);
|
||||
|
||||
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
// 计算刻度位置
|
||||
const ratio = i / steps;
|
||||
@ -635,25 +651,25 @@
|
||||
lat1 + dy * ratio,
|
||||
lon1 + (dx / lonScale) * ratio
|
||||
];
|
||||
|
||||
|
||||
// 计算垂直偏移(考虑经纬度投影)
|
||||
const offsetPos = [
|
||||
pos[0] + Math.sin(perpAngle) * offset,
|
||||
pos[1] + Math.cos(perpAngle) * offset / lonScale
|
||||
];
|
||||
|
||||
|
||||
// 计算刻度线终点(考虑经纬度投影)
|
||||
const markEnd = [
|
||||
offsetPos[0] + Math.sin(perpAngle) * markLength,
|
||||
offsetPos[1] + Math.cos(perpAngle) * markLength / lonScale
|
||||
];
|
||||
|
||||
|
||||
// 添加刻度线
|
||||
L.polyline([offsetPos, markEnd], {
|
||||
color: '#666',
|
||||
weight: 1.5
|
||||
}).addTo(map);
|
||||
|
||||
|
||||
// 添加距离标签
|
||||
const distance = Math.round(i * 50);
|
||||
if (distance > 0) {
|
||||
@ -663,7 +679,7 @@
|
||||
iconSize: [40, 20],
|
||||
iconAnchor: [-5, 10]
|
||||
});
|
||||
|
||||
|
||||
L.marker(markEnd, {
|
||||
icon: label,
|
||||
interactive: false
|
||||
@ -720,4 +736,5 @@
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user