Initial commit of akmon project
This commit is contained in:
268
uni_modules/ak-sbsrv/utssdk/mp-weixin/service_manager.uts
Normal file
268
uni_modules/ak-sbsrv/utssdk/mp-weixin/service_manager.uts
Normal file
@@ -0,0 +1,268 @@
|
||||
import type { BleService, BleCharacteristic, BleDataReceivedCallback, BleCharacteristicProperties, WriteCharacteristicOptions, AutoDiscoverAllResult } from '../interface.uts';
|
||||
import type { BleDevice } from '../interface.uts';
|
||||
import { DeviceManager } from './device_manager.uts';
|
||||
|
||||
declare const wx: any;
|
||||
|
||||
type PendingRead = {
|
||||
resolve: (data: ArrayBuffer) => void;
|
||||
reject: (err?: any) => void;
|
||||
timer?: number;
|
||||
};
|
||||
|
||||
function toUint8Array(buffer: ArrayBuffer): Uint8Array {
|
||||
return new Uint8Array(buffer ?? new ArrayBuffer(0));
|
||||
}
|
||||
|
||||
function toArrayBuffer(bytes: Uint8Array | ArrayBuffer): ArrayBuffer {
|
||||
return bytes instanceof Uint8Array ? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) : (bytes ?? new ArrayBuffer(0));
|
||||
}
|
||||
|
||||
function makeProperties(item: any): BleCharacteristicProperties {
|
||||
const props = item?.properties ?? {};
|
||||
const read = props.read === true;
|
||||
const write = props.write === true;
|
||||
const notify = props.notify === true;
|
||||
const indicate = props.indicate === true;
|
||||
const writeNoRsp = props.writeNoResponse === true || props.writeWithoutResponse === true;
|
||||
return {
|
||||
read,
|
||||
write,
|
||||
notify,
|
||||
indicate,
|
||||
writeWithoutResponse: writeNoRsp,
|
||||
canRead: read,
|
||||
canWrite: write || writeNoRsp,
|
||||
canNotify: notify || indicate
|
||||
};
|
||||
}
|
||||
|
||||
export class ServiceManager {
|
||||
private static instance: ServiceManager | null = null;
|
||||
private services = new Map<string, BleService[]>();
|
||||
private characteristics = new Map<string, Map<string, BleCharacteristic[]>>();
|
||||
private pendingReads = new Map<string, PendingRead>();
|
||||
private notifyCallbacks = new Map<string, BleDataReceivedCallback>();
|
||||
private listenersRegistered: boolean = false;
|
||||
private deviceManager = DeviceManager.getInstance();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): ServiceManager {
|
||||
if (ServiceManager.instance == null) {
|
||||
ServiceManager.instance = new ServiceManager();
|
||||
}
|
||||
return ServiceManager.instance!;
|
||||
}
|
||||
|
||||
private ensureListeners() {
|
||||
if (this.listenersRegistered) return;
|
||||
this.listenersRegistered = true;
|
||||
wx.onBLECharacteristicValueChange((res: any) => {
|
||||
try { this.handleNotify(res); } catch (e) { }
|
||||
});
|
||||
}
|
||||
|
||||
private cacheKey(deviceId: string, serviceId: string): string {
|
||||
return `${deviceId}|${serviceId}`;
|
||||
}
|
||||
|
||||
private notifyKey(deviceId: string, serviceId: string, characteristicId: string): string {
|
||||
return `${deviceId}|${serviceId}|${characteristicId}`;
|
||||
}
|
||||
|
||||
async getServices(deviceId: string, callback?: (services: BleService[] | null, error?: Error) => void): Promise<BleService[]> {
|
||||
const cached = this.services.get(deviceId);
|
||||
if (cached != null && cached.length > 0) {
|
||||
if (callback != null) callback(cached, null);
|
||||
return cached;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getBLEDeviceServices({
|
||||
deviceId,
|
||||
success: (res: any) => {
|
||||
const list: BleService[] = [];
|
||||
const services: any[] = res?.services ?? [];
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
const svc = services[i];
|
||||
if (svc == null) continue;
|
||||
list.push({ uuid: svc.uuid, isPrimary: svc.isPrimary === true });
|
||||
}
|
||||
this.services.set(deviceId, list);
|
||||
if (callback != null) callback(list, null);
|
||||
resolve(list);
|
||||
},
|
||||
fail: (err: any) => {
|
||||
const error = err ?? new Error('getBLEDeviceServices failed');
|
||||
if (callback != null) callback(null, error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getCharacteristics(deviceId: string, serviceId: string, callback?: (list: BleCharacteristic[] | null, error?: Error) => void): Promise<BleCharacteristic[]> {
|
||||
const map = this.characteristics.get(deviceId);
|
||||
const cached = map != null ? map.get(serviceId) : null;
|
||||
if (cached != null && cached.length > 0) {
|
||||
if (callback != null) callback(cached, null);
|
||||
return cached;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getBLEDeviceCharacteristics({
|
||||
deviceId,
|
||||
serviceId,
|
||||
success: (res: any) => {
|
||||
const list: BleCharacteristic[] = [];
|
||||
const chars: any[] = res?.characteristics ?? [];
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const ch = chars[i];
|
||||
if (ch == null) continue;
|
||||
list.push({
|
||||
uuid: ch.uuid,
|
||||
service: { uuid: serviceId, isPrimary: true },
|
||||
properties: makeProperties(ch)
|
||||
});
|
||||
}
|
||||
let mapRef = this.characteristics.get(deviceId);
|
||||
if (mapRef == null) {
|
||||
mapRef = new Map<string, BleCharacteristic[]>();
|
||||
this.characteristics.set(deviceId, mapRef);
|
||||
}
|
||||
mapRef.set(serviceId, list);
|
||||
if (callback != null) callback(list, null);
|
||||
resolve(list);
|
||||
},
|
||||
fail: (err: any) => {
|
||||
const error = err ?? new Error('getBLEDeviceCharacteristics failed');
|
||||
if (callback != null) callback(null, error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async readCharacteristic(deviceId: string, serviceId: string, characteristicId: string): Promise<ArrayBuffer> {
|
||||
this.ensureListeners();
|
||||
return new Promise<ArrayBuffer>((resolve, reject) => {
|
||||
const key = `${deviceId}|${serviceId}|${characteristicId}|read`;
|
||||
const timer = setTimeout(() => {
|
||||
this.pendingReads.delete(key);
|
||||
reject(new Error('读取超时'));
|
||||
}, 10000);
|
||||
this.pendingReads.set(key, { resolve, reject, timer });
|
||||
wx.readBLECharacteristicValue({
|
||||
deviceId,
|
||||
serviceId,
|
||||
characteristicId,
|
||||
fail: (err: any) => {
|
||||
this.pendingReads.delete(key);
|
||||
clearTimeout(timer);
|
||||
reject(err ?? new Error('readBLECharacteristicValue failed'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async writeCharacteristic(deviceId: string, serviceId: string, characteristicId: string, value: Uint8Array | ArrayBuffer, options?: WriteCharacteristicOptions): Promise<boolean> {
|
||||
const buffer = toArrayBuffer(value);
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
wx.writeBLECharacteristicValue({
|
||||
deviceId,
|
||||
serviceId,
|
||||
characteristicId,
|
||||
value: buffer,
|
||||
fail: (err: any) => {
|
||||
reject(err ?? new Error('writeBLECharacteristicValue failed'));
|
||||
},
|
||||
success: () => {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async subscribeCharacteristic(deviceId: string, serviceId: string, characteristicId: string, callback: BleDataReceivedCallback): Promise<void> {
|
||||
this.ensureListeners();
|
||||
const key = this.notifyKey(deviceId, serviceId, characteristicId);
|
||||
this.notifyCallbacks.set(key, callback);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
wx.notifyBLECharacteristicValueChange({
|
||||
deviceId,
|
||||
serviceId,
|
||||
characteristicId,
|
||||
state: true,
|
||||
success: () => resolve(),
|
||||
fail: (err: any) => {
|
||||
this.notifyCallbacks.delete(key);
|
||||
reject(err ?? new Error('notifyBLECharacteristicValueChange failed'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async unsubscribeCharacteristic(deviceId: string, serviceId: string, characteristicId: string): Promise<void> {
|
||||
const key = this.notifyKey(deviceId, serviceId, characteristicId);
|
||||
this.notifyCallbacks.delete(key);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
wx.notifyBLECharacteristicValueChange({
|
||||
deviceId,
|
||||
serviceId,
|
||||
characteristicId,
|
||||
state: false,
|
||||
success: () => resolve(),
|
||||
fail: (err: any) => {
|
||||
reject(err ?? new Error('notifyBLECharacteristicValueChange disable failed'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async autoDiscoverAll(deviceId: string): Promise<AutoDiscoverAllResult> {
|
||||
const services = await this.getServices(deviceId);
|
||||
const allChars: BleCharacteristic[] = [];
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
const svc = services[i];
|
||||
const chars = await this.getCharacteristics(deviceId, svc.uuid);
|
||||
for (let j = 0; j < chars.length; j++) {
|
||||
allChars.push(chars[j]);
|
||||
}
|
||||
}
|
||||
return { services, characteristics: allChars };
|
||||
}
|
||||
|
||||
async subscribeAllNotifications(deviceId: string, callback: BleDataReceivedCallback): Promise<void> {
|
||||
const services = await this.getServices(deviceId);
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
const svc = services[i];
|
||||
const chars = await this.getCharacteristics(deviceId, svc.uuid);
|
||||
for (let j = 0; j < chars.length; j++) {
|
||||
const ch = chars[j];
|
||||
if (ch.properties != null && (ch.properties.notify || ch.properties.indicate)) {
|
||||
try {
|
||||
await this.subscribeCharacteristic(deviceId, svc.uuid, ch.uuid, callback);
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleNotify(res: any) {
|
||||
const deviceId = res?.deviceId ?? '';
|
||||
const serviceId = res?.serviceId ?? '';
|
||||
const characteristicId = res?.characteristicId ?? '';
|
||||
const key = `${deviceId}|${serviceId}|${characteristicId}|read`;
|
||||
const buffer: ArrayBuffer = res?.value ?? new ArrayBuffer(0);
|
||||
const pending = this.pendingReads.get(key);
|
||||
if (pending != null) {
|
||||
this.pendingReads.delete(key);
|
||||
if (pending.timer != null) clearTimeout(pending.timer);
|
||||
try { pending.resolve(buffer); } catch (e) { }
|
||||
}
|
||||
const notifyKey = this.notifyKey(deviceId, serviceId, characteristicId);
|
||||
const cb = this.notifyCallbacks.get(notifyKey);
|
||||
if (cb != null) {
|
||||
try { cb(toUint8Array(buffer)); } catch (e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user