Commit 89246caf by chenjinjing

no message

parent c402ed7d
...@@ -11,8 +11,8 @@ import { updateManyData } from "../data/updateData"; ...@@ -11,8 +11,8 @@ import { updateManyData } from "../data/updateData";
* 注册或更新设备信息 * 注册或更新设备信息
* @param deviceId 设备唯一标识(必填) * @param deviceId 设备唯一标识(必填)
* @param regionKey 区域编号(必填),对应 region 表的 id * @param regionKey 区域编号(必填),对应 region 表的 id
* @param deviceType 设备类型(必填),如“灯光”“空调” * @param deviceType 设备类型(必填),如"灯光""空调"
* @param deviceName 设备名称(可选),如“主照明灯” * @param deviceName 设备名称(可选),如"主照明灯"
* @param controlParams 设备支持的参数定义(可选),JSON对象 * @param controlParams 设备支持的参数定义(可选),JSON对象
* @returns {Promise<{success: boolean}>} * @returns {Promise<{success: boolean}>}
* @throws 如果 regionKey 对应的区域不存在,则抛出错误 * @throws 如果 regionKey 对应的区域不存在,则抛出错误
...@@ -87,7 +87,7 @@ export async function handleDevicePush(deviceId: string, data: any, deviceTime?: ...@@ -87,7 +87,7 @@ export async function handleDevicePush(deviceId: string, data: any, deviceTime?:
/** /**
* 处理设备商推送的故障数据 * 处理设备商推送的故障数据
* @param deviceId 设备唯一标识(必填) * @param deviceId 设备唯一标识(必填)
* @param faultType 故障类型(必填),如“离线”“数据异常”“硬件故障” * @param faultType 故障类型(必填),如"离线""数据异常""硬件故障"
* @param faultDescription 故障详细描述(可选) * @param faultDescription 故障详细描述(可选)
* @param occurredTime 故障发生时间(可选),格式:YYYY-MM-DD HH:MM:SS,不传则使用服务器时间 * @param occurredTime 故障发生时间(可选),格式:YYYY-MM-DD HH:MM:SS,不传则使用服务器时间
* @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID * @returns {Promise<{faultId: number}>} 返回新插入故障记录的ID
...@@ -126,27 +126,26 @@ export async function handleDeviceFaultPush( deviceId: string, faultType: string ...@@ -126,27 +126,26 @@ export async function handleDeviceFaultPush( deviceId: string, faultType: string
/** /**
* 运行分析 todu(待测试) * 安全解析设备数据
* @returns 大屏所需的所有指标数据 * @param data 可能是字符串或对象的数据
* @returns 解析后的对象
*/ */
export async function getRunAnalysis() { function parseDeviceData(data: any): any {
// 1. 获取所有三相电表及电能监测设备的用电数据(用于能耗管理) if (!data) return null;
let energyDevices = await selectDataListByParam(TABLENAME.设备表, { if (typeof data === 'string') {
device_type: { "%in%": ["三相电表", "电能监测"] } // 使用 %in% 操作符 try {
}, ["device_id"]); return JSON.parse(data);
let energyDeviceIds = energyDevices.data.map((d: any) => d.device_id); } catch (e) {
console.error('解析设备数据失败:', e);
return null;
}
}
return data;
}
// 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) // 辅助函数:查询某个时间段内的总用电量(单位 kwh)
async function getTotalEnergy(deviceIds: string[], startTime: Date, endTime: Date): Promise<number> { async function getTotalEnergy(deviceIds: string[], startTime: Date, endTime: Date): Promise<number> {
if (deviceIds.length === 0) return 0; if (deviceIds.length === 0) return 0;
let records = await selectDataListByParam(TABLENAME.设备数据表, { let records = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": deviceIds }, device_id: { "%in%": deviceIds },
...@@ -157,8 +156,8 @@ export async function getRunAnalysis() { ...@@ -157,8 +156,8 @@ export async function getRunAnalysis() {
let total = 0; let total = 0;
let deviceEnergyMap = new Map(); let deviceEnergyMap = new Map();
for (let rec of records.data) { for (let rec of records.data) {
let data = rec.data; let data = parseDeviceData(rec.data);
let energy = data.energy ?? data.total_energy; let energy = data?.energy ?? data?.total_energy;
if (energy === undefined) continue; if (energy === undefined) continue;
if (!deviceEnergyMap.has(rec.device_id)) { if (!deviceEnergyMap.has(rec.device_id)) {
deviceEnergyMap.set(rec.device_id, { min: energy, max: energy }); deviceEnergyMap.set(rec.device_id, { min: energy, max: energy });
...@@ -172,7 +171,28 @@ export async function getRunAnalysis() { ...@@ -172,7 +171,28 @@ export async function getRunAnalysis() {
total += (max - min); total += (max - min);
} }
return total; return total;
} }
/**
* 运行分析 todu(待测试)
* @returns 大屏所需的所有指标数据
*/
export async function getRunAnalysis() {
// 1. 获取所有三相电表及电能监测设备的用电数据(用于能耗管理)
let energyDevices = await selectDataListByParam(TABLENAME.设备表, {
device_type: { "%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);
let todayEnergy = await getTotalEnergy(energyDeviceIds, todayStart, todayEnd); let todayEnergy = await getTotalEnergy(energyDeviceIds, todayStart, todayEnd);
let yesterdayEnergy = await getTotalEnergy(energyDeviceIds, yesterdayStart, yesterdayEnd); let yesterdayEnergy = await getTotalEnergy(energyDeviceIds, yesterdayStart, yesterdayEnd);
...@@ -202,7 +222,7 @@ export async function getRunAnalysis() { ...@@ -202,7 +222,7 @@ export async function getRunAnalysis() {
} }
let deviceTable = []; let deviceTable = [];
for (let [type, count] of typeCountMap.entries()) { for (let [type, count] of typeCountMap.entries()) {
// 耗电比例示例(实际可关联用电数据 // 耗电比例示例(实际关联用电数据计算
let powerRatio = '30%'; let powerRatio = '30%';
if (type === '空调') powerRatio = '97%'; if (type === '空调') powerRatio = '97%';
else if (type === '灯光') powerRatio = '45%'; else if (type === '灯光') powerRatio = '45%';
...@@ -213,13 +233,12 @@ export async function getRunAnalysis() { ...@@ -213,13 +233,12 @@ export async function getRunAnalysis() {
}); });
} }
// 5. 汇总数据(简化) // 5. 汇总数据
let totalDevices = allDevices.data.length; let totalDevices = allDevices.data.length;
let runningDevices = totalDevices; // 或根据在线数量计算,在线即运行 let runningDevices = totalDevices;
// 查询未解决的故障设备数量(去重,一个设备可能有多个故障,取 status != 2 的)
let faultRecords = await selectDataListByParam( let faultRecords = await selectDataListByParam(
TABLENAME.设备故障表, TABLENAME.设备故障表,
{ status: { "%ne%": 2 } }, // 未解决的状态不是2(已解决) { status: { "%ne%": 2 } },
["device_id"] ["device_id"]
); );
let faultDeviceIds = new Set(faultRecords.data.map((r: any) => r.device_id)); let faultDeviceIds = new Set(faultRecords.data.map((r: any) => r.device_id));
...@@ -244,7 +263,7 @@ export async function getRunAnalysis() { ...@@ -244,7 +263,7 @@ export async function getRunAnalysis() {
} }
let onlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length; let onlineCount = Array.from(deviceLatestMap.values()).filter(v => v.isOnline).length;
let offlineCount = totalDevices - onlineCount; let offlineCount = totalDevices - onlineCount;
let faultCount = faultRecords.data.length; // 所有未解决的故障单数量(含同一设备多个故障) let faultCount = faultRecords.data.length; //所有未解决的故障单数量(含同一设备多个故障)
// 7. 监测设备趋势:过去24小时每小时在线数量 // 7. 监测设备趋势:过去24小时每小时在线数量
let hourlyOnlineTrend = []; let hourlyOnlineTrend = [];
...@@ -286,20 +305,20 @@ export async function getRunAnalysis() { ...@@ -286,20 +305,20 @@ export async function getRunAnalysis() {
let co2TrendDetail = []; let co2TrendDetail = [];
if (airDeviceIds.length) { if (airDeviceIds.length) {
// 最新数据
let latestAir = await selectDataListByParam(TABLENAME.设备数据表, { let latestAir = await selectDataListByParam(TABLENAME.设备数据表, {
device_id: { "%in%": airDeviceIds }, device_id: { "%in%": airDeviceIds },
"%orderDesc%": "received_time", "%orderDesc%": "received_time",
"%limit%": 1 "%limit%": 1
}, ["data"]); }, ["data"]);
if (latestAir.data.length) { if (latestAir.data.length) {
let airData = latestAir.data[0].data; let airData = parseDeviceData(latestAir.data[0].data);
currentTemperature = `${airData.temperature ?? 21}`; currentTemperature = `${airData?.temperature ?? 21}`;
currentHumidity = `${airData.humidity ?? 47}%`; currentHumidity = `${airData?.humidity ?? 47}%`;
currentPm25 = `${airData.pm25 ?? 21}μg/m³`; currentPm25 = `${airData?.pm25 ?? 21}μg/m³`;
currentCo2 = `${airData.co2 ?? 400}ppm`; currentCo2 = `${airData?.co2 ?? 400}ppm`;
qualityIndex = 500 - (airData.pm25 ?? 0); qualityIndex = 500 - (airData?.pm25 ?? 0);
} }
// 过去24小时趋势 // 过去24小时趋势
let last24h = new Date(nowTime.getTime() - 24*3600000); let last24h = new Date(nowTime.getTime() - 24*3600000);
let airHistory = await selectDataListByParam(TABLENAME.设备数据表, { let airHistory = await selectDataListByParam(TABLENAME.设备数据表, {
...@@ -310,9 +329,9 @@ export async function getRunAnalysis() { ...@@ -310,9 +329,9 @@ export async function getRunAnalysis() {
for (let rec of airHistory.data) { for (let rec of airHistory.data) {
let d = new Date(rec.received_time); let d = new Date(rec.received_time);
let hourKey = `${d.getHours()}`; let hourKey = `${d.getHours()}`;
let data = rec.data; let data = parseDeviceData(rec.data);
if (!hourlyData.has(hourKey)) { if (!hourlyData.has(hourKey)) {
hourlyData.set(hourKey, { temp: data.temperature, hum: data.humidity, pm: data.pm25, co2: data.co2 }); hourlyData.set(hourKey, { temp: data?.temperature, hum: data?.humidity, pm: data?.pm25, co2: data?.co2 });
} }
} }
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
...@@ -336,7 +355,6 @@ export async function getRunAnalysis() { ...@@ -336,7 +355,6 @@ export async function getRunAnalysis() {
]; ];
let roomPowerTrend = hourlyOnlineTrend.slice(0, 24).map(item => ({ time: item.time.replace('时',':00'), value: item.value })); let roomPowerTrend = hourlyOnlineTrend.slice(0, 24).map(item => ({ time: item.time.replace('时',':00'), value: item.value }));
// 最终组装返回数据
return { return {
energyManagement: { energyManagement: {
todayElectricity: `${todayEnergy.toFixed(0)}kwh`, todayElectricity: `${todayEnergy.toFixed(0)}kwh`,
...@@ -392,10 +410,10 @@ async function getEnergyTrend(deviceIds: string[], startTime: Date, endTime: Dat ...@@ -392,10 +410,10 @@ async function getEnergyTrend(deviceIds: string[], startTime: Date, endTime: Dat
"%orderAsc%": "received_time" "%orderAsc%": "received_time"
}, ["device_id", "data", "received_time"]); }, ["device_id", "data", "received_time"]);
let intervalMap = new Map(); // key: 时间字符串, value: { deviceMin, deviceMax } let intervalMap = new Map();
for (let rec of records.data) { for (let rec of records.data) {
let data = rec.data; let data = parseDeviceData(rec.data);
let energy = data.energy ?? data.total_energy; let energy = data?.energy ?? data?.total_energy;
if (energy === undefined) continue; if (energy === undefined) continue;
let intervalKey: string; let intervalKey: string;
let d = new Date(rec.received_time); let d = new Date(rec.received_time);
...@@ -469,10 +487,15 @@ function toDateTimeStr(date: Date): string { ...@@ -469,10 +487,15 @@ function toDateTimeStr(date: Date): string {
/** /**
* 获取运行分析区域弹窗 * 获取运行分析区域弹窗
*/ */
export async function getRunAnalysisPop(regionKey: number) { export async function getRunAnalysisPop(regionKey: string) {
const regionKeyNum = parseInt(regionKey);
if (isNaN(regionKeyNum)) {
throw new BizError(ERRORENUM.参数错误, `无效的区域编号: ${regionKey}`);
}
let devices = await selectDataListByParam( let devices = await selectDataListByParam(
TABLENAME.设备表, TABLENAME.设备表,
{ region_key: regionKey }, { region_key: regionKeyNum },
["device_id", "device_type", "device_name"] ["device_id", "device_type", "device_name"]
); );
let deviceList = devices.data; let deviceList = devices.data;
...@@ -487,7 +510,7 @@ export async function getRunAnalysisPop(regionKey: number) { ...@@ -487,7 +510,7 @@ export async function getRunAnalysisPop(regionKey: number) {
}; };
} }
// 获取每个设备的最新数据(用于判定业务在线状态) // 获取每个设备的最新数据
let deviceLatestMap = new Map<string, { isOnline: boolean; latestData: any }>(); let deviceLatestMap = new Map<string, { isOnline: boolean; latestData: any }>();
for (let devId of deviceIds) { for (let devId of deviceIds) {
let rec = await selectDataListByParam( let rec = await selectDataListByParam(
...@@ -498,14 +521,14 @@ export async function getRunAnalysisPop(regionKey: number) { ...@@ -498,14 +521,14 @@ export async function getRunAnalysisPop(regionKey: number) {
let isOnline = false; let isOnline = false;
let latestData = null; let latestData = null;
if (rec.data.length) { if (rec.data.length) {
latestData = rec.data[0].data // 已解析的对象 latestData = parseDeviceData(rec.data[0].data);
let devType = deviceList.find((d: any) => d.device_id === devId)?.device_type || ""; let devType = deviceList.find((d: any) => d.device_id === devId)?.device_type || "";
isOnline = isDeviceOnlineByStatus(devType, latestData); isOnline = isDeviceOnlineByStatus(devType, latestData);
} }
deviceLatestMap.set(devId, { isOnline, latestData }); deviceLatestMap.set(devId, { isOnline, latestData });
} }
// 设备在线状态(按类型聚合) // 设备在线状态
let deviceOnlineStatus: { [key: string]: boolean } = {}; let deviceOnlineStatus: { [key: string]: boolean } = {};
for (let dev of deviceList) { for (let dev of deviceList) {
let cnType = dev.device_type; let cnType = dev.device_type;
...@@ -551,7 +574,7 @@ export async function getRunAnalysisPop(regionKey: number) { ...@@ -551,7 +574,7 @@ export async function getRunAnalysisPop(regionKey: number) {
} }
// 辅助函数:将 Date 转换为本地时间字符串 YYYY-MM-DD HH:MM:SS // 辅助函数:将 Date 转换为本地时间字符串
function toLocalDateTimeStr(date: Date): string { function toLocalDateTimeStr(date: Date): string {
let year = date.getFullYear(); let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0'); let month = String(date.getMonth() + 1).padStart(2, '0');
...@@ -562,16 +585,12 @@ function toLocalDateTimeStr(date: Date): string { ...@@ -562,16 +585,12 @@ function toLocalDateTimeStr(date: Date): string {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} }
// 获取当前本地时间字符串
function getNowStr(): string { function getNowStr(): string {
return toLocalDateTimeStr(new Date()); return toLocalDateTimeStr(new Date());
} }
/** /**
* 获取过去24小时内每小时的设备在线统计(基于业务状态) * 获取过去24小时内每小时的设备在线统计
* @param deviceList 设备列表
* @param nowStr 当前时间字符串(本地时间)
* @returns 数组,从最早到最晚,每个元素包含 key(整点小时)、online、offline
*/ */
async function getDeviceStatusTrend( async function getDeviceStatusTrend(
deviceList: any[], deviceList: any[],
...@@ -581,14 +600,11 @@ async function getDeviceStatusTrend( ...@@ -581,14 +600,11 @@ async function getDeviceStatusTrend(
let totalDevices = deviceIds.length; let totalDevices = deviceIds.length;
let now = new Date(nowStr); let now = new Date(nowStr);
// 计算起始时间:24小时前,并取整到小时(向下取整)
let startDate = new Date(now.getTime() - 24 * 3600000); let startDate = new Date(now.getTime() - 24 * 3600000);
startDate.setMinutes(0, 0, 0); startDate.setMinutes(0, 0, 0);
// 结束时间:当前小时整点(不含当前小时区间,因为未结束)
let endDate = new Date(now); let endDate = new Date(now);
endDate.setMinutes(0, 0, 0); endDate.setMinutes(0, 0, 0);
// 生成小时区间列表(从 startDate 开始,每小时递增,直到 < endDate)
let hourRanges: { start: Date; end: Date; key: string }[] = []; let hourRanges: { start: Date; end: Date; key: string }[] = [];
let current = new Date(startDate); let current = new Date(startDate);
while (current < endDate) { while (current < endDate) {
...@@ -601,9 +617,8 @@ async function getDeviceStatusTrend( ...@@ -601,9 +617,8 @@ async function getDeviceStatusTrend(
current = next; current = next;
} }
// 查询所有设备在时间范围内的数据
let startStr = toLocalDateTimeStr(startDate); let startStr = toLocalDateTimeStr(startDate);
let endStr = toLocalDateTimeStr(now); // 使用当前时间作为结束,但实际查询时用 < endStr let endStr = toLocalDateTimeStr(now);
let records = await selectDataListByParam( let records = await selectDataListByParam(
TABLENAME.设备数据表, TABLENAME.设备数据表,
{ {
...@@ -614,7 +629,6 @@ async function getDeviceStatusTrend( ...@@ -614,7 +629,6 @@ async function getDeviceStatusTrend(
["device_id", "data", "received_time"] ["device_id", "data", "received_time"]
); );
// 按设备分组
let deviceDataMap = new Map<string, Array<{ timeStr: string; data: any }>>(); let deviceDataMap = new Map<string, Array<{ timeStr: string; data: any }>>();
for (let rec of records.data) { for (let rec of records.data) {
if (!deviceDataMap.has(rec.device_id)) { if (!deviceDataMap.has(rec.device_id)) {
...@@ -622,11 +636,10 @@ async function getDeviceStatusTrend( ...@@ -622,11 +636,10 @@ async function getDeviceStatusTrend(
} }
deviceDataMap.get(rec.device_id)!.push({ deviceDataMap.get(rec.device_id)!.push({
timeStr: rec.received_time, timeStr: rec.received_time,
data: rec.data data: parseDeviceData(rec.data)
}); });
} }
// 对每个小时区间统计在线设备数
let result = []; let result = [];
for (let range of hourRanges) { for (let range of hourRanges) {
let startStrRange = toLocalDateTimeStr(range.start); let startStrRange = toLocalDateTimeStr(range.start);
...@@ -637,7 +650,6 @@ async function getDeviceStatusTrend( ...@@ -637,7 +650,6 @@ async function getDeviceStatusTrend(
let devId = dev.device_id; let devId = dev.device_id;
let devType = dev.device_type; let devType = dev.device_type;
let dataPoints = deviceDataMap.get(devId) || []; let dataPoints = deviceDataMap.get(devId) || [];
// 查找该小时内最后一条数据
let lastInRange: { timeStr: string; data: any } | null = null; let lastInRange: { timeStr: string; data: any } | null = null;
for (let point of dataPoints) { for (let point of dataPoints) {
if (point.timeStr >= startStrRange && point.timeStr < endStrRange) { if (point.timeStr >= startStrRange && point.timeStr < endStrRange) {
...@@ -661,8 +673,6 @@ async function getDeviceStatusTrend( ...@@ -661,8 +673,6 @@ async function getDeviceStatusTrend(
/** /**
* 获取过去24小时每小时的能耗趋势 * 获取过去24小时每小时的能耗趋势
* @param deviceIds 设备ID列表
* @param nowStr 当前时间字符串
*/ */
async function getEnergyTrendByRegion(deviceIds: string[], nowStr: string) { async function getEnergyTrendByRegion(deviceIds: string[], nowStr: string) {
let nowTime = toDate(nowStr).getTime(); let nowTime = toDate(nowStr).getTime();
...@@ -679,11 +689,10 @@ async function getEnergyTrendByRegion(deviceIds: string[], nowStr: string) { ...@@ -679,11 +689,10 @@ async function getEnergyTrendByRegion(deviceIds: string[], nowStr: string) {
["device_id", "data", "received_time"] ["device_id", "data", "received_time"]
); );
// 按设备分组提取能耗值
let deviceEnergySeries = new Map<string, Array<{ timestamp: number; energy: number }>>(); let deviceEnergySeries = new Map<string, Array<{ timestamp: number; energy: number }>>();
for (let rec of records.data) { for (let rec of records.data) {
let dataObj = rec.data; let dataObj = parseDeviceData(rec.data);
let energy = dataObj.energy ?? dataObj.total_energy; let energy = dataObj?.energy ?? dataObj?.total_energy;
if (energy === undefined) continue; if (energy === undefined) continue;
let ts = toDate(rec.received_time).getTime(); let ts = toDate(rec.received_time).getTime();
if (!deviceEnergySeries.has(rec.device_id)) { if (!deviceEnergySeries.has(rec.device_id)) {
...@@ -736,11 +745,11 @@ function isDeviceOnlineByStatus(deviceType: string, latestData: any): boolean { ...@@ -736,11 +745,11 @@ function isDeviceOnlineByStatus(deviceType: string, latestData: any): boolean {
case "灌溉": case "灌溉":
case "驱蚊": case "驱蚊":
case "增压泵": case "增压泵":
case "智慧庭院-水景": // 兼容旧数据 case "智慧庭院-水景":
case "智慧庭院-雾森": // 兼容旧数据 case "智慧庭院-雾森":
case "智慧庭院-灌溉": // 兼容旧数据 case "智慧庭院-灌溉":
case "智慧庭院-驱蚊": // 兼容旧数据 case "智慧庭院-驱蚊":
case "智慧庭院-增压泵": // 兼容旧数据 case "智慧庭院-增压泵":
return latestData.power === "on" || latestData.power === true; return latestData.power === "on" || latestData.power === true;
case "窗帘": case "窗帘":
case "推窗器": case "推窗器":
...@@ -749,25 +758,38 @@ function isDeviceOnlineByStatus(deviceType: string, latestData: any): boolean { ...@@ -749,25 +758,38 @@ function isDeviceOnlineByStatus(deviceType: string, latestData: any): boolean {
case "人体传感器": case "人体传感器":
return true; return true;
default: default:
// 空气质量传感、土壤传感器、气象设备、电能监测等默认只要有数据即视为激活
return true; return true;
} }
} }
function isOnline(receivedTime: string | undefined): boolean {
let nowTime = new Date();
if (!receivedTime) return false;
let lastTime = new Date(receivedTime);
let diffMinutes = (nowTime.getTime() - lastTime.getTime()) / 60000;
return diffMinutes <= 30;
}
/** /**
* 获取智能监控弹窗数据 * 获取智能监控弹窗数据
* @param regionKey 区域编号(region.id) * @param regionKey 区域编号(region.id)
* @returns 设备在线状态、空气质量监测、设备监测详情 * @returns 设备在线状态、空气质量监测、设备监测详情
*/ */
export async function getSmartMonitorPop(regionKey: number) { export async function getSmartMonitorPop(regionKey: string) {
// 1. 获取该区域下所有设备 const regionKeyNum = parseInt(regionKey);
if (isNaN(regionKeyNum)) {
throw new BizError(ERRORENUM.参数错误, `无效的区域编号: ${regionKey}`);
}
let devices = await selectDataListByParam( let devices = await selectDataListByParam(
TABLENAME.设备表, TABLENAME.设备表,
{ region_key: regionKey }, { region_key: regionKeyNum },
["device_id", "device_type", "device_name"] ["device_id", "device_type", "device_name"]
); );
let deviceList = devices.data; let deviceList = devices.data;
if (deviceList.length === 0) { if (deviceList.length === 0) {
return { return {
deviceOnlineStatus: {}, deviceOnlineStatus: {},
...@@ -783,9 +805,8 @@ export async function getSmartMonitorPop(regionKey: number) { ...@@ -783,9 +805,8 @@ export async function getSmartMonitorPop(regionKey: number) {
}; };
} }
// 2. 获取每个设备的最新数据(按设备ID分组取最新一条)
let deviceIds = deviceList.map(d => d.device_id); let deviceIds = deviceList.map(d => d.device_id);
let latestDataMap = new Map<string, any>(); // key: device_id, value: { data, received_time } let latestDataMap = new Map<string, any>();
for (let devId of deviceIds) { for (let devId of deviceIds) {
let result = await selectDataListByParam( let result = await selectDataListByParam(
...@@ -795,41 +816,29 @@ export async function getSmartMonitorPop(regionKey: number) { ...@@ -795,41 +816,29 @@ export async function getSmartMonitorPop(regionKey: number) {
); );
if (result.data.length > 0) { if (result.data.length > 0) {
latestDataMap.set(devId, { latestDataMap.set(devId, {
data: JSON.parse(result.data[0].data), data: parseDeviceData(result.data[0].data),
received_time: result.data[0].received_time received_time: result.data[0].received_time
}); });
} }
} }
let nowTime = new Date();
let deviceOnlineStatus: { [key: string]: boolean } = {}; let deviceOnlineStatus: { [key: string]: boolean } = {};
let deviceMonitoring: { [key: string]: any } = {}; 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) { for (let dev of deviceList) {
let cnType = dev.device_type; let cnType = dev.device_type;
let enKey = deviceTypeKeyMap[cnType]; let enKey = deviceTypeKeyMap[cnType];
if (!enKey) continue; // 跳过未映射的设备类型 if (!enKey) continue;
let latest = latestDataMap.get(dev.device_id); let latest = latestDataMap.get(dev.device_id);
let online = isOnline(latest?.received_time); let online = isOnline(latest?.received_time);
// 设备在线状态:同类型只要有一个在线即为 true(按业务需求可调整)
if (deviceOnlineStatus[enKey] === undefined) { if (deviceOnlineStatus[enKey] === undefined) {
deviceOnlineStatus[enKey] = online; deviceOnlineStatus[enKey] = online;
} else { } else {
deviceOnlineStatus[enKey] = deviceOnlineStatus[enKey] || online; deviceOnlineStatus[enKey] = deviceOnlineStatus[enKey] || online;
} }
// 设备监测详情:每种类型只保留最新一个设备的数据(若同类型多设备,可改为取最后上报的设备)
if (!deviceMonitoring[enKey] && latest) { if (!deviceMonitoring[enKey] && latest) {
let data = latest.data; let data = latest.data;
switch (cnType) { switch (cnType) {
...@@ -875,14 +884,12 @@ export async function getSmartMonitorPop(regionKey: number) { ...@@ -875,14 +884,12 @@ export async function getSmartMonitorPop(regionKey: number) {
occupied: data.occupied === true occupied: data.occupied === true
}; };
break; break;
// 空气质量传感、电表等不在 deviceMonitoring 中展示,跳过
default: default:
break; break;
} }
} }
} }
// 3. 空气质量监测数据(从空气质量传感设备获取)
let airQualityMonitoring = { let airQualityMonitoring = {
airQualityLevel: "无数据", airQualityLevel: "无数据",
temperature: "--", temperature: "--",
...@@ -903,7 +910,6 @@ export async function getSmartMonitorPop(regionKey: number) { ...@@ -903,7 +910,6 @@ export async function getSmartMonitorPop(regionKey: number) {
let co2 = data.co2 !== undefined ? `${data.co2}ppm` : undefined; let co2 = data.co2 !== undefined ? `${data.co2}ppm` : undefined;
let tvoc = data.tvoc !== undefined ? `${data.tvoc}` : "0"; let tvoc = data.tvoc !== undefined ? `${data.tvoc}` : "0";
// 空气质量等级:根据 PM2.5 简单划分(也可根据 AQI)
let level = "优"; let level = "优";
if (pm25) { if (pm25) {
let val = data.pm25; let val = data.pm25;
...@@ -921,7 +927,7 @@ export async function getSmartMonitorPop(regionKey: number) { ...@@ -921,7 +927,7 @@ export async function getSmartMonitorPop(regionKey: number) {
co2: co2 ?? "--", co2: co2 ?? "--",
tvoc: tvoc tvoc: tvoc
}; };
break; // 取第一个空气质量设备的数据 break;
} }
} }
......
...@@ -22,7 +22,7 @@ export function setRouter(httpServer) { ...@@ -22,7 +22,7 @@ export function setRouter(httpServer) {
/**运行分析-弹窗 */ /**运行分析-弹窗 */
httpServer.post('/api/zc/run/analysis/pop', asyncHandler(getRunAnalysisPop)); httpServer.post('/api/zc/run/analysis/pop', asyncHandler(getRunAnalysisPop));
/** 智能监控弹窗 */ /** 智能监控-弹窗 */
httpServer.post('/api/zc/smart/monitor/pop', asyncHandler(getSmartMonitorPop)); httpServer.post('/api/zc/smart/monitor/pop', asyncHandler(getSmartMonitorPop));
} }
...@@ -79,7 +79,7 @@ async function deviceFaultPush(req, res) { ...@@ -79,7 +79,7 @@ async function deviceFaultPush(req, res) {
* 运行分析-弹窗 * 运行分析-弹窗
*/ */
async function getRunAnalysisPop(req, res) { async function getRunAnalysisPop(req, res) {
let reqConf = {regionKey:'Number'}; let reqConf = {regionKey:'String'};
const NotMustHaveKeys = []; const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys); let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getRunAnalysisPop(regionKey); const result = await deviceBiz.getRunAnalysisPop(regionKey);
...@@ -88,10 +88,10 @@ async function deviceFaultPush(req, res) { ...@@ -88,10 +88,10 @@ async function deviceFaultPush(req, res) {
/** /**
* 智能监控弹窗 * 智能监控-弹窗
*/ */
async function getSmartMonitorPop(req, res) { async function getSmartMonitorPop(req, res) {
let reqConf = {regionKey:'Number'}; let reqConf = {regionKey:'String'};
const NotMustHaveKeys = []; const NotMustHaveKeys = [];
let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys); let { regionKey } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
const result = await deviceBiz.getSmartMonitorPop(regionKey); const result = await deviceBiz.getSmartMonitorPop(regionKey);
......
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