const pool = require('../config/db'); const logger = require('../logger'); // 引入日志记录器 const mqtt = require('../mqttClient'); // 从根目录引入 const client = mqtt.client; // 根据你的导出方式调整 // 新增设备查询方法 getDevices: async (req, res) => { try { const fields = req.query.fields || 'device_id,name,model,system_version,status' const [results] = await pool.promise().query(` SELECT ${fields} FROM devices WHERE system_version IS NOT NULL `) res.json(results) } catch (error) { logger.error('获取设备列表失败:', error) res.status(500).json({ code: 500, message: '服务器内部错误' }) } } const getDevices = (req, res) => { logger.info('开始获取所有设备信息'); const query = ` SELECT d.id, d.device_id, d.room_id, r.room_name, d.status, d.last_seen, d.name, d.temperature, d.upload_time, d.switch_status, d.switch_status_time, d.level_status, d.level_status_time, d.bound_device_id, d.bound_time, d.first_online_time, d.last_online_time, d.last_offline_time, d.ip_address FROM devices d LEFT JOIN rooms r ON d.room_id = r.id `; pool.query(query, (err, results) => { if (err) { logger.error('查询所有设备失败:', err); return res.status(500).send('Error querying devices'); } logger.info(`成功获取 ${results.length} 个设备信息`); res.status(200).json(results); }); }; // 绑定设备到房间 const bindDeviceToRoom = (req, res) => { const { deviceId, roomId } = req.body; logger.info(`尝试绑定设备 ${deviceId} 到房间 ${roomId}`); if (!deviceId || !roomId) { logger.warn('绑定设备失败:设备 ID 和房间 ID 不能为空'); return res.status(400).json({ success: false, message: '设备 ID 和房间 ID 不能为空' }); } const isRelayDevice = deviceId.includes('ESP32'); if (isRelayDevice) { // 检查房间是否已经绑定了继电器 const checkRelayQuery = ` SELECT device_id FROM devices WHERE room_id = ? AND device_id LIKE '%ESP32%' `; pool.query(checkRelayQuery, [roomId], (err, results) => { if (err) { logger.error('检查继电器设备失败:', err); return res.status(500).json({ success: false, message: '检查继电器设备时出错' }); } if (results.length > 0) { logger.warn(`绑定设备失败:房间 ${roomId} 已经绑定了继电器`); return res.status(400).json({ success: false, message: '该房间已经绑定了继电器,无法再次绑定' }); } // 执行绑定操作 executeBinding(deviceId, roomId, res); }); } else { // 非继电器设备直接执行绑定 executeBinding(deviceId, roomId, res); } }; // 辅助函数:执行设备绑定 const executeBinding = (deviceId, roomId, res) => { // 首先获取房间名称 const getRoomNameQuery = 'SELECT room_name FROM rooms WHERE id = ?'; pool.query(getRoomNameQuery, [roomId], (err, roomResults) => { if (err) { logger.error('获取房间名称失败:', err); return res.status(500).json({ success: false, message: '获取房间名称时出错' }); } if (roomResults.length === 0) { logger.warn(`房间 ${roomId} 不存在`); return res.status(404).json({ success: false, message: '房间不存在' }); } const roomName = roomResults[0].room_name; // 更新设备表中的 room_id 和 room_name const bindQuery = 'UPDATE devices SET room_id = ?, room_name = ? WHERE device_id = ?'; pool.query(bindQuery, [roomId, roomName, deviceId], (err, results) => { if (err) { logger.error('绑定设备失败:', err); return res.status(500).json({ success: false, message: '绑定设备时出错' }); } if (results.affectedRows === 0) { logger.warn(`设备 ${deviceId} 不存在或已绑定到相同房间`); return res.status(404).json({ success: false, message: '设备不存在或已绑定到相同房间' }); } logger.info(`设备 ${deviceId} 成功绑定到房间 ${roomId}`); res.status(200).json({ success: true, message: '设备绑定成功' }); }); }); }; // 解绑设备 const unbindDevice = (req, res) => { const { deviceId } = req.params; logger.info(`尝试解绑设备: ${deviceId}`); const query = 'UPDATE devices SET room_id = NULL, room_name = NULL WHERE device_id = ?'; pool.query(query, [deviceId], (err, results) => { if (err) { logger.error('解绑设备失败:', err); return res.status(500).send('Error unbinding device'); } logger.info(`设备 ${deviceId} 解绑成功`); res.status(200).send('设备已从房间中移除'); }); }; // 删除设备 const deleteDevice = (req, res) => { const { deviceId } = req.params; logger.info(`尝试删除设备: ${deviceId}`); const query = 'DELETE FROM devices WHERE device_id = ?'; pool.query(query, [deviceId], (err, results) => { if (err) { logger.error('删除设备失败:', err); return res.status(500).send('Error deleting device'); } logger.info(`设备 ${deviceId} 删除成功`); res.status(200).send('设备删除成功'); }); }; // 更新设备信息 const updateDevice = async (req, res) => { const { deviceId, name, roomId } = req.body; logger.info(`尝试更新设备信息: deviceId=${deviceId}, name=${name}, roomId=${roomId}`); try { // 检查设备类型 const deviceQuery = 'SELECT device_id FROM devices WHERE device_id = ?'; const [deviceResults] = await pool.promise().query(deviceQuery, [deviceId]); if (deviceResults.length === 0) { logger.warn(`设备 ${deviceId} 不存在`); return res.status(404).json({ success: false, message: '设备不存在' }); } // 如果是继电器设备,检查目标房间是否已有其他继电器 if (deviceId.includes('ESP32')) { const relayQuery = ` SELECT device_id FROM devices WHERE room_id = ? AND device_id LIKE "%ESP32%" AND device_id != ? `; const [relayResults] = await pool.promise().query(relayQuery, [roomId, deviceId]); if (relayResults.length > 0) { logger.warn(`房间 ${roomId} 已绑定其他继电器`); return res.status(400).json({ success: false, message: '该房间已绑定其他继电器' }); } } // 获取房间名称 let roomName = null; if (roomId) { const roomQuery = 'SELECT room_name FROM rooms WHERE id = ?'; const [roomResults] = await pool.promise().query(roomQuery, [roomId]); if (roomResults.length > 0) { roomName = roomResults[0].room_name; } else { logger.warn(`房间 ${roomId} 不存在`); return res.status(404).json({ success: false, message: '房间不存在' }); } } // 更新设备信息,包括房间名称 const updateQuery = 'UPDATE devices SET name = ?, room_id = ?, room_name = ? WHERE device_id = ?'; await pool.promise().query(updateQuery, [name, roomId, roomName, deviceId]); logger.info(`成功更新设备信息: deviceId=${deviceId}, roomName=${roomName}`); res.status(200).json({ success: true, message: '设备信息更新成功' }); } catch (err) { logger.error('更新设备信息失败:', err); res.status(500).json({ success: false, message: '更新设备信息失败' }); } }; // 获取设备状态 const getDeviceStatus = (req, res) => { logger.info('开始获取设备状态信息'); const query = ` SELECT d.device_id, d.status AS device_status, rs.state AS relay_state, rs.temperature, rs.timestamp FROM devices d LEFT JOIN relay_state rs ON d.device_id = rs.device_id ORDER BY rs.timestamp DESC `; pool.query(query, (err, results) => { if (err) { logger.error('查询设备状态失败:', err); return res.status(500).send('Error querying device status'); } logger.info(`成功获取 ${results.length} 个设备的状态信息`); res.status(200).json(results); }); }; // 设备管理界面查询绑定状态 const getDevicesByRoom = (req, res) => { const { roomId } = req.query; logger.info(`开始获取房间 ${roomId} 的设备信息`); if (!roomId) { logger.warn('获取设备失败:房间ID为空'); return res.status(400).send('房间 ID 不能为空'); } const query = ` SELECT device_id, name, status, temperature, switch_status, level_status FROM devices WHERE room_id = ? `; pool.query(query, [roomId], (err, results) => { if (err) { logger.error(`获取房间 ${roomId} 的设备失败:`, err); return res.status(500).send('Error querying devices by room'); } logger.info(`成功获取房间 ${roomId} 的 ${results.length} 个设备信息`); res.status(200).json(results); }); }; // 设备管理在线状态 const getDeviceStatusByRoom = (req, res) => { const { roomId } = req.query; logger.info(`开始获取房间 ${roomId} 的设备状态`); if (!roomId) { logger.warn('获取设备状态失败:房间ID为空'); return res.status(400).send('房间 ID 不能为空'); } const query = ` SELECT d.device_id, d.status AS device_status, rs.state AS relay_state, rs.temperature, rs.timestamp FROM devices d LEFT JOIN relay_state rs ON d.device_id = rs.device_id WHERE d.room_id = ? ORDER BY rs.timestamp DESC `; pool.query(query, [roomId], (err, results) => { if (err) { logger.error(`获取房间 ${roomId} 的设备状态失败:`, err); return res.status(500).send('Error querying device status'); } logger.info(`成功获取房间 ${roomId} 的 ${results.length} 个设备状态`); res.status(200).json(results); }); }; // 获取 GPIO 状态 const getGpioState = (req, res) => { const { deviceId } = req.query; logger.info(`开始获取设备 ${deviceId} 的GPIO状态`); if (!deviceId) { logger.warn('获取GPIO状态失败:设备ID为空'); return res.status(400).send('设备 ID 不能为空'); } const query = ` SELECT state FROM gpio_state WHERE device_id = ? ORDER BY timestamp DESC LIMIT 1 `; pool.query(query, [deviceId], (err, results) => { if (err) { logger.error(`获取设备 ${deviceId} 的GPIO状态失败:`, err); return res.status(500).send('Error querying GPIO state'); } logger.info(`成功获取设备 ${deviceId} 的GPIO状态`); res.status(200).json(results[0] || { state: 'low' }); }); }; // 绑定继电器到人体传感器 const bindRelayToSensor = async (req, res) => { const { sensorId, roomId } = req.body; logger.info(`尝试绑定房间 ${roomId} 的继电器到传感器 ${sensorId}`); if (!sensorId || !roomId) { logger.warn('绑定失败:传感器ID或房间ID为空'); return res.status(400).json({ success: false, message: '传感器ID和房间ID不能为空' }); } try { // 查询房间中的继电器设备 const relayQuery = ` SELECT device_id FROM devices WHERE room_id = ? AND device_id LIKE '%ESP32%' `; const [relayResults] = await pool.promise().query(relayQuery, [roomId]); if (relayResults.length === 0) { logger.warn(`房间 ${roomId} 没有继电器设备`); return res.status(404).json({ success: false, message: '该房间没有继电器设备' }); } const relayId = relayResults[0].device_id; logger.info(`找到继电器设备: ${relayId}`); // 更新人体传感器模块的 bound_device_id 和 bound_time const updateQuery = ` UPDATE devices SET bound_device_id = ?, bound_time = NOW() WHERE device_id = ? `; await pool.promise().query(updateQuery, [relayId, sensorId]); // 通过 MQTT 发送继电器 ID const controlTopic = `device/${sensorId}/control`; client.publish(controlTopic, relayId, { qos: 1 }, (err) => { // 添加了 qos: 1 确保消息可靠传递 if (err) { logger.error('MQTT消息发送失败:', err); return res.status(500).json({ success: false, message: 'MQTT 消息发布失败' }); } logger.info(`成功绑定并配置继电器: 传感器=${sensorId}, 继电器=${relayId}`); res.status(200).json({ success: true, message: '继电器与传感器绑定成功,并已发送配置信息' }); }); } catch (error) { logger.error('绑定继电器到传感器失败:', error); res.status(500).json({ success: false, message: '处理绑定请求时出错' }); } }; // 新增:获取所有房间及其设备信息 const getAllRoomsWithDevices = async (req, res) => { logger.info('开始获取所有房间及其设备信息'); const query = ` SELECT r.id AS room_id, r.room_name, r.orientation, d.id AS device_id, d.device_id AS device_uid, d.status, d.temperature, d.switch_status, d.level_status FROM rooms r LEFT JOIN devices d ON r.id = d.room_id ORDER BY r.id `; pool.query(query, (err, results) => { if (err) { logger.error('获取房间和设备信息失败:', err); return res.status(500).send('Error querying rooms and devices'); } // 将结果按房间分组 const roomsMap = new Map(); results.forEach(row => { if (!roomsMap.has(row.room_id)) { roomsMap.set(row.room_id, { id: row.room_id, room_name: row.room_name, orientation: row.orientation, devices: [] }); } if (row.device_id) { roomsMap.get(row.room_id).devices.push({ device_id: row.device_uid, status: row.status, temperature: row.temperature, switch_status: row.switch_status, level_status: row.level_status }); } }); const rooms = Array.from(roomsMap.values()); logger.info(`成功获取 ${rooms.length} 个房间及其设备信息`); res.status(200).json(rooms); }); }; module.exports = { getDevices, bindDeviceToRoom, unbindDevice, deleteDevice, updateDevice, getDeviceStatus, getDeviceStatusByRoom, getDevicesByRoom, getGpioState, bindRelayToSensor, getAllRoomsWithDevices };