对接飞奕API获取token

parent f8e817ba
import { selectOneDataByParam, selectDataListByParam, selectPaginatedDataWithOrder } from "../data/findData";
import { selectOneDataByParam } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
import { getMySqlMs } from "../tools/systemTools";
import { BizError } from "../util/bizError";
......@@ -83,17 +83,17 @@ export async function handleDevicePush(deviceId: string, data: any, deviceTime?:
return { success: true };
}
/**
* 处理设备商推送的故障数据
* @param deviceId 设备唯一标识(必填)
* @param faultType 故障类型(必填),如“离线”“数据异常”“硬件故障”
* @param faultLevel 故障等级
* @param faultDescription 故障详细描述(可选)
* @param occurredTime 故障发生时间(可选),格式:YYYY-MM-DD HH:MM:SS,不传则使用服务器时间
* @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID
* @throws 如果设备未注册,则抛出错误
*/
export async function handleDeviceFaultPush( deviceId: string, faultType: string, faultDescription?: string, occurredTime?: string ) {
export async function handleDeviceFaultPush( deviceId: string, faultType: string, faultLevel: string, faultDescription?: string, occurredTime?: string ) {
if (!deviceId || !faultType) {
throw new BizError(ERRORENUM.参数错误, "deviceId 和 faultType 不能为空");
}
......@@ -115,6 +115,7 @@ export async function handleDeviceFaultPush( deviceId: string, faultType: string
fault_type: faultType,
fault_description: faultDescription || null,
occurred_time: faultTime,
level: faultLevel || '1', // 默认一级故障
status: 0, // 0=未处理
resolved_time: null,
created_at: nowStr,
......@@ -124,832 +125,17 @@ export async function handleDeviceFaultPush( deviceId: string, faultType: string
return { faultId: insertResult.insertId, success: true };
}
/**
* 运行分析 todu(待测试)
* @returns 大屏所需的所有指标数据
*/
export async function getRunAnalysis() {
// 1. 获取所有三相电表及电能监测设备的用电数据(用于能耗管理)
let energyDevices = await selectDataListByParam(TABLENAME.设备表, {
device_type: { "%in%": ["三相电表", "电能监测"] } // 使用 %in% 操作符
}, ["device_id"]);
let energyDeviceIds = energyDevices.data.map((d: any) => d.device_id);
// 2. 获取今日、昨日、本月、上月用电量
let todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
let todayEnd = new Date();
let yesterdayStart = new Date(Date.now() - 86400000); yesterdayStart.setHours(0,0,0,0);
let yesterdayEnd = new Date(Date.now() - 86400000); yesterdayEnd.setHours(23,59,59,999);
let currentMonthStart = new Date(); currentMonthStart.setDate(1); currentMonthStart.setHours(0,0,0,0);
let lastMonthStart = new Date(); lastMonthStart.setMonth(lastMonthStart.getMonth() - 1); lastMonthStart.setDate(1); lastMonthStart.setHours(0,0,0,0);
let lastMonthEnd = new Date(currentMonthStart.getTime() - 1);
// 辅助函数:查询某个时间段内的总用电量(单位 kwh)
async function getTotalEnergy(deviceIds: string[], startTime: Date, endTime: Date): Promise<number> {
if (deviceIds.length === 0) return 0;
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let total = 0;
let deviceEnergyMap = new Map();
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
if (!deviceEnergyMap.has(rec.device_id)) {
deviceEnergyMap.set(rec.device_id, { min: energy, max: energy });
} else {
let cur = deviceEnergyMap.get(rec.device_id);
if (energy < cur.min) cur.min = energy;
if (energy > cur.max) cur.max = energy;
}
}
for (let { min, max } of deviceEnergyMap.values()) {
total += (max - min);
}
return total;
}
let todayEnergy = await getTotalEnergy(energyDeviceIds, todayStart, todayEnd);
let yesterdayEnergy = await getTotalEnergy(energyDeviceIds, yesterdayStart, yesterdayEnd);
let currentMonthEnergy = await getTotalEnergy(energyDeviceIds, currentMonthStart, new Date());
let previousMonthEnergy = await getTotalEnergy(energyDeviceIds, lastMonthStart, lastMonthEnd);
let dailyYearOnYear = yesterdayEnergy === 0 ? '0%' : `${(((todayEnergy - yesterdayEnergy) / yesterdayEnergy) * 100).toFixed(2)}%`;
let monthlyYearOnYear = previousMonthEnergy === 0 ? '0%' : `${(((currentMonthEnergy - previousMonthEnergy) / previousMonthEnergy) * 100).toFixed(2)}%`;
// 3. 用电趋势:过去24小时(按小时)、过去7天、过去30天、过去一年(按月)
let now = new Date();
let last24hStart = new Date(now.getTime() - 24 * 3600000);
let hourlyEnergy = await getEnergyTrend(energyDeviceIds, last24hStart, now, 'hour');
let last7DaysStart = new Date(now.getTime() - 7 * 86400000);
let dailyEnergy7 = await getEnergyTrend(energyDeviceIds, last7DaysStart, now, 'day');
let last30DaysStart = new Date(now.getTime() - 30 * 86400000);
let dailyEnergy30 = await getEnergyTrend(energyDeviceIds, last30DaysStart, now, 'day');
let lastYearStart = new Date(now.getFullYear() - 1, now.getMonth(), 1);
let monthlyEnergyYear = await getEnergyTrend(energyDeviceIds, lastYearStart, now, 'month');
// 4. 设备表格:按设备类型统计数量和耗电情况
let allDevices = await selectDataListByParam(TABLENAME.设备表, {}, ["device_type", "device_id"]);
let typeCountMap = new Map();
for (let dev of allDevices.data) {
let type = dev.device_type;
typeCountMap.set(type, (typeCountMap.get(type) || 0) + 1);
}
let deviceTable = [];
for (let [type, count] of typeCountMap.entries()) {
// 耗电比例示例(实际可关联用电数据)
let powerRatio = '30%';
if (type === '空调') powerRatio = '97%';
else if (type === '灯光') powerRatio = '45%';
deviceTable.push({
deviceName: type,
quantity: count,
powerConsumptionRatio: powerRatio
});
}
// 5. 汇总数据(简化)
let totalDevices = allDevices.data.length;
let runningDevices = totalDevices; // 或根据在线数量计算,在线即运行
// 查询未解决的故障设备数量(去重,一个设备可能有多个故障,取 status != 2 的)
let faultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 } }, // 未解决的状态不是2(已解决)
["device_id"]
);
let faultDeviceIds = new Set(faultRecords.data.map((r: any) => r.device_id));
let faultDevices = faultDeviceIds.size;
let normalRate = totalDevices === 0 ? '100%' : (((totalDevices - faultDevices) / totalDevices) * 100).toFixed(2) + '%';
// 6. 设备监测情况:在线/离线(根据最后数据时间是否超过30分钟)
let nowTime = new Date();
let deviceLatestMap = new Map();
let deviceIds = allDevices.data.map(d => d.device_id);
for (let devId of deviceIds) {
let rec = await selectDataListByParam(TABLENAME.设备数据表,
{ device_id: devId, "%orderDesc%": "received_time", "%limit%": 1 },
["received_time"]);
if (rec.data.length) {
let lastTime = new Date(rec.data[0].received_time);
let diffMinutes = (nowTime.getTime() - lastTime.getTime()) / 60000;
deviceLatestMap.set(devId, { lastTime, isOnline: diffMinutes <= 30 });
} else {
deviceLatestMap.set(devId, { lastTime: null, isOnline: false });
}
}
let onlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length;
let offlineCount = totalDevices - onlineCount;
let faultCount = faultRecords.data.length; // 所有未解决的故障单数量(含同一设备多个故障)
// 7. 监测设备趋势:过去24小时每小时在线数量
let hourlyOnlineTrend = [];
for (let i = 0; i < 24; i++) {
let hourStart = new Date(nowTime.getTime() - (23 - i) * 3600000);
let hourEnd = new Date(hourStart.getTime() + 3600000);
let onlineInHour = 0;
for (let { lastTime } of deviceLatestMap.values()) {
if (lastTime && lastTime >= hourStart && lastTime <= hourEnd) onlineInHour++;
}
hourlyOnlineTrend.push({ time: `${i}时`, value: onlineInHour.toString() });
}
// 8. 预警处理状态和预警工单(模拟数据,待接入真实告警表)
let alertStatus = {
totalAlerts: 48,
resolved: 20,
responded: 24,
unresolved: 4
};
let alertWorkOrders = [
{ riskContent: "空调压缩机异常", alertTime: "26/02/01", status: "已解决" },
{ riskContent: "温度传感器离线", alertTime: "26/02/01", status: "已响应" },
{ riskContent: "湿度超标", alertTime: "26/02/02", status: "未处理" }
];
// 9. 空气质量相关数据
let airQualityDevices = await selectDataListByParam(TABLENAME.设备表, { device_type: "空气质量传感" }, ["device_id"]);
let airDeviceIds = airQualityDevices.data.map(d => d.device_id);
let qualityIndex = 300;
let co2Trend = [];
let currentCo2 = '21ppm';
let currentTemperature = '21℃';
let temperatureTrend = [];
let currentHumidity = '47%';
let humidityTrend = [];
let currentPm25 = '21μg/m³';
let pm25Trend = [];
let co2TrendDetail = [];
if (airDeviceIds.length) {
// 最新数据
let latestAir = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
"%orderDesc%": "received_time",
"%limit%": 1
}, ["data"]);
if (latestAir.data.length) {
let airData = latestAir.data[0].data;
currentTemperature = `${airData.temperature ?? 21}`;
currentHumidity = `${airData.humidity ?? 47}%`;
currentPm25 = `${airData.pm25 ?? 21}μg/m³`;
currentCo2 = `${airData.co2 ?? 400}ppm`;
qualityIndex = 500 - (airData.pm25 ?? 0);
}
// 过去24小时趋势
let last24h = new Date(nowTime.getTime() - 24*3600000);
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
received_time: { "%gte%": last24h.toISOString().slice(0,19).replace('T',' ') }
}, ["data", "received_time"]);
let hourlyData = new Map();
for (let rec of airHistory.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { temp: data.temperature, hum: data.humidity, pm: data.pm25, co2: data.co2 });
}
}
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyData.get(key);
temperatureTrend.push({ time: `${i}:00`, value: val?.temp?.toString() ?? '21' });
humidityTrend.push({ time: `${i}:00`, value: val?.hum?.toString() ?? '47' });
pm25Trend.push({ time: `${i}:00`, value: val?.pm?.toString() ?? '21' });
let co2Val = val?.co2 ?? 400;
co2TrendDetail.push({ time: `${i}:00`, value: (co2Val / 100).toFixed(1) });
co2Trend.push({ time: `${i}`, value: co2Val.toString() });
}
}
// 10. 机房环境监控和功率监控
let roomEnvironment = [
{ key: "温度", value: currentTemperature },
{ key: "湿度", value: currentHumidity },
{ key: "配置", value: "烟感" },
{ key: "常开", value: "水浸" }
];
let roomPowerTrend = hourlyOnlineTrend.slice(0, 24).map(item => ({ time: item.time.replace('时',':00'), value: item.value }));
// 最终组装返回数据
return {
energyManagement: {
todayElectricity: `${todayEnergy.toFixed(0)}kwh`,
yesterdayElectricity: `${yesterdayEnergy.toFixed(0)}kwh`,
dailyYearOnYear,
currentMonthElectricity: `${currentMonthEnergy.toFixed(0)}kwh`,
previousMonthElectricity: `${previousMonthEnergy.toFixed(0)}kwh`,
monthlyYearOnYear
},
electricityTrend: {
last24Hours: hourlyEnergy,
last7Days: dailyEnergy7,
last30Days: dailyEnergy30,
lastYear: monthlyEnergyYear
},
deviceTable,
summaryData: {
runningDevices,
normalRate,
faultDevices
},
deviceMonitoring: {
totalMonitored: totalDevices,
offlineCount,
onlineCount,
faultCount
},
monitoringDeviceTrend: hourlyOnlineTrend,
alertStatus,
alertWorkOrders,
qualityIndex,
co2Trend,
currentTemperature,
temperatureTrend,
currentHumidity,
humidityTrend,
currentPm25,
pm25Trend,
currentCo2,
co2TrendDetail,
roomEnvironment,
roomPowerTrend
};
}
// 辅助函数:获取用电趋势(按间隔分组)
async function getEnergyTrend(deviceIds: string[], startTime: Date, endTime: Date, granularity: 'hour' | 'day' | 'month') {
if (deviceIds.length === 0) return [];
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let intervalMap = new Map(); // key: 时间字符串, value: { deviceMin, deviceMax }
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
let intervalKey: string;
let d = new Date(rec.received_time);
if (granularity === 'hour') {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()} ${d.getHours()}`;
} else if (granularity === 'day') {
intervalKey = `${d.getFullYear()}/${d.getMonth()+1}/${d.getDate()}`;
} else {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}`;
}
if (!intervalMap.has(intervalKey)) {
intervalMap.set(intervalKey, { deviceMin: new Map(), deviceMax: new Map() });
}
let entry = intervalMap.get(intervalKey);
if (!entry.deviceMin.has(rec.device_id)) {
entry.deviceMin.set(rec.device_id, energy);
entry.deviceMax.set(rec.device_id, energy);
} else {
let curMin = entry.deviceMin.get(rec.device_id);
let curMax = entry.deviceMax.get(rec.device_id);
if (energy < curMin) entry.deviceMin.set(rec.device_id, energy);
if (energy > curMax) entry.deviceMax.set(rec.device_id, energy);
}
}
let result = [];
for (let [key, { deviceMin, deviceMax }] of intervalMap.entries()) {
let total = 0;
for (let [deviceId, minVal] of deviceMin.entries()) {
let maxVal = deviceMax.get(deviceId);
total += (maxVal - minVal);
}
result.push({ key, value: total.toFixed(0) });
}
result.sort((a,b) => a.key.localeCompare(b.key));
return result;
}
/**
* 设备类型中文到英文Key的映射
*/
let deviceTypeKeyMap: { [key: string]: string } = {
"灯光": "lighting",
"窗帘": "curtain",
"推窗器": "windowOpener",
"空调": "hvac",
"新风": "freshAir",
"音乐": "music",
"人体传感": "occupancySensor",
"人体传感器": "occupancySensor",
"空气质量传感": "airSensor",
"燃气表": "gasMeter",
"水表": "waterMeter",
"三相电表": "electricityMeter",
"电表": "electricityMeter",
"电能监测": "electricityMeter"
};
// 辅助函数:将字符串或 Date 转换为 Date 对象
function toDate(dateInput: string | Date): Date {
if (dateInput instanceof Date) return dateInput;
return new Date(dateInput);
}
// 辅助函数:将 Date 对象转换为数据库存储的字符串格式 YYYY-MM-DD HH:MM:SS
function toDateTimeStr(date: Date): string {
return date.toISOString().slice(0, 19).replace('T', ' ');
}
/**
* 获取运行分析区域弹窗
*/
export async function getRunAnalysisPop(regionKey: number) {
let devices = await selectDataListByParam(
TABLENAME.设备表,
{ region_key: regionKey },
["device_id", "device_type", "device_name"]
);
let deviceList = devices.data;
let deviceIds = deviceList.map((d: any) => d.device_id);
if (deviceIds.length === 0) {
return {
deviceOnlineStatus: {},
deviceStatusMonitoring: { totalDevices: "0", online: "0", offline: "0", fault: "0" },
deviceStatusTrend: [],
energyConsumptionMonitoring: []
};
}
// 获取每个设备的最新数据(用于判定业务在线状态)
let deviceLatestMap = new Map<string, { isOnline: boolean; latestData: any }>();
for (let devId of deviceIds) {
let rec = await selectDataListByParam(
TABLENAME.设备数据表,
{ device_id: devId, "%orderDesc%": "received_time", "%limit%": 1 },
["data"]
);
let isOnline = false;
let latestData = null;
if (rec.data.length) {
latestData = rec.data[0].data // 已解析的对象
let devType = deviceList.find((d: any) => d.device_id === devId)?.device_type || "";
isOnline = isDeviceOnlineByStatus(devType, latestData);
}
deviceLatestMap.set(devId, { isOnline, latestData });
}
// 设备在线状态(按类型聚合)
let deviceOnlineStatus: { [key: string]: boolean } = {};
for (let dev of deviceList) {
let cnType = dev.device_type;
let enKey = deviceTypeKeyMap[cnType];
if (enKey) {
let isOnline = deviceLatestMap.get(dev.device_id)?.isOnline ?? false;
if (deviceOnlineStatus[enKey] === undefined) {
deviceOnlineStatus[enKey] = isOnline;
} else {
deviceOnlineStatus[enKey] = deviceOnlineStatus[enKey] || isOnline;
}
}
}
let totalDevices = deviceIds.length;
let onlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length;
let offlineCount = totalDevices - onlineCount;
// 故障设备数量
let faultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ device_id: { "%in%": deviceIds }, status: { "%ne%": 2 } },
["device_id"]
);
let faultDeviceIds = new Set(faultRecords.data.map((r: any) => r.device_id));
let faultCount = faultDeviceIds.size;
let nowStr = getNowStr();
let hourlyStatusTrend = await getDeviceStatusTrend(deviceList, nowStr);
let hourlyEnergyTrend = await getEnergyTrendByRegion(deviceIds, nowStr);
return {
deviceOnlineStatus,
deviceStatusMonitoring: {
totalDevices: totalDevices.toString(),
online: onlineCount.toString(),
offline: offlineCount.toString(),
fault: faultCount.toString()
},
deviceStatusTrend: hourlyStatusTrend,
energyConsumptionMonitoring: hourlyEnergyTrend
};
}
// 辅助函数:将 Date 转换为本地时间字符串 YYYY-MM-DD HH:MM:SS
function toLocalDateTimeStr(date: Date): string {
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
let hours = String(date.getHours()).padStart(2, '0');
let minutes = String(date.getMinutes()).padStart(2, '0');
let seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 获取当前本地时间字符串
function getNowStr(): string {
return toLocalDateTimeStr(new Date());
}
/**
* 获取过去24小时内每小时的设备在线统计(基于业务状态)
* @param deviceList 设备列表
* @param nowStr 当前时间字符串(本地时间)
* @returns 数组,从最早到最晚,每个元素包含 key(整点小时)、online、offline
*/
async function getDeviceStatusTrend(
deviceList: any[],
nowStr: string
) {
let deviceIds = deviceList.map((d: any) => d.device_id);
let totalDevices = deviceIds.length;
let now = new Date(nowStr);
// 计算起始时间:24小时前,并取整到小时(向下取整)
let startDate = new Date(now.getTime() - 24 * 3600000);
startDate.setMinutes(0, 0, 0);
// 结束时间:当前小时整点(不含当前小时区间,因为未结束)
let endDate = new Date(now);
endDate.setMinutes(0, 0, 0);
// 生成小时区间列表(从 startDate 开始,每小时递增,直到 < endDate)
let hourRanges: { start: Date; end: Date; key: string }[] = [];
let current = new Date(startDate);
while (current < endDate) {
let next = new Date(current.getTime() + 3600000);
hourRanges.push({
start: current,
end: next,
key: `${current.getHours()}:00`
});
current = next;
}
// 查询所有设备在时间范围内的数据
let startStr = toLocalDateTimeStr(startDate);
let endStr = toLocalDateTimeStr(now); // 使用当前时间作为结束,但实际查询时用 < endStr
let records = await selectDataListByParam(
TABLENAME.设备数据表,
{
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startStr, "%lte%": endStr },
"%orderAsc%": "received_time"
},
["device_id", "data", "received_time"]
);
// 按设备分组
let deviceDataMap = new Map<string, Array<{ timeStr: string; data: any }>>();
for (let rec of records.data) {
if (!deviceDataMap.has(rec.device_id)) {
deviceDataMap.set(rec.device_id, []);
}
deviceDataMap.get(rec.device_id)!.push({
timeStr: rec.received_time,
data: rec.data
});
}
// 对每个小时区间统计在线设备数
let result = [];
for (let range of hourRanges) {
let startStrRange = toLocalDateTimeStr(range.start);
let endStrRange = toLocalDateTimeStr(range.end);
let onlineCount = 0;
for (let dev of deviceList) {
let devId = dev.device_id;
let devType = dev.device_type;
let dataPoints = deviceDataMap.get(devId) || [];
// 查找该小时内最后一条数据
let lastInRange: { timeStr: string; data: any } | null = null;
for (let point of dataPoints) {
if (point.timeStr >= startStrRange && point.timeStr < endStrRange) {
lastInRange = point;
}
}
if (lastInRange && isDeviceOnlineByStatus(devType, lastInRange.data)) {
onlineCount++;
}
}
result.push({
key: range.key,
online: onlineCount,
offline: totalDevices - onlineCount,
fault: 0
});
}
return result;
}
/**
* 获取过去24小时每小时的能耗趋势
* @param deviceIds 设备ID列表
* @param nowStr 当前时间字符串
*/
async function getEnergyTrendByRegion(deviceIds: string[], nowStr: string) {
let nowTime = toDate(nowStr).getTime();
let startTime = new Date(nowTime - 24 * 3600000);
let startStr = toDateTimeStr(startTime);
let records = await selectDataListByParam(
TABLENAME.设备数据表,
{
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startStr },
"%orderAsc%": "received_time"
},
["device_id", "data", "received_time"]
);
// 按设备分组提取能耗值
let deviceEnergySeries = new Map<string, Array<{ timestamp: number; energy: number }>>();
for (let rec of records.data) {
let dataObj = rec.data;
let energy = dataObj.energy ?? dataObj.total_energy;
if (energy === undefined) continue;
let ts = toDate(rec.received_time).getTime();
if (!deviceEnergySeries.has(rec.device_id)) {
deviceEnergySeries.set(rec.device_id, []);
}
deviceEnergySeries.get(rec.device_id)!.push({ timestamp: ts, energy });
}
let hourlyEnergy = new Array(24).fill(0);
for (let i = 0; i < 24; i++) {
let hourStart = new Date(nowTime - (23 - i) * 3600000);
let hourEnd = new Date(hourStart.getTime() + 3600000);
let startTs = hourStart.getTime();
let endTs = hourEnd.getTime();
for (let series of deviceEnergySeries.values()) {
let firstInHour: { timestamp: number; energy: number } | null = null;
let lastInHour: { timestamp: number; energy: number } | null = null;
for (let point of series) {
if (point.timestamp >= startTs && point.timestamp < endTs) {
if (!firstInHour || point.timestamp < firstInHour.timestamp) firstInHour = point;
if (!lastInHour || point.timestamp > lastInHour.timestamp) lastInHour = point;
}
}
if (firstInHour && lastInHour && lastInHour.energy > firstInHour.energy) {
hourlyEnergy[i] += (lastInHour.energy - firstInHour.energy);
}
}
}
let result = [];
for (let i = 0; i < 24; i++) {
let hourPoint = new Date(nowTime - (23 - i) * 3600000);
let hourKey = `${hourPoint.getHours()}:00`;
result.push({ key: hourKey, energy: Number(hourlyEnergy[i].toFixed(2)) });
}
return result;
}
function isDeviceOnlineByStatus(deviceType: string, latestData: any): boolean {
if (!latestData) return false;
switch (deviceType) {
case "灯光":
case "空调":
case "新风":
case "音乐":
case "水景":
case "雾森":
case "灌溉":
case "驱蚊":
case "增压泵":
case "智慧庭院-水景": // 兼容旧数据
case "智慧庭院-雾森": // 兼容旧数据
case "智慧庭院-灌溉": // 兼容旧数据
case "智慧庭院-驱蚊": // 兼容旧数据
case "智慧庭院-增压泵": // 兼容旧数据
return latestData.power === "on" || latestData.power === true;
case "窗帘":
case "推窗器":
return latestData.action !== undefined && latestData.action !== null;
case "人体传感":
case "人体传感器":
return true;
default:
// 空气质量传感、土壤传感器、气象设备、电能监测等默认只要有数据即视为激活
return true;
}
}
/**
* 获取智能监控弹窗数据
* @param regionKey 区域编号(region.id)
* @returns 设备在线状态、空气质量监测、设备监测详情
*/
export async function getSmartMonitorPop(regionKey: number) {
// 1. 获取该区域下所有设备
let devices = await selectDataListByParam(
TABLENAME.设备表,
{ region_key: regionKey },
["device_id", "device_type", "device_name"]
);
let deviceList = devices.data;
if (deviceList.length === 0) {
return {
deviceOnlineStatus: {},
airQualityMonitoring: {
airQualityLevel: "无数据",
temperature: "--",
humidity: "--",
pm25: "--",
co2: "--",
tvoc: "--"
},
deviceMonitoring: {}
};
}
// 2. 获取每个设备的最新数据(按设备ID分组取最新一条)
let deviceIds = deviceList.map(d => d.device_id);
let latestDataMap = new Map<string, any>(); // key: device_id, value: { data, received_time }
for (let devId of deviceIds) {
let result = await selectDataListByParam(
TABLENAME.设备数据表,
{ device_id: devId, "%orderDesc%": "received_time", "%limit%": 1 },
["data", "received_time"]
);
if (result.data.length > 0) {
latestDataMap.set(devId, {
data: JSON.parse(result.data[0].data),
received_time: result.data[0].received_time
});
}
}
let nowTime = new Date();
let deviceOnlineStatus: { [key: string]: boolean } = {};
let deviceMonitoring: { [key: string]: any } = {};
// 辅助:判断在线状态(30分钟内有数据视为在线)
function isOnline(receivedTime: string | undefined): boolean {
if (!receivedTime) return false;
let lastTime = new Date(receivedTime);
let diffMinutes = (nowTime.getTime() - lastTime.getTime()) / 60000;
return diffMinutes <= 30;
}
// 遍历设备列表,按类型聚合
for (let dev of deviceList) {
let cnType = dev.device_type;
let enKey = deviceTypeKeyMap[cnType];
if (!enKey) continue; // 跳过未映射的设备类型
let latest = latestDataMap.get(dev.device_id);
let online = isOnline(latest?.received_time);
// 设备在线状态:同类型只要有一个在线即为 true(按业务需求可调整)
if (deviceOnlineStatus[enKey] === undefined) {
deviceOnlineStatus[enKey] = online;
} else {
deviceOnlineStatus[enKey] = deviceOnlineStatus[enKey] || online;
}
// 设备监测详情:每种类型只保留最新一个设备的数据(若同类型多设备,可改为取最后上报的设备)
if (!deviceMonitoring[enKey] && latest) {
let data = latest.data;
switch (cnType) {
case "灯光":
deviceMonitoring[enKey] = {
power: data.power === true || data.power === "on",
brightness: data.brightness ?? 0,
colorTemperature: data.colorTemperature ?? 3000
};
break;
case "窗帘":
case "推窗器":
let position = data.position ?? 0;
deviceMonitoring[enKey] = {
position: position,
action: position >= 50 ? "open" : "close"
};
break;
case "空调":
deviceMonitoring[enKey] = {
power: data.power === true || data.power === "on",
targetTemperature: data.targetTemperature ?? 22.0,
roomTemperature: data.roomTemperature ?? 21.0,
mode: data.mode ?? "auto",
fanSpeed: data.fanSpeed ?? "auto"
};
break;
case "新风":
deviceMonitoring[enKey] = {
power: data.power === true || data.power === "on",
fanSpeed: data.fanSpeed ?? "auto"
};
break;
case "音乐":
deviceMonitoring[enKey] = {
power: data.power === true || data.power === "on",
volume: data.volume ?? 0
};
break;
case "人体传感":
case "人体传感器":
deviceMonitoring[enKey] = {
occupied: data.occupied === true
};
break;
// 空气质量传感、电表等不在 deviceMonitoring 中展示,跳过
default:
break;
}
}
}
// 3. 空气质量监测数据(从空气质量传感设备获取)
let airQualityMonitoring = {
airQualityLevel: "无数据",
temperature: "--",
humidity: "--",
pm25: "--",
co2: "--",
tvoc: "--"
};
let airDevices = deviceList.filter(d => d.device_type === "空气质量传感");
for (let airDev of airDevices) {
let latestAir = latestDataMap.get(airDev.device_id);
if (latestAir) {
let data = latestAir.data;
let temperature = data.temperature !== undefined ? `${data.temperature}℃` : undefined;
let humidity = data.humidity !== undefined ? `${data.humidity}%` : undefined;
let pm25 = data.pm25 !== undefined ? `${data.pm25}μg/m³` : undefined;
let co2 = data.co2 !== undefined ? `${data.co2}ppm` : undefined;
let tvoc = data.tvoc !== undefined ? `${data.tvoc}` : "0";
// 空气质量等级:根据 PM2.5 简单划分(也可根据 AQI)
let level = "优";
if (pm25) {
let val = data.pm25;
if (val <= 35) level = "优";
else if (val <= 75) level = "良";
else if (val <= 115) level = "轻度";
else if (val <= 150) level = "中度";
else level = "重度";
}
airQualityMonitoring = {
airQualityLevel: level,
temperature: temperature ?? "--",
humidity: humidity ?? "--",
pm25: pm25 ?? "--",
co2: co2 ?? "--",
tvoc: tvoc
};
break; // 取第一个空气质量设备的数据
}
}
return {
deviceOnlineStatus,
airQualityMonitoring,
deviceMonitoring
};
}
/**
* 获取所有区域列表(供前端下拉选择)
* @returns 区域列表,按 sort_order 排序
*/
export async function getRegionList() {
let result = await selectDataListByParam(
TABLENAME.区域表,
{ "%orderAsc%": "sort_order" },
["id", "name", "sort_order"]
);
return {
list: result.data.map((r: any) => ({
regionKey: r.id,
regionName: r.name,
sortOrder: r.sort_order,
})),
};
}
......@@ -8,6 +8,14 @@ import { post } from '../util/request';
import { BizError } from '../util/bizError';
import { ERRORENUM } from '../config/errorEnum';
// 全局变量缓存 token 和过期时间
let feiYiToken = "";
let expireTime = 0;
let getTokenTime = 0;
let isFetchingToken = false; // 避免并发获取 token 的标志位
let refreshTokenPromise: Promise<string> | null = null; // 用于避免并发刷新 token 的 Promise
/**
* 飞奕云平台配置(建议从环境变量或配置文件中读取)
*/
......@@ -21,8 +29,8 @@ const FEIYI_CONFIG = {
* 生成随机字符串 nonce
* @param length 长度,默认 16
*/
function generateNonce(length: number = 16): string {
return crypto.randomBytes(length).toString('hex');
function generateNonce(): string {
return crypto.randomBytes(16).toString('hex');
}
/**
......@@ -36,7 +44,7 @@ function generateNonce(length: number = 16): string {
* @param params 请求参数对象
* @param secretKey 密钥
*/
function generateSign(params: Record<string, any>, secretKey: string): string {
function generateSign(params: Record<string, any>, timestamp: string, nonce: string, secretKey: string, charset: string = 'UTF-8'): string {
// 1. 过滤掉值为 null 或 undefined 的参数(空字符串保留)
const filteredParams: Record<string, any> = {};
for (const key in params) {
......@@ -48,11 +56,11 @@ function generateSign(params: Record<string, any>, secretKey: string): string {
const sortedKeys = Object.keys(filteredParams).sort();
// 3. 拼接成 key1=value1&key2=value2...
const paramStr = sortedKeys.map(key => `${key}=${filteredParams[key]}`).join('&');
// 4. 拼接 SecretKey
const stringSignTemp = paramStr + secretKey;
// 5. MD5 并转大写
const md5 = crypto.createHash('md5').update(stringSignTemp).digest('hex');
return md5.toUpperCase();
// 4. 拼接timestamp、nonce和SecretKey
const stringSignTemp = paramStr + `&timestamp=${timestamp}&nonce=${nonce}&SecretKey=${secretKey}`;
console.log('MD5加密请求参数:', stringSignTemp);
// 6. MD5 并转大写
return crypto.createHash('md5').update(stringSignTemp).digest('hex').toUpperCase();
}
/**
......@@ -61,21 +69,29 @@ function generateSign(params: Record<string, any>, secretKey: string): string {
* @throws 如果请求失败或响应码非 00000,则抛出 BizError
*/
export async function getFeiYiToken(): Promise<string> {
const timestamp = Date.now().toString(); // 毫秒时间戳
const nonce = generateNonce();
// 校验token是否有效,有效则直接返回
const now = Date.now();
if (feiYiToken && expireTime > now) {
console.log(`使用缓存 token,剩余有效时间: ${(expireTime - now) / 1000} 秒`);
return feiYiToken;
}
// 根据文档,获取 token 的请求参数
const params = {
accessKey: '', // 文档要求传空串
clientId: FEIYI_CONFIG.clientId,
nonce: nonce,
timestamp: timestamp,
AccessKey: '', // 文档要求传空串
clientId: FEIYI_CONFIG.clientId
};
const sign = generateSign(params, FEIYI_CONFIG.secretKey);
// 生成签名
const timestamp = Date.now().toString(); // 毫秒时间戳
const nonce = generateNonce();
const sign = generateSign(params, timestamp, nonce, FEIYI_CONFIG.secretKey);
console.log('MD5加密签名:', sign);
const requestBody = {
...params,
sign: sign,
timestamp: timestamp,
nonce: nonce,
sign: sign
};
const url = `${FEIYI_CONFIG.baseUrl}/open/oauth2/openapi/token`;
......@@ -88,6 +104,7 @@ export async function getFeiYiToken(): Promise<string> {
} catch (err) {
throw new BizError(ERRORENUM.网络错误, `获取 token 网络失败: ${err.message}`);
}
console.log('获取 token 响应:', result);
// 检查响应结构:文档中返回 { code, data: { access_token } }
if (!result || result.code !== '00000') {
......@@ -97,19 +114,28 @@ export async function getFeiYiToken(): Promise<string> {
const accessToken = result.data?.access_token;
if (!accessToken) {
throw new BizError(ERRORENUM.第三方接口错误, '响应中未包含 access_token');
} else {
// 更新全局 token 和过期时间
feiYiToken = accessToken;
getTokenTime = now;
// 默认 2分钟 过期,如果响应中包含 expires_in 字段,则使用该值(单位毫秒)
expireTime = now + (result.data.expires_in ? result.data.expires_in : 2 * 60 * 1000);
console.log(`获取新 token: ${accessToken}, 过期时间: ${new Date(expireTime).toISOString()}`);
}
return accessToken;
}
/**
* 带 Token 的 POST 请求封装
* 自动获取/刷新 token(简单实现,生产环境建议做 token 缓存和过期刷新)
* 自动获取/刷新 token
* @param path 接口路径(相对路径)
* @param body 请求体
* @param retry 是否重试(用于 token 过期重试)
*/
async function feiYiPost(path: string, body: any, retry: boolean = true): Promise<any> {
const token = await getFeiYiToken(); // 每次都获取最新 token(简单起见,可缓存)
// 获取 token
const token = await getFeiYiToken();
const url = `${FEIYI_CONFIG.baseUrl}${path}`;
const headers = {
'Authorization': `Bearer ${token}`,
......@@ -119,6 +145,7 @@ async function feiYiPost(path: string, body: any, retry: boolean = true): Promis
let result: any;
try {
result = await post(url, body, headers);
console.log(`请求 ${path} 成功,响应:`, result);
} catch (err) {
throw new BizError(ERRORENUM.网络错误, `请求 ${path} 失败: ${err.message}`);
}
......
import { selectOneDataByParam, selectDataListByParam, selectPaginatedDataWithOrder } from "../data/findData";
import { selectDataListByParam } from "../data/findData";
import { TABLENAME } from "../config/dbEnum";
import { getMySqlMs } from "../tools/systemTools";
import { BizError } from "../util/bizError";
import { ERRORENUM } from "../config/errorEnum";
import { addData } from "../data/addData";
import { updateManyData } from "../data/updateData";
import { region } from "tencentcloud-sdk-nodejs";
/**
* 注册或更新设备信息
* @param deviceId 设备唯一标识(必填)
* @param regionKey 区域编号(必填),对应 region 表的 id
* @param deviceType 设备类型(必填),如“灯光”“空调”
* @param deviceName 设备名称(可选),如“主照明灯”
* @param controlParams 设备支持的参数定义(可选),JSON对象
* @returns {Promise<{success: boolean}>}
* @throws 如果 regionKey 对应的区域不存在,则抛出错误
*/
export async function registerOrUpdateDevice( deviceId: string, regionKey: number, deviceType: string, deviceName?: string | null, controlParams?: any ) {
// 校验区域是否存在
let region = await selectOneDataByParam(TABLENAME.区域表, { id: regionKey });
if (!region.data) {
throw new BizError(ERRORENUM.参数错误, `区域编号 ${regionKey} 不存在`);
}
let exist = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
let now = new Date().toISOString().slice(0, 19).replace('T', ' ');
if (Object.keys(exist.data).length) {
await updateManyData(TABLENAME.设备表, { device_id: deviceId }, {
region_key: regionKey,
device_type: deviceType,
device_name: deviceName,
control_params: controlParams || null,
updated_at: now,
});
} else {
await addData(TABLENAME.设备表, {
device_id: deviceId,
region_key: regionKey,
device_type: deviceType,
device_name: deviceName,
control_params: controlParams || null,
created_at: now,
updated_at: now,
});
}
return { success: true };
}
/**
* 处理设备商推送的数据
* @param deviceId 设备唯一标识(必填)
* @param data 设备上报的JSON数据(必填),例如:{ "power": "on", "brightness": 80 }
* @param deviceTime 设备本地时间(可选),格式:YYYY-MM-DD HH:MM:SS
* @returns {Promise<{recordId: number}>} 返回新插入记录的ID
* @throws 如果设备未在 device 表中注册,则抛出错误(需先调用注册接口)
*/
export async function handleDevicePush(deviceId: string, data: any, deviceTime?: string) {
if (!deviceId || !data) {
throw new BizError(ERRORENUM.参数错误, "deviceId和data不能为空");
}
// 检查设备是否存在,不存在则抛出错误(要求先注册)
let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
if (!device.data) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册,请先调用注册接口`);
}
let nowMs = getMySqlMs();
let receivedTime = new Date(nowMs).toISOString().slice(0, 19).replace('T', ' ');
await addData(TABLENAME.设备数据表, {
device_id: deviceId,
data: data,
received_time: receivedTime,
device_time: deviceTime ? new Date(deviceTime).toISOString().slice(0, 19).replace('T', ' ') : null,
created_at: receivedTime,
});
return { success: true };
}
/**
* 处理设备商推送的故障数据
* @param deviceId 设备唯一标识(必填)
* @param faultType 故障类型(必填),如“离线”“数据异常”“硬件故障”
* @param faultLevel 故障等级
* @param faultDescription 故障详细描述(可选)
* @param occurredTime 故障发生时间(可选),格式:YYYY-MM-DD HH:MM:SS,不传则使用服务器时间
* @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID
* @throws 如果设备未注册,则抛出错误
*/
export async function handleDeviceFaultPush( deviceId: string, faultType: string, faultLevel: string, faultDescription?: string, occurredTime?: string ) {
if (!deviceId || !faultType) {
throw new BizError(ERRORENUM.参数错误, "deviceId 和 faultType 不能为空");
}
// 检查设备是否存在
let device = await selectOneDataByParam(TABLENAME.设备表, { device_id: deviceId });
if (!device.data) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 未注册,请先调用注册接口`);
}
let nowMs = getMySqlMs();
let nowStr = new Date(nowMs).toISOString().slice(0, 19).replace('T', ' ');
let faultTime = occurredTime
? new Date(occurredTime).toISOString().slice(0, 19).replace('T', ' ')
: nowStr;
let insertResult: any = await addData(TABLENAME.设备故障表, {
device_id: deviceId,
fault_type: faultType,
fault_description: faultDescription || null,
occurred_time: faultTime,
level: faultLevel || '1', // 默认一级故障
status: 0, // 0=未处理
resolved_time: null,
created_at: nowStr,
updated_at: nowStr,
});
return { faultId: insertResult.insertId, success: true };
}
/**
* 智能管控-运行分析
* @param regionGroup 区域组(可选),如“A馆”,如果提供则返回该类型下所有区域的分析数据
* @param regionType 区域类型(可选),如“1F”,如果提供则返回该区域的分析数据
* @returns 大屏所需的所有指标数据
*/
export async function getRunAnalysis(regionGroup: string, regionType: string) {
// 根据区域类型和名称筛选设备ID列表(如果提供了区域信息)
let regionParam: any = { groups: regionGroup };
if (regionType) {
regionParam.type = regionType;
}
let regionInfo: any = await selectDataListByParam(TABLENAME.区域表, regionParam, ["id"]);
let regionKeys = regionInfo.data.map((r: any) => r.id);
console.log("筛选区域Key:", regionKeys);
// 1.1 获取所选区域三相电表及电能监测设备的用电数据(用于能耗管理)
let powerParams: any = {device_type: { "%in%": ["三相电表", "电能监测"] }};
if (regionKeys.length > 0) {
powerParams.region_key = { "%in%": regionKeys };
}
let energyDevices = await selectDataListByParam(TABLENAME.设备表, powerParams, ["device_id"]);
let energyDeviceIds = energyDevices.data.map((d: any) => d.device_id);
// 1.2 获取今日、昨日、本月、上月用电量
let todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
let todayEnd = new Date();
let yesterdayStart = new Date(Date.now() - 86400000); yesterdayStart.setHours(0,0,0,0);
let yesterdayEnd = new Date(Date.now() - 86400000); yesterdayEnd.setHours(23,59,59,999);
let currentMonthStart = new Date(); currentMonthStart.setDate(1); currentMonthStart.setHours(0,0,0,0);
let lastMonthStart = new Date(); lastMonthStart.setMonth(lastMonthStart.getMonth() - 1); lastMonthStart.setDate(1); lastMonthStart.setHours(0,0,0,0);
let lastMonthEnd = new Date(currentMonthStart.getTime() - 1);
// 辅助函数:查询某个时间段内的总用电量(单位 kwh)
async function getTotalEnergy(deviceIds: string[], startTime: Date, endTime: Date): Promise<number> {
if (deviceIds.length === 0) return 0;
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let total = 0;
let deviceEnergyMap = new Map();
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
if (!deviceEnergyMap.has(rec.device_id)) {
deviceEnergyMap.set(rec.device_id, { min: energy, max: energy });
} else {
let cur = deviceEnergyMap.get(rec.device_id);
if (energy < cur.min) cur.min = energy;
if (energy > cur.max) cur.max = energy;
}
}
for (let { min, max } of deviceEnergyMap.values()) {
total += (max - min);
}
return total;
}
let todayEnergy = await getTotalEnergy(energyDeviceIds, todayStart, todayEnd);
let yesterdayEnergy = await getTotalEnergy(energyDeviceIds, yesterdayStart, yesterdayEnd);
let currentMonthEnergy = await getTotalEnergy(energyDeviceIds, currentMonthStart, new Date());
let previousMonthEnergy = await getTotalEnergy(energyDeviceIds, lastMonthStart, lastMonthEnd);
let dailyYearOnYear = yesterdayEnergy === 0 ? '0%' : `${(((todayEnergy - yesterdayEnergy) / yesterdayEnergy) * 100).toFixed(2)}%`;
let monthlyYearOnYear = previousMonthEnergy === 0 ? '0%' : `${(((currentMonthEnergy - previousMonthEnergy) / previousMonthEnergy) * 100).toFixed(2)}%`;
// 1.3 用电趋势:过去24小时(按小时)、过去7天、过去30天、过去一年(按月)
let nowTime = new Date();
let last24hStart = new Date(nowTime.getTime() - 24 * 3600000);
let hourlyEnergy = await getEnergyTrend(energyDeviceIds, last24hStart, nowTime, 'hour');
let last7DaysStart = new Date(nowTime.getTime() - 7 * 86400000);
let dailyEnergy7 = await getEnergyTrend(energyDeviceIds, last7DaysStart, nowTime, 'day');
let last30DaysStart = new Date(nowTime.getTime() - 30 * 86400000);
let dailyEnergy30 = await getEnergyTrend(energyDeviceIds, last30DaysStart, nowTime, 'day');
let lastYearStart = new Date(nowTime.getFullYear() - 1, nowTime.getMonth(), 1);
let monthlyEnergyYear = await getEnergyTrend(energyDeviceIds, lastYearStart, nowTime, 'month');
// 2.1 获取所选区域空调设备运行状态数据(如空调数、在线率、故障率等)
let acParams: any = {device_type: "空调" };
if (regionKeys.length > 0) {
acParams.region_key = { "%in%": regionKeys };
}
let acDevices = await selectDataListByParam(TABLENAME.设备表, acParams, ["device_id"]);
let acDeviceIds = acDevices.data.map((d: any) => d.device_id);
let acCount = acDeviceIds.length;
let acOfflineCount = 0;
let acOnlineCount = acCount - acOfflineCount;
let acFaultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 }, device_id: { "%in%": acDeviceIds } },
["device_id"]
);
let acFaultDeviceIds = new Set(acFaultRecords.data.map((r: any) => r.device_id));
let acFaultDevices = acFaultDeviceIds.size;
let acNormalRate = acCount === 0 ? '100%' : (((acCount - acFaultDevices) / acCount) * 100).toFixed(2) + '%';
let acFaultCount = acFaultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
// 2.2 获取今日空调在线趋势分析数据
let acOnlineTrend = [];
if (acDeviceIds.length) {
// 2.2.1 获取过去24小时设备数据
let last24h = new Date(nowTime.getTime() - 24*3600000);
let acHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": acDeviceIds },
received_time: { "%gte%": last24h.toISOString().slice(0,19).replace('T',' ') }
}, ["device_id", "data", "received_time"]);
// 2.2.2 按小时统计在线设备数和总设备数
let hourlyData = new Map();
for (let rec of acHistory.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}时`;
let data = rec.data;
console.log(data);
if (!hourlyData.has(hourKey)) {
let totalNum = 1;
let onlineNum = data.power === "on" ? 1 : 0;
hourlyData.set(hourKey, { online: onlineNum, total: totalNum });
} else {
let totalNum = hourlyData.get(hourKey).total;
let onlineNum = hourlyData.get(hourKey).online;
if (data.power === "on") onlineNum += 1;
hourlyData.set(hourKey, { online: onlineNum, total: totalNum + 1 });
}
}
console.log("空调在线数据(过去24小时):", hourlyData);
// 2.2.3 填充24小时空调在线数据,没有在线数据的小时段则设为零,并计算每小时的空调在线率
for (let i = 0; i < 24; i++) {
let key = `${i}时`;
let val = hourlyData.get(key);
acOnlineTrend.push({ time: `${i}:00`, value: val ? ((val.online / val.total) * 100).toFixed(2) : '0' });
}
// 2.3. 设备监测情况:在线/离线(根据最后数据时间是否超过30分钟)
let deviceLatestMap = new Map();
for (let devId of acDeviceIds) {
let rec = await selectDataListByParam(TABLENAME.设备数据表,
{ device_id: devId, "%orderDesc%": "received_time", "%limit%": 1 },
["received_time"]);
if (rec.data.length) {
let lastTime = new Date(rec.data[0].received_time);
let diffMinutes = (nowTime.getTime() - lastTime.getTime()) / 60000;
deviceLatestMap.set(devId, { lastTime, isOnline: diffMinutes <= 30 });
} else {
deviceLatestMap.set(devId, { lastTime: null, isOnline: false });
}
}
acOnlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length;
acOfflineCount = acCount - acOnlineCount;
}
// 3.1 新风运行监控
let fanParams: any = {device_type: "新风" };
if (regionKeys.length > 0) {
fanParams.region_key = { "%in%": regionKeys };
}
let fanDevices = await selectDataListByParam(TABLENAME.设备表, fanParams, ["device_id"]);
let fanDeviceIds = fanDevices.data.map((d: any) => d.device_id);
let fanCount = fanDeviceIds.length;
let fanOfflineCount = 0;
let fanOnlineCount = fanCount - fanOfflineCount;
let fanFaultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 }, device_id: { "%in%": fanDeviceIds } },
["device_id"]
);
let fanFaultDeviceIds = new Set(fanFaultRecords.data.map((r: any) => r.device_id));
let fanFaultDevices = fanFaultDeviceIds.size;
let fanNormalRate = fanCount === 0 ? '100%' : (((fanCount - fanFaultDevices) / fanCount) * 100).toFixed(2) + '%';
let fanFaultCount = fanFaultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
// 3.2 新风风量运行监测趋势分析数据(如果有风量数据)
let fanOnlineTrend = [];
if (fanDeviceIds.length) {
let fanDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": fanDeviceIds },
received_time: { "%gte%": last24hStart.toISOString().slice(0,19).replace('T',' '), "%lte%": nowTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
// 3.2.1 按小时统计新风送风量(换气次数=送风量/房间体积)
let hourlyData = new Map(); // {key: 00:00, ly: 0, ry: 0 }
for (let rec of fanDeviceDatas.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}时`;
let data = rec.data;
if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { airSupply: data.airSupply });
}
}
// 3.2.2 填充24小时新风监控数据,没有在线数据的小时段则设为零,并计算每小时的新风送风量
for (let i = 0; i < 24; i++) {
let key = `${i}时`;
let val = hourlyData.get(key);
fanOnlineTrend.push({ time: `${i}:00`, value: val ? val.airSupply : '0' });
}
}
// 4.1 环境监测九合一
let airParams: any = {device_type: "环境监测" };
if (regionKeys.length > 0) {
airParams.region_key = { "%in%": regionKeys };
}
let airQualityDevices = await selectDataListByParam(TABLENAME.设备表, airParams, ["device_id"]);
let airDeviceIds = airQualityDevices.data.map(d => d.device_id);
let qualityIndex = 300;
let co2Trend = [];
let currentCo2 = '21ppm';
let currentTemperature = '21℃';
let temperatureTrend = [];
let currentHumidity = '47%';
let humidityTrend = [];
let currentPm25 = '21μg/m³';
let pm25Trend = [];
let co2TrendDetail = [];
if (airDeviceIds.length) {
// 最新数据
let latestAir = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
"%orderDesc%": "received_time",
"%limit%": 1
}, ["data"]);
if (latestAir.data.length) {
let airData = latestAir.data[0].data;
currentTemperature = `${airData.temperature ?? 21}`;
currentHumidity = `${airData.humidity ?? 47}%`;
currentPm25 = `${airData.pm25 ?? 21}μg/m³`;
currentCo2 = `${airData.co2 ?? 400}ppm`;
qualityIndex = 500 - (airData.pm25 ?? 0);
}
// 过去24小时趋势
let last24h = new Date(nowTime.getTime() - 24*3600000);
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
received_time: { "%gte%": last24h.toISOString().slice(0,19).replace('T',' ') }
}, ["data", "received_time"]);
let hourlyData = new Map();
for (let rec of airHistory.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { temp: data.temperature, hum: data.humidity, pm: data.pm25, co2: data.co2 });
}
}
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyData.get(key);
temperatureTrend.push({ time: `${i}:00`, value: val?.temp?.toString() ?? '21' });
humidityTrend.push({ time: `${i}:00`, value: val?.hum?.toString() ?? '47' });
pm25Trend.push({ time: `${i}:00`, value: val?.pm?.toString() ?? '21' });
let co2Val = val?.co2 ?? 400;
co2TrendDetail.push({ time: `${i}:00`, value: (co2Val / 100).toFixed(1) });
co2Trend.push({ time: `${i}`, value: co2Val.toString() });
}
}
// 5.1 预警监控和预警工单处理进度分析
// 6.1 人流监控及客流量趋势分析
let regionInfos: any = [];
if (regionKeys.length === 0) {
// 6.1.1 若没有选择区域,则查询区域表获取所有区域类型
regionInfos = await selectDataListByParam(TABLENAME.区域表, {}, ["id", "name", "type"]);
} else {
// 6.1.2 若选择了区域,则查询选择的区域
regionInfos = await selectDataListByParam(TABLENAME.区域表, { id: { "%in%": regionKeys } }, ["id", "name", "type"]);
}
// 6.2 循环区域查询客流传感器设备,获取过去24小时每个区域的客流量
let totalRegionCustomerNum = 0; // 所有区域的总客流量
let regionMaxCustomerMap = new Map(); // {regionName: customerMaxNum}
let regionCustomerMap = new Map(); // {regionName: customerNum}
let regionCustomerTable = []; // [{regionName: A馆, customerNum: 5000, saturation: '80%'}]
for (let regionInfo of regionInfos.data) {
let customerDevices = await selectDataListByParam(TABLENAME.设备表, {
device_type: "客流传感器", region_key: regionInfo.id
}, ["device_type", "device_id"]);
let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id);
// 获取今日的客流量数据
if (customerDeviceIds) {
let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": customerDeviceIds },
received_time: { "%gte%": todayStart.toISOString().slice(0,19).replace('T',' '), "%lte%": todayEnd.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
// 统计区域类型的客流量
for (let rec of customerDeviceDatas.data) {
let data = rec.data;
if (data.presence === '进') {
totalRegionCustomerNum += 1;
// 区分楼栋与楼层
let regionName = regionKeys.length === 0 ? regionInfo.type : regionInfo.name;
if (!regionCustomerMap.has(regionName)) {
regionCustomerMap.set(regionName, 1);
// 如果是按区域类型统计,则最大承载量取该类型下所有区域的总和;如果是按区域名称统计,则取该区域的最大承载量
let maxCapacity = regionKeys.length === 0 ? regionMaxCustomerMap.get(regionName) : regionInfo.max_capacity;
regionMaxCustomerMap.set(regionName, maxCapacity); // 区域最大承载量
} else {
let totalNum = regionCustomerMap.get(regionName);
regionCustomerMap.set(regionName, totalNum + 1);
}
}
}
}
}
// 计算每个区域的饱和度(客流量/区域最大承载量),并转换成表格
for (let [regionName, customerNum] of regionCustomerMap.entries()) {
let regionMaxCustomer = regionMaxCustomerMap.get(regionName) || 10000; // 默认最大承载量为10000
let saturation = `${((customerNum / regionMaxCustomer) * 100).toFixed(2)}%`;
regionCustomerTable.push({ regionName, customerNum, saturation });
}
// 6.3 客流量趋势分析:根据查询到的客流传感器数据,统计所选区域过去24小时客流量变化趋势
let customerTrend = [];
let customerParams: any = { device_type: "客流传感器" };
if (regionKeys.length > 0) {
customerParams.region_key = { "%in%": regionKeys };
}
let customerDevices = await selectDataListByParam(TABLENAME.设备表, customerParams, ["device_type", "device_id"]);
if (customerDevices.data.length > 0) {
let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id);
// 获取过去24小时客流量数据
let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": customerDeviceIds },
received_time: { "%gte%": last24hStart.toISOString().slice(0,19).replace('T',' '), "%lte%": nowTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let hourlyCustomerMap = new Map();
for (let rec of customerDeviceDatas.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
let customerNum = data.presence === '进' ? 1 : 0;
if (!hourlyCustomerMap.has(hourKey)) {
hourlyCustomerMap.set(hourKey, customerNum);
} else {
let totalNum = hourlyCustomerMap.get(hourKey);
hourlyCustomerMap.set(hourKey, totalNum + customerNum);
}
}
// 填充过去24小时的客流量趋势数据
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyCustomerMap.get(key) ?? 0;
customerTrend.push({ time: `${i}:00`, value: val });
}
}
// 7.1 获取区域列表接口,供前端下拉选择区域类型和区域名称
let regionList: any = await getRegionList(regionGroup);
// 最终组装返回数据
return {
// 区域列表数据
regionList: regionList.data,
// 运行监控总数据(一年)
mainRunningManagement: {
totalCustomerNumber: 10000,
totalDeviceOnlineRate: '99.8%',
totalElectricity: `360kwh`,
},
// 能耗监控数据
energyManagement: {
todayElectricity: `${todayEnergy.toFixed(0)}kwh`,
yesterdayElectricity: `${yesterdayEnergy.toFixed(0)}kwh`,
dailyYearOnYear,
currentMonthElectricity: `${currentMonthEnergy.toFixed(0)}kwh`,
previousMonthElectricity: `${previousMonthEnergy.toFixed(0)}kwh`,
monthlyYearOnYear
},
// 能耗监控趋势图数据
electricityTrend: {
last24Hours: hourlyEnergy,
last7Days: dailyEnergy7,
last30Days: dailyEnergy30,
lastYear: monthlyEnergyYear
},
// 空调设备在线趋势图数据
acDeviceOnlineTrend: {
acCount: acDeviceIds.length,
acOnlineCount: acOnlineCount,
acOfflineCount: acOfflineCount,
acFaultCount: acFaultCount,
acOnlineTrend: acOnlineTrend
},
// 新风设备在线趋势图数据
fanDeviceOnlineTrend: {
fanCount: fanDeviceIds.length,
fanOnlineCount: fanOnlineCount,
fanOfflineCount: fanOfflineCount,
fanFaultCount: fanFaultCount,
fanOnlineTrend: fanOnlineTrend
},
// 环境监测趋势图数据
environmentalTrend: {
currentTemperature: currentTemperature,
currentHumidity: currentHumidity,
currentPm25: currentPm25,
currentCo2: currentCo2,
temperatureTrend: temperatureTrend,
humidityTrend: humidityTrend,
pm25Trend: pm25Trend,
co2Trend: co2Trend,
qualityIndex: qualityIndex
},
// 人流监控实时数据和趋势图数据
customerOnlineTrend: {
passengerFlowTable: regionCustomerTable,
customerTrend: customerTrend
}
}
}
/**
* 智能管控-空调设备运行弹框
* @param regionKey
* @param deviceId
*/
export async function getAcRunningPop(regionKey: number, deviceId: string) {
// 获取设备信息
let devices = await selectDataListByParam(
TABLENAME.设备表,
{ region_key: regionKey, device_id: deviceId },
["device_id", "device_type", "device_name", "control_params"]
);
if (!devices.data.length) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 在区域 ${regionKey} 中未找到`);
}
// 获取设备最新数据
let deviceData = await selectDataListByParam(
TABLENAME.设备数据表,
{ device_id: deviceId, "%orderDesc%": "received_time", "%limit%": 1 },
["data", "received_time"]
);
return {
deviceInfo: devices.data[0].control_params,
latestData: deviceData.data.length ? deviceData.data[0].data : null
}
}
/**
* 设备运行弹框 - 空调启停
* @param deviceId
*/
export async function controlAcRunning(deviceId?: string) {
}
// 辅助函数:获取用电趋势(按间隔分组)
async function getEnergyTrend(deviceIds: string[], startTime: Date, endTime: Date, granularity: 'hour' | 'day' | 'month') {
if (deviceIds.length === 0) return [];
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let intervalMap = new Map(); // key: 时间字符串, value: { deviceMin, deviceMax }
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
let intervalKey: string;
let d = new Date(rec.received_time);
if (granularity === 'hour') {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()} ${d.getHours()}`;
} else if (granularity === 'day') {
intervalKey = `${d.getFullYear()}/${d.getMonth()+1}/${d.getDate()}`;
} else {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}`;
}
if (!intervalMap.has(intervalKey)) {
intervalMap.set(intervalKey, { deviceMin: new Map(), deviceMax: new Map() });
}
let entry = intervalMap.get(intervalKey);
if (!entry.deviceMin.has(rec.device_id)) {
entry.deviceMin.set(rec.device_id, energy);
entry.deviceMax.set(rec.device_id, energy);
} else {
let curMin = entry.deviceMin.get(rec.device_id);
let curMax = entry.deviceMax.get(rec.device_id);
if (energy < curMin) entry.deviceMin.set(rec.device_id, energy);
if (energy > curMax) entry.deviceMax.set(rec.device_id, energy);
}
}
let result = [];
for (let [key, { deviceMin, deviceMax }] of intervalMap.entries()) {
let total = 0;
for (let [deviceId, minVal] of deviceMin.entries()) {
let maxVal = deviceMax.get(deviceId);
total += (maxVal - minVal);
}
result.push({ key, value: total.toFixed(0) });
}
result.sort((a,b) => a.key.localeCompare(b.key));
return result;
}
// 基于馆内环境温度、湿度变化等,联动空调启停
async function controlAcByEnvironment(deviceId: string, data: any) {
// 获取区域内环境监测设备数据
let envDevices = await selectDataListByParam(TABLENAME.设备表, {
device_id: deviceId,
device_type: "环境监测"
}, ["id", "device_name", "region_key"]);
// 若是环境设备,则解析环境设备数据,判断是否需要联动空调设备
if (envDevices.data.length) {
// 通过设备所属区域查询当前区域中的空调设备,获取空调设备标准参数中预设的温度、湿度阈值
let regionKey = envDevices.data[0].region_key;
let acDevices = await selectDataListByParam(TABLENAME.设备表, {
region_key: regionKey,
device_type: "空调"
}, ["id", "device_name", "control_params"]);
acDevices.data.forEach((ac: any) => {
let controlParams = ac.control_params;
let maxTemp = controlParams.maxTemp;
let minTemp = controlParams.minTemp;
if (data.temperature && (data.temperature > maxTemp || data.temperature < minTemp)){
// 调用空调控制接口启停空调设备
console.log(`联动空调 ${ac.device_name}${ac.id})启停,当前温度:${data.temperature},阈值范围:${minTemp}-${maxTemp}`);
// 单个 TODO: 接入空调控制API,传入设备ID和控制指令(如开/关、温度设定等)
}
});
// 多个 TODO: 接入空调控制API,传入设备ID和控制指令(如开/关、温度设定等)
return { success: true, message: "成功" };
}
}
/**
* 设备类型中文到英文Key的映射
*/
let deviceTypeKeyMap: { [key: string]: string } = {
// "灯光": "lighting",
// "窗帘": "curtain",
// "推窗器": "windowOpener",
"空调": "hvac",
"新风": "freshAir",
// "音乐": "music",
"人体传感": "occupancySensor",
"人体传感器": "occupancySensor",
"空气质量传感": "airSensor",
// "燃气表": "gasMeter",
// "水表": "waterMeter",
"三相电表": "electricityMeter",
"电表": "electricityMeter",
"电能监测": "electricityMeter"
};
/**
* 获取所有区域列表(供前端下拉选择)
......
import { TABLENAME } from "../config/dbEnum";
import { ERRORENUM } from "../config/errorEnum";
import { selectDataListByParam } from "../data/findData";
import { BizError } from "../util/bizError";
import { getRegionList } from "./region";
/**
* 智能管控-运行分析
* @param regionGroup 区域组(可选),如“A馆”,如果提供则返回该类型下所有区域的分析数据
* @param regionType 区域类型(可选),如“1F”,如果提供则返回该区域的分析数据
* @returns 大屏所需的所有指标数据
*/
export async function getRunAnalysis(regionGroup: string, regionType: string) {
// 最终响应结果数据
let resultAny: any = { };
// 根据区域类型和名称筛选设备ID列表(如果提供了区域信息)
let regionParam: any = { groups: regionGroup };
if (regionType) {
regionParam.type = regionType;
}
let regionInfo: any = await selectDataListByParam(TABLENAME.区域表, regionParam, ["id"]);
let regionKeys = regionInfo.data.map((r: any) => r.id);
console.log("筛选区域Key:", regionKeys);
// 1.1 获取所选区域三相电表及电能监测设备的用电数据(用于能耗管理)
let powerParams: any = {device_type: { "%in%": ["三相电表", "电能监测"] }};
if (regionGroup) {
powerParams.region_key = { "%in%": regionKeys };
}
let energyDevices = await selectDataListByParam(TABLENAME.设备表, powerParams, ["device_id"]);
let energyDeviceIds = energyDevices.data.map((d: any) => d.device_id);
// 1.2 获取今日、昨日、本月、上月用电量
let todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
let todayEnd = new Date();
let yesterdayStart = new Date(Date.now() - 86400000); yesterdayStart.setHours(0,0,0,0);
let yesterdayEnd = new Date(Date.now() - 86400000); yesterdayEnd.setHours(23,59,59,999);
let currentMonthStart = new Date(); currentMonthStart.setDate(1); currentMonthStart.setHours(0,0,0,0);
let lastMonthStart = new Date(); lastMonthStart.setMonth(lastMonthStart.getMonth() - 1); lastMonthStart.setDate(1); lastMonthStart.setHours(0,0,0,0);
let lastMonthEnd = new Date(currentMonthStart.getTime() - 1);
// 辅助函数:查询某个时间段内的总用电量(单位 kwh)
async function getTotalEnergy(deviceIds: string[], startTime: Date, endTime: Date): Promise<number> {
if (deviceIds.length === 0) return 0;
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let total = 0;
let deviceEnergyMap = new Map();
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
if (!deviceEnergyMap.has(rec.device_id)) {
deviceEnergyMap.set(rec.device_id, { min: energy, max: energy });
} else {
let cur = deviceEnergyMap.get(rec.device_id);
if (energy < cur.min) cur.min = energy;
if (energy > cur.max) cur.max = energy;
}
}
for (let { min, max } of deviceEnergyMap.values()) {
total += (max - min);
}
return total;
}
let todayEnergy = await getTotalEnergy(energyDeviceIds, todayStart, todayEnd);
let yesterdayEnergy = await getTotalEnergy(energyDeviceIds, yesterdayStart, yesterdayEnd);
let currentMonthEnergy = await getTotalEnergy(energyDeviceIds, currentMonthStart, new Date());
let previousMonthEnergy = await getTotalEnergy(energyDeviceIds, lastMonthStart, lastMonthEnd);
let dailyYearOnYear = yesterdayEnergy === 0 ? '0%' : `${(((todayEnergy - yesterdayEnergy) / yesterdayEnergy) * 100).toFixed(2)}%`;
let monthlyYearOnYear = previousMonthEnergy === 0 ? '0%' : `${(((currentMonthEnergy - previousMonthEnergy) / previousMonthEnergy) * 100).toFixed(2)}%`;
// 1.3 用电趋势:过去24小时(按小时)、过去7天、过去30天、过去一年(按月)
let nowTime = new Date();
let last24hStart = new Date(nowTime.getTime() - 24 * 3600000);
let hourlyEnergy = await getEnergyTrend(energyDeviceIds, last24hStart, nowTime, 'hour');
let last7DaysStart = new Date(nowTime.getTime() - 7 * 86400000);
let dailyEnergy7 = await getEnergyTrend(energyDeviceIds, last7DaysStart, nowTime, 'day');
let last30DaysStart = new Date(nowTime.getTime() - 30 * 86400000);
let dailyEnergy30 = await getEnergyTrend(energyDeviceIds, last30DaysStart, nowTime, 'day');
let lastYearStart = new Date(nowTime.getFullYear() - 1, nowTime.getMonth(), 1);
let monthlyEnergyYear = await getEnergyTrend(energyDeviceIds, lastYearStart, nowTime, 'month');
// 1.4. 能耗监控数据和能耗监控趋势图数据
resultAny.energyManagement = {
todayElectricity: `${todayEnergy.toFixed(0)}kwh`,
yesterdayElectricity: `${yesterdayEnergy.toFixed(0)}kwh`,
dailyYearOnYear,
currentMonthElectricity: `${currentMonthEnergy.toFixed(0)}kwh`,
previousMonthElectricity: `${previousMonthEnergy.toFixed(0)}kwh`,
monthlyYearOnYear
};
resultAny.electricityTrend = {
last24Hours: hourlyEnergy,
last7Days: dailyEnergy7,
last30Days: dailyEnergy30,
lastYear: monthlyEnergyYear
};
// 4.1 环境监测九合一
let airParams: any = {device_type: "环境监测" };
if (regionGroup) {
airParams.region_key = { "%in%": regionKeys };
}
let airQualityDevices = await selectDataListByParam(TABLENAME.设备表, airParams, ["device_id"]);
let airDeviceIds = airQualityDevices.data.map(d => d.device_id);
let qualityIndex = 300;
let co2Trend = [];
let currentCo2 = '21ppm';
let currentTemperature = '21℃';
let temperatureTrend = [];
let currentHumidity = '47%';
let humidityTrend = [];
let currentPm25 = '21μg/m³';
let pm25Trend = [];
let co2TrendDetail = [];
if (airDeviceIds.length) {
// 最新数据
let latestAir = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
"%orderDesc%": "received_time",
"%limit%": 1
}, ["data"]);
if (latestAir.data.length) {
let airData = latestAir.data[0].data;
currentTemperature = `${airData.temperature ?? 21}`;
currentHumidity = `${airData.humidity ?? 47}%`;
currentPm25 = `${airData.pm25 ?? 21}μg/m³`;
currentCo2 = `${airData.co2 ?? 400}ppm`;
qualityIndex = 500 - (airData.pm25 ?? 0);
}
// 过去24小时趋势
let last24h = new Date(nowTime.getTime() - 24*3600000);
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds },
received_time: { "%gte%": last24h.toISOString().slice(0,19).replace('T',' ') }
}, ["data", "received_time"]);
let hourlyData = new Map();
for (let rec of airHistory.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { temp: data.temperature, hum: data.humidity, pm: data.pm25, co2: data.co2 });
}
}
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyData.get(key);
temperatureTrend.push({ time: `${i}:00`, value: val?.temp?.toString() ?? '21' });
humidityTrend.push({ time: `${i}:00`, value: val?.hum?.toString() ?? '47' });
pm25Trend.push({ time: `${i}:00`, value: val?.pm?.toString() ?? '21' });
let co2Val = val?.co2 ?? 400;
co2TrendDetail.push({ time: `${i}:00`, value: (co2Val / 100).toFixed(1) });
co2Trend.push({ time: `${i}`, value: co2Val.toString() });
}
}
// 4.2. 环境监测趋势图数据
resultAny.environmentalTrend = {
currentTemperature: currentTemperature,
currentHumidity: currentHumidity,
currentPm25: currentPm25,
currentCo2: currentCo2,
temperatureTrend: temperatureTrend,
humidityTrend: humidityTrend,
pm25Trend: pm25Trend,
co2Trend: co2Trend,
qualityIndex: qualityIndex
};
// 5.1 预警监控和预警工单处理进度分析
let alertStatus = {
totalAlerts: 48,
resolved: 20,
responded: 24,
unresolved: 4
};
let alertWorkOrders = [
{ riskContent: "空调压缩机异常", alertTime: "26/02/01", status: "已解决" },
{ riskContent: "温度传感器离线", alertTime: "26/02/01", status: "已响应" },
{ riskContent: "湿度超标", alertTime: "26/02/02", status: "未处理" }
];
let alertWorkLevel = {
"一级预警": 10,
"二级预警": 20,
"三级预警": 18
};
let alertWorkType = {
"设备故障": { count: 25, proportion: '52%' },
"环境异常": { count: 15, proportion: '30%' },
"安全事件": { count: 8, proportion: '18%' }
};
// 5.2. 预警监控数据
resultAny.alertManagement = {
alertWorkLevel: alertWorkLevel,
alertWorkType: alertWorkType,
alertWorkOrders: alertWorkOrders
};
// 6.1 人流监控及客流量趋势分析
let regionInfos: any = [];
if (regionKeys.length === 0) {
// 6.1.1 若没有选择区域,则查询区域表获取所有区域类型
regionInfos = await selectDataListByParam(TABLENAME.区域表, {}, ["id", "name", "type"]);
} else {
// 6.1.2 若选择了区域,则查询选择的区域
regionInfos = await selectDataListByParam(TABLENAME.区域表, { id: { "%in%": regionKeys } }, ["id", "name", "type"]);
}
// 6.2 循环区域查询客流传感器设备,获取过去24小时每个区域的客流量
let totalRegionCustomerNum = 0; // 所有区域的总客流量
let regionMaxCustomerMap = new Map(); // {regionName: customerMaxNum}
let regionCustomerMap = new Map(); // {regionName: customerNum}
let regionCustomerTable = []; // [{regionName: A馆, customerNum: 5000, saturation: '80%'}]
for (let regionInfo of regionInfos.data) {
let customerDevices = await selectDataListByParam(TABLENAME.设备表, {
device_type: "客流传感器", region_key: regionInfo.id
}, ["device_type", "device_id"]);
let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id);
// 获取今日的客流量数据
if (customerDeviceIds) {
let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": customerDeviceIds },
received_time: { "%gte%": todayStart.toISOString().slice(0,19).replace('T',' '), "%lte%": todayEnd.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
// 统计区域类型的客流量
for (let rec of customerDeviceDatas.data) {
let data = rec.data;
if (data.presence === '进') {
totalRegionCustomerNum += 1;
// 区分楼栋与楼层
let regionName = regionKeys.length === 0 ? regionInfo.type : regionInfo.name;
if (!regionCustomerMap.has(regionName)) {
regionCustomerMap.set(regionName, 1);
// 如果是按区域类型统计,则最大承载量取该类型下所有区域的总和;如果是按区域名称统计,则取该区域的最大承载量
let maxCapacity = regionKeys.length === 0 ? regionMaxCustomerMap.get(regionName) : regionInfo.max_capacity;
regionMaxCustomerMap.set(regionName, maxCapacity); // 区域最大承载量
} else {
let totalNum = regionCustomerMap.get(regionName);
regionCustomerMap.set(regionName, totalNum + 1);
}
}
}
}
}
// 计算每个区域的饱和度(客流量/区域最大承载量),并转换成表格
for (let [regionName, customerNum] of regionCustomerMap.entries()) {
let regionMaxCustomer = regionMaxCustomerMap.get(regionName) || 10000; // 默认最大承载量为10000
let saturation = `${((customerNum / regionMaxCustomer) * 100).toFixed(2)}%`;
regionCustomerTable.push({ regionName, customerNum, saturation });
}
// 6.3 客流量趋势分析:根据查询到的客流传感器数据,统计所选区域过去24小时客流量变化趋势
let customerTrend = [];
let customerParams: any = { device_type: "客流传感器" };
if (regionGroup) {
customerParams.region_key = { "%in%": regionKeys };
}
let customerDevices = await selectDataListByParam(TABLENAME.设备表, customerParams, ["device_type", "device_id"]);
if (customerDevices.data.length > 0) {
let customerDeviceIds = customerDevices.data.map((d: any) => d.device_id);
// 获取过去24小时客流量数据
let customerDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": customerDeviceIds },
received_time: { "%gte%": last24hStart.toISOString().slice(0,19).replace('T',' '), "%lte%": nowTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let hourlyCustomerMap = new Map();
for (let rec of customerDeviceDatas.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
let customerNum = data.presence === '进' ? 1 : 0;
if (!hourlyCustomerMap.has(hourKey)) {
hourlyCustomerMap.set(hourKey, customerNum);
} else {
let totalNum = hourlyCustomerMap.get(hourKey);
hourlyCustomerMap.set(hourKey, totalNum + customerNum);
}
}
// 填充过去24小时的客流量趋势数据
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyCustomerMap.get(key) ?? 0;
customerTrend.push({ time: `${i}:00`, value: val });
}
}
// 6.4. 人流监控实时数据和趋势图数据
resultAny.customerOnlineTrend = {
passengerFlowTable: regionCustomerTable,
customerTrend: customerTrend
};
// 三级页面不需要执行此逻辑
if (!regionType) {
// 2.1 获取所选区域空调设备运行状态数据(如空调数、在线率、故障率等)
let acParams: any = { device_type: "空调" };
if (regionGroup) {
acParams.region_key = { "%in%": regionKeys };
}
let acDevices = await selectDataListByParam(TABLENAME.设备表, acParams, ["device_id"]);
let acDeviceIds = acDevices.data.map((d: any) => d.device_id);
let acCount = acDeviceIds.length;
let acOfflineCount = 0;
let acOnlineCount = acCount - acOfflineCount;
let acFaultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 }, device_id: { "%in%": acDeviceIds } },
["device_id"]
);
let acFaultDeviceIds = new Set(acFaultRecords.data.map((r: any) => r.device_id));
let acFaultDevices = acFaultDeviceIds.size;
let acNormalRate = acCount === 0 ? '100%' : (((acCount - acFaultDevices) / acCount) * 100).toFixed(2) + '%';
let acFaultCount = acFaultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
// 2.2 获取今日空调在线趋势分析数据
let acOnlineTrend = [];
if (acDeviceIds.length) {
// 2.2.1 获取过去24小时设备数据
let last24h = new Date(nowTime.getTime() - 24 * 3600000);
let acHistory = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": acDeviceIds },
received_time: { "%gte%": last24h.toISOString().slice(0, 19).replace('T', ' ') }
}, ["device_id", "data", "received_time"]);
// 2.2.2 按小时统计在线设备数和总设备数
let hourlyData = new Map();
for (let rec of acHistory.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
console.log(data);
if (!hourlyData.has(hourKey)) {
let totalNum = 1;
let onlineNum = data.power === "on" ? 1 : 0;
hourlyData.set(hourKey, { online: onlineNum, total: totalNum });
} else {
let totalNum = hourlyData.get(hourKey).total;
let onlineNum = hourlyData.get(hourKey).online;
if (data.power === "on") onlineNum += 1;
hourlyData.set(hourKey, { online: onlineNum, total: totalNum + 1 });
}
}
console.log("空调在线数据(过去24小时):", hourlyData);
// 2.2.3 填充24小时空调在线数据,没有在线数据的小时段则设为零,并计算每小时的空调在线率
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyData.get(key);
acOnlineTrend.push({ time: `${i}:00`, value: val ? ((val.online / val.total) * 100).toFixed(2) : '0' });
}
// 2.3 设备监测情况:在线/离线(根据最后数据时间是否超过30分钟)
let deviceLatestMap = new Map();
for (let devId of acDeviceIds) {
let rec = await selectDataListByParam(TABLENAME.设备数据表,
{ device_id: devId, "%orderDesc%": "received_time", "%limit%": 1 },
["received_time"]);
if (rec.data.length) {
let lastTime = new Date(rec.data[0].received_time);
let diffMinutes = (nowTime.getTime() - lastTime.getTime()) / 60000;
deviceLatestMap.set(devId, { lastTime, isOnline: diffMinutes <= 30 });
} else {
deviceLatestMap.set(devId, { lastTime: null, isOnline: false });
}
}
acOnlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length;
acOfflineCount = acCount - acOnlineCount;
}
// 2.4. 空调设备在线趋势图数据
resultAny.acDeviceOnlineTrend = {
acCount: acDeviceIds.length,
acOnlineCount: acOnlineCount,
acOfflineCount: acOfflineCount,
acFaultCount: acFaultCount,
acOnlineTrend: acOnlineTrend
};
// 3.1 新风运行监控
let fanParams: any = { device_type: "新风" };
if (regionGroup) {
fanParams.region_key = { "%in%": regionKeys };
}
let fanDevices = await selectDataListByParam(TABLENAME.设备表, fanParams, ["device_id"]);
let fanDeviceIds = fanDevices.data.map((d: any) => d.device_id);
let fanCount = fanDeviceIds.length;
let fanOfflineCount = 0;
let fanOnlineCount = fanCount - fanOfflineCount;
let fanFaultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 }, device_id: { "%in%": fanDeviceIds } },
["device_id"]
);
let fanFaultDeviceIds = new Set(fanFaultRecords.data.map((r: any) => r.device_id));
let fanFaultDevices = fanFaultDeviceIds.size;
let fanNormalRate = fanCount === 0 ? '100%' : (((fanCount - fanFaultDevices) / fanCount) * 100).toFixed(2) + '%';
let fanFaultCount = fanFaultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
// 3.2 新风风量运行监测趋势分析数据(如果有风量数据)
let fanOnlineTrend = [];
if (fanDeviceIds.length) {
let fanDeviceDatas = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": fanDeviceIds },
received_time: { "%gte%": last24hStart.toISOString().slice(0, 19).replace('T', ' '), "%lte%": nowTime.toISOString().slice(0, 19).replace('T', ' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
// 3.2.1 按小时统计新风送风量(换气次数=送风量/房间体积)
let hourlyData = new Map(); // {key: 00:00, ly: 0, ry: 0 }
for (let rec of fanDeviceDatas.data) {
let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`;
let data = rec.data;
if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { airSupply: data.airSupply });
}
}
// 3.2.2 填充24小时新风监控数据,没有在线数据的小时段则设为零,并计算每小时的新风送风量
for (let i = 0; i < 24; i++) {
let key = `${i}`;
let val = hourlyData.get(key);
fanOnlineTrend.push({ time: `${i}:00`, value: val ? val.airSupply : '0' });
}
}
// 3.3. 新风设备在线趋势图数据
resultAny.fanDeviceOnlineTrend = {
fanCount: fanDeviceIds.length,
fanOnlineCount: fanOnlineCount,
fanOfflineCount: fanOfflineCount,
fanFaultCount: fanFaultCount,
fanOnlineTrend: fanOnlineTrend
};
// 7.1 获取区域列表接口,供前端下拉选择区域类型和区域名称
let regionMap: any = await getRegionList(regionGroup);
// 7.2. 区域列表数据
resultAny.regionData = regionMap;
// 运行监控总数据(一天)
resultAny.mainRunningManagement = {
totalCustomerNumber: 10000,
totalDeviceOnlineRate: '99.8%',
totalElectricity: `360kwh`,
};
} else {
// 设备监控总数据
let devices = await selectDataListByParam(TABLENAME.设备表, {
region_key: { "%in%": regionKeys }
}, ["device_id"]);
let deviceIds = devices.data.map((d: any) => d.device_id);
let deviceCount = deviceIds.length;
let deviceOfflineCount = 0;
let deviceOnlineCount = deviceCount - deviceOfflineCount;
let deviceFaultRecords = await selectDataListByParam(
TABLENAME.设备故障表,
{ status: { "%ne%": 2 }, device_id: { "%in%": deviceIds } },
["device_id"]
);
let deviceFaultDeviceIds = new Set(deviceFaultRecords.data.map((r: any) => r.device_id));
let deviceFaultDevices = deviceFaultDeviceIds.size;
let deviceNormalRate = deviceCount === 0 ? '100%' : (((deviceCount - deviceFaultDevices) / deviceCount) * 100).toFixed(2) + '%';
let deviceFaultCount = deviceFaultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
resultAny.mainRunningManagement = {
deviceCount: deviceIds.length,
deviceOnlineCount: deviceOnlineCount,
deviceOfflineCount: deviceOfflineCount,
deviceFaultCount: deviceFaultCount,
};
}
// 最终组装返回数据
// let resultAny: any = {
// // 区域列表数据
// regionData: regionMap,
// // 运行监控总数据(一天)
// mainRunningManagement: {
// totalCustomerNumber: 10000,
// totalDeviceOnlineRate: '99.8%',
// totalElectricity: `360kwh`,
// },
// // 能耗监控数据
// energyManagement: {
// todayElectricity: `${todayEnergy.toFixed(0)}kwh`,
// yesterdayElectricity: `${yesterdayEnergy.toFixed(0)}kwh`,
// dailyYearOnYear,
// currentMonthElectricity: `${currentMonthEnergy.toFixed(0)}kwh`,
// previousMonthElectricity: `${previousMonthEnergy.toFixed(0)}kwh`,
// monthlyYearOnYear
// },
// // 能耗监控趋势图数据
// electricityTrend: {
// last24Hours: hourlyEnergy,
// last7Days: dailyEnergy7,
// last30Days: dailyEnergy30,
// lastYear: monthlyEnergyYear
// },
// // 空调设备在线趋势图数据
// acDeviceOnlineTrend: {
// acCount: acDeviceIds.length,
// acOnlineCount: acOnlineCount,
// acOfflineCount: acOfflineCount,
// acFaultCount: acFaultCount,
// acOnlineTrend: acOnlineTrend
// },
// // 新风设备在线趋势图数据
// fanDeviceOnlineTrend: {
// fanCount: fanDeviceIds.length,
// fanOnlineCount: fanOnlineCount,
// fanOfflineCount: fanOfflineCount,
// fanFaultCount: fanFaultCount,
// fanOnlineTrend: fanOnlineTrend
// },
// // 环境监测趋势图数据
// environmentalTrend: {
// currentTemperature: currentTemperature,
// currentHumidity: currentHumidity,
// currentPm25: currentPm25,
// currentCo2: currentCo2,
// temperatureTrend: temperatureTrend,
// humidityTrend: humidityTrend,
// pm25Trend: pm25Trend,
// co2Trend: co2Trend,
// qualityIndex: qualityIndex
// },
// // 人流监控实时数据和趋势图数据
// customerOnlineTrend: {
// passengerFlowTable: regionCustomerTable,
// customerTrend: customerTrend
// },
// // 预警监控数据
// alertManagement: {
// alertWorkLevel: alertWorkLevel,
// alertWorkType: alertWorkType,
// alertWorkOrders: alertWorkOrders
// }
// };
return resultAny;
}
/**
* 智能管控-空调设备运行弹框
* @param regionKey
* @param deviceId
*/
export async function getAcRunningPop(regionKey: number, deviceId: string) {
// 获取设备信息
let devices = await selectDataListByParam(
TABLENAME.设备表,
{ region_key: regionKey, device_id: deviceId },
["device_id", "device_type", "device_name", "control_params"]
);
if (!devices.data.length) {
throw new BizError(ERRORENUM.未找到数据, `设备 ${deviceId} 在区域 ${regionKey} 中未找到`);
}
// 获取设备最新数据
let deviceData = await selectDataListByParam(
TABLENAME.设备数据表,
{ device_id: deviceId, "%orderDesc%": "received_time", "%limit%": 1 },
["data", "received_time"]
);
return {
deviceInfo: devices.data[0].control_params,
latestData: deviceData.data.length ? deviceData.data[0].data : null
}
}
/**
* 设备运行弹框 - 空调启停
* @param deviceId
*/
export async function controlAcRunning(deviceId?: string) {
}
// 辅助函数:获取用电趋势(按间隔分组)
async function getEnergyTrend(deviceIds: string[], startTime: Date, endTime: Date, granularity: 'hour' | 'day' | 'month') {
if (deviceIds.length === 0) return [];
let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds },
received_time: { "%gte%": startTime.toISOString().slice(0,19).replace('T',' '), "%lte%": endTime.toISOString().slice(0,19).replace('T',' ') },
"%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]);
let intervalMap = new Map(); // key: 时间字符串, value: { deviceMin, deviceMax }
for (let rec of records.data) {
let data = rec.data;
let energy = data.energy ?? data.total_energy;
if (energy === undefined) continue;
let intervalKey: string;
let d = new Date(rec.received_time);
if (granularity === 'hour') {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()} ${d.getHours()}`;
} else if (granularity === 'day') {
intervalKey = `${d.getFullYear()}/${d.getMonth()+1}/${d.getDate()}`;
} else {
intervalKey = `${d.getFullYear()}-${d.getMonth()+1}`;
}
if (!intervalMap.has(intervalKey)) {
intervalMap.set(intervalKey, { deviceMin: new Map(), deviceMax: new Map() });
}
let entry = intervalMap.get(intervalKey);
if (!entry.deviceMin.has(rec.device_id)) {
entry.deviceMin.set(rec.device_id, energy);
entry.deviceMax.set(rec.device_id, energy);
} else {
let curMin = entry.deviceMin.get(rec.device_id);
let curMax = entry.deviceMax.get(rec.device_id);
if (energy < curMin) entry.deviceMin.set(rec.device_id, energy);
if (energy > curMax) entry.deviceMax.set(rec.device_id, energy);
}
}
let result = [];
for (let [key, { deviceMin, deviceMax }] of intervalMap.entries()) {
let total = 0;
for (let [deviceId, minVal] of deviceMin.entries()) {
let maxVal = deviceMax.get(deviceId);
total += (maxVal - minVal);
}
result.push({ key, value: total.toFixed(0) });
}
result.sort((a,b) => a.key.localeCompare(b.key));
return result;
}
// 基于馆内环境温度、湿度变化等,联动空调启停
async function controlAcByEnvironment(deviceId: string, data: any) {
// 获取区域内环境监测设备数据
let envDevices = await selectDataListByParam(TABLENAME.设备表, {
device_id: deviceId,
device_type: "环境监测"
}, ["id", "device_name", "region_key"]);
// 若是环境设备,则解析环境设备数据,判断是否需要联动空调设备
if (envDevices.data.length) {
// 通过设备所属区域查询当前区域中的空调设备,获取空调设备标准参数中预设的温度、湿度阈值
let regionKey = envDevices.data[0].region_key;
let acDevices = await selectDataListByParam(TABLENAME.设备表, {
region_key: regionKey,
device_type: "空调"
}, ["id", "device_name", "control_params"]);
acDevices.data.forEach((ac: any) => {
let controlParams = ac.control_params;
let maxTemp = controlParams.maxTemp;
let minTemp = controlParams.minTemp;
if (data.temperature && (data.temperature > maxTemp || data.temperature < minTemp)){
// 调用空调控制接口启停空调设备
console.log(`联动空调 ${ac.device_name}${ac.id})启停,当前温度:${data.temperature},阈值范围:${minTemp}-${maxTemp}`);
// 单个 TODO: 接入空调控制API,传入设备ID和控制指令(如开/关、温度设定等)
}
});
// 多个 TODO: 接入空调控制API,传入设备ID和控制指令(如开/关、温度设定等)
return { success: true, message: "成功" };
}
}
\ No newline at end of file
......@@ -5,39 +5,36 @@
import asyncHandler from 'express-async-handler';
import * as deviceBiz from '../biz/device';
import * as regionBiz from '../biz/region';
import * as runningBiz from '../biz/running';
import { eccReqParamater } from '../util/verificationParam';
import { checkUser } from '../middleware/user';
export function setRouter(httpServer) {
/** 注册/更新设备信息 */
httpServer.post('/api/zc/device/register', asyncHandler(registerDevice));
httpServer.post('/api/zc/region/regionList', asyncHandler(getRegionList));
httpServer.post('/api/qdm/device/register', asyncHandler(registerDevice));
/** 设备商推送实时数据 */
httpServer.post('/api/zc/device/push', asyncHandler(deviceDataPush));
httpServer.post('/api/qdm/device/push', asyncHandler(deviceDataPush));
/** 设备商推送故障数据 */
httpServer.post('/api/zc/device/fault/push', asyncHandler(deviceFaultPush));
httpServer.post('/api/qdm/device/fault/push', asyncHandler(deviceFaultPush));
/** 运行分析 */
httpServer.post('/api/zc/run/analysis', asyncHandler(getRunAnalysis));
/** 获取区域列表 */
httpServer.post('/api/qdm/region/regionList', asyncHandler(getRegionList));
/** 运行分析 */
httpServer.post('/api/qdm/run/analysis', asyncHandler(getRunAnalysis));
/** 运行分析 - 楼栋 */
httpServer.post('/api/zc/run/analysis/building', asyncHandler(getRunAnalysisByBuilding));
httpServer.post('/api/qdm/run/analysis/building', asyncHandler(getRunAnalysisByBuilding));
/** 运行分析 - 楼层 */
httpServer.post('/api/zc/run/analysis/Floor', asyncHandler(getRunAnalysisByFloor));
httpServer.post('/api/qdm/run/analysis/Floor', asyncHandler(getRunAnalysisByFloor));
/** 空调运行弹框 */
httpServer.post('/api/zc/run/monitor/acPop', asyncHandler(getAcRunningPop));
httpServer.post('/api/qdm/run/monitor/acPop', asyncHandler(getAcRunningPop));
/** 空调开关 */
httpServer.post('/api/zc/run/monitor/acRun', asyncHandler(controlAcRunning));
httpServer.post('/api/qdm/run/monitor/acRun', asyncHandler(controlAcRunning));
/**运行分析-弹窗 */
httpServer.post('/api/zc/run/analysis/pop', asyncHandler(getRunAnalysisPop));
/** 智能监控弹窗 */
httpServer.post('/api/zc/smart/monitor/pop', asyncHandler(getSmartMonitorPop));
}
/**
* 注册或更新设备信息
*/
......@@ -50,20 +47,6 @@ export function setRouter(httpServer) {
}
/**
* 获取区域列表接口,供前端下拉选择区域类型和区域名称
* @param req
* @param res
*/
async function getRegionList(req, res) {
let reqConf = {regionGroup:'String'};
const NotMustHaveKeys = [];
let { regionGroup } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRegionList(regionGroup);
res.success(result);
}
/**
* 设备商推送数据
*/
async function deviceDataPush(req, res) {
......@@ -75,7 +58,6 @@ async function deviceDataPush(req, res) {
res.success(result);
}
/**
* 设备商推送故障数据
*/
......@@ -84,17 +66,29 @@ async function deviceFaultPush(req, res) {
const NotMustHaveKeys = ["faultDescription"];
let { deviceId, faultType, faultLevel, faultDescription, occurredTime } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.handleDeviceFaultPush(deviceId, faultType, faultLevel, faultDescription, occurredTime);
const result = await deviceBiz.handleDeviceFaultPush(deviceId, faultType, faultLevel, faultDescription, occurredTime);
res.success(result);
}
/**
* 获取区域列表接口,供前端下拉选择区域类型和区域名称
* @param req
* @param res
*/
async function getRegionList(req, res) {
let reqConf = {regionGroup:'String'};
const NotMustHaveKeys = [];
let { regionGroup } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRegionList(regionGroup);
res.success(result);
}
/**
* 运行分析
*/
async function getRunAnalysis(req, res) {
const { } = req.body;
const result = await regionBiz.getRunAnalysis("","");
const result = await runningBiz.getRunAnalysis("","");
res.success(result);
}
......@@ -105,7 +99,7 @@ async function deviceFaultPush(req, res) {
let reqConf = {regionGroup:'String'};
const NotMustHaveKeys = [];
let { regionGroup } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRunAnalysis("", regionGroup);
const result = await runningBiz.getRunAnalysis(regionGroup, "");
res.success(result);
}
......@@ -116,7 +110,7 @@ async function deviceFaultPush(req, res) {
let reqConf = {regionGroup:'String', regionType:'String'};
const NotMustHaveKeys = [];
let { regionGroup, regionType } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getRunAnalysis(regionType, regionGroup);
const result = await runningBiz.getRunAnalysis(regionGroup, regionType);
res.success(result);
}
......@@ -127,7 +121,7 @@ async function deviceFaultPush(req, res) {
let reqConf = {regionKey:'Number', deviceId:'String'};
const NotMustHaveKeys = [];
let { regionKey, deviceId } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.getAcRunningPop(regionKey, deviceId);
const result = await runningBiz.getAcRunningPop(regionKey, deviceId);
res.success(result);
}
......@@ -138,33 +132,11 @@ async function controlAcRunning(req, res) {
let reqConf = {deviceId:'String'};
const NotMustHaveKeys = [];
let { deviceId } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await regionBiz.controlAcRunning(deviceId);
res.success(result);
}
/**
* 运行分析-弹窗
*/
async function getRunAnalysisPop(req, res) {
let reqConf = {regionKey:'Number'};
const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getRunAnalysisPop(regionKey);
const result = await runningBiz.controlAcRunning(deviceId);
res.success(result);
}
/**
* 智能监控弹窗
*/
async function getSmartMonitorPop(req, res) {
let reqConf = {regionKey:'Number'};
const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getSmartMonitorPop(regionKey);
res.success(result);
}
......
/**
*
* 访问飞翼客户端API接口
*/
import asyncHandler from 'express-async-handler';
import * as feiyiClientBiz from '../biz/feiyiClient';
import { eccReqParamater } from '../util/verificationParam';
export function setRouter(httpServer) {
/** 注册/更新设备信息 */
httpServer.post('/api/zc/feiyi/gateway/list', asyncHandler(getFeiyiGatewayList));
httpServer.post('/api/qdm/feiyi/gateway/list', asyncHandler(getFeiyiGatewayList));
}
......
......@@ -7,10 +7,10 @@ import { eccReqParamater } from '../util/verificationParam';
export function setRouter(httpServer) {
/** 用户注册 */
httpServer.post('/api/zc/user/register', asyncHandler(registerUser));
httpServer.post('/api/qdm/user/register', asyncHandler(registerUser));
/** 用户登录 */
httpServer.post('/api/zc/user/login', asyncHandler(login));
httpServer.post('/api/qdm/user/login', asyncHandler(login));
/**
* 注册信息
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment