SEC_Warehouse/utils/ble/core/controller.js

185 lines
4.6 KiB
JavaScript

import { createBleCore } from './bleCore'
import { createAuthModule } from '../modules/auth'
import { createWifiModule } from '../modules/wifi'
import { createRadarModule } from '../modules/radar'
import { parseStateCode, parseDeviceInfo } from '../parsers'
import { DEVICE_PROFILE, guessProfileByRadarFrame } from '../protocols/profiles'
import { uuidEquals } from '../utils/uuid'
function createCharacteristicDispatcher(ble) {
return (event) => {
const state = ble.getState()
const profile = ble.getProfile()
const current = state.characteristics
if (current.state && uuidEquals(event.characteristicId, current.state.uuid)) {
const stateCode = parseStateCode(event.value)
ble.setAuthState({
lastStateCode: stateCode,
keyMatched: stateCode === 7 || stateCode === 5 || state.auth.keyMatched
})
ble.emit('protocol:state', {
...event,
profileId: profile.id,
stateCode
})
return
}
if (current.msg && uuidEquals(event.characteristicId, current.msg.uuid)) {
ble.emit('protocol:msg', {
...event,
profileId: profile.id,
deviceInfo: parseDeviceInfo(event.value)
})
return
}
if (current.radar && uuidEquals(event.characteristicId, current.radar.uuid)) {
const guessed = guessProfileByRadarFrame(event.value)
if (state.profileId === DEVICE_PROFILE.UNKNOWN && guessed !== DEVICE_PROFILE.UNKNOWN) {
ble.setProfile(guessed)
}
ble.emit('protocol:radar', {
...event,
profileId: ble.getProfile().id
})
return
}
ble.emit('protocol:raw', {
...event,
profileId: profile.id
})
}
}
export function createUnifiedBleController(options = {}) {
const ble = createBleCore(options)
const auth = createAuthModule(ble)
const wifi = createWifiModule(ble)
const radar = createRadarModule(ble)
const characteristicDispatcher = createCharacteristicDispatcher(ble)
let offCharacteristic = null
function bindDispatcher() {
if (offCharacteristic) {
return
}
offCharacteristic = ble.on('characteristic:value', characteristicDispatcher)
}
function unbindDispatcher() {
if (!offCharacteristic) {
return
}
offCharacteristic()
offCharacteristic = null
}
async function init() {
await ble.openAdapter()
ble.watchAdapterState()
ble.watchConnectionChange()
ble.watchCharacteristicValue()
bindDispatcher()
}
async function scanAndPickDevice({
serviceUuid,
scanDuration = 5000,
fallbackWithoutFilter = true
} = {}) {
const profile = ble.getProfile()
const targetService = serviceUuid || profile.uuids.service
ble.watchDeviceFound()
await ble.startDiscovery({ services: [targetService] })
await new Promise((resolve) => setTimeout(resolve, scanDuration))
await ble.stopDiscovery()
let selected = ble.pickDeviceByService(targetService)
if (!selected && fallbackWithoutFilter) {
await ble.startDiscovery({ services: [] })
await new Promise((resolve) => setTimeout(resolve, scanDuration))
await ble.stopDiscovery()
selected = ble.pickDeviceByService(targetService) || ble.listDiscoveredDevices()[0] || null
}
return selected
}
/**
* 连接后严格执行协议前置:
* - 发现服务和特征
* - 订阅 UUID_STATE / UUID_RADAR
*/
async function connectAndDiscover(deviceId) {
await ble.connect(deviceId)
await ble.discoverServices(deviceId)
const chars = await ble.discoverCharacteristics()
if (chars.state && chars.state.uuid) {
await ble.notifyCharacteristic(chars.state.uuid, true)
}
if (chars.radar && chars.radar.uuid) {
await ble.notifyCharacteristic(chars.radar.uuid, true)
}
return chars
}
/**
* 标准流程:连接 -> 订阅 -> 鉴权。
* 鉴权内部会按 bindStatus 分支处理绑定/匹配。
*/
async function standardConnectFlow({ deviceId, key }) {
await init()
let targetDeviceId = deviceId
if (!targetDeviceId) {
const device = await scanAndPickDevice()
if (!device) {
throw new Error('未找到可连接设备')
}
targetDeviceId = device.deviceId
}
await connectAndDiscover(targetDeviceId)
const authResult = await auth.ensureAuthorized(key)
return {
deviceId: targetDeviceId,
authResult,
state: ble.getState(),
profile: ble.getProfile()
}
}
async function cleanup() {
unbindDispatcher()
await ble.cleanup()
}
return {
ble,
auth,
wifi,
radar,
init,
scanAndPickDevice,
connectAndDiscover,
standardConnectFlow,
cleanup
}
}