// 模拟工业以太网/5G 网关数据接入与远程控制(前端造数)
|
// 设备围绕煤炭加工场景:带式输送机、破碎机、皮带秤、除尘风机、给煤机等
|
|
const DEFAULT_THRESHOLDS = {
|
temperatureC: 85, // ℃
|
pressureBar: 12, // bar
|
currentA: 180, // A
|
voltageV: 420, // V(三相线电压)
|
powerFactor: 0.85 // cosφ
|
}
|
|
const STORAGE_KEYS = {
|
thresholds: 'monitor_equipment_thresholds',
|
channels: 'monitor_equipment_alarm_channels'
|
}
|
|
const DEFAULT_CHANNELS = {
|
platform: true,
|
sms: false,
|
voice: false
|
}
|
|
const BASE_EQUIPMENTS = [
|
{ id: 'conv-01', name: '主煤流带式输送机#1', location: '筛分车间一线', type: 'conveyor' },
|
{ id: 'crusher-01', name: '齿辊破碎机#1', location: '破碎工段', type: 'crusher' },
|
{ id: 'feeder-01', name: '电机振动给煤机#1', location: '原煤仓下口', type: 'feeder' },
|
{ id: 'blower-01', name: '除尘离心风机#1', location: '装车除尘点', type: 'blower' },
|
{ id: 'scale-01', name: '皮带秤#1', location: '计量段', type: 'beltScale' }
|
]
|
|
function getRandomAround(base, fluct = 0.05) {
|
const delta = base * fluct
|
return +(base + (Math.random() * 2 - 1) * delta).toFixed(2)
|
}
|
|
function seededInitMetrics(type) {
|
switch (type) {
|
case 'conveyor':
|
return { temperatureC: 55, pressureBar: 6.5, currentA: 95, voltageV: 400, powerFactor: 0.92 }
|
case 'crusher':
|
return { temperatureC: 65, pressureBar: 10.5, currentA: 140, voltageV: 405, powerFactor: 0.9 }
|
case 'feeder':
|
return { temperatureC: 50, pressureBar: 5.5, currentA: 60, voltageV: 398, powerFactor: 0.93 }
|
case 'blower':
|
return { temperatureC: 70, pressureBar: 8.2, currentA: 120, voltageV: 402, powerFactor: 0.88 }
|
case 'beltScale':
|
return { temperatureC: 48, pressureBar: 0.0, currentA: 35, voltageV: 401, powerFactor: 0.95 }
|
default:
|
return { temperatureC: 55, pressureBar: 6, currentA: 90, voltageV: 400, powerFactor: 0.9 }
|
}
|
}
|
|
function nextMetrics(current, type) {
|
const drift = {
|
temperatureC: getRandomAround(current.temperatureC, 0.03),
|
pressureBar: getRandomAround(current.pressureBar, 0.06),
|
currentA: getRandomAround(current.currentA, 0.08),
|
voltageV: getRandomAround(current.voltageV, 0.01),
|
powerFactor: Math.max(0.6, Math.min(0.99, getRandomAround(current.powerFactor, 0.02)))
|
}
|
if (type === 'crusher') drift.currentA += Math.random() * 4 - 2
|
if (type === 'blower') drift.powerFactor -= Math.random() * 0.01
|
return {
|
temperatureC: +drift.temperatureC.toFixed(2),
|
pressureBar: +drift.pressureBar.toFixed(2),
|
currentA: +drift.currentA.toFixed(2),
|
voltageV: +drift.voltageV.toFixed(2),
|
powerFactor: +drift.powerFactor.toFixed(2)
|
}
|
}
|
|
export function getThresholds() {
|
try {
|
const raw = localStorage.getItem(STORAGE_KEYS.thresholds)
|
if (!raw) return { ...DEFAULT_THRESHOLDS }
|
const obj = JSON.parse(raw)
|
return { ...DEFAULT_THRESHOLDS, ...obj }
|
} catch (e) {
|
return { ...DEFAULT_THRESHOLDS }
|
}
|
}
|
|
export function saveThresholds(thresholds) {
|
const merged = { ...DEFAULT_THRESHOLDS, ...thresholds }
|
localStorage.setItem(STORAGE_KEYS.thresholds, JSON.stringify(merged))
|
return merged
|
}
|
|
export function getAlarmChannels() {
|
try {
|
const raw = localStorage.getItem(STORAGE_KEYS.channels)
|
if (!raw) return { ...DEFAULT_CHANNELS }
|
const obj = JSON.parse(raw)
|
return { ...DEFAULT_CHANNELS, ...obj }
|
} catch (e) {
|
return { ...DEFAULT_CHANNELS }
|
}
|
}
|
|
export function saveAlarmChannels(channels) {
|
const merged = { ...DEFAULT_CHANNELS, ...channels }
|
localStorage.setItem(STORAGE_KEYS.channels, JSON.stringify(merged))
|
return merged
|
}
|
|
// 订阅实时数据(模拟低时延、高可靠链路)
|
export function subscribeEquipmentData(callback, options = {}) {
|
const { intervalMs = 1000, streaming = true } = options
|
let isStopped = false
|
|
const equipmentStates = BASE_EQUIPMENTS.map(e => ({
|
...e,
|
status: 'RUNNING',
|
metrics: seededInitMetrics(e.type)
|
}))
|
|
// 非流模式:仅推送一次快照
|
if (!streaming) {
|
const now = Date.now()
|
equipmentStates.forEach(es => {
|
callback({
|
ts: now,
|
equipmentId: es.id,
|
name: es.name,
|
location: es.location,
|
status: es.status,
|
metrics: { ...es.metrics }
|
})
|
})
|
return () => {}
|
}
|
|
function tick() {
|
if (isStopped) return
|
const now = Date.now()
|
equipmentStates.forEach(es => {
|
if (Math.random() < 0.01) {
|
es.status = es.status === 'RUNNING' ? 'STOPPED' : 'RUNNING'
|
}
|
if (es.status === 'RUNNING') {
|
es.metrics = nextMetrics(es.metrics, es.type)
|
} else {
|
es.metrics = {
|
...es.metrics,
|
currentA: +(es.metrics.currentA * 0.1).toFixed(2),
|
powerFactor: Math.max(0.5, +(es.metrics.powerFactor * 0.8).toFixed(2))
|
}
|
}
|
callback({
|
ts: now,
|
equipmentId: es.id,
|
name: es.name,
|
location: es.location,
|
status: es.status,
|
metrics: { ...es.metrics }
|
})
|
})
|
timer = setTimeout(tick, intervalMs)
|
}
|
|
let timer = setTimeout(tick, intervalMs)
|
return () => {
|
isStopped = true
|
if (timer) clearTimeout(timer)
|
}
|
}
|
|
export function sendControlCommand(equipmentId, action) {
|
return new Promise(resolve => {
|
const delay = 50 + Math.round(Math.random() * 70)
|
setTimeout(() => {
|
resolve({ code: 200, msg: 'OK', data: { equipmentId, action, acceptedAt: Date.now() } })
|
/* eslint-disable no-console */
|
console.log(`[CONTROL] equipment=${equipmentId} action=${action}`)
|
}, delay)
|
})
|
}
|
|
export function detectAlarms(metrics, thresholds) {
|
const over = []
|
if (metrics.temperatureC > thresholds.temperatureC) over.push({ field: 'temperatureC', value: metrics.temperatureC, threshold: thresholds.temperatureC })
|
if (metrics.pressureBar > thresholds.pressureBar) over.push({ field: 'pressureBar', value: metrics.pressureBar, threshold: thresholds.pressureBar })
|
if (metrics.currentA > thresholds.currentA) over.push({ field: 'currentA', value: metrics.currentA, threshold: thresholds.currentA })
|
if (metrics.voltageV > thresholds.voltageV) over.push({ field: 'voltageV', value: metrics.voltageV, threshold: thresholds.voltageV })
|
if (metrics.powerFactor < thresholds.powerFactor) over.push({ field: 'powerFactor', value: metrics.powerFactor, threshold: thresholds.powerFactor })
|
return over
|
}
|
|
export function listEquipments() {
|
return BASE_EQUIPMENTS.map(e => ({ id: e.id, name: e.name, location: e.location, type: e.type }))
|
}
|
|
export default {
|
subscribeEquipmentData,
|
sendControlCommand,
|
detectAlarms,
|
getThresholds,
|
saveThresholds,
|
getAlarmChannels,
|
saveAlarmChannels,
|
listEquipments
|
}
|