Files
autoAiWorkSys/api/middleware/schedule/deviceManager.js
张成 8506d974c5 1
2025-12-15 22:10:24 +08:00

249 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const dayjs = require('dayjs');
const Sequelize = require('sequelize');
const db = require('../dbProxy');
const config = require('./config');
const utils = require('./utils');
/**
* 设备管理器(简化版)
* 合并了 Monitor 和 Strategy 的核心功能
*/
class DeviceManager {
constructor() {
// 设备状态 { sn_code: { isOnline, lastHeartbeat, lastSearch, lastApply, lastChat, dailyCounts } }
this.devices = new Map();
// 系统统计
this.stats = {
totalDevices: 0,
onlineDevices: 0,
totalTasks: 0,
completedTasks: 0,
failedTasks: 0,
startTime: new Date()
};
}
/**
* 初始化
*/
async init() {
console.log('[设备管理器] 初始化中...');
await this.loadStats();
console.log('[设备管理器] 初始化完成');
}
/**
* 加载统计数据
*/
async loadStats() {
try {
const devices = await db.getModel('pla_account').findAll();
this.stats.totalDevices = devices.length;
const completedCount = await db.getModel('task_status').count({
where: { status: 'completed' }
});
const failedCount = await db.getModel('task_status').count({
where: { status: 'failed' }
});
this.stats.completedTasks = completedCount;
this.stats.failedTasks = failedCount;
this.stats.totalTasks = completedCount + failedCount;
} catch (error) {
console.error('[设备管理器] 加载统计失败:', error);
}
}
/**
* 记录心跳
*/
async recordHeartbeat(sn_code, heartbeatData = {}) {
const now = Date.now();
if (!this.devices.has(sn_code)) {
this.devices.set(sn_code, {
isOnline: true,
lastHeartbeat: now,
dailyCounts: { date: utils.getTodayString(), searchCount: 0, applyCount: 0, chatCount: 0 }
});
}
const device = this.devices.get(sn_code);
device.isOnline = true;
device.lastHeartbeat = now;
}
/**
* 检查设备是否在线
*/
isDeviceOnline(sn_code) {
const device = this.devices.get(sn_code);
if (!device) return false;
const elapsed = Date.now() - device.lastHeartbeat;
if (elapsed > config.monitoring.heartbeatTimeout) {
device.isOnline = false;
return false;
}
return device.isOnline;
}
/**
* 检查是否可以执行操作
*/
canExecuteOperation(sn_code, operation_type) {
// 检查工作时间
if (!config.isWorkingHours()) {
return { allowed: false, reason: '不在工作时间内' };
}
// 检查频率限制
const device = this.devices.get(sn_code);
if (device) {
const lastTime = device[`last${operation_type.charAt(0).toUpperCase() + operation_type.slice(1)}`] || 0;
const interval = config.getRateLimit(operation_type);
if (Date.now() - lastTime < interval) {
return { allowed: false, reason: '操作过于频繁' };
}
}
// 检查日限制
if (device && device.dailyCounts) {
const today = utils.getTodayString();
if (device.dailyCounts.date !== today) {
device.dailyCounts = { date: today, searchCount: 0, applyCount: 0, chatCount: 0 };
}
const countKey = `${operation_type}Count`;
const current = device.dailyCounts[countKey] || 0;
const max = config.getDailyLimit(operation_type);
if (current >= max) {
return { allowed: false, reason: `今日${operation_type}操作已达上限` };
}
}
return { allowed: true };
}
/**
* 记录操作
*/
recordOperation(sn_code, operation_type) {
const device = this.devices.get(sn_code) || {};
device[`last${operation_type.charAt(0).toUpperCase() + operation_type.slice(1)}`] = Date.now();
if (device.dailyCounts) {
const countKey = `${operation_type}Count`;
device.dailyCounts[countKey] = (device.dailyCounts[countKey] || 0) + 1;
}
this.devices.set(sn_code, device);
}
/**
* 记录任务开始
*/
recordTaskStart(sn_code, task) {
// 简化实现,只记录日志
console.log(`[设备管理器] 设备 ${sn_code} 开始执行任务: ${task.taskName}`);
}
/**
* 记录任务完成
*/
recordTaskComplete(sn_code, task, success, duration) {
if (success) {
this.stats.completedTasks++;
} else {
this.stats.failedTasks++;
}
this.stats.totalTasks++;
console.log(`[设备管理器] 设备 ${sn_code} 任务${success ? '成功' : '失败'}: ${task.taskName} (${duration}ms)`);
}
/**
* 获取系统统计
*/
getSystemStats() {
const onlineCount = Array.from(this.devices.values()).filter(d => d.isOnline).length;
return {
...this.stats,
onlineDevices: onlineCount,
uptime: utils.formatDuration(Date.now() - this.stats.startTime.getTime())
};
}
/**
* 获取所有设备状态
*/
getAllDevicesStatus() {
const result = {};
for (const [sn_code, device] of this.devices.entries()) {
result[sn_code] = {
isOnline: device.isOnline,
lastHeartbeat: device.lastHeartbeat,
dailyCounts: device.dailyCounts || {}
};
}
return result;
}
/**
* 检查心跳状态仅更新内存状态device_status 表已移除)
*/
async checkHeartbeatStatus() {
try {
const now = Date.now();
const offlineDevices = [];
// 检查内存中的设备状态
for (const [sn_code, device] of this.devices.entries()) {
if (now - device.lastHeartbeat > config.monitoring.heartbeatTimeout) {
// 如果之前是在线状态,现在检测到离线
if (device.isOnline) {
device.isOnline = false;
offlineDevices.push(sn_code);
}
}
}
// 记录离线设备(仅日志,不再更新数据库)
if (offlineDevices.length > 0) {
console.log(`[设备管理器] 检测到 ${offlineDevices.length} 个设备心跳超时: ${offlineDevices.join(', ')}`);
// 注意device_status 表已移除,设备状态仅在内存中维护
}
} catch (error) {
console.error('[设备管理器] 检查心跳状态失败:', error);
}
}
/**
* 重置所有日计数器
*/
resetAllDailyCounters() {
const today = utils.getTodayString();
for (const device of this.devices.values()) {
if (device.dailyCounts && device.dailyCounts.date !== today) {
device.dailyCounts = { date: today, searchCount: 0, applyCount: 0, chatCount: 0 };
}
}
}
/**
* 清理离线设备
*/
cleanupOfflineDevices(threshold = 3600000) {
const now = Date.now();
for (const [sn_code, device] of this.devices.entries()) {
if (now - device.lastHeartbeat > threshold) {
this.devices.delete(sn_code);
}
}
}
}
// 导出单例
const deviceManager = new DeviceManager();
module.exports = deviceManager;