增加红绿灯的两个方向的信号状态处理和前端页面显示

This commit is contained in:
Tian jianyong 2025-05-06 13:27:19 +08:00
parent d645dbc486
commit 5f2c0e4d86
8 changed files with 256 additions and 215 deletions

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
};

View File

@ -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
}
// 停止数据采集

View File

@ -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;
}

View File

@ -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>