const { DEFAULT_PACKET_SIZE, formatSignalText, hasTargetAdvertisedUuid, inferPacketSize, mergeAdvertisedServiceUUIDs, normalizeDevice } = require('./ble-utils.js') const DEFAULT_DEVICE_LIMIT = 30 function createBleDeviceRegistry(options = {}) { const deviceLimit = Math.max(1, Number(options.deviceLimit) || DEFAULT_DEVICE_LIMIT) let deviceMap = {} let deviceSequence = 0 function getDevice(deviceId) { return deviceMap[deviceId] || null } function getSortedDeviceList() { return Object.keys(deviceMap) .map((deviceId) => deviceMap[deviceId]) .sort((left, right) => { const leftIndex = Number(left.seenIndex) || 0 const rightIndex = Number(right.seenIndex) || 0 return leftIndex - rightIndex }) } function getDeviceList() { return getSortedDeviceList().slice(0, deviceLimit) } function mergeDevices(devices = []) { if (!Array.isArray(devices) || !devices.length) return false let changed = false devices.forEach((device) => { if (!device || !device.deviceId) return const previousDevice = deviceMap[device.deviceId] || {} const nextDevice = normalizeDevice(device) const advertisServiceUUIDs = mergeAdvertisedServiceUUIDs( previousDevice.advertisServiceUUIDs, nextDevice.advertisServiceUUIDs ) const isTargetAdvertised = !!previousDevice.isTargetAdvertised || hasTargetAdvertisedUuid({ advertisServiceUUIDs }) const isTargetDevice = !!previousDevice.isTargetDevice const seenIndex = previousDevice.seenIndex || (deviceSequence += 1) deviceMap[device.deviceId] = { ...previousDevice, ...nextDevice, advertisServiceUUIDs, displayName: nextDevice.displayName === '未命名设备' && previousDevice.displayName ? previousDevice.displayName : nextDevice.displayName, isTargetAdvertised, isTargetDevice, packetSize: nextDevice.packetSize || previousDevice.packetSize || DEFAULT_PACKET_SIZE, seenIndex, serviceText: advertisServiceUUIDs.length ? advertisServiceUUIDs.join(', ') : '未广播服务', targetText: isTargetDevice ? '已发现目标特征' : (isTargetAdvertised ? '广播含目标 UUID' : '') } changed = true }) return changed } function markConnectedDevice(deviceId, discovery = {}) { const device = getDevice(deviceId) if (!device) return null const isTargetDevice = !!discovery.isTargetDevice const connectedDevice = { ...device, isTargetDevice, packetSize: device.packetSize || inferPacketSize(device), targetText: isTargetDevice ? '已发现目标特征' : device.targetText } deviceMap[deviceId] = connectedDevice return connectedDevice } function applyRssiUpdate(deviceId, rssi, connectedDevice = null) { if (typeof rssi !== 'number') return null if (connectedDevice && connectedDevice.deviceId !== deviceId) return null const signalText = formatSignalText(rssi) const lastSeenAt = Date.now() const updatedDevice = connectedDevice ? { ...connectedDevice, RSSI: rssi, lastSeenAt, signalText } : null deviceMap[deviceId] = { ...(deviceMap[deviceId] || {}), RSSI: rssi, lastSeenAt, signalText } return { deviceList: getDeviceList(), updatedDevice } } function clear() { deviceMap = {} deviceSequence = 0 } return { applyRssiUpdate, clear, getDevice, getDeviceList, getSortedDeviceList, markConnectedDevice, mergeDevices } } module.exports = { DEFAULT_DEVICE_LIMIT, createBleDeviceRegistry }