|
|
@@ -37,6 +37,13 @@ function delay(base = 200) { return sleep(base + Math.floor(Math.random() * 200)
|
|
|
function ok(data) { return { code: 200, message: 'success', data } }
|
|
|
function fail(msg, code = 400) { return { code, message: msg, data: null } }
|
|
|
|
|
|
+/** 根据路口ID生成稳定的设备状态(所有API共用,确保一致) */
|
|
|
+function _getDeviceStatus(id) {
|
|
|
+ const seed = id ? Array.from(id).reduce((s, c, i) => s + c.charCodeAt(0) * (i + 1), 0) : 0
|
|
|
+ const statusList = ['在线', '在线', '在线', '在线', '在线', '在线', '在线', '离线']
|
|
|
+ return statusList[seed % statusList.length]
|
|
|
+}
|
|
|
+
|
|
|
/** 基于当前秒数产生稳定随机(同一秒内多次调用返回相同值) */
|
|
|
function seededRand(seed) {
|
|
|
const x = Math.sin(seed) * 10000
|
|
|
@@ -113,11 +120,12 @@ function _makeIntersectionConfig(id, name, { fixedNsGreen } = {}) {
|
|
|
const armCamTypes = _camerasToArmTypes(cameras)
|
|
|
|
|
|
const lanePresets = [
|
|
|
- ['U', 'L', 'S', 'R'], [null, 'L', 'S', 'R'],
|
|
|
- [null, 'L', 'S', null], ['U', 'L', 'S', null],
|
|
|
- ].sort(() => seededRand(seed) - 0.5)
|
|
|
+ ['U', 'L', 'S', 'R'], ['U', 'L', 'S', 'R'],
|
|
|
+ ['U', 'L', 'S', 'R'], ['U', 'L', 'S', 'R'],
|
|
|
+ ]
|
|
|
|
|
|
return {
|
|
|
+ status: _getDeviceStatus(id),
|
|
|
signals: {
|
|
|
ns: { phaseName: phases[0], time: countdown, isGreen: nsGreen },
|
|
|
ew: { phaseName: phases[1], time: countdown, isGreen: !nsGreen },
|
|
|
@@ -166,6 +174,8 @@ function _makePhaseData(cycleLength = 140, isTwoRows = true) {
|
|
|
const currentIconPool = (i < 2) ? iconsUD : iconsLR;
|
|
|
|
|
|
// 辅助函数:生成单条轨道的一个阶段
|
|
|
+ // 第8列 [7] 标记方向: 'ns'(南北) 或 'ew'(东西)
|
|
|
+ const direction = (i < 2) ? 'ns' : 'ew';
|
|
|
const pushTrackData = (trackIdx, phaseNamePrefix) => {
|
|
|
// 这里的 icon 现在抽出来的是诸如 "STRAIGHT_DOWN,STRAIGHT_UP" 的字符串
|
|
|
const icon = getRandomIcon(currentIconPool);
|
|
|
@@ -173,22 +183,22 @@ function _makePhaseData(cycleLength = 140, isTwoRows = true) {
|
|
|
const g = Math.floor(Math.random() * 11) + 20; // 绿灯 20-30s
|
|
|
const s = 3; // 闪烁/条纹 3s
|
|
|
const y = 2; // 黄灯 2s
|
|
|
-
|
|
|
+
|
|
|
let curT = stageStart;
|
|
|
-
|
|
|
- // 1. 绿灯 (第6个索引项传入组装好的成对 icon 字符串)
|
|
|
- pd.push([trackIdx, curT, curT + g, phaseName, g, 'green', icon]);
|
|
|
+
|
|
|
+ // 1. 绿灯 (第6个索引项传入组装好的成对 icon 字符串, 第7个索引项标记方向)
|
|
|
+ pd.push([trackIdx, curT, curT + g, phaseName, g, 'green', icon, direction]);
|
|
|
curT += g;
|
|
|
// 2. 绿闪/条纹
|
|
|
- pd.push([trackIdx, curT, curT + s, '', s, 'stripe', null]);
|
|
|
+ pd.push([trackIdx, curT, curT + s, '', s, 'stripe', null, direction]);
|
|
|
curT += s;
|
|
|
// 3. 黄灯
|
|
|
- pd.push([trackIdx, curT, curT + y, '', y, 'yellow', null]);
|
|
|
+ pd.push([trackIdx, curT, curT + y, '', y, 'yellow', null, direction]);
|
|
|
curT += y;
|
|
|
// 4. 红灯补齐 (确保阶段对齐)
|
|
|
let remainRed = stageEnd - curT;
|
|
|
if (remainRed > 0) {
|
|
|
- pd.push([trackIdx, curT, stageEnd, '', remainRed, 'red', null]);
|
|
|
+ pd.push([trackIdx, curT, stageEnd, '', remainRed, 'red', null, direction]);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -328,6 +338,7 @@ export async function apiGetIntersectionData(id, { fixedNsGreen } = {}) {
|
|
|
const nsGreen = nsGreenVal
|
|
|
|
|
|
const config = base ? {
|
|
|
+ status: base.status || _getDeviceStatus(id),
|
|
|
signals: {
|
|
|
ns: { ...base.signals.ns, time: Math.max(1, cycle - elapsed), isGreen: nsGreen },
|
|
|
ew: { ...base.signals.ew, time: elapsed || 1, isGreen: !nsGreen },
|
|
|
@@ -604,7 +615,7 @@ export async function apiGetCrossingList(params = {}) {
|
|
|
const phaseData = _makePhaseData(cycleLength, false)
|
|
|
return {
|
|
|
...r,
|
|
|
- status: statuses[Math.floor(seededRand(i + 42) * statuses.length)],
|
|
|
+ status: _getDeviceStatus(r.id),
|
|
|
cycle: cycleLength,
|
|
|
phaseData,
|
|
|
currentTime: Math.floor(seededRand(i * 31 + page * 97) * cycleLength),
|
|
|
@@ -731,11 +742,9 @@ export async function apiGetSpecialTaskMonitorData(id, { fixedNsGreen } = {}) {
|
|
|
const colorList = ['#ffaa00', '#00e5ff', '#68e75f', '#00e5ff']
|
|
|
const nowSec = Math.floor(Date.now() / 1000)
|
|
|
|
|
|
+ const allLanes = ['U', 'L', 'S', 'R'];
|
|
|
const lanePresets = [
|
|
|
- { N: ['U', 'L', 'S', 'R'], S: [null, 'L', 'S', 'R'], E: [null, 'L', 'S', null], W: ['U', 'L', 'S', null] },
|
|
|
- { N: ['L', 'S', 'R'], S: ['L', 'S', 'R'], E: ['L', 'S', 'R'], W: ['L', 'S', 'R'] },
|
|
|
- { N: ['L', 'S', 'S', 'R'], S: ['U', 'L', 'S', 'R'], E: ['L', 'S', null], W: [null, 'S', 'R'] },
|
|
|
- { N: [null, 'L', 'S', 'R'], S: ['L', 'S', 'R', null], E: ['U', 'L', 'S', 'R'], W: ['L', 'S', 'R'] },
|
|
|
+ { N: allLanes, S: allLanes, E: allLanes, W: allLanes },
|
|
|
]
|
|
|
|
|
|
const intersections = keyPoints.map((jnc, i) => {
|
|
|
@@ -788,6 +797,10 @@ export async function apiGetCrossingPanelData(id) {
|
|
|
const point = DB.points.find(p => p.id === id)
|
|
|
const config = DB.intersectionConfigs[id] || _makeIntersectionConfig(id, null, { fixedNsGreen: false })
|
|
|
const seed = id ? id.charCodeAt(id.length - 1) : 0
|
|
|
+ // 确保 config 有 status
|
|
|
+ if (!config.status) {
|
|
|
+ config.status = _getDeviceStatus(id)
|
|
|
+ }
|
|
|
|
|
|
const preset = DB.signalTimings[id]
|
|
|
const cycleLength = preset ? preset.data.cycleLength : [100, 120, 130, 140, 150, 160][Math.abs(seed) % 6]
|
|
|
@@ -818,6 +831,11 @@ export async function apiGetCrossingDetailData(id) {
|
|
|
// 用 id 的全部字符生成稳定 seed(加权位置避免 charCode 总和碰撞)
|
|
|
const seed = id ? Array.from(id).reduce((s, c, i) => s + c.charCodeAt(0) * (i + 1), 0) : 0
|
|
|
|
|
|
+ // 确保 config 有 status 字段(预存配置可能缺失)
|
|
|
+ if (!config.status) {
|
|
|
+ config.status = _getDeviceStatus(id)
|
|
|
+ }
|
|
|
+
|
|
|
// 从真实阶段数据推导周期和相位
|
|
|
const preset = DB.signalTimings[id]
|
|
|
const cycleLength = preset ? preset.data.cycleLength : [100, 120, 130, 140, 150, 160][seed % 6]
|
|
|
@@ -1003,7 +1021,7 @@ export async function apiGetDeviceFaultStatus() {
|
|
|
|
|
|
// 从在线数据推算故障数,每次波动
|
|
|
const smTotal = sm.chartData[0].value + sm.chartData[1].value
|
|
|
- const smFault = _fluctuate(sm.chartData[1].value, 3)
|
|
|
+ const smFault = 0 // 信号机无故障,用于测试无故障状态
|
|
|
const dtTotal = dt.chartData[0].value + dt.chartData[1].value
|
|
|
const dtFault = _fluctuate(dt.chartData[1].value, 5)
|
|
|
const camTotal = cam.chartData[0].value + cam.chartData[1].value
|