From: liangzhenying <867174124@qq.com> Date: Thu, 22 Aug 2024 07:34:11 +0000 (+0800) Subject: 支持泰凌微方案 X-Git-Tag: 千寻适配d9~47 X-Git-Url: http://git.snicker.com.cn/sitemap.xml?a=commitdiff_plain;h=31d4d7c5b1d3c82375ba3b3dc672a3d26ee8a082;p=mirrors%2FMouseSdk-MacOS.git 支持泰凌微方案 --- diff --git "a/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" "b/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" index 14ab19f..51b6dd9 100644 --- "a/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" +++ "b/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.md" @@ -4,11 +4,11 @@ ## 介绍 -当前版本:v1.0.31 +当前版本:v1.0.32 -更新内容20240816: +更新内容20240821: -1. 支持阿里鉴权 +1. 新增支持泰凌微方案 ## 环境要求 @@ -18,7 +18,7 @@ MacOS 13.0 ## 引入依赖库 -将 libAiMouseSdk2 添加进项目中。然后,在 Build Phases 选项卡中,添加以下框架和库: +将 sdk 添加进项目中。然后,在 Build Phases 选项卡中,添加以下框架和库: libz.tbd、libbz2.tbd、libiconv.tbd、liblzma5.tbd、CoreAudio.framework、VideoToolbox.framework @@ -46,7 +46,11 @@ libz.tbd、libbz2.tbd、libiconv.tbd、liblzma5.tbd、CoreAudio.framework、Vide ![image-20240401162536522](/Users/apple/Library/Application Support/typora-user-images/image-20240401162536522.png) -## SDK相关接口 +## 富瑞坤方案 + + + +### SDK相关接口 ```objective-c /// 单利 @@ -145,7 +149,7 @@ libz.tbd、libbz2.tbd、libiconv.tbd、liblzma5.tbd、CoreAudio.framework、Vide - (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; ``` -## SDK相关代理 +### SDK相关代理 @@ -187,7 +191,7 @@ libz.tbd、libbz2.tbd、libiconv.tbd、liblzma5.tbd、CoreAudio.framework、Vide ``` -## 其他类型数据 +### 其他类型数据 ```objective-c @@ -229,7 +233,7 @@ typedef NS_ENUM(NSInteger,AiMouseAuthState) { -## 蓝牙示例 +### 蓝牙示例 ```objective-c // @@ -498,7 +502,7 @@ typedef NS_ENUM(NSInteger,AiMouseAuthState) { -## USB示例 +### USB示例 ​ @@ -773,10 +777,635 @@ static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStr ``` +## 泰凌微方案 + +### sdk相关接口 + +```objective-c +/// 单利 ++(TlwMouse *)shared; + +/// 设置日志等级 +/// - Parameter level: 等级 +- (void)setLogLevel:(TlwMouseLogLevel)level; + +/// 断开连接 +- (void)didDisconnect; + +/// 切换连接类型 +/// - Parameter type: 连接类型 +- (void)switchConnect:(TlwConnectType)type; + +/// 传输业务数据给sdk +/// - Parameter data:ai特征传过来的数据 +- (void)sendAiDataToSdk:(NSData *)data; + +/// 传输设备信息数据给sdk +/// - Parameter data:设备信息特征传过来的数据 +- (void)sendDeviceDataToSdk:(NSData *)data; + +/// 获取设备信息 +/// - Parameter completation: 如果获取成功,则deviceInfo有值,否则为nil +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))complete; + +/// 设置DPI +/// - Parameters: +/// - value: 数值1 ~ 4 (分别对应 800 1200 1800 2400) +/// - finished: 设置结果回调 +- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 打开录音 +/// - finished: 执行结果回调 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 关闭录音 +/// - finished: 执行结果回调 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; +``` + +### sdk相关代理 + +```objective-c +@protocol TlwMouseDelegate + +/// 设备连接状态 +/// - Parameter connected: 是否连接 +- (void)aiMouseConnectStateDidChange:(BOOL)connected; + +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +- (void)tlwMouseSendDeviceInfoData:(NSData *)data; + +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音) +/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音) +/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音) +/// 其他未定义 +- (void)tlwMouseSendCmdData:(NSData *)data; + +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)tlwMouseSendRecordData:(NSData *)data; + +@optional +/// 输出sdk日志 +/// - Parameter log: 日志 +- (void)aiMouseAddLog:(NSString *)log; + +@end +``` + +### 其他数据类型 + +```objective-c +@interface TlwDeviceInfo : NSObject +@property (nonatomic, assign) int DPI;/// DPI值 数值1 ~ 4 (分别对应 800 1200 1800 2400) +@property (nonatomic, assign) int version;///软件版本 +@property (nonatomic, assign) int batteryState;/// 电池状态 +@property (nonatomic, assign) int batteryValue;/// 电池百分百电量,0-100 +@end + +/// 日志等级 +typedef NS_ENUM(NSInteger,TlwMouseLogLevel) { + TlwMouseLogLevel_none = 0, + TlwMouseLogLevel_error, + TlwMouseLogLevel_info, +}; + +typedef NS_ENUM(NSInteger,TlwConnectType) { + TlwConnectType_bluetooth = 0,///蓝牙 + TlwConnectType_hid,///hid +}; +``` + +### 蓝牙示例 + +```objective-c +// +// BleManager.m +// AliIotSdkDemo +// +// Created by incar on 2024/1/30. +// + +#import "BleManager.h" +#import "AiMouseSdk.h" +#import "Config.h" + +// 服务id +#define kServiceUUID @"FFF1" +//ai特征 +#define kAiUUID @"0783B03E-8535-B5A0-7140-A304D2194CB8" +//设备信息特征 +#define kDeviceInfoUUID @"0783B63E-8535-B5A0-7140-A304D2495CB6" + +@interface BleManager () +@property(nonatomic, strong) CBCentralManager *centralManager;///蓝牙管理 +@property(nonatomic, strong) CBPeripheral *connectPeripheral;//连接的设备 +@property(nonatomic, strong) CBCharacteristic *aiCha;//ai特征 +@property(nonatomic, strong) CBCharacteristic *deviceInfoCha;//设备信息特征 +@property (nonatomic,strong) NSMutableData *recordData;//录音的数据 +@end + +@implementation BleManager +- (instancetype)init{ + self = [super init]; + if (self) { + self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil]; + } + return self; +} + +#pragma mark - CBCentralManagerDelegate +- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central { + [self showLog:[NSString stringWithFormat:@"蓝牙状态 = %@",@(central.state)]]; + switch (central.state) { + case CBManagerStateUnknown: + + break; + case CBManagerStateResetting: + + break; + case CBManagerStateUnsupported: + + break; + case CBManagerStateUnauthorized: + + break; + case CBManagerStatePoweredOff: + + break; + case CBManagerStatePoweredOn: + [self autoConnectDevice]; + break; + } +} + +- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ +// [self showLog:[NSString stringWithFormat:@"扫描到设备 = %@, data = %@",peripheral,advertisementData]]; + if(self.discoverDevice){ + self.discoverDevice(peripheral); + } +} + +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ + [self showLog:[NSString stringWithFormat:@"连接成功 name = %@",peripheral.name]]; + if(self.didConnectDevice){ + self.didConnectDevice(peripheral); + } + peripheral.delegate = self; + [peripheral discoverServices:nil]; + + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil]; +} +-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ + [self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]]; + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil]; + if(self.didDisConnectDevice){ + self.didDisConnectDevice(peripheral); + } +// [self aiMouseConnectStateDidChange:false]; + [[AiMouse shared] didDisconnect]; +} +- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{ + [self showLog:[NSString stringWithFormat:@"连接失败。。。%@",error.description]]; +} +#pragma mark - CBPeripheralDelegate +//发现服务 +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { + for (CBService *service in peripheral.services){ + [peripheral discoverCharacteristics:nil forService:service]; + } +} +// 发现特征 +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { + for (CBCharacteristic *characteristic in service.characteristics){ + if ([characteristic.UUID.UUIDString isEqualToString:kAiUUID]) { + //AI特征 + self.aiCha = characteristic; + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; + } + else if ([characteristic.UUID.UUIDString isEqualToString:kDeviceInfoUUID]) { + //设备信息特征 + self.deviceInfoCha = characteristic; + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; + + //切换代理 + [AiMouse shared].delegate = self; + //切换当前连接类型 + [[AiMouse shared] switchConnect:AiMouseConnectType_bluetooth]; + + } + } +} +//收到蓝牙数据 +- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { + NSData *data = characteristic.value; + if([characteristic.UUID.UUIDString isEqualToString:kAiUUID]){ + //AI数据 + [[AiMouse shared] sendAiDataToSdk:data]; + } + else if([characteristic.UUID.UUIDString isEqualToString:kDeviceInfoUUID]){ + //设备信息数据 + [[AiMouse shared] sendDeviceDataToSdk:data]; + } +} + +#pragma mark - AiDelegate +/// 设备鉴权结果返回 +/// - Parameter authState: 鉴权状态 +-(void)aiMouseAuthenticationResult:(AiMouseAuthState)authState{ + [self showLog:[NSString stringWithFormat:@"鉴权结果 = %@",@(authState)]]; + if(self.authCallBack){ + self.authCallBack(authState == AiMouseAuthState_Success); + } +} +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +-(void)aiMouseSendDeviceInfoData:(NSData *)data{ +// [self showLog:[NSString stringWithFormat:@"[send] %@",data]]; + [self.connectPeripheral writeValue:data forCharacteristic:self.deviceInfoCha type:CBCharacteristicWriteWithoutResponse]; +} + +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 第一次单击31(开始录音), 第二次单击32(结束录音) , 按下21(开始录音)抬起22(结束录音) +/// 中 单击42, 按下25(开始录音)抬起26(结束录音) +/// 后 单击43,按下27(开始录音)抬起28(结束录音) +/// 其他未定义 +- (void)aiMouseSendCmdData:(NSData *)data{ + uint8 cmd; + [data getBytes:&cmd length:1]; + [self showLog:[NSString stringWithFormat:@"收到指令 = %X",cmd]]; + + if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){ + [self showLog:@"开始录音"]; + self.recordData = [[NSMutableData alloc] init]; + } + else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){ + [self showLog:@"结束录音"]; + [self finishRecored:self.recordData]; + } +} + +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)aiMouseSendRecordData:(NSData *)data{ + [self.recordData appendData:data]; +} +/// 输出日志 +- (void)aiMouseAddLog:(NSString *)log { +// [self showLog:log]; +} + +// 连接状态改变 +- (void)aiMouseConnectStateDidChange:(BOOL)connected{ + if (connected) { + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil]; + } + else{ + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil]; + } +} +#pragma mark - public +/// 连接设备 +- (void)startScanDevice{ + [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO]}]; +} + +/// 扫描设备 +- (void)stopScanDevice{ + [self.centralManager stopScan]; +} + +/// 连接设备 +- (void)connectPeripheral:(CBPeripheral *)peripheral{ + self.connectPeripheral = peripheral; + [self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : [NSNumber numberWithBool:YES]}]; +} + +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo * _Nonnull, NSError * _Nonnull))completation{ + [[AiMouse shared] getDeviceInfo:completation]; +} + +#pragma mark - other +//自动连接,获取已配对的蓝牙,取第一个连接 +- (void)autoConnectDevice{ + //如果是已连接或者正在连接,则不再重连 + if (self.connectPeripheral && (self.connectPeripheral.state == CBPeripheralStateConnected || self.connectPeripheral.state == CBPeripheralStateConnecting)) { + return; + } + NSArray *devices = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]]; + if (devices.count > 0) { + CBPeripheral *peripheral = devices[0]; + [self showLog:[NSString stringWithFormat:@"匹配过的设备%@",peripheral.name]]; + [self connectPeripheral:peripheral]; + } + // 10s自动重连 + [self performSelector:@selector(autoConnectDevice) withObject:nil afterDelay:10]; +} +- (void)showLog:(NSString *)log{ + NSLog(@"%@",log); +} + +//将录音数据保存 +- (void)finishRecored:(NSData *)data{ + NSString *doc = NSTemporaryDirectory(); + NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc]; + NSError *err; + [data writeToFile:fullPath options:NSDataWritingAtomic error:&err]; + if (err) { + [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]]; + } + else{ + [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]]; + } +} + +- (NSString *)dataToHexString:(NSData *)data { + NSUInteger length = data.length; + NSMutableString *result = [NSMutableString stringWithCapacity:length * 2]; + const unsigned char *byte = data.bytes; + for (int i = 0; i < length; i++, byte++) { + [result appendFormat:@"%02X", *byte]; + } + return result; +} +@end + +``` + +### usb示例 + +```objective-c +// +// HidManager.m +// MouseSdk +// +// Created by incar on 2024/3/26. +// + +#import "HidManager.h" +#include +#include +#import "Config.h" +#import "DeviceManger.h" + +//供应商id +const long vendorID = 0x6675; +//产品id +const long productID = 0x8601; + +@interface HidManager () + +@property(nonatomic, assign) IOHIDManagerRef hidManager;///hid管理 +@property(nonatomic, assign) IOHIDDeviceRef aiCha;//ai通道 +@property(nonatomic, assign) IOHIDDeviceRef deviceInfoCha;//设备信息通道 +@property (nonatomic,strong) NSMutableData *recordData;//录音的数据 +@end + +@implementation HidManager + +- (instancetype)init{ + self = [super init]; + if (self) { + [self initHid]; + [self open]; + } + return self; +} + +- (void)initHid{ + +} + +- (void)open{ + self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L); + if (ret != kIOReturnSuccess) { + NSLog(@"打开设备失败!"); + }else{ + NSLog(@"打开设备成功!"); + } + //设置匹配设备 + NSMutableDictionary* dict= [NSMutableDictionary dictionary]; + [dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]]; + [dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]]; + IOHIDManagerSetDeviceMatching(self.hidManager, (__bridge CFMutableDictionaryRef)dict); + //设置匹配到设备回调 + IOHIDManagerRegisterDeviceMatchingCallback(self.hidManager, &hidAppearDevice, NULL); + //设置移除设备回调 + IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, &hidDisAppearDevice, NULL); +} + +- (void)close{ + IOReturn ret = IOHIDManagerClose(self.hidManager, 0); + if (ret != kIOReturnSuccess) { + NSLog(@"关闭设备失败!"); + }else{ + NSLog(@"关闭设备成功!"); + } +} + +#pragma mark - Hid CallBack +/// 匹配到设备回调 +static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]]; + uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey)); + uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey)); + if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ + //设备信息 + [DeviceManger shared].hid.deviceInfoCha = inIOHIDDeviceRef; + [[DeviceManger shared].hid connectDevice]; + //注册消息回调 + unsigned char InputBuffer[128]; + IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL); + IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef, 0); + if (ret != kIOReturnSuccess) { + NSLog(@"打开设备信息失败!"); + }else{ + NSLog(@"打开设备信息成功!"); + } + CFRetain(inIOHIDDeviceRef); + IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } + else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){ + //ai + [DeviceManger shared].hid.aiCha = inIOHIDDeviceRef; + //注册消息回调 + unsigned char InputBuffer[128]; + IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL); + IOReturn ret = IOHIDDeviceOpen(inIOHIDDeviceRef, 0); + if (ret != kIOReturnSuccess) { + NSLog(@"打开AI失败!"); + }else{ + NSLog(@"打开AI成功!"); + } + CFRetain(inIOHIDDeviceRef); + IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + +/// 设备移除回调 +static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]]; + uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey)); + uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey)); + + if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ + [DeviceManger shared].hid.deviceInfoCha = nil; + [DeviceManger shared].hid.aiCha = nil; + [[AiMouse shared] didDisconnect]; + } +} + +static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){ +// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]]; + + NSInteger len = get_data_len(report, reportLength); + NSData *data = [[NSData alloc] initWithBytes:report length:len]; +// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]]; + + uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsagePageKey)); + uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsageKey)); + + if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ + [[AiMouse shared] sendDeviceDataToSdk:data]; + + } + else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){ + [[AiMouse shared] sendAiDataToSdk:data]; + } +} +#pragma mark - AiDelegate +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +-(void)aiMouseSendDeviceInfoData:(NSData *)data{ + [self sendData:data toDevice:self.deviceInfoCha]; +} + +/// 设备鉴权结果返回 +/// - Parameter authState: 鉴权状态 +-(void)aiMouseAuthenticationResult:(AiMouseAuthState)authState{ + [self showLog:[NSString stringWithFormat:@"鉴权结果 = %@",@(authState)]]; + if(self.authCallBack){ + self.authCallBack(authState == AiMouseAuthState_Success); + } +} +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 第一次单击31(开始录音), 第二次单击32(结束录音) , 按下21(开始录音)抬起22(结束录音) +/// 中 单击42, 按下25(开始录音)抬起26(结束录音) +/// 后 单击43,按下27(开始录音)抬起28(结束录音) +/// 其他未定义 +- (void)aiMouseSendCmdData:(NSData *)data{ + uint8 cmd; + [data getBytes:&cmd length:1]; + [self showLog:[NSString stringWithFormat:@"收到指令 = %X",cmd]]; + + if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){ + [self showLog:@"开始录音"]; + self.recordData = [[NSMutableData alloc] init]; + } + else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){ + [self showLog:@"结束录音"]; + [self finishRecored:self.recordData]; + } +} +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)aiMouseSendRecordData:(NSData *)data{ + [self.recordData appendData:data]; +} +/// 输出日志 +- (void)aiMouseAddLog:(NSString *)log { +// [self showLog:log]; +} + +-(void)hidDataError{ + [self close]; + [self open]; +} + +// 连接状态改变 +- (void)aiMouseConnectStateDidChange:(BOOL)connected{ + [DeviceManger shared].hid.connectStateCallBack(connected); +} +#pragma mark - public +-(void)connectDevice{ + //设置代理 + [AiMouse shared].delegate = self; + //切换当前连接类型 + [[AiMouse shared] switchConnect:AiMouseConnectType_hid]; +} +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo * _Nonnull, NSError * _Nonnull))completation{ + [[AiMouse shared] getDeviceInfo:completation]; +} + +/// 发送数据给设备 +-(void)sendData:(NSData *)data toDevice:(IOHIDDeviceRef)device{ + if(device == nil){ + [self showLog:@"请先连上设备"]; + return; + } + + IOReturn ret = IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, 0, data.bytes, data.length); + if (ret != kIOReturnSuccess) { + [self showLog:[NSString stringWithFormat:@"数据发送失败! code = %d",ret]]; + }else{ + [self showLog:@"数据发送成功!"]; + } +} +//将录音数据保存 +- (void)finishRecored:(NSData *)data{ + NSString *doc = NSTemporaryDirectory(); + NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc]; + NSError *err; + [data writeToFile:fullPath options:NSDataWritingAtomic error:&err]; + if (err) { + [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]]; + } + else{ + [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]]; + } +} +- (void)showLog:(NSString *)log{ + NSLog(@"%@",log); +} + +/// 实际到数据长度 +static inline int get_data_len(uint8_t *buf, u_long len) { + if (len <= 0 ) return 0; + int url_len = 0; + while (buf[url_len] != 0x00) url_len++; + return url_len; +} + +/// 获取对应属性值 +static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey) { + uint32_t outValue = 0; + if (inIOHIDDeviceRef != NULL) { + CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); + if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) { + Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue); + if (!result) { + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]]; + } + } + } + return outValue; +} +@end + +``` + ## 注意事项 -- 设备信息数据使用的是设备信息通道,走设备信息接口;AI数据使用的是AI通道,走AI接口。 -- 只有鉴权成功了以后,AI相关功能才可用。 +- 不同的方案,使用不同的通讯方式 diff --git "a/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.pdf" "b/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.pdf" index f404dbd..bea1301 100644 Binary files "a/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.pdf" and "b/Document/AiMouseSdk\346\216\245\345\217\243\346\226\207\346\241\243.pdf" differ diff --git a/MouseSdk/AiMouseSdk/AiMouseSdk.xcodeproj/project.pbxproj b/MouseSdk/AiMouseSdk/AiMouseSdk.xcodeproj/project.pbxproj index d3b5733..3c2c2c2 100644 --- a/MouseSdk/AiMouseSdk/AiMouseSdk.xcodeproj/project.pbxproj +++ b/MouseSdk/AiMouseSdk/AiMouseSdk.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ @@ -191,6 +191,20 @@ 18AD83262BFAE8B9009BDE3B /* AiDeviceInfoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AD83242BFAE8B9009BDE3B /* AiDeviceInfoManager.m */; }; 18AD83292BFB22DE009BDE3B /* AiSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 18AD83272BFB22DE009BDE3B /* AiSender.h */; }; 18AD832A2BFB22DE009BDE3B /* AiSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 18AD83282BFB22DE009BDE3B /* AiSender.m */; }; + 18EF7AF72C7603E800AC5223 /* TlwRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */; }; + 18EF7AF82C7603E800AC5223 /* ADPCMConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */; }; + 18EF7AF92C7603E800AC5223 /* adpcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEA2C7603E800AC5223 /* adpcm.h */; }; + 18EF7AFA2C7603E800AC5223 /* TlwMouseSdk.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */; }; + 18EF7AFB2C7603E800AC5223 /* TlwTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AEC2C7603E800AC5223 /* TlwTools.m */; }; + 18EF7AFC2C7603E800AC5223 /* TlwConnectManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */; }; + 18EF7AFD2C7603E800AC5223 /* TlwAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */; }; + 18EF7AFE2C7603E800AC5223 /* ADPCMConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */; }; + 18EF7B002C7603E800AC5223 /* TlwRecordManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */; }; + 18EF7B012C7603E800AC5223 /* adpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AF22C7603E800AC5223 /* adpcm.c */; }; + 18EF7B022C7603E800AC5223 /* TlwTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF32C7603E800AC5223 /* TlwTools.h */; }; + 18EF7B032C7603E800AC5223 /* TlwMouseSdk.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 18EF7B042C7603E800AC5223 /* TlwAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */; }; + 18EF7B052C7603E800AC5223 /* TlwConnectManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -379,6 +393,20 @@ 18AD83242BFAE8B9009BDE3B /* AiDeviceInfoManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AiDeviceInfoManager.m; sourceTree = ""; }; 18AD83272BFB22DE009BDE3B /* AiSender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AiSender.h; sourceTree = ""; }; 18AD83282BFB22DE009BDE3B /* AiSender.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AiSender.m; sourceTree = ""; }; + 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwRecordManager.m; sourceTree = ""; }; + 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADPCMConverter.m; sourceTree = ""; }; + 18EF7AEA2C7603E800AC5223 /* adpcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adpcm.h; sourceTree = ""; }; + 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwMouseSdk.m; sourceTree = ""; }; + 18EF7AEC2C7603E800AC5223 /* TlwTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwTools.m; sourceTree = ""; }; + 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwConnectManager.m; sourceTree = ""; }; + 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwAudioManager.h; sourceTree = ""; }; + 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADPCMConverter.h; sourceTree = ""; }; + 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwRecordManager.h; sourceTree = ""; }; + 18EF7AF22C7603E800AC5223 /* adpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adpcm.c; sourceTree = ""; }; + 18EF7AF32C7603E800AC5223 /* TlwTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwTools.h; sourceTree = ""; }; + 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwMouseSdk.h; sourceTree = ""; }; + 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwAudioManager.m; sourceTree = ""; }; + 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwConnectManager.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -408,6 +436,7 @@ 180E7A9D2BD7BB12008558E8 = { isa = PBXGroup; children = ( + 18EF7AE72C7603E800AC5223 /* TlwMouseSdk */, 180E7AA82BD7BB12008558E8 /* AiMouseSdk */, 180E7AA72BD7BB12008558E8 /* Products */, 180E7D092BD8FEB2008558E8 /* Frameworks */, @@ -708,6 +737,27 @@ path = AFNetworking; sourceTree = ""; }; + 18EF7AE72C7603E800AC5223 /* TlwMouseSdk */ = { + isa = PBXGroup; + children = ( + 18EF7AF42C7603E800AC5223 /* TlwMouseSdk.h */, + 18EF7AEB2C7603E800AC5223 /* TlwMouseSdk.m */, + 18EF7AF12C7603E800AC5223 /* TlwRecordManager.h */, + 18EF7AE82C7603E800AC5223 /* TlwRecordManager.m */, + 18EF7AF32C7603E800AC5223 /* TlwTools.h */, + 18EF7AEC2C7603E800AC5223 /* TlwTools.m */, + 18EF7AF62C7603E800AC5223 /* TlwConnectManager.h */, + 18EF7AED2C7603E800AC5223 /* TlwConnectManager.m */, + 18EF7AEE2C7603E800AC5223 /* TlwAudioManager.h */, + 18EF7AF52C7603E800AC5223 /* TlwAudioManager.m */, + 18EF7AEF2C7603E800AC5223 /* ADPCMConverter.h */, + 18EF7AE92C7603E800AC5223 /* ADPCMConverter.m */, + 18EF7AEA2C7603E800AC5223 /* adpcm.h */, + 18EF7AF22C7603E800AC5223 /* adpcm.c */, + ); + path = TlwMouseSdk; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -753,6 +803,7 @@ 180E7C882BD8FC4E008558E8 /* hwcontext.h in Headers */, 180E7CB12BD8FC4E008558E8 /* dovi_meta.h in Headers */, 184A710F2BDA4C050025A332 /* AFHTTPSessionManager.h in Headers */, + 18EF7B052C7603E800AC5223 /* TlwConnectManager.h in Headers */, 180E7CF92BD8FC4F008558E8 /* dirac.h in Headers */, 184A71032BDA4C050025A332 /* AFNetworkReachabilityManager.h in Headers */, 184A71242BDE2DA60025A332 /* AiSecretModel.h in Headers */, @@ -781,6 +832,7 @@ 180E7CBA2BD8FC4E008558E8 /* common.h in Headers */, 180E7C9D2BD8FC4E008558E8 /* avutil.h in Headers */, 180E7C982BD8FC4E008558E8 /* rational.h in Headers */, + 18EF7B022C7603E800AC5223 /* TlwTools.h in Headers */, 180E7CF82BD8FC4F008558E8 /* codec_id.h in Headers */, 180E7C9B2BD8FC4E008558E8 /* avconfig.h in Headers */, 180E7CDA2BD8FC4E008558E8 /* avassert.h in Headers */, @@ -798,6 +850,7 @@ 180E7CD92BD8FC4E008558E8 /* downmix_info.h in Headers */, 180E7CA72BD8FC4E008558E8 /* spherical.h in Headers */, 180E7CA92BD8FC4E008558E8 /* hwcontext_opencl.h in Headers */, + 18EF7B032C7603E800AC5223 /* TlwMouseSdk.h in Headers */, 180E7B542BD7BD83008558E8 /* AiFrameManager.h in Headers */, 180E7CD12BD8FC4E008558E8 /* parseutils.h in Headers */, 180E7CEC2BD8FC4E008558E8 /* codec_par.h in Headers */, @@ -817,6 +870,7 @@ 180E7CB92BD8FC4E008558E8 /* hwcontext_drm.h in Headers */, 180E7CE22BD8FC4E008558E8 /* version.h in Headers */, 189C15862C05726600676A80 /* AiConnectManager.h in Headers */, + 18EF7AFD2C7603E800AC5223 /* TlwAudioManager.h in Headers */, 180E7C892BD8FC4E008558E8 /* time.h in Headers */, 180E7CD32BD8FC4E008558E8 /* bprint.h in Headers */, 180E7CB02BD8FC4E008558E8 /* timecode.h in Headers */, @@ -841,9 +895,11 @@ 180E7CBB2BD8FC4E008558E8 /* hmac.h in Headers */, 184A710B2BDA4C050025A332 /* AFNetworking.h in Headers */, 180E7CAC2BD8FC4E008558E8 /* threadmessage.h in Headers */, + 18EF7AF92C7603E800AC5223 /* adpcm.h in Headers */, 180E7B582BD7BD83008558E8 /* AiRecordManager.h in Headers */, 180E7CE12BD8FC4E008558E8 /* avdevice.h in Headers */, 180E7CE92BD8FC4E008558E8 /* defs.h in Headers */, + 18EF7AFE2C7603E800AC5223 /* ADPCMConverter.h in Headers */, 180E7C942BD8FC4E008558E8 /* stereo3d.h in Headers */, 180E7CDE2BD8FC4E008558E8 /* film_grain_params.h in Headers */, 180E7C8C2BD8FC4E008558E8 /* error.h in Headers */, @@ -862,6 +918,7 @@ 180E7CD52BD8FC4E008558E8 /* hwcontext_qsv.h in Headers */, 180E7CD72BD8FC4E008558E8 /* aes_ctr.h in Headers */, 180E7CFC2BD8FC4F008558E8 /* vorbis_parser.h in Headers */, + 18EF7B002C7603E800AC5223 /* TlwRecordManager.h in Headers */, 180E7CE82BD8FC4E008558E8 /* avcodec.h in Headers */, 180E7C962BD8FC4E008558E8 /* pixdesc.h in Headers */, ); @@ -902,7 +959,7 @@ }; }; buildConfigurationList = 180E7AA12BD7BB12008558E8 /* Build configuration list for PBXProject "AiMouseSdk" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -936,14 +993,21 @@ 184A71062BDA4C050025A332 /* AFURLResponseSerialization.m in Sources */, 184A710C2BDA4C050025A332 /* AFNetworkReachabilityManager.m in Sources */, 180E7BE42BD7BD84008558E8 /* AiRecordManager.m in Sources */, + 18EF7B042C7603E800AC5223 /* TlwAudioManager.m in Sources */, + 18EF7AF72C7603E800AC5223 /* TlwRecordManager.m in Sources */, 180E7BE32BD7BD84008558E8 /* AiFrameManager.m in Sources */, 180E7BE72BD7BD84008558E8 /* AiAudioManager.m in Sources */, 189C15872C05726600676A80 /* AiConnectManager.m in Sources */, + 18EF7AF82C7603E800AC5223 /* ADPCMConverter.m in Sources */, + 18EF7AFB2C7603E800AC5223 /* TlwTools.m in Sources */, + 18EF7AFA2C7603E800AC5223 /* TlwMouseSdk.m in Sources */, 180E7BDF2BD7BD84008558E8 /* AiMouseConfig.m in Sources */, + 18EF7AFC2C7603E800AC5223 /* TlwConnectManager.m in Sources */, 181CE5C22C6EED9900EF8D07 /* AiAliAuthManager.m in Sources */, 184A710D2BDA4C050025A332 /* AFSecurityPolicy.m in Sources */, 180E7B552BD7BD83008558E8 /* AiAudioDecoder.m in Sources */, 18AD83262BFAE8B9009BDE3B /* AiDeviceInfoManager.m in Sources */, + 18EF7B012C7603E800AC5223 /* adpcm.c in Sources */, 184A71092BDA4C050025A332 /* AFURLSessionManager.m in Sources */, 180E7BE82BD7BD84008558E8 /* AiAuthManager.m in Sources */, ); diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h new file mode 100644 index 0000000..0558f1e --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.h @@ -0,0 +1,18 @@ +// +// AiAdpcm2pcm.h +// AiMouseSdk +// +// Created by incar on 2024/8/20. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ADPCMConverter : NSObject + +- (NSData *)convertADPCMToPCM:(NSData *)adpcmData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m new file mode 100644 index 0000000..42e25f3 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/ADPCMConverter.m @@ -0,0 +1,92 @@ + +#import "ADPCMConverter.h" +#import + + + +@implementation ADPCMConverter + +static const signed char idxtbl[] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8}; +static const unsigned short steptbl[] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +#define NUM_OF_ORIG_SAMPLE 2 + +- (NSData *)convertADPCMToPCM:(NSData *)adpcmData { + NSMutableData *pcmData = [NSMutableData data]; + const signed short *source = (const signed short *)[adpcmData bytes]; + signed short *destination = (signed short *)[pcmData mutableBytes]; + int len = (int)[adpcmData length] / sizeof(signed short); + + int i; + // 假设 source 中的前两个元素分别是 predict 和 predict_idx + int predict = source[0]; + int predict_idx = source[1] & 0xff; + const unsigned char *pcode = (const unsigned char *)(source + NUM_OF_ORIG_SAMPLE); + unsigned char code; + code = *pcode++; + + for (i = 0; i < len; i++) { + if (1) { + int step = steptbl[predict_idx]; + + int diffq = step >> 3; + + if (code & 4) { + diffq = diffq + step; + } + step = step >> 1; + if (code & 2) { + diffq = diffq + step; + } + step = step >> 1; + if (code & 1) { + diffq = diffq + step; + } + + if (code & 8) { + predict = predict - diffq; + } else { + predict = predict + diffq; + } + + if (predict > 32767) { + predict = 32767; + } else if (predict < -32768) { + predict = -32768; + } + + predict_idx = predict_idx + idxtbl[code & 15]; + + if (predict_idx < 0) { + predict_idx = 0; + } else if (predict_idx > 88) { + predict_idx = 88; + } + + if (i & 1) { + code = *pcode++; + } else { + code = code >> 4; + } + } + + if (0 && i < NUM_OF_ORIG_SAMPLE) { + destination[i] = source[i]; + } else { + destination[i] = predict; + } + } + return pcmData; +} + +@end diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h new file mode 100644 index 0000000..bcbc620 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.h @@ -0,0 +1,20 @@ +// +// TlwAudioManager.h +// AiMouse +// +// Created by incar on 2024/2/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwAudioManager : NSObject +@property (nonatomic,strong)void (^outputData)(NSData *data); +@property (nonatomic,strong)void (^decodeCallBack)(NSData *data); +@property (nonatomic,strong)void (^outputLog)(NSString *log); + +- (void)receiveData:(NSData *)data; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m new file mode 100644 index 0000000..460f163 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwAudioManager.m @@ -0,0 +1,50 @@ +// +// TlwAudioManager.m +// AiMouse +// +// Created by incar on 2024/2/28. +// + +#import "TlwAudioManager.h" +#import +#import "ADPCMConverter.h" +#import "adpcm.h" + +@interface TlwAudioManager () +@end + +@implementation TlwAudioManager + +-(instancetype)init{ + if (self = [super init]) { + + } + return self; +} + +- (void)receiveData:(NSData *)data{ +// [self.audioDecoder decodeSBCData:data]; + + NSData *pcmData = [self convertADPCMDataToPCMData:data]; + if(self.decodeCallBack)self.decodeCallBack(pcmData); +} +- (NSData *)convertADPCMDataToPCMData:(NSData *)adpcmData { + const unsigned char *adpcmBytes = (const unsigned char *)[adpcmData bytes]; + NSUInteger adpcmLength = [adpcmData length]; + + signed short *ps = (signed short *)malloc(adpcmLength); + memcpy(ps, adpcmBytes, adpcmLength); + + signed short *pd = (signed short *)malloc(adpcmLength * 4); + + adpcm_to_pcm(ps, pd, adpcmLength / sizeof(signed short)*2); + + NSData *pcmData = [NSData dataWithBytes:pd length:adpcmLength * 4]; + + free(ps); + free(pd); + + return pcmData; +} + +@end diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h new file mode 100644 index 0000000..8a76ef8 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.h @@ -0,0 +1,35 @@ +// +// TlwConnectManager.h +// AiMouseSdk +// +// Created by incar on 2024/5/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class TlwConnectManager; +@protocol TlwConnectManagerDelegate + +- (void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected; + +- (void)TlwConnectManagerReSendData:(TlwConnectManager *)manager; + +- (void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log; + +@end +@interface TlwConnectManager : NSObject +@property (nonatomic, weak) id delegate; + +- (void)start; + +//重置 +- (void)resetTimeOut; + +/// 增加超时 +- (void)addTimeOut; + +- (void)disConnected; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m new file mode 100644 index 0000000..e81e7ce --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwConnectManager.m @@ -0,0 +1,94 @@ +// +// TlwConnectManager.m +// AiMouseSdk +// +// Created by incar on 2024/5/28. +// + +#import "TlwConnectManager.h" + +@interface TlwConnectManager() +@property (nonatomic, assign) NSInteger currentValue;//当前计数 +@property (nonatomic, assign) NSInteger timeOut;//超时次数 +@property (nonatomic, strong) NSTimer *timer; +@property (nonatomic, assign) int connecteState;//连接状态0未知,1连上,2断开连接 +@end + +@implementation TlwConnectManager +-(instancetype)init{ + if (self = [super init]) { + + } + return self; +} + +- (void)start{ + self.timeOut = 0; + self.connecteState = 0; + + [self addSendTimer]; +} + +- (void)resetTimeOut{ + self.timeOut = 0; + self.currentValue = 0; + + if (self.connecteState != 1) { + [self showLog:@"连上设备。。。"]; + [self.delegate TlwConnectManager:self connectStateDidChange:true]; + } + self.connecteState = 1; +} + +- (void)addTimeOut{ + self.timeOut++; + + if (self.timeOut >= 3) { + if (self.connecteState != 2) { + [self showLog:@"设备断开连接!!!"]; + [self.delegate TlwConnectManager:self connectStateDidChange:false]; + } + self.connecteState = 2; + } +} + +- (void)disConnected{ + [self showLog:@"设备断开连接!!!"]; + [self.delegate TlwConnectManager:self connectStateDidChange:false]; + self.connecteState = 2; +} +#pragma mark - timer +- (void)addSendTimer{ + dispatch_async(dispatch_queue_create("timeoutQueue", DISPATCH_QUEUE_SERIAL), ^{ + self.timer = [NSTimer timerWithTimeInterval:1000 target:self selector:@selector(timerAction) userInfo:nil repeats:true]; + [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; + [[NSRunLoop currentRunLoop] run]; + }); +// self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:true]; +// [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; +} + +//- (void)removeSendTimer{ +// if (self.timer && [self.timer isValid]) { +// [self.timer invalidate]; +// } +//} + +- (void)timerAction{ + self.currentValue++; +// [self showLog:[NSString stringWithFormat:@"time = %ld",self.currentValue]]; + if (self.currentValue >= 3) { + self.currentValue = 0; + if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManagerReSendData:)]) { +// [self showLog:@"检查是否断开连接"]; + [self.delegate TlwConnectManagerReSendData:self]; + } + } +} + +- (void)showLog:(NSString *)log{ + if (self.delegate && [self.delegate respondsToSelector:@selector(TlwConnectManager:outputLog:)]) { + [self.delegate TlwConnectManager:self outputLog:log]; + } +} +@end diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h new file mode 100644 index 0000000..98376d5 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.h @@ -0,0 +1,102 @@ +// +// AiMouseSdk.h +// AiMouseSdk +// +// Created by incar on 2024/4/23. +// + +#import + +@interface TlwDeviceInfo : NSObject +@property (nonatomic, assign) int DPI;/// DPI值 数值1 ~ 4 (分别对应 800 1200 1800 2400) +@property (nonatomic, assign) int version;///软件版本 +@property (nonatomic, assign) int batteryState;/// 电池状态 +@property (nonatomic, assign) int batteryValue;/// 电池百分百电量,0-100 +@end + +/// 日志等级 +typedef NS_ENUM(NSInteger,TlwMouseLogLevel) { + TlwMouseLogLevel_none = 0, + TlwMouseLogLevel_error, + TlwMouseLogLevel_info, +}; + +typedef NS_ENUM(NSInteger,TlwConnectType) { + TlwConnectType_bluetooth = 0,///蓝牙 + TlwConnectType_hid,///hid +}; + +@protocol TlwMouseDelegate + +/// 设备连接状态 +/// - Parameter connected: 是否连接 +- (void)aiMouseConnectStateDidChange:(BOOL)connected; + +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +- (void)tlwMouseSendDeviceInfoData:(NSData *)data; + +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音) +/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音) +/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音) +/// 其他未定义 +- (void)tlwMouseSendCmdData:(NSData *)data; + +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)tlwMouseSendRecordData:(NSData *)data; + +@optional +/// 输出sdk日志 +/// - Parameter log: 日志 +- (void)aiMouseAddLog:(NSString *)log; + +@end + +@interface TlwMouse : NSObject +@property (nonatomic, weak) id delegate;/// 代理 +@property (nonatomic, readonly) TlwConnectType connectType;//连接类型 +@property (nonatomic, readonly) BOOL connected;//是否已连接 + +/// 单利 ++(TlwMouse *)shared; + +/// 设置日志等级 +/// - Parameter level: 等级 +- (void)setLogLevel:(TlwMouseLogLevel)level; + +/// 断开连接 +- (void)didDisconnect; + +/// 切换连接类型 +/// - Parameter type: 连接类型 +- (void)switchConnect:(TlwConnectType)type; + +/// 传输业务数据给sdk +/// - Parameter data:ai特征传过来的数据 +- (void)sendAiDataToSdk:(NSData *)data; + +/// 传输设备信息数据给sdk +/// - Parameter data:设备信息特征传过来的数据 +- (void)sendDeviceDataToSdk:(NSData *)data; + +/// 获取设备信息 +/// - Parameter completation: 如果获取成功,则deviceInfo有值,否则为nil +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))complete; + +/// 设置DPI +/// - Parameters: +/// - value: 数值1 ~ 4 (分别对应 800 1200 1800 2400) +/// - finished: 设置结果回调 +- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 打开录音 +/// - finished: 执行结果回调 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 关闭录音 +/// - finished: 执行结果回调 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; +@end diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m new file mode 100644 index 0000000..3785506 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwMouseSdk.m @@ -0,0 +1,492 @@ +// +// AiMouseSdk.m +// AiMouseSdk +// +// Created by incar on 2024/4/23. +// + +#import "TlwMouseSdk.h" +#import "TlwAudioManager.h" +#import "TlwTools.h" +#import "TlwRecordManager.h" +#import "TlwConnectManager.h" + +@interface TlwDeviceInfo() +@end +@implementation TlwDeviceInfo +-(NSString *)description{ + NSString *info = [NSString stringWithFormat:@"\n version = %d\n DPI = %d\n batteryState = %d\n batteryValue = %d\n",self.version,self.DPI,self.batteryState,self.batteryValue]; + return info; +} +@end + +@interface TlwMouse() + +@property (nonatomic, assign) TlwMouseLogLevel logLevel; +@property (nonatomic, strong) TlwAudioManager *audioManager; +@property (nonatomic, strong) TlwRecordManager *recordManager; +@property (nonatomic, strong) TlwConnectManager *connectManager; + +@property (nonatomic,strong)void (^openRecord)(BOOL isSuccess,NSError *error); +@property (nonatomic,strong)void (^closeRecord)(BOOL isSuccess,NSError *error); +@property (nonatomic,strong)void (^setDpi)(BOOL isSuccess,NSError *error); +@property (nonatomic,strong)void (^getMouseInfo)(TlwDeviceInfo *deviceInfo,NSError *error); +@end + +@implementation TlwMouse ++(TlwMouse *)shared{ + static TlwMouse *manager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + manager = [[TlwMouse alloc]init]; + }); + return manager; +} + +-(instancetype)init{ + if (self = [super init]) { + _logLevel = TlwMouseLogLevel_none; + _connectType = TlwConnectType_bluetooth; + [self.connectManager start]; + NSLog(@"[TlwMouseSdk]%@",[NSString stringWithFormat:@"AiMouseSdk_v%@",self.version]); + } + return self; +} + +-(NSString *)version{ + return @"1.0.0"; +} + +-(TlwAudioManager *)audioManager{ + if (!_audioManager) { + _audioManager = [[TlwAudioManager alloc] init]; + __weak typeof(self) ws = self; + _audioManager.decodeCallBack = ^(NSData * _Nonnull data) { + [ws sendRecordData:data]; + }; + _audioManager.outputLog = ^(NSString * _Nonnull log) { + [ws showLog:log]; + }; + } + return _audioManager; +} + +- (TlwRecordManager *)recordManager{ + if (!_recordManager) { + _recordManager = [[TlwRecordManager alloc] init]; + __weak typeof(self) ws = self; + _recordManager.didRecordData = ^(NSData * _Nonnull data) { + [ws showLog:[NSString stringWithFormat:@"%@",data]]; + [ws sendRecordData:data]; + }; + } + return _recordManager; +} + +-(TlwConnectManager *)connectManager{ + if (!_connectManager) { + _connectManager = [[TlwConnectManager alloc] init]; + _connectManager.delegate = self; + } + return _connectManager; +} + +#pragma mark - pub Api +- (void)setLogLevel:(TlwMouseLogLevel)level{ + _logLevel = level; +} +- (void)didDisconnect{ + [self showLog:@"系统断开连接"]; + [self stopRecord]; + [self.connectManager disConnected]; +} + +- (void)switchConnect:(TlwConnectType)type{ + if(type != _connectType && self.connected){ + [self.connectManager disConnected]; + } + _connectType = type; + [self showLog:type == TlwConnectType_bluetooth?@"sdk切换到蓝牙模式":@"sdk切换到usb模式"]; + + + if (type == TlwConnectType_hid) { + [self.recordManager checkPermissions]; + } +} + +- (void)sendAiDataToSdk:(NSData *)data{ + [self.connectManager resetTimeOut]; + + [self showLog:[NSString stringWithFormat:@"[receive][Ai] %@",data]]; + if(self.connectType == TlwConnectType_hid){ +//// 0AFFFF12BC32 +// if(data.length == 6){ +// NSData *subData = [data subdataWithRange:NSMakeRange(5, 1)]; +// [self sendCmdData:subData]; +// uint8 cmd; +// [subData getBytes:&cmd length:1]; +//// [self showLog:[NSString stringWithFormat:@"cmd = %X",cmdInt]]; +// if(cmd == 0x31 || cmd == 0x21 || cmd == 0x25 ||cmd == 0x27){ +// self.recordManager.startCmd = subData; +// [self startRecord]; +// } +// else if(cmd == 0x32 || cmd == 0x22 || cmd == 0x26 ||cmd == 0x28){ +// [self stopRecord:false]; +// } +// } +// else{ +// [self showLog:@"未实现指令"]; +// } + } + else if (self.connectType == TlwConnectType_bluetooth){ + [self bleReceiveAiData:data]; + } +} + +-(void)sendDeviceDataToSdk:(NSData *)data{ + [self showLog:[NSString stringWithFormat:@"[receive][info] %@",data]]; + if(self.connectType == TlwConnectType_hid){ + [self hidReceiveDeviceData:data]; + } + else if (self.connectType == TlwConnectType_bluetooth){ + [self bleReceiveDeviceData:data]; + } +} +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + if(self.connectType == TlwConnectType_hid){ + self.openRecord = finished; + uint8_t cmd = 0x03; + uint8_t len = 0x00; + uint8_t tail = 0xEF; + NSMutableData *mData = NSMutableData.data; + [mData appendData:[self getHidHeader]]; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&len length:1]; + [mData appendBytes:&tail length:1]; + [self sendToDevice:mData]; + } + else if (self.connectType == TlwConnectType_bluetooth){ + self.openRecord = finished; + uint8_t cmd = 0x03; + uint8_t dir = 0x01; + uint8_t len = 0x00; + NSMutableData *mData = NSMutableData.data; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&dir length:1]; + [mData appendBytes:&len length:1]; + [self sendToDevice:mData]; + } +} +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + if(self.connectType == TlwConnectType_hid){ + self.closeRecord = finished; + uint8_t cmd = 0x04; + uint8_t len = 0x00; + uint8_t tail = 0xEF; + NSMutableData *mData = NSMutableData.data; + [mData appendData:[self getHidHeader]]; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&len length:1]; + [mData appendBytes:&tail length:1]; + [self sendToDevice:mData]; + } + else if (self.connectType == TlwConnectType_bluetooth){ + self.closeRecord = finished; + uint8_t cmd = 0x04; + uint8_t dir = 0x01; + uint8_t len = 0x00; + NSMutableData *mData = NSMutableData.data; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&dir length:1]; + [mData appendBytes:&len length:1]; + [self sendToDevice:mData]; + } + +} + +- (void)setDPI:(uint8_t)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{ + if(self.connectType == TlwConnectType_hid){ + self.setDpi = finished; + uint8_t type = 0x06; + uint8_t len = 0x01; + uint8_t tail = 0xEF; + NSMutableData *mData = NSMutableData.data; + [mData appendData:[self getHidHeader]]; + [mData appendBytes:&type length:1]; + [mData appendBytes:&len length:1]; + [mData appendBytes:&value length:1]; + [mData appendBytes:&tail length:1]; + [self sendToDevice:mData]; + } + else if (self.connectType == TlwConnectType_bluetooth){ + self.setDpi = finished; + uint8_t cmd = 0x06; + uint8_t dir = 0x01; + uint8_t len = 0x01; + NSMutableData *mData = NSMutableData.data; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&dir length:1]; + [mData appendBytes:&len length:1]; + [mData appendBytes:&value length:1]; + [self sendToDevice:mData]; + } + + +} +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished{ + if(self.connectType == TlwConnectType_hid){ + self.getMouseInfo = finished; +// 0x05fff1fec09f0114ef + uint8_t cmd = 0x0c; + uint8_t len = 0x00; + uint8_t tail = 0xEF; + + NSMutableData *mData = NSMutableData.data; + [mData appendData:[self getHidHeader]]; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&len length:1]; + [mData appendBytes:&tail length:1]; + [self sendToDevice:mData]; + } + else if (self.connectType == TlwConnectType_bluetooth){ + self.getMouseInfo = finished; + uint8_t cmd = 0x0c; + uint8_t dir = 0x01; + uint8_t len = 0x00; + NSMutableData *mData = NSMutableData.data; + [mData appendBytes:&cmd length:1]; + [mData appendBytes:&dir length:1]; + [mData appendBytes:&len length:1]; + [self sendToDevice:mData]; + } +} + +#pragma mark - ble +- (void)bleReceiveAiData:(NSData *)data{ + [self.connectManager resetTimeOut]; + [self.audioManager receiveData:data]; +} + +-(void)bleReceiveDeviceData:(NSData *)data{ + NSData *typeData = [data subdataWithRange:NSMakeRange(0, 1)]; + uint8 type; + [typeData getBytes:&type length:1]; + + NSData *stateData = [data subdataWithRange:NSMakeRange(1, 1)]; + uint8 state; + [stateData getBytes:&state length:1]; + BOOL success = state == 0x02; + + NSData *lenData = [data subdataWithRange:NSMakeRange(2, 1)]; + uint8 len; + [lenData getBytes:&len length:1]; + + switch (type) { + case 0x6A: + //语音开启回复 + [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]]; + self.openRecord(success, nil); + break; + case 0x6B: + //语音停止回复 + [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]]; + self.closeRecord(success, nil); + break; + case 0x96: + //设置DPI回复 + [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]]; + self.setDpi(success, nil); + break; + case 0x9c: + { //鼠标状态查询回复 + uint16_t version; + [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2]; + + uint16_t DPI; + [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2]; + + uint16_t batteryState; + [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1]; + + uint16_t batteryValue; + [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1]; + + TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init]; + info.version = version; + info.DPI = DPI; + info.batteryState = batteryState; + info.batteryValue = batteryValue; + [self showLog:info.description]; + self.getMouseInfo(info, nil); + } + break; + case 0x9f: + { + //鼠标按键上报 + NSData *subData = [data subdataWithRange:NSMakeRange(3, 1)]; + [self sendCmdData:subData]; + } + break; + default: + break; + } +} + +#pragma mark - hid +- (void)hidReceiveDeviceData:(NSData *)data{ +// 0x05fff1fec09f0114ef + uint8 type; + [[data subdataWithRange:NSMakeRange(5, 1)] getBytes:&type length:1]; + + NSData *lenData = [data subdataWithRange:NSMakeRange(6, 1)]; + uint8 len; + [lenData getBytes:&len length:1]; + + switch (type) { + case 0x6A: + { + //语音开启回复 + uint8 state; + [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1]; + BOOL success = state == 0x02; + [self showLog:[NSString stringWithFormat:@"语音开启回复 %@",@(success)]]; + self.openRecord(success, nil); + [self startRecord]; + break; + } + + case 0x6B: + { + //语音停止回复 + uint8 state; + [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1]; + BOOL success = state == 0x02; + + [self showLog:[NSString stringWithFormat:@"语音停止回复 %@",@(success)]]; + [self stopRecord]; + self.closeRecord(success, nil); + break; + } + case 0x96: + { + //设置DPI回复 + uint8 state; + [[data subdataWithRange:NSMakeRange(6, 1)] getBytes:&state length:1]; + BOOL success = state == 0x02; + + [self showLog:[NSString stringWithFormat:@"设置DPI回复 %@",@(success)]]; + self.setDpi(success, nil); + break; + } + case 0x9c: + { //鼠标状态查询回复 + uint16_t version; + [[data subdataWithRange:NSMakeRange(3, 2)] getBytes:&version length:2]; + + uint16_t DPI; + [[data subdataWithRange:NSMakeRange(5, 2)] getBytes:&DPI length:2]; + + uint16_t batteryState; + [[data subdataWithRange:NSMakeRange(7, 1)] getBytes:&batteryState length:1]; + + uint16_t batteryValue; + [[data subdataWithRange:NSMakeRange(8, 1)] getBytes:&batteryValue length:1]; + + TlwDeviceInfo *info = [[TlwDeviceInfo alloc] init]; + info.version = version; + info.DPI = DPI; + info.batteryState = batteryState; + info.batteryValue = batteryValue; + [self showLog:info.description]; + self.getMouseInfo(info, nil); + } + break; + case 0x9f: + { + NSData *subData = [data subdataWithRange:NSMakeRange(7, 1)]; + [self sendCmdData:subData]; + + uint8 cmd; + [subData getBytes:&cmd length:1]; + + if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){ + self.recordManager.startCmd = subData; + [self startRecord]; + } + else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){ + [self stopRecord]; + } + } + break; + default: + break; + } +} +- (NSData *)getHidHeader{ + uint8_t head1 = 0x05; + uint16_t head2 = 0xFFF1; + uint8_t head3 = 0xfe; + uint8_t head4 = 0x00; + + NSMutableData *mData = NSMutableData.data; + [mData appendBytes:&head1 length:1]; + [mData appendBytes:&head2 length:2]; + [mData appendBytes:&head3 length:1]; + [mData appendBytes:&head4 length:1]; + + return mData; +} +#pragma mark - other +- (void)sendRecordData:(NSData *)data{ + [self showLog:[NSString stringWithFormat:@"[send][audio] %@",data]]; + if (self.delegate) [self.delegate tlwMouseSendRecordData:data]; +} + +- (void)sendCmdData:(NSData *)data{ + [self showLog:[NSString stringWithFormat:@"[send] %@",data]]; + if (self.delegate) [self.delegate tlwMouseSendCmdData:data]; +} + +- (void)showLog:(NSString *)log{ + if (self.delegate && [self.delegate respondsToSelector:@selector(aiMouseAddLog:)]) { + [self.delegate aiMouseAddLog:[NSString stringWithFormat:@"[AiMouseSdk]%@",log]]; + } + if(self.logLevel == TlwMouseLogLevel_info){ + NSLog(@"[AiMouseSdk]%@",log); + } +} + + +- (void)sendToDevice:(NSData *)data{ + [self showLog:[NSString stringWithFormat:@"[send][info] %@",data]]; + [self.delegate tlwMouseSendDeviceInfoData:data]; +} +#pragma mark - deviceInfo +- (void)startRecord{ + [self.recordManager start]; +} + +- (void)stopRecord{ + if (self.recordManager.recording ) { + [self.recordManager stop]; + } +} + +#pragma mark - TlwConnectManagerDelegate +-(void)TlwConnectManager:(TlwConnectManager *)manager connectStateDidChange:(BOOL)connected{ + _connected = connected; + if([self.delegate respondsToSelector:@selector(aiMouseConnectStateDidChange:)]) [self.delegate aiMouseConnectStateDidChange:connected]; +} +-(void)TlwConnectManagerReSendData:(TlwConnectManager *)manager{ +// __weak typeof(self) ws = self; +// [self.deviceInfoManager getVersion:^(NSString * _Nonnull version, NSError * _Nonnull error) { +// if (error.code == AiMouseErrorCode_timeOut) { +// [ws.connectManager addTimeOut]; +// } +// }]; +} +-(void)TlwConnectManager:(TlwConnectManager *)manager outputLog:(NSString *)log{ + [self showLog:log]; +} +@end + diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h new file mode 100644 index 0000000..3d499c6 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.h @@ -0,0 +1,25 @@ +// +// TlwRecordManager.h +// AudioRecorder +// +// Created by incar on 2024/3/29. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwRecordManager : NSObject +@property (nonatomic,assign)BOOL recording; +@property (nonatomic,strong)NSData *startCmd; +@property (nonatomic,readonly)NSData *stopCmd; +@property (nonatomic,strong)void (^didRecordData)(NSData *data); + +- (void)checkPermissions; + +- (void)start; + +- (void)stop; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m new file mode 100644 index 0000000..7b3bf47 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwRecordManager.m @@ -0,0 +1,194 @@ +// +// TlwRecordManager.m +// AudioRecorder +// +// Created by incar on 2024/3/29. +// + +#import "TlwRecordManager.h" +#import + +@interface TlwRecordManager() +@property (nonatomic,assign)BOOL isAllowRecord; +@property (nonatomic,strong)AVAudioEngine *audioEngine; +@property (nonatomic,strong)AVAudioFile *outputFile; + +@end + +@implementation TlwRecordManager +//+(TlwRecordManager *)shared{ +// static TlwRecordManager *manager = nil; +// static dispatch_once_t oncePredicate; +// dispatch_once(&oncePredicate, ^{ +// manager = [[TlwRecordManager alloc]init]; +// }); +// return manager; +//} + + +-(instancetype)init{ + if (self = [super init ]) { + self.isAllowRecord = false; +// [self checkPermissions]; + } + return self; +} + +- (void)start{ + if (self.isAllowRecord == false) { + return; + } + // let audioSession = AVAudioSession.sharedInstance() + // do { + // try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker) + // try audioSession.setActive(true, options: .notifyOthersOnDeactivation) + // catch let error as NSError { + // print("ERROR:", error) + // } + self.recording = true; + + self.audioEngine = [[AVAudioEngine alloc] init]; + AVAudioInputNode *input = self.audioEngine.inputNode; + AVAudioNodeBus bus = 0; + AVAudioFormat *inputFormat = [input inputFormatForBus:bus]; + // NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",NSTemporaryDirectory()]; + // NSURL *fileURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject URLByAppendingPathComponent:@"record.caf"]; + // [self showLog:fileURL.absoluteString]; + // NSError *err0; + // self.outputFile = [[AVAudioFile alloc] initForWriting:fileURL settings:inputFormat.settings commonFormat:inputFormat.commonFormat interleaved:inputFormat.isInterleaved error:&err0]; + // if (err0) { + // [self showLog:[NSString stringWithFormat:@"设置写入路径 %@", err0.description]]; + // } + // else{ + // [self showLog:@"写入路径设置成功"]; + // } + // self.recordData = [[NSMutableData alloc] init]; + __weak typeof(self) ws = self; + [input installTapOnBus:bus bufferSize:32 format:inputFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) { + NSData *data = [ws pcm_downsample:buffer]; + if (ws.didRecordData) { + ws.didRecordData(data); + } +// NSLog(@"%@",data); +// NSError *err0; + // [ws.outputFile writeFromBuffer:buffer error:&err0]; + // if (err0) { + // [self showLog:[NSString stringWithFormat:@"写入失败 %@", err0.description]]; + // } + // else{ + // [self showLog:@"写入成功"]; + // } + }]; + + NSError *err1; + [self.audioEngine prepare]; + [self.audioEngine startAndReturnError:&err1]; + if (err1) { + [self showLog:[NSString stringWithFormat:@"启动录音失败 %@", err1.description]]; + } + else{ + [self showLog:@"启动成功"]; + } +} + +- (void)stop{ + [self.audioEngine stop]; + self.recording = false; + // [self finishRecored:self.recordData]; +} + +- (void)checkPermissions{ + switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) { + case AVAuthorizationStatusAuthorized: + self.isAllowRecord = true; + [self showLog:@"录音已经授权"]; + break; + case AVAuthorizationStatusNotDetermined: + { + [self showLog:@"录音未授权"]; + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) { + if (granted) { + self.isAllowRecord = true; + [self showLog:@"录音已经授权"]; + } + else{ + [self showLog:@"已拒绝"]; + } + }]; + } + break; + case AVAuthorizationStatusDenied: + [self showLog:@"已拒绝"]; + [self openSettings]; + break; + default: + [self showLog:@"已拒绝"]; + [self openSettings]; + break; + } +} + +- (void)openSettings{ +// NSURL *url = [NSURL URLWithString:@""]; +} + +- (void)showLog:(NSString *)log{ +// NSLog(@"%@",log); +} + +//将录音数据保存 +- (void)finishRecored:(NSData *)data{ + NSString *doc = NSTemporaryDirectory(); + NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc]; + NSError *err; + [data writeToFile:fullPath options:NSDataWritingAtomic error:&err]; + if (err) { + [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]]; + } + else{ + [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]]; + } +} + +-(NSData*) pcm_downsample:(AVAudioPCMBuffer*)buffer { + //位深转换 float32 转 int16_t + NSData* pData = [[NSData alloc] initWithBytes:buffer.floatChannelData[0] length:buffer.frameLength * 4]; + Byte*dataByte = (Byte*)[pData bytes]; + NSUInteger srcSize = pData.length/4; + Float32 vf32[srcSize]; + memcpy(vf32, &dataByte[0], pData.length); + int16_t bArr[srcSize]; + int r; + for(size_t i =0; i < srcSize; ++i) { + float x = vf32[i]; + float c; + c = ((x < -1) ? -1: ((x >1) ?1: x)); + c = c +1; + r = (int)(c *32767.5f); + r = r -32768; + bArr[i] = (short)r; + } + //码率转换 44100 转 16000 + uint32_t last_pos = (uint32_t)(srcSize -1); + uint32_t dstSize = (srcSize/44100.0)*16000; + int16_t bArr16[dstSize]; + for(uint32_t idx =0; idx < dstSize; idx++) { + float index = ((float) idx *44100.0) / (16000.0); + uint32_t p1 = (uint32_t) index; + float coef = index - p1; + uint32_t p2 = (p1 == last_pos) ? last_pos : p1 +1; + bArr16[idx] = (int16_t) ((1.0f- coef) * bArr[p1] + coef * bArr[p2]); + } + NSMutableData *data1 = [[NSMutableData alloc] init]; + for(NSUInteger i =0; i < dstSize; i++) { + unsigned char valChar[2]; + valChar[0] =0xff& bArr16[i]; + valChar[1] = (0xff00& bArr16[i]) >>8; + [data1 appendBytes:valChar length:2]; + } + return data1; +} + + + +@end diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h new file mode 100644 index 0000000..4eb7bb4 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.h @@ -0,0 +1,44 @@ +// +// TlwTools.h +// AiMouse +// +// Created by incar on 2024/2/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwTools : NSObject ++ (NSArray *)stringToArray:(NSString *)jsonString; ++ (NSString *)arrayToString:(NSArray *)arr; + ++ (NSDictionary *)stringToDictionary:(NSString *)jsonString; ++ (NSString *)dictionaryToString:(NSDictionary *)dic; + ++ (NSDictionary *)dataToDictionary:(NSData *)jsonData; ++ (NSData *)dictionaryToData:(NSDictionary *)dicJson; + + ++ (NSString *)dataToHexString:(NSData *)data; + ++ (NSString *)dataToHexString_placeholder:(NSData *)data; + ++ (NSData *)hexStringToData:(NSString *)hexStr ; + ++ (NSString *)dataToUtf8String:(NSData *)data; + ++ (NSString *)stringTomd5:(NSString *)str; + ++ (NSString *)stringToHmacSha1:(NSString*)key text:(NSString*)text; + ++ (NSString *)MD5ForUpper32:(NSString *)str; + ++ (NSData *)dataWithHexString:(NSString *)hexStr; + ++(NSString *)randomString:(NSInteger)length; + ++(NSString *)getCurrentTime; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m new file mode 100644 index 0000000..be8f60a --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/TlwTools.m @@ -0,0 +1,226 @@ +// +// TlwTools.m +// AiMouse +// +// Created by incar on 2024/2/28. +// + +#import "TlwTools.h" +#import +#include +#include + +@implementation TlwTools ++ (NSArray *)stringToArray:(NSString *)jsonString{ + NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error; + NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; + return arr; +} ++ (NSString *)arrayToString:(NSArray *)arr{ + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:NSJSONWritingPrettyPrinted error:&error]; + NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return string; +} ++ (NSDictionary *)stringToDictionary:(NSString *)jsonString{ + NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; + if (jsonData == nil) { + return nil; + } + NSError *error; + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; + return dict; +} ++ (NSString *)dictionaryToString:(NSDictionary *)dic{ + if ([dic isKindOfClass:[NSDictionary class]]) { + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error]; + NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return string; + } + else{ + return @""; + } +} + ++ (NSDictionary *)dataToDictionary:(NSData *)jsonData{ + if (![jsonData isKindOfClass:[NSData class]] || jsonData.length < 1) { + return nil; + } + id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil]; + if (![jsonObj isKindOfClass:[NSDictionary class]]) { + return nil; + } + return [NSDictionary dictionaryWithDictionary:(NSDictionary *)jsonObj]; +} + + ++ (NSData *)dictionaryToData:(NSDictionary *)dicJson{ + if (![dicJson isKindOfClass:[NSDictionary class]]) { + return nil; + } + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dicJson options:0 error:nil]; + if (![jsonData isKindOfClass:[NSData class]]) { + return nil; + } + return jsonData; +} + ++ (NSString *)dataToHexString:(NSData *)data { + NSUInteger length = data.length; + NSMutableString *result = [NSMutableString stringWithCapacity:length * 2]; + const unsigned char *byte = data.bytes; + for (int i = 0; i < length; i++, byte++) { + [result appendFormat:@"%02X", *byte]; + } + return result; +} + ++ (NSString *)dataToHexString_placeholder:(NSData *)data { + NSUInteger length = data.length; + NSString *placeholder = @" "; + NSMutableString *result = [NSMutableString stringWithCapacity:length * 2]; + const unsigned char *byte = data.bytes; + for (int i = 0; i < length; i++, byte++) { + [result appendFormat:@"%02X%@", *byte, placeholder]; + } + return result; +} + ++ (NSData *)hexStringToData:(NSString *)hexStr { + hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""]; + hexStr = [hexStr lowercaseString]; + NSUInteger len = hexStr.length; + if (!len) return nil; + unichar *buf = malloc(sizeof(unichar) * len); + if (!buf) return nil; + [hexStr getCharacters:buf range:NSMakeRange(0, len)]; + + NSMutableData *result = [NSMutableData data]; + unsigned char bytes; + char str[3] = { '\0', '\0', '\0' }; + int i; + for (i = 0; i < len / 2; i++) { + str[0] = buf[i * 2]; + str[1] = buf[i * 2 + 1]; + bytes = strtol(str, NULL, 16); + [result appendBytes:&bytes length:1]; + } + free(buf); + return result; +} + ++ (NSString *)dataToUtf8String:(NSData *)data { + if (data.length > 0) { + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + return @""; +} + + ++ (NSString *)stringTomd5:(NSString *)str { + if (str.length == 0) { + return nil; + } + // 初始化C字符数组 + const char* original_str = (const char *)[[str dataUsingEncoding:NSUTF8StringEncoding] bytes]; + // 盛放数字校验的字符数组(长度为16bytes) + unsigned char digist[CC_MD5_DIGEST_LENGTH]; //CC_MD5_DIGEST_LENGTH = 16 + CC_MD5(original_str, (uint)strlen(original_str), digist); + // NSMutableString* outPutStr = [NSMutableString stringWithCapacity:10]; + NSMutableString* outPutStr = [NSMutableString new]; + for(int i =0; i>8) & 0xff; + unsigned char *pcode = (unsigned char *) (ps + NUM_OF_ORIG_SAMPLE); + unsigned char code; + code = *pcode ++; + + //byte5- byte128: 124 byte(62 sample) adpcm data + for (i=0; i> 3; + + if (code & 4) { + diffq = diffq + step; + } + step = step >> 1; + if (code & 2) { + diffq = diffq + step; + } + step = step >> 1; + if (code & 1) { + diffq = diffq + step; + } + + if (code & 8) { + predict = predict - diffq; + } + else { + predict = predict + diffq; + } + + if (predict > 32767) { + predict = 32767; + } + else if (predict < -32768) { + predict = -32768; + } + + predict_idx = predict_idx + idxtbl[code & 15]; + + if(predict_idx < 0) { + predict_idx = 0; + } + else if(predict_idx > 88) { + predict_idx = 88; + } + + if (i&1) { + code = *pcode ++; + } + else { + code = code >> 4; + } + } + + if (0 && i < NUM_OF_ORIG_SAMPLE) { + *pd++ = ps[i]; + } + else { + *pd++ = predict; + } + } +} + +//例子 +#if 0 +#define MIC_ADPCM_FRAME_SIZE 128 //一包 adpcm 的数据长度 128 byte +#define MIC_SHORT_DEC_SIZE 248 +#define DEC_BUFFER_SIZE (MIC_SHORT_DEC_SIZE<<2) + +unsigned char abuf_dec_wptr; // 0~3 , 4个缓冲 ,循环使用 +unsigned char abuf_mic[MIC_ADPCM_FRAME_SIZE * 4]; //压缩的语音数据adpcm缓冲区 ,4个缓冲 ,每个缓冲区 128 byte , 循环使用 +signed short abuf_dec[DEC_BUFFER_SIZE]; //解压后存放的数据,注意数组类型是 16 bit,4个缓冲,循环使用 + +//abuf_mic 源 , abuf_dec 目的 , 每次解压 128 byte adpcm +adpcm_to_pcm ((signed short *)(abuf_mic + (abuf_dec_wptr & 3) * MIC_ADPCM_FRAME_SIZE), abuf_dec + (abuf_dec_wptr & 3) * MIC_SHORT_DEC_SIZE, MIC_SHORT_DEC_SIZE ); + +#endif + diff --git a/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h b/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h new file mode 100644 index 0000000..0cb25a9 --- /dev/null +++ b/MouseSdk/AiMouseSdk/TlwMouseSdk/adpcm.h @@ -0,0 +1,51 @@ +/******************************************************************************************************** + * @file adpcm.h + * + * @brief This is the header file for BLE SDK + * + * @author BLE GROUP + * @date 06,2020 + * + * @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK") + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Unless for usage inside a TELINK integrated circuit, redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of TELINK, nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * TELINK integrated circuit. All other usages are subject to written permission + * from TELINK and different commercial license may apply. + * + * 5. Licensee shall be solely responsible for any claim to the extent arising out of or + * relating to such deletion(s), modification(s) or alteration(s). + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************************************/ +#ifndef ADPCM_H_ +#define ADPCM_H_ + +void adpcm_to_pcm(signed short *ps, signed short *pd, int len); + +#endif /* ADPCM_H_ */ diff --git a/MouseSdk/MouseSdk.xcodeproj/project.pbxproj b/MouseSdk/MouseSdk.xcodeproj/project.pbxproj index c02e0db..8eddb30 100644 --- a/MouseSdk/MouseSdk.xcodeproj/project.pbxproj +++ b/MouseSdk/MouseSdk.xcodeproj/project.pbxproj @@ -26,6 +26,14 @@ 18BF1DF02B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DEF2B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m */; }; 18BF1E222B83066200B8D90F /* BleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E212B83066200B8D90F /* BleManager.m */; }; 18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E752B832D2E00B8D90F /* ScanVC.m */; }; + 18EF7B0A2C7605BF00AC5223 /* MouseSdkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */; }; + 18EF7B0D2C76079E00AC5223 /* TlwVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B0C2C76079E00AC5223 /* TlwVC.m */; }; + 18EF7B102C7607C100AC5223 /* FrkVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B0F2C7607C100AC5223 /* FrkVC.m */; }; + 18EF7B172C760A5B00AC5223 /* TlwBleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */; }; + 18EF7B182C760A5B00AC5223 /* TlwHidManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */; }; + 18EF7B192C760A5B00AC5223 /* TlwDeviceManger.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */; }; + 18EF7B1D2C760FF700AC5223 /* TlwScanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */; }; + 18EF7B1E2C760FF700AC5223 /* TlwScanVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */; }; 18F3235D2B8ED78300CABA2E /* ScanCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F3235C2B8ED78300CABA2E /* ScanCell.m */; }; 18F328D52B98569D00CABA2E /* Config.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F328D42B98569D00CABA2E /* Config.m */; }; 8A6657BD1406314D4CBB7CEF /* libPods-MouseSdk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0311DF3AD67ED792549B96BD /* libPods-MouseSdk.a */; }; @@ -89,6 +97,21 @@ 18BF1E752B832D2E00B8D90F /* ScanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanVC.m; sourceTree = ""; }; 18C31D6F2BBAA6CC0089D656 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 18CB080B2BA401D0005F5854 /* liblzma.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.5.tbd; path = usr/lib/liblzma.5.tbd; sourceTree = SDKROOT; }; + 18EF7B082C7605BF00AC5223 /* MouseSdkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MouseSdkManager.h; sourceTree = ""; }; + 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MouseSdkManager.m; sourceTree = ""; }; + 18EF7B0B2C76079E00AC5223 /* TlwVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TlwVC.h; sourceTree = ""; }; + 18EF7B0C2C76079E00AC5223 /* TlwVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TlwVC.m; sourceTree = ""; }; + 18EF7B0E2C7607C100AC5223 /* FrkVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrkVC.h; sourceTree = ""; }; + 18EF7B0F2C7607C100AC5223 /* FrkVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrkVC.m; sourceTree = ""; }; + 18EF7B112C760A5A00AC5223 /* TlwHidManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwHidManager.h; sourceTree = ""; }; + 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwBleManager.m; sourceTree = ""; }; + 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwHidManager.m; sourceTree = ""; }; + 18EF7B142C760A5A00AC5223 /* TlwDeviceManger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwDeviceManger.h; sourceTree = ""; }; + 18EF7B152C760A5A00AC5223 /* TlwBleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TlwBleManager.h; sourceTree = ""; }; + 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TlwDeviceManger.m; sourceTree = ""; }; + 18EF7B1A2C760FF700AC5223 /* TlwScanVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TlwScanVC.h; sourceTree = ""; }; + 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TlwScanVC.m; sourceTree = ""; }; + 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TlwScanVC.xib; sourceTree = ""; }; 18F3235B2B8ED78300CABA2E /* ScanCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanCell.h; sourceTree = ""; }; 18F3235C2B8ED78300CABA2E /* ScanCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanCell.m; sourceTree = ""; }; 18F3249D2B9337D500CABA2E /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; @@ -162,17 +185,22 @@ 18BF1DCC2B81AB7C00B8D90F /* MouseSdk */ = { isa = PBXGroup; children = ( + 18EF7B072C7604BF00AC5223 /* Ble */, + 18EF7B062C7604AA00AC5223 /* Tlw */, 18C31D6F2BBAA6CC0089D656 /* Info.plist */, - 18BF1E202B83066200B8D90F /* BleManager.h */, - 18BF1E212B83066200B8D90F /* BleManager.m */, - 1867E5A72BB2706F008FC5FB /* HidManager.h */, - 1867E5A82BB2706F008FC5FB /* HidManager.m */, - 182B548B2BB7EC9B009F53B6 /* DeviceManger.h */, - 182B548C2BB7EC9B009F53B6 /* DeviceManger.m */, 18BF1DCD2B81AB7C00B8D90F /* AppDelegate.h */, 18BF1DCE2B81AB7C00B8D90F /* AppDelegate.m */, + 18EF7B082C7605BF00AC5223 /* MouseSdkManager.h */, + 18EF7B092C7605BF00AC5223 /* MouseSdkManager.m */, 18BF1DD02B81AB7C00B8D90F /* ViewController.h */, 18BF1DD12B81AB7C00B8D90F /* ViewController.m */, + 18EF7B0B2C76079E00AC5223 /* TlwVC.h */, + 18EF7B0C2C76079E00AC5223 /* TlwVC.m */, + 18EF7B0E2C7607C100AC5223 /* FrkVC.h */, + 18EF7B0F2C7607C100AC5223 /* FrkVC.m */, + 18EF7B1A2C760FF700AC5223 /* TlwScanVC.h */, + 18EF7B1B2C760FF700AC5223 /* TlwScanVC.m */, + 18EF7B1C2C760FF700AC5223 /* TlwScanVC.xib */, 18BF1E742B832D2E00B8D90F /* ScanVC.h */, 18BF1E752B832D2E00B8D90F /* ScanVC.m */, 18F3235B2B8ED78300CABA2E /* ScanCell.h */, @@ -205,6 +233,32 @@ path = MouseSdkUITests; sourceTree = ""; }; + 18EF7B062C7604AA00AC5223 /* Tlw */ = { + isa = PBXGroup; + children = ( + 18EF7B142C760A5A00AC5223 /* TlwDeviceManger.h */, + 18EF7B162C760A5A00AC5223 /* TlwDeviceManger.m */, + 18EF7B152C760A5A00AC5223 /* TlwBleManager.h */, + 18EF7B122C760A5A00AC5223 /* TlwBleManager.m */, + 18EF7B112C760A5A00AC5223 /* TlwHidManager.h */, + 18EF7B132C760A5A00AC5223 /* TlwHidManager.m */, + ); + path = Tlw; + sourceTree = ""; + }; + 18EF7B072C7604BF00AC5223 /* Ble */ = { + isa = PBXGroup; + children = ( + 182B548B2BB7EC9B009F53B6 /* DeviceManger.h */, + 182B548C2BB7EC9B009F53B6 /* DeviceManger.m */, + 18BF1E202B83066200B8D90F /* BleManager.h */, + 18BF1E212B83066200B8D90F /* BleManager.m */, + 1867E5A72BB2706F008FC5FB /* HidManager.h */, + 1867E5A82BB2706F008FC5FB /* HidManager.m */, + ); + path = Ble; + sourceTree = ""; + }; 543EC4E5F9FC310D6B850947 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -339,6 +393,7 @@ files = ( 18BF1DD42B81AB7F00B8D90F /* Assets.xcassets in Resources */, 18BF1DD72B81AB7F00B8D90F /* Main.storyboard in Resources */, + 18EF7B1E2C760FF700AC5223 /* TlwScanVC.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -388,12 +443,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 18EF7B192C760A5B00AC5223 /* TlwDeviceManger.m in Sources */, 18F3235D2B8ED78300CABA2E /* ScanCell.m in Sources */, 18BF1DD22B81AB7C00B8D90F /* ViewController.m in Sources */, 18BF1DD92B81AB7F00B8D90F /* main.m in Sources */, + 18EF7B172C760A5B00AC5223 /* TlwBleManager.m in Sources */, 18F328D52B98569D00CABA2E /* Config.m in Sources */, + 18EF7B182C760A5B00AC5223 /* TlwHidManager.m in Sources */, + 18EF7B0D2C76079E00AC5223 /* TlwVC.m in Sources */, 182B548D2BB7EC9B009F53B6 /* DeviceManger.m in Sources */, + 18EF7B102C7607C100AC5223 /* FrkVC.m in Sources */, 18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */, + 18EF7B1D2C760FF700AC5223 /* TlwScanVC.m in Sources */, + 18EF7B0A2C7605BF00AC5223 /* MouseSdkManager.m in Sources */, 18BF1E222B83066200B8D90F /* BleManager.m in Sources */, 18BF1DCF2B81AB7C00B8D90F /* AppDelegate.m in Sources */, 1867E5A92BB2706F008FC5FB /* HidManager.m in Sources */, @@ -600,7 +662,10 @@ SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\""; + USER_HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"", + "\"$(SRCROOT)/AiMouseSdk/TlwMouseSdk\"", + ); }; name = Debug; }; @@ -645,7 +710,10 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h"; SWIFT_VERSION = 5.0; - USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\""; + USER_HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/AiMouseSdk/AiMouseSdk\"", + "\"$(SRCROOT)/AiMouseSdk/TlwMouseSdk\"", + ); }; name = Release; }; diff --git a/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate b/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate index a1373c5..4a419ff 100644 Binary files a/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate and b/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index b9bdb28..675cff0 100644 --- a/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/MouseSdk/MouseSdk.xcworkspace/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -95,5 +95,21 @@ + + + + diff --git a/MouseSdk/MouseSdk/BleManager.h b/MouseSdk/MouseSdk/Ble/BleManager.h similarity index 84% rename from MouseSdk/MouseSdk/BleManager.h rename to MouseSdk/MouseSdk/Ble/BleManager.h index 536301f..b71c66a 100644 --- a/MouseSdk/MouseSdk/BleManager.h +++ b/MouseSdk/MouseSdk/Ble/BleManager.h @@ -9,8 +9,8 @@ #import #import "AiMouseSdk.h" -#define kDidConnectBleNotification @"kDidConnectBleNotification" -#define kDidDisconnectBleNotification @"kDidDisconnectBleNotification" +#define kTlwDidConnectBleNotification @"kTlwDidConnectBleNotification" +#define kTlwDidDisconnectBleNotification @"kTlwDidDisconnectBleNotification" @@ -19,8 +19,6 @@ @property (nonatomic,strong)void (^didConnectDevice)(CBPeripheral *peripheral);/// 连接成功 @property (nonatomic,strong)void (^didDisConnectDevice)(CBPeripheral *peripheral);/// 断开连接 @property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调 -/// 单利 -+(BleManager *)shared; /// 扫描 - (void)startScanDevice; diff --git a/MouseSdk/MouseSdk/BleManager.m b/MouseSdk/MouseSdk/Ble/BleManager.m similarity index 96% rename from MouseSdk/MouseSdk/BleManager.m rename to MouseSdk/MouseSdk/Ble/BleManager.m index 57ee6ed..303523a 100644 --- a/MouseSdk/MouseSdk/BleManager.m +++ b/MouseSdk/MouseSdk/Ble/BleManager.m @@ -25,15 +25,6 @@ @end @implementation BleManager -+(BleManager *)shared{ - static BleManager *manager = nil; - static dispatch_once_t oncePredicate; - dispatch_once(&oncePredicate, ^{ - manager = [[BleManager alloc]init]; - }); - return manager; -} - - (instancetype)init{ self = [super init]; if (self) { @@ -82,11 +73,11 @@ peripheral.delegate = self; [peripheral discoverServices:nil]; - [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil]; } -(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ [self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]]; - [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil]; if(self.didDisConnectDevice){ self.didDisConnectDevice(peripheral); } @@ -187,10 +178,10 @@ // 连接状态改变 - (void)aiMouseConnectStateDidChange:(BOOL)connected{ if (connected) { - [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidConnectBleNotification object:nil]; } else{ - [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kTlwDidDisconnectBleNotification object:nil]; } } #pragma mark - public diff --git a/MouseSdk/MouseSdk/DeviceManger.h b/MouseSdk/MouseSdk/Ble/DeviceManger.h similarity index 73% rename from MouseSdk/MouseSdk/DeviceManger.h rename to MouseSdk/MouseSdk/Ble/DeviceManger.h index 7918f92..108b310 100644 --- a/MouseSdk/MouseSdk/DeviceManger.h +++ b/MouseSdk/MouseSdk/Ble/DeviceManger.h @@ -7,16 +7,26 @@ #import #import "AiMouseSdk.h" +#import "HidManager.h" +#import "BleManager.h" -NS_ASSUME_NONNULL_BEGIN @interface DeviceManger : NSObject +@property (nonatomic,strong) BleManager *ble; +@property (nonatomic,strong) HidManager *hid; + @property (nonatomic, readonly) AiMouseConnectType connectType;//连接类型 @property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调 @property (nonatomic,strong)void (^connectStateCallBack)(AiMouseConnectType connectType,BOOL isConnect);///是否连接成功 +(DeviceManger *)shared; +//开始,使用该sdk需要调用该方法 +- (void)start; + +//停止,停止使用sdk需要调用该方法 +- (void)stop; + /// 切换连接类型 /// - Parameter type: 连接类型 - (void)switchConnect:(AiMouseConnectType)type; @@ -26,4 +36,3 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/DeviceManger.m b/MouseSdk/MouseSdk/Ble/DeviceManger.m similarity index 84% rename from MouseSdk/MouseSdk/DeviceManger.m rename to MouseSdk/MouseSdk/Ble/DeviceManger.m index 77f411b..09d8f74 100644 --- a/MouseSdk/MouseSdk/DeviceManger.m +++ b/MouseSdk/MouseSdk/Ble/DeviceManger.m @@ -6,13 +6,10 @@ // #import "DeviceManger.h" -#import "HidManager.h" -#import "BleManager.h" + @interface DeviceManger() -@property (nonatomic,strong) BleManager *ble; -@property (nonatomic,strong) HidManager *hid; @end @implementation DeviceManger @@ -25,12 +22,22 @@ return manager; } +//开始,使用该sdk需要调用该方法 +- (void)start{ + [self setupBle]; + [self setupHid]; +} + +//停止,停止使用sdk需要调用该方法 +- (void)stop{ + self.ble = nil; + self.hid = nil; +} + - (instancetype)init{ self = [super init]; if (self) { [[AiMouse shared] setLogLevel:AiMouseLogLevel_info]; - [self setupBle]; - [self setupHid]; } return self; } @@ -51,7 +58,7 @@ } - (void)setupHid{ - self.hid = [HidManager shared]; + self.hid = [[HidManager alloc] init]; __weak typeof(self) ws = self; self.hid.authCallBack = ^(BOOL isSuccess) { if(ws.authCallBack)ws.authCallBack(isSuccess); @@ -62,7 +69,7 @@ } - (void)setupBle{ - self.ble = [BleManager shared]; + self.ble = [[BleManager alloc] init]; __weak typeof(self) ws = self; self.ble.authCallBack = ^(BOOL isSuccess) { if(ws.authCallBack)ws.authCallBack(isSuccess); @@ -88,8 +95,8 @@ #pragma mark - notication - (void)setupBleNotification{ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kDidConnectBleNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kDidDisconnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kTlwDidConnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kTlwDidDisconnectBleNotification object:nil]; } - (void)bleDidConnect:(NSNotification *)notification{ diff --git a/MouseSdk/MouseSdk/HidManager.h b/MouseSdk/MouseSdk/Ble/HidManager.h similarity index 95% rename from MouseSdk/MouseSdk/HidManager.h rename to MouseSdk/MouseSdk/Ble/HidManager.h index 427f198..94b62f5 100644 --- a/MouseSdk/MouseSdk/HidManager.h +++ b/MouseSdk/MouseSdk/Ble/HidManager.h @@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic,strong)void (^connectStateCallBack)(BOOL isConnnect);/// 设备是否连上 @property (nonatomic,strong)void (^authCallBack)(BOOL isSuccess);/// 鉴权回调 -+(HidManager *)shared; - /// 获取设备信息 - (void)getDeviceInfo:(void (^)(AiMouseDeviceInfo *deviceInfo,NSError *error))completation; diff --git a/MouseSdk/MouseSdk/HidManager.m b/MouseSdk/MouseSdk/Ble/HidManager.m similarity index 87% rename from MouseSdk/MouseSdk/HidManager.m rename to MouseSdk/MouseSdk/Ble/HidManager.m index 17e3f98..8e78d35 100644 --- a/MouseSdk/MouseSdk/HidManager.m +++ b/MouseSdk/MouseSdk/Ble/HidManager.m @@ -9,6 +9,7 @@ #include #include #import "Config.h" +#import "DeviceManger.h" //供应商id const long vendorID = 0x6675; @@ -24,14 +25,6 @@ const long productID = 0x8601; @end @implementation HidManager -+(HidManager *)shared{ - static HidManager *manager = nil; - static dispatch_once_t oncePredicate; - dispatch_once(&oncePredicate, ^{ - manager = [[HidManager alloc]init]; - }); - return manager; -} - (instancetype)init{ self = [super init]; @@ -51,9 +44,9 @@ const long productID = 0x8601; IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L); if (ret != kIOReturnSuccess) { - NSLog(@"打开设备失败!"); + NSLog(@"打开hid失败!"); }else{ - NSLog(@"打开设备成功!"); + NSLog(@"打开hid成功!"); } //设置匹配设备 NSMutableDictionary* dict= [NSMutableDictionary dictionary]; @@ -78,13 +71,13 @@ const long productID = 0x8601; #pragma mark - Hid CallBack /// 匹配到设备回调 static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { - [[HidManager shared] showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]]; + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]]; uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey)); uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey)); if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ //设备信息 - [HidManager shared].deviceInfoCha = inIOHIDDeviceRef; - [[HidManager shared] connectDevice]; + [DeviceManger shared].hid.deviceInfoCha = inIOHIDDeviceRef; + [[DeviceManger shared].hid connectDevice]; //注册消息回调 unsigned char InputBuffer[128]; IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL); @@ -99,7 +92,7 @@ static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOH } else if(PrimaryUsagePage == 65283 && PrimaryUsage == 0){ //ai - [HidManager shared].aiCha = inIOHIDDeviceRef; + [DeviceManger shared].hid.aiCha = inIOHIDDeviceRef; //注册消息回调 unsigned char InputBuffer[128]; IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL); @@ -116,23 +109,23 @@ static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOH /// 设备移除回调 static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { - [[HidManager shared] showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]]; + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]]; uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey)); uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey)); if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ - [HidManager shared].deviceInfoCha = nil; - [HidManager shared].aiCha = nil; + [DeviceManger shared].hid.deviceInfoCha = nil; + [DeviceManger shared].hid.aiCha = nil; [[AiMouse shared] didDisconnect]; } } static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){ -// [[HidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]]; +// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[AiMouseTools dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]]; NSInteger len = get_data_len(report, reportLength); NSData *data = [[NSData alloc] initWithBytes:report length:len]; -// [[HidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]]; +// [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"[receive] 处理后 %@",[AiMouseTools dataToHexString:data]]]; uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsagePageKey)); uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(sender, CFSTR(kIOHIDPrimaryUsageKey)); @@ -197,7 +190,7 @@ static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDRe // 连接状态改变 - (void)aiMouseConnectStateDidChange:(BOOL)connected{ - [HidManager shared].connectStateCallBack(connected); + [DeviceManger shared].hid.connectStateCallBack(connected); } #pragma mark - public -(void)connectDevice{ @@ -258,7 +251,7 @@ static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStr if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) { Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue); if (!result) { - [[HidManager shared] showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]]; + [[DeviceManger shared].hid showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]]; } } } diff --git a/MouseSdk/MouseSdk/FrkVC.h b/MouseSdk/MouseSdk/FrkVC.h new file mode 100644 index 0000000..26d0149 --- /dev/null +++ b/MouseSdk/MouseSdk/FrkVC.h @@ -0,0 +1,16 @@ +// +// FrkVC.h +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FrkVC : NSViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/FrkVC.m b/MouseSdk/MouseSdk/FrkVC.m new file mode 100644 index 0000000..449d613 --- /dev/null +++ b/MouseSdk/MouseSdk/FrkVC.m @@ -0,0 +1,168 @@ +// +// FrkVC.m +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import "FrkVC.h" +#import "ScanVC.h" +#import "DeviceManger.h" + +@interface FrkVC () +@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态 +@property(nonatomic,strong)NSTextField *connectLabel;///连接状态 +@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态 +@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息 +@end + +@implementation FrkVC + +-(instancetype)init{ + if (self = [super init]) { +// [[DeviceManger shared] start]; + } + return self; +} + +-(void)dealloc{ + [[DeviceManger shared] stop]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view. + [[DeviceManger shared] start]; + [self setupUI]; + [self setupDeviceManger]; +} + +- (void)loadView{ + self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +} + +- (void)setupUI{ + CALayer *viewLayer = [CALayer layer]; + NSView *backgroundView= [[NSView alloc]init]; + [backgroundView setWantsLayer:YES]; + [backgroundView setLayer:viewLayer]; + backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor; + [backgroundView setNeedsDisplay:YES]; + [self.view addSubview:backgroundView]; + + self.title = @"AI SDK"; + { + NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)]; + btn.bordered = true; + btn.title = @"蓝牙扫描"; + [btn setTarget:self]; + [btn setAction:@selector(scanAction)]; + btn.state = 1; + [self.view addSubview:btn]; + } + { + NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)]; + btn.bordered = true; + btn.title = @"获取设备信息"; + [btn setTarget:self]; + [btn setAction:@selector(geviceInfoAction)]; + btn.state = 1; + [self.view addSubview:btn]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"连接模式:--"; + label.backgroundColor = NSColor.clearColor; + self.typeLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"连接状态:未连接"; + label.backgroundColor = NSColor.clearColor; + self.connectLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"鉴权状态:unknow"; + label.backgroundColor = NSColor.clearColor; + self.authLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @""; + label.backgroundColor = NSColor.clearColor; + self.deviceInfoLabel = label; + [self.view addSubview:label]; + } +} + +#pragma mark - action +- (void)scanAction{ + ScanVC *vc = [[ScanVC alloc] initWithNibName:nil bundle:nil]; + [self presentViewControllerAsModalWindow:vc]; +} + +/// 获取设备信息 +- (void)geviceInfoAction{ + [[DeviceManger shared] getDeviceInfo:^(AiMouseDeviceInfo *deviceInfo, NSError *error) { + if (deviceInfo) { + NSString *info = deviceInfo.description; + dispatch_async(dispatch_get_main_queue(), ^{ + self.deviceInfoLabel.stringValue = info; + }); + } + else{ + NSString *info = [NSString stringWithFormat:@"error == %@",error.description]; + NSLog(@"%@",info); + dispatch_async(dispatch_get_main_queue(), ^{ + self.deviceInfoLabel.stringValue = info; + }); + } + }]; +} +- (void)showAuthResult:(BOOL)isSuccess{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.authLabel.stringValue = isSuccess?@"鉴权状态:鉴权成功":@"鉴权状态:鉴权失败"; + if(isSuccess){ + [self geviceInfoAction]; + } + }); +} +- (void)connectStateDidChange:(AiMouseConnectType)type connectState:(BOOL)isConnected{ + dispatch_async(dispatch_get_main_queue(), ^{ + if(isConnected){ + self.typeLabel.stringValue = type == AiMouseConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid"; + self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接"; + [self geviceInfoAction]; + } + else{ + self.typeLabel.stringValue = @"连接模式:--"; + self.connectLabel.stringValue = @"连接状态:未连接"; + } + }); +} + +- (void)setupDeviceManger{ + DeviceManger *manager = [DeviceManger shared]; + __weak typeof(self) ws = self; + manager.authCallBack = ^(BOOL isSuccess) { + [ws showAuthResult:isSuccess]; + }; + manager.connectStateCallBack = ^(AiMouseConnectType type,BOOL isConnected) { + [ws connectStateDidChange:type connectState:isConnected]; + }; +} + +@end diff --git a/MouseSdk/MouseSdk/MouseSdkManager.h b/MouseSdk/MouseSdk/MouseSdkManager.h new file mode 100644 index 0000000..e984e2e --- /dev/null +++ b/MouseSdk/MouseSdk/MouseSdkManager.h @@ -0,0 +1,24 @@ +// +// MouseSdkManager.h +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import + +typedef NS_ENUM(NSInteger,MouseSdkType) { + TlwMouseLogLevel_frk = 1,//富瑞坤 + TlwMouseLogLevel_tlw = 2,//泰凌微 +}; + +NS_ASSUME_NONNULL_BEGIN + + +@interface MouseSdkManager : NSObject +@property (nonatomic,assign)MouseSdkType type;//默认是富瑞坤 + ++(MouseSdkManager *)shared; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/MouseSdkManager.m b/MouseSdk/MouseSdk/MouseSdkManager.m new file mode 100644 index 0000000..ec8fda1 --- /dev/null +++ b/MouseSdk/MouseSdk/MouseSdkManager.m @@ -0,0 +1,27 @@ +// +// MouseSdkManager.m +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import "MouseSdkManager.h" + +@implementation MouseSdkManager ++(MouseSdkManager *)shared{ + static MouseSdkManager *manager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + manager = [[MouseSdkManager alloc]init]; + }); + return manager; +} + +- (instancetype)init{ + self = [super init]; + if (self) { + self.type = TlwMouseLogLevel_frk; + } + return self; +} +@end diff --git a/MouseSdk/MouseSdk/ScanVC.m b/MouseSdk/MouseSdk/ScanVC.m index 75ad42d..d63b398 100644 --- a/MouseSdk/MouseSdk/ScanVC.m +++ b/MouseSdk/MouseSdk/ScanVC.m @@ -7,7 +7,7 @@ #import "ScanVC.h" #import "ScanCell.h" -#import "BleManager.h" +#import "DeviceManger.h" @interface ScanVC () @property (nonatomic,strong) NSTableView *tableView; @property (nonatomic,strong) NSMutableArray *scanArray; @@ -67,7 +67,7 @@ } - (void)setupBleManger{ - BleManager *manager = [BleManager shared]; + BleManager *manager = [DeviceManger shared].ble; __weak typeof(self) weakSelf= self; __weak typeof(BleManager) *weakManager = manager; manager.discoverDevice = ^(CBPeripheral *peripheral){ @@ -87,11 +87,11 @@ } - (void)tapDevice:(CBPeripheral *)peripheral{ - [[BleManager shared] connectPeripheral:peripheral]; + [[DeviceManger shared].ble connectPeripheral:peripheral]; } - (void)connectSuccess{ - [[BleManager shared] stopScanDevice]; + [[DeviceManger shared].ble stopScanDevice]; dispatch_async(dispatch_get_main_queue(), ^{ NSAlert *alert = [NSAlert new]; [alert addButtonWithTitle:@"确定"]; @@ -108,17 +108,6 @@ } -(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{ -// NSString *columnID = tableColumn.identifier; -// NSString *strIdt = @"NSTableCellView"; -// NSTableCellView *cell = [tableView makeViewWithIdentifier:strIdt owner:self]; -// if (!cell) { -// cell = [[NSTableCellView alloc]init]; -// cell.identifier = strIdt; -// } -// cell.wantsLayer = YES; -// cell.textField.stringValue = [NSString stringWithFormat:@"cell %ld",(long)row]; -// -// return cell; ScanCell *cell = [[ScanCell alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)]; cell.peripheral = self.scanArray[row]; __weak typeof(self) weakSelf = self; diff --git a/MouseSdk/MouseSdk/Tlw/TlwBleManager.h b/MouseSdk/MouseSdk/Tlw/TlwBleManager.h new file mode 100644 index 0000000..f93737a --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwBleManager.h @@ -0,0 +1,46 @@ +// +// TlwBleManager.h +// AliIotSdkDemo +// +// Created by incar on 2024/1/30. +// + +#import +#import +#import "TlwMouseSdk.h" + +#define kDidConnectBleNotification @"kDidConnectBleNotification" +#define kDidDisconnectBleNotification @"kDidDisconnectBleNotification" + + + +@interface TlwBleManager : NSObject +@property (nonatomic,strong)void (^discoverDevice)(CBPeripheral *peripheral) ;/// 扫描到设备 +@property (nonatomic,strong)void (^didConnectDevice)(CBPeripheral *peripheral);/// 连接成功 +@property (nonatomic,strong)void (^didDisConnectDevice)(CBPeripheral *peripheral);/// 断开连接 +/// 单利 ++(TlwBleManager *)shared; + +/// 扫描 +- (void)startScanDevice; + +/// 停止扫描 +- (void)stopScanDevice; + +/// 连接设备 +/// - Parameter peripheral: 蓝牙设备 +- (void)connectPeripheral:(CBPeripheral *)peripheral; + +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished; + +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; +@end + diff --git a/MouseSdk/MouseSdk/Tlw/TlwBleManager.m b/MouseSdk/MouseSdk/Tlw/TlwBleManager.m new file mode 100644 index 0000000..a815010 --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwBleManager.m @@ -0,0 +1,267 @@ +// +// TlwBleManager.m +// AliIotSdkDemo +// +// Created by incar on 2024/1/30. +// + +#import "TlwBleManager.h" +#import "TlwMouseSdk.h" +#import "Config.h" + +// 服务id +#define kServiceUUID @"FFF0" +//ai特征 +#define kAiUUID @"00010203-0405-0607-0809-0A0B0C0D2B18" +//写设备信息特征 +#define kSetDeviceInfoUUID @"FFF1" +//通知设备信息特征 +#define kNotifyDeviceInfoUUID @"FFF2" + +@interface TlwBleManager () +@property(nonatomic, strong) CBCentralManager *centralManager;///蓝牙管理 +@property(nonatomic, strong) CBPeripheral *connectPeripheral;//连接的设备 +@property(nonatomic, strong) CBCharacteristic *deviceInfoCha;//设备信息特征 +@property (nonatomic,strong) NSMutableData *recordData;//录音的数据 +@end + +@implementation TlwBleManager ++(TlwBleManager *)shared{ + static TlwBleManager *manager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + manager = [[TlwBleManager alloc]init]; + }); + return manager; +} + +- (instancetype)init{ + self = [super init]; + if (self) { + self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil]; + } + return self; +} + +#pragma mark - CBCentralManagerDelegate +- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central { + [self showLog:[NSString stringWithFormat:@"蓝牙状态 = %@",@(central.state)]]; + switch (central.state) { + case CBManagerStateUnknown: + + break; + case CBManagerStateResetting: + + break; + case CBManagerStateUnsupported: + + break; + case CBManagerStateUnauthorized: + + break; + case CBManagerStatePoweredOff: + + break; + case CBManagerStatePoweredOn: + [self autoConnectDevice]; + break; + } +} + +- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ +// [self showLog:[NSString stringWithFormat:@"扫描到设备 = %@, data = %@",peripheral,advertisementData]]; + if(self.discoverDevice){ + self.discoverDevice(peripheral); + } +} + +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ + [self showLog:[NSString stringWithFormat:@"连接成功 name = %@",peripheral.name]]; + if(self.didConnectDevice){ + self.didConnectDevice(peripheral); + } + peripheral.delegate = self; + [peripheral discoverServices:nil]; + + [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil]; +} +-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ + [self showLog:[NSString stringWithFormat:@"断开连接。。。%@",error.description]]; + [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil]; + if(self.didDisConnectDevice){ + self.didDisConnectDevice(peripheral); + } +// [self aiMouseConnectStateDidChange:false]; + [[TlwMouse shared] didDisconnect]; +} +- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{ + [self showLog:[NSString stringWithFormat:@"连接失败。。。%@",error.description]]; +} +#pragma mark - CBPeripheralDelegate +//发现服务 +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { + for (CBService *service in peripheral.services){ + [peripheral discoverCharacteristics:nil forService:service]; + } +} +// 发现特征 +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { + for (CBCharacteristic *characteristic in service.characteristics){ + if ([characteristic.UUID.UUIDString isEqualToString:kAiUUID]) { + //语音特征 + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; + } + else if ([characteristic.UUID.UUIDString isEqualToString:kNotifyDeviceInfoUUID]) { + //通知设备信息特征 + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; + } + else if ([characteristic.UUID.UUIDString isEqualToString:kSetDeviceInfoUUID]) { + //设备信息特征 + self.deviceInfoCha = characteristic; + //切换代理 + [TlwMouse shared].delegate = self; + //切换当前连接类型 + [[TlwMouse shared] switchConnect:TlwConnectType_bluetooth]; + } + } +} +//收到蓝牙数据 +- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { + NSData *data = characteristic.value; + if([characteristic.UUID.UUIDString isEqualToString:kAiUUID]){ + //AI数据 + [[TlwMouse shared] sendAiDataToSdk:data]; + } + else if([characteristic.UUID.UUIDString isEqualToString:kNotifyDeviceInfoUUID]){ + //设备信息数据 + [[TlwMouse shared] sendDeviceDataToSdk:data]; + } +} + +#pragma mark - AiDelegate + +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +-(void)tlwMouseSendDeviceInfoData:(NSData *)data{ + [self.connectPeripheral writeValue:data forCharacteristic:self.deviceInfoCha type:CBCharacteristicWriteWithResponse]; +} + +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音) +/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音) +/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音) +/// 其他未定义 +- (void)tlwMouseSendCmdData:(NSData *)data{ + uint8 cmd; + [data getBytes:&cmd length:1]; + + if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){ + [self showLog:@"开始录音"]; + self.recordData = [[NSMutableData alloc] init]; + } + else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){ + [self showLog:@"结束录音"]; + [self finishRecored:self.recordData]; + } +} + +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)tlwMouseSendRecordData:(NSData *)data{ + [self.recordData appendData:data]; +} +/// 输出日志 +- (void)aiMouseAddLog:(NSString *)log { +// [self showLog:log]; +} + +// 连接状态改变 +- (void)aiMouseConnectStateDidChange:(BOOL)connected{ + if (connected) { + [[NSNotificationCenter defaultCenter] postNotificationName:kDidConnectBleNotification object:nil]; + } + else{ + [[NSNotificationCenter defaultCenter] postNotificationName:kDidDisconnectBleNotification object:nil]; + } +} +#pragma mark - public +/// 连接设备 +- (void)startScanDevice{ + [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO]}]; +} + +/// 扫描设备 +- (void)stopScanDevice{ + [self.centralManager stopScan]; +} + +/// 连接设备 +- (void)connectPeripheral:(CBPeripheral *)peripheral{ + self.connectPeripheral = peripheral; + [self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : [NSNumber numberWithBool:YES]}]; +} + +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo * _Nonnull, NSError * _Nonnull))finished{ + [[TlwMouse shared] getDeviceInfo:finished]; +} + +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] setDPI:value completation:finished]; +} + +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] openRecord:finished]; +} + +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] closeRecord:finished]; +} +#pragma mark - other +//自动连接,获取已配对的蓝牙,取第一个连接 +- (void)autoConnectDevice{ + //如果是已连接或者正在连接,则不再重连 + if (self.connectPeripheral && (self.connectPeripheral.state == CBPeripheralStateConnected || self.connectPeripheral.state == CBPeripheralStateConnecting)) { + return; + } + NSArray *devices = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]]]; + if (devices.count > 0) { + CBPeripheral *peripheral = devices[0]; + [self showLog:[NSString stringWithFormat:@"匹配过的设备%@",peripheral.name]]; + [self connectPeripheral:peripheral]; + } + // 10s自动重连 + [self performSelector:@selector(autoConnectDevice) withObject:nil afterDelay:10]; +} +- (void)showLog:(NSString *)log{ + NSLog(@"%@",log); +} + +//将录音数据保存 +- (void)finishRecored:(NSData *)data{ + NSString *doc = NSTemporaryDirectory(); + NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc]; + NSError *err; + [data writeToFile:fullPath options:NSDataWritingAtomic error:&err]; + if (err) { + [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]]; + } + else{ + [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]]; + } +} + +- (NSString *)dataToHexString:(NSData *)data { + NSUInteger length = data.length; + NSMutableString *result = [NSMutableString stringWithCapacity:length * 2]; + const unsigned char *byte = data.bytes; + for (int i = 0; i < length; i++, byte++) { + [result appendFormat:@"%02X", *byte]; + } + return result; +} +@end diff --git a/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.h b/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.h new file mode 100644 index 0000000..c3641a0 --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.h @@ -0,0 +1,36 @@ +// +// TlwDeviceManger.h +// MouseSdk +// +// Created by incar on 2024/3/30. +// + +#import +#import "TlwMouseSdk.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwDeviceManger : NSObject +@property (nonatomic, readonly) TlwConnectType connectType;//连接类型 +@property (nonatomic,strong)void (^connectStateCallBack)(TlwConnectType connectType,BOOL isConnect);///是否连接成功 + ++(TlwDeviceManger *)shared; + +/// 切换连接类型 +/// - Parameter type: 连接类型 +- (void)switchConnect:(TlwConnectType)type; + +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished; + +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.m b/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.m new file mode 100644 index 0000000..5f5c3b4 --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwDeviceManger.m @@ -0,0 +1,125 @@ +// +// TlwDeviceManger.m +// MouseSdk +// +// Created by incar on 2024/3/30. +// + +#import "TlwDeviceManger.h" +#import "TlwHidManager.h" +#import "TlwBleManager.h" + +@interface TlwDeviceManger() + +@property (nonatomic,strong) TlwBleManager *ble; +@property (nonatomic,strong) TlwHidManager *hid; +@end + +@implementation TlwDeviceManger ++(TlwDeviceManger *)shared{ + static TlwDeviceManger *manager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + manager = [[TlwDeviceManger alloc]init]; + }); + return manager; +} + +- (instancetype)init{ + self = [super init]; + if (self) { + [[TlwMouse shared] setLogLevel:TlwMouseLogLevel_info]; + [self setupBle]; + [self setupHid]; + } + return self; +} + +-(TlwConnectType)connectType{ + return [TlwMouse shared].connectType; +} + +- (void)switchConnect:(TlwConnectType)type{ + [[TlwMouse shared] switchConnect:type]; + + if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) { + [self setupBle]; + } + else{ + [self setupHid]; + } +} + +- (void)setupHid{ + self.hid = [TlwHidManager shared]; + __weak typeof(self) ws = self; + self.hid.connectStateCallBack = ^(BOOL isConnnect) { + if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_hid,isConnnect); + }; +} + +- (void)setupBle{ + self.ble = [TlwBleManager shared]; + __weak typeof(self) ws = self; + self.ble.didConnectDevice = ^(CBPeripheral *peripheral) { + if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_bluetooth,true); + }; + self.ble.didDisConnectDevice = ^(CBPeripheral *peripheral) { + if(ws.connectStateCallBack)ws.connectStateCallBack(TlwConnectType_bluetooth,false); + }; + [self setupBleNotification]; +} + +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished{ + if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) { + [self.ble getDeviceInfo:finished]; + } + else{ + [self.hid getDeviceInfo:finished]; + } +} + +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{ + if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) { + [self.ble setDPI:value completation:finished]; + } + else{ + [self.hid setDPI:value completation:finished]; + } +} + +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) { + [self.ble openRecord:finished]; + } + else{ + [self.hid openRecord:finished]; + } +} +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + if ([TlwMouse shared].connectType == TlwConnectType_bluetooth) { + [self.ble closeRecord:finished]; + } + else{ + [self.hid closeRecord:finished]; + } +} +#pragma mark - notication +- (void)setupBleNotification{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidConnect:) name:kDidConnectBleNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bleDidDisconnect:) name:kDidDisconnectBleNotification object:nil]; +} + +- (void)bleDidConnect:(NSNotification *)notification{ + if(self.connectStateCallBack)self.connectStateCallBack(TlwConnectType_bluetooth,true); +} + +- (void)bleDidDisconnect:(NSNotification *)notification{ + if(self.connectStateCallBack)self.connectStateCallBack(TlwConnectType_bluetooth,false); + [[TlwMouse shared] switchConnect:TlwConnectType_hid]; +} + +@end diff --git a/MouseSdk/MouseSdk/Tlw/TlwHidManager.h b/MouseSdk/MouseSdk/Tlw/TlwHidManager.h new file mode 100644 index 0000000..76407ec --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwHidManager.h @@ -0,0 +1,33 @@ +// +// TlwHidManager.h +// MouseSdk +// +// Created by incar on 2024/3/26. +// + +#import +#import "TlwMouseSdk.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwHidManager : NSObject +@property (nonatomic,strong)void (^connectStateCallBack)(BOOL isConnnect);/// 设备是否连上 + ++(TlwHidManager *)shared; + +- (void)showLog:(NSString *)log; + +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo *deviceInfo,NSError *error))finished; + +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished; + +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished; +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/Tlw/TlwHidManager.m b/MouseSdk/MouseSdk/Tlw/TlwHidManager.m new file mode 100644 index 0000000..721653a --- /dev/null +++ b/MouseSdk/MouseSdk/Tlw/TlwHidManager.m @@ -0,0 +1,256 @@ +// +// HidManager.m +// MouseSdk +// +// Created by incar on 2024/3/26. +// + +#import "TlwHidManager.h" +#include +#include +#import "Config.h" + + + +//供应商id +//const long vendorID = 0x6675; +//const long productID = 0x8601; + +@interface TlwHidManager () + +@property(nonatomic, assign) IOHIDManagerRef hidManager;///hid管理 +@property(nonatomic, assign) IOHIDDeviceRef deviceInfoCha;//设备信息通道 +@property (nonatomic,strong) NSMutableData *recordData;//录音的数据 +@end + +@implementation TlwHidManager ++(TlwHidManager *)shared{ + static TlwHidManager *manager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + manager = [[TlwHidManager alloc]init]; + }); + return manager; +} + +- (instancetype)init{ + self = [super init]; + if (self) { + [self initHid]; + [self open]; + } + return self; +} + +- (void)initHid{ + +} + +- (void)open{ + self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerScheduleWithRunLoop(self.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L); + if (ret != kIOReturnSuccess) { + NSLog(@"打开设备失败!"); + }else{ + NSLog(@"打开设备成功!"); + } + //设置匹配设备 + //供应商id + const long vendorID = 0x10D6; + //产品id + const long productID = 0xB008; + NSMutableDictionary* dict= [NSMutableDictionary dictionary]; + [dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]]; + [dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]]; + IOHIDManagerSetDeviceMatching(self.hidManager, (__bridge CFMutableDictionaryRef)dict); + //设置匹配到设备回调 + IOHIDManagerRegisterDeviceMatchingCallback(self.hidManager, &hidAppearDevice, NULL); + //设置移除设备回调 + IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, &hidDisAppearDevice, NULL); +} + +- (void)close{ + IOReturn ret = IOHIDManagerClose(self.hidManager, 0); + if (ret != kIOReturnSuccess) { + NSLog(@"关闭设备失败!"); + }else{ + NSLog(@"关闭设备成功!"); + } +} + +#pragma mark - Hid CallBack +/// 匹配到设备回调 +static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { + [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"发现设备 %@\n ",inIOHIDDeviceRef]]; + [TlwHidManager shared].deviceInfoCha = inIOHIDDeviceRef; + [[TlwHidManager shared] connectDevice]; + //注册消息回调 + unsigned char InputBuffer[64]; + IOHIDDeviceRegisterInputReportCallback(inIOHIDDeviceRef, InputBuffer, sizeof(InputBuffer), hidReceiveData, NULL); + IOReturn result = IOHIDDeviceOpen(inIOHIDDeviceRef, kIOHIDOptionsTypeNone); + if (result != kIOReturnSuccess) { + NSLog([NSString stringWithFormat:@"打开设备信息失败! %d",result]); + }else{ + NSLog(@"打开设备信息成功!"); + } + CFRetain(inIOHIDDeviceRef); + IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); +} + +/// 设备移除回调 +static void hidDisAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) { + [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"拔出设备 %@ ",inIOHIDDeviceRef]]; + uint32_t PrimaryUsagePage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey)); + uint32_t PrimaryUsage = IOHIDDevGetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey)); + + if(PrimaryUsagePage == 1 && PrimaryUsage == 0){ + [TlwHidManager shared].deviceInfoCha = nil; + [[TlwMouse shared] didDisconnect]; + } +} + +static void hidReceiveData(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength){ + if (report[0] == 0x01 || report[0] == 0x02) { + //鼠标键盘活动事件 + return; + } + [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"[receive] 处理前 %@",[[TlwHidManager shared] dataToHexString:[[NSData alloc] initWithBytes:report length:reportLength]]]]; + + NSInteger len = get_data_len(report, reportLength); + NSData *data = [[NSData alloc] initWithBytes:report length:len]; + [[TlwMouse shared] sendDeviceDataToSdk:data]; +} + +#pragma mark - AiDelegate +/// 设备信息数据回调,需要将此数据通过设备信息特征转发给蓝牙设备 +/// - Parameter data: 数据 +-(void)tlwMouseSendDeviceInfoData:(NSData *)data{ + [self sendData:data toDevice:self.deviceInfoCha]; +} + +/// 鼠标按键指令回调 +/// - Parameter data: 数据。 +/// 前 单击0x06, 按下0x13(开始录音)抬起0x14(结束录音) +/// 中 单击0x03, 按下0x11(开始录音)抬起0x12(结束录音) +/// 后 第一次单击0x01, 第二次单击0x02(结束录音) , 按下0x09(开始录音)抬起0x10(结束录音) +/// 其他未定义 +- (void)tlwMouseSendCmdData:(NSData *)data{ + uint8 cmd; + [data getBytes:&cmd length:1]; + + if(cmd == 0x13 || cmd == 0x11 || cmd == 0x01 ||cmd == 0x09){ + [self showLog:@"开始录音"]; + self.recordData = [[NSMutableData alloc] init]; + } + else if(cmd == 0x14 || cmd == 0x12 || cmd == 0x02 ||cmd == 0x10){ + [self showLog:@"结束录音"]; + [self finishRecored:self.recordData]; + } +} +/// 录音回调 +/// - Parameter data: pcm格式语音数据 +- (void)tlwMouseSendRecordData:(NSData *)data{ + [self.recordData appendData:data]; +} +/// 输出日志 +- (void)aiMouseAddLog:(NSString *)log { +// [self showLog:log]; +} + +// 连接状态改变 +- (void)aiMouseConnectStateDidChange:(BOOL)connected{ + [TlwHidManager shared].connectStateCallBack(connected); +} + +#pragma mark - public +/// 获取设备信息 +- (void)getDeviceInfo:(void (^)(TlwDeviceInfo * _Nonnull, NSError * _Nonnull))completation{ + [[TlwMouse shared] getDeviceInfo:completation]; +} +/// setDPI +- (void)setDPI:(int)value completation:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] setDPI:value completation:finished]; +} +/// 打开录音 +- (void)openRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] openRecord:finished]; +} +/// 停止录音 +- (void)closeRecord:(void (^)(BOOL isSuccess,NSError *error))finished{ + [[TlwMouse shared] closeRecord:finished]; +} + +#pragma mark - other +-(void)connectDevice{ + //设置代理 + [TlwMouse shared].delegate = self; + //切换当前连接类型 + [[TlwMouse shared] switchConnect:TlwConnectType_hid]; +} + +/// 发送数据给设备 +-(void)sendData:(NSData *)data toDevice:(IOHIDDeviceRef)device{ + if(device == nil){ + [self showLog:@"请先连上设备"]; + return; + } + + IOReturn ret = IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, 0, data.bytes, data.length); + if (ret != kIOReturnSuccess) { + [self showLog:[NSString stringWithFormat:@"数据发送失败! code = %d",ret]]; + }else{ + [self showLog:@"数据发送成功!"]; + } +} + +//将录音数据保存 +- (void)finishRecored:(NSData *)data{ + NSString *doc = NSTemporaryDirectory(); + NSString *fullPath = [NSString stringWithFormat:@"%@1.pcm",doc]; + NSError *err; + [data writeToFile:fullPath options:NSDataWritingAtomic error:&err]; + if (err) { + [self showLog:[NSString stringWithFormat:@"录音保存失败 %@",err.description]]; + } + else{ + [self showLog:[NSString stringWithFormat:@"录音保存成功 path = %@",fullPath]]; + } +} +- (void)showLog:(NSString *)log{ + NSLog(@"%@",log); +} + + +/// 实际到数据长度 +static inline int get_data_len(uint8_t *buf, u_long len) { + if (len <= 0 ) return 0; + int url_len = 0; + while (buf[url_len] != 0x00) url_len++; + return url_len; +} + +/// 获取对应属性值 +static uint32_t IOHIDDevGetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey) { + uint32_t outValue = 0; + if (inIOHIDDeviceRef != NULL) { + CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); + if (tCFTypeRef != NULL && (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) ) { + Boolean result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, &outValue); + if (!result) { + [[TlwHidManager shared] showLog:[NSString stringWithFormat:@"get %@ failed.", inKey]]; + } + } + } + return outValue; +} +- (NSString *)dataToHexString:(NSData *)data { + NSUInteger length = data.length; + NSMutableString *result = [NSMutableString stringWithCapacity:length * 2]; + const unsigned char *byte = data.bytes; + for (int i = 0; i < length; i++, byte++) { + [result appendFormat:@"%02X", *byte]; + } + return result; +} +@end diff --git a/MouseSdk/MouseSdk/TlwScanVC.h b/MouseSdk/MouseSdk/TlwScanVC.h new file mode 100644 index 0000000..4be9c77 --- /dev/null +++ b/MouseSdk/MouseSdk/TlwScanVC.h @@ -0,0 +1,16 @@ +// +// TlwScanVC.h +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwScanVC : NSViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/TlwScanVC.m b/MouseSdk/MouseSdk/TlwScanVC.m new file mode 100644 index 0000000..603c230 --- /dev/null +++ b/MouseSdk/MouseSdk/TlwScanVC.m @@ -0,0 +1,124 @@ +// +// TlwScanVC.m +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import "TlwScanVC.h" + +#import "ScanCell.h" +#import "TlwDeviceManger.h" +@interface TlwScanVC () +@property (nonatomic,strong) NSTableView *tableView; +@property (nonatomic,strong) NSMutableArray *scanArray; +@end + +@implementation TlwScanVC +-(NSMutableArray *)scanArray{ + if (_scanArray == nil) { + _scanArray = [[NSMutableArray alloc] init]; + } + return _scanArray; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do view setup here. + [self setupUI]; + [self setupBleManger]; +} +- (void)loadView{ + self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +} + +- (void)setupUI{ + CALayer *viewLayer = [CALayer layer]; + NSView *backgroundView= [[NSView alloc]init]; + [backgroundView setWantsLayer:YES]; + [backgroundView setLayer:viewLayer]; + backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor; + [backgroundView setNeedsDisplay:YES]; + [self.view addSubview:backgroundView]; + + //实现tableview的滚动效果 + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:self.view.bounds]; + scrollView.backgroundColor = [NSColor redColor]; + [scrollView setDrawsBackground:NO];//不画背景(背景默认画成白色) + [scrollView setHasVerticalScroller:YES];//有垂直滚动条 + //[_tableContainer setHasHorizontalScroller:YES]; //有水平滚动条 + scrollView.autohidesScrollers = YES;//自动隐藏滚动条(滚动的时候出现) + [self.view addSubview:scrollView]; + + self.tableView = [[NSTableView alloc] initWithFrame:scrollView.bounds]; + [scrollView setDocumentView:self.tableView]; + + NSTableColumn *column1 = [[NSTableColumn alloc] initWithIdentifier:@"columnFrist"]; + column1.title = @"设备名称"; + [column1 setWidth:self.tableView.bounds.size.width]; + [self.tableView addTableColumn:column1]; + +// self.tableView.focusRingType = NSFocusRingTypeNone;//tableview获得焦点时的风格 +// self.tableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;//行高亮的风格 +// self.tableView.backgroundColor = [NSColor greenColor]; +// self.tableView.usesAlternatingRowBackgroundColors = YES; //背景颜色的交替,一行白色,一行灰色。设置后,原来设置的 backgroundColor 就无效了。 + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.gridStyleMask = NSTableViewSolidHorizontalGridLineMask; +} + +- (void)setupBleManger{ +// TlwBleManager *manager = [TlwDeviceManger shared].ble; +// __weak typeof(self) weakSelf= self; +// manager.discoverDevice = ^(CBPeripheral *peripheral){ +// if ([weakSelf.scanArray containsObject:peripheral]){ +// return; +// } +// if(peripheral.name.length == 0){ +// return; +// } +// [weakSelf.scanArray addObject:peripheral]; +// [weakSelf.tableView reloadData]; +// }; +// manager.didConnectDevice = ^(CBPeripheral * _Nonnull peripheral) { +// [weakSelf connectSuccess]; +// }; +// [manager startScanDevice]; +} + +- (void)tapDevice:(CBPeripheral *)peripheral{ +// [[TlwDeviceManger shared].ble connectPeripheral:peripheral]; +} + +- (void)connectSuccess{ +// [[TlwDeviceManger shared].ble stopScanDevice]; +// dispatch_async(dispatch_get_main_queue(), ^{ +// NSAlert *alert = [NSAlert new]; +// [alert addButtonWithTitle:@"确定"]; +// [alert setMessageText:@"连接成功"]; +// [alert setAlertStyle:NSAlertStyleInformational]; +// [alert beginSheetModalForWindow:[self.view window] completionHandler:^(NSModalResponse returnCode) { +// [self dismissViewController:self]; +// }]; +// }); +} +#pragma mark - NSTableViewDelegate,NSTableViewDataSource +-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{ + return self.scanArray.count; +} + +-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{ + ScanCell *cell = [[ScanCell alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)]; + cell.peripheral = self.scanArray[row]; + __weak typeof(self) weakSelf = self; + cell.connecBtnActionCallBack = ^(CBPeripheral * _Nonnull peripheral) { + [weakSelf tapDevice:peripheral]; + }; + return cell; +} +-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row{ + return 44; +} + +@end + diff --git a/MouseSdk/MouseSdk/TlwScanVC.xib b/MouseSdk/MouseSdk/TlwScanVC.xib new file mode 100644 index 0000000..76e313c --- /dev/null +++ b/MouseSdk/MouseSdk/TlwScanVC.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/MouseSdk/MouseSdk/TlwVC.h b/MouseSdk/MouseSdk/TlwVC.h new file mode 100644 index 0000000..e5f0f4d --- /dev/null +++ b/MouseSdk/MouseSdk/TlwVC.h @@ -0,0 +1,16 @@ +// +// TlwVC.h +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TlwVC : NSViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/MouseSdk/MouseSdk/TlwVC.m b/MouseSdk/MouseSdk/TlwVC.m new file mode 100644 index 0000000..03659fb --- /dev/null +++ b/MouseSdk/MouseSdk/TlwVC.m @@ -0,0 +1,192 @@ +// +// TlwVC.m +// MouseSdk +// +// Created by incar on 2024/8/21. +// + +#import "TlwVC.h" +#import "TlwScanVC.h" +#import "TlwDeviceManger.h" + +@interface TlwVC () +@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态 +@property(nonatomic,strong)NSTextField *connectLabel;///连接状态 +@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态 +@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息 +@end + +@implementation TlwVC + +//-(instancetype)init{ +// if (self = [super init]) { +// +// } +// return self; +//} + +-(void)dealloc{ +// [[TlwDeviceManger shared] stop]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view. + + [self setupUI]; + [self setupTlwDeviceManger]; +} + +- (void)loadView{ + self.view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +} + +- (void)setupUI{ + CALayer *viewLayer = [CALayer layer]; + NSView *backgroundView= [[NSView alloc]init]; + [backgroundView setWantsLayer:YES]; + [backgroundView setLayer:viewLayer]; + backgroundView.layer.backgroundColor = [NSColor greenColor].CGColor; + [backgroundView setNeedsDisplay:YES]; + [self.view addSubview:backgroundView]; + self.title = @"AI SDK"; + { + NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)]; + btn.bordered = true; + btn.title = @"蓝牙扫描"; + [btn setTarget:self]; + [btn setAction:@selector(scanAction)]; + btn.state = 1; + [self.view addSubview:btn]; + } + { + NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)]; + btn.bordered = true; + btn.title = @"选择指令"; + [btn setTarget:self]; + [btn setAction:@selector(showPopupMenu:)]; + + btn.state = 1; + [self.view addSubview:btn]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"连接模式:--"; + label.backgroundColor = NSColor.clearColor; + self.typeLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"连接状态:未连接"; + label.backgroundColor = NSColor.clearColor; + self.connectLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @"鉴权状态:unknow"; + label.backgroundColor = NSColor.clearColor; + self.authLabel = label; + [self.view addSubview:label]; + } + { + NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)]; + label.editable = NO; + label.bordered = NO; + label.stringValue = @""; + label.backgroundColor = NSColor.clearColor; + self.deviceInfoLabel = label; + [self.view addSubview:label]; + } +} + +#pragma mark - action +- (void)scanAction{ + TlwScanVC *vc = [[TlwScanVC alloc] initWithNibName:nil bundle:nil]; + [self presentViewControllerAsModalWindow:vc]; +} + +- (void)connectStateDidChange:(TlwConnectType)type connectState:(BOOL)isConnected{ + dispatch_async(dispatch_get_main_queue(), ^{ + if(isConnected){ + self.typeLabel.stringValue = type == TlwConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid"; + self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接"; + } + else{ + self.typeLabel.stringValue = @"连接模式:--"; + self.connectLabel.stringValue = @"连接状态:未连接"; + } + }); +} + +- (void)setupTlwDeviceManger{ + TlwDeviceManger *manager = [TlwDeviceManger shared]; +// [manager start]; + __weak typeof(self) ws = self; + manager.connectStateCallBack = ^(TlwConnectType type,BOOL isConnected) { + [ws connectStateDidChange:type connectState:isConnected]; + }; +} + + +- (void)showPopupMenu:(NSButton *)sender { + NSMenu *popupMenu = [[NSMenu alloc] initWithTitle:@"Popup Menu"]; + NSMenuItem *item1 = [[NSMenuItem alloc] initWithTitle:@"语音开启" action:@selector(voiceStart:) keyEquivalent:@"1"]; + NSMenuItem *item2 = [[NSMenuItem alloc] initWithTitle:@"语音停止" action:@selector(voiceStop:) keyEquivalent:@"2"]; + NSMenuItem *item3 = [[NSMenuItem alloc] initWithTitle:@"设置DPI" action:@selector(setDPI:) keyEquivalent:@"3"]; + NSMenuItem *item4 = [[NSMenuItem alloc] initWithTitle:@"鼠标状态查询" action:@selector(searchState:) keyEquivalent:@"3"]; +// NSMenuItem *item5 = [[NSMenuItem alloc] initWithTitle:@"向鼠标告知APP下载地址" action:@selector(setDownLoadURL:) keyEquivalent:@"3"]; +// NSMenuItem *item8 = [[NSMenuItem alloc] initWithTitle:@":读取鼠标序列号" action:@selector(getSN:) keyEquivalent:@"3"]; +// NSMenuItem *item9 = [[NSMenuItem alloc] initWithTitle:@":发送弹UR开关命令" action:@selector(sendUR:) keyEquivalent:@"3"]; + + [popupMenu addItem:item1]; + [popupMenu addItem:item2]; + [popupMenu addItem:item3]; + [popupMenu addItem:item4]; + + NSPoint buttonLocation = [sender convertPoint:NSZeroPoint toView:self.view]; + [popupMenu popUpMenuPositioningItem:nil atLocation:buttonLocation inView:self.view]; +} + +- (void)voiceStart:(NSMenuItem*)sender { + [[TlwDeviceManger shared] openRecord:^(BOOL isSuccess, NSError * _Nonnull error) { + + }]; +} +- (void)voiceStop:(NSMenuItem*)sender { + [[TlwDeviceManger shared] closeRecord:^(BOOL isSuccess, NSError * _Nonnull error) { + + }]; +} +- (void)setDPI:(NSMenuItem*)sender { + [[TlwDeviceManger shared] setDPI:2 completation:^(BOOL isSuccess, NSError * _Nonnull error) { + + }]; +} +- (void)searchState:(NSMenuItem*)sender { + [[TlwDeviceManger shared] getDeviceInfo:^(TlwDeviceInfo *deviceInfo, NSError *error) { +// if (deviceInfo) { +// NSString *info = deviceInfo.description; +// dispatch_async(dispatch_get_main_queue(), ^{ +// self.deviceInfoLabel.stringValue = info; +// }); +// } +// else{ +// NSString *info = [NSString stringWithFormat:@"error == %@",error.description]; +// NSLog(@"%@",info); +// dispatch_async(dispatch_get_main_queue(), ^{ +// self.deviceInfoLabel.stringValue = info; +// }); +// } + }]; +} + +@end diff --git a/MouseSdk/MouseSdk/ViewController.m b/MouseSdk/MouseSdk/ViewController.m index 6f1ef58..3a0fec8 100644 --- a/MouseSdk/MouseSdk/ViewController.m +++ b/MouseSdk/MouseSdk/ViewController.m @@ -6,14 +6,12 @@ // #import "ViewController.h" +#import "FrkVC.h" +#import "TlwVC.h" #import "ScanVC.h" -#import "DeviceManger.h" +#import "TlwHidManager.h" @interface ViewController () -@property(nonatomic,strong)NSTextField *typeLabel;///当前模式状态 -@property(nonatomic,strong)NSTextField *connectLabel;///连接状态 -@property(nonatomic,strong)NSTextField *authLabel;///鉴权状态 -@property(nonatomic,strong)NSTextField *deviceInfoLabel;///设备信息 @end @implementation ViewController @@ -23,7 +21,6 @@ // Do any additional setup after loading the view. [self setupUI]; - [self setupDeviceManger]; } - (void)setRepresentedObject:(id)representedObject { @@ -37,114 +34,33 @@ { NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 80, 100, 45)]; btn.bordered = true; - btn.title = @"蓝牙扫描"; + btn.title = @"富瑞坤"; [btn setTarget:self]; - [btn setAction:@selector(scanAction)]; + [btn setAction:@selector(frkAction)]; btn.state = 1; [self.view addSubview:btn]; } { - NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 180, 100, 45)]; + NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(50, self.view.bounds.size.height - 200, 100, 45)]; btn.bordered = true; - btn.title = @"获取设备信息"; + btn.title = @"泰凌微"; [btn setTarget:self]; - [btn setAction:@selector(geviceInfoAction)]; + [btn setAction:@selector(tlwAction)]; btn.state = 1; [self.view addSubview:btn]; } - { - NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 80, 300, 35)]; - label.editable = NO; - label.bordered = NO; - label.stringValue = @"连接模式:--"; - label.backgroundColor = NSColor.clearColor; - self.typeLabel = label; - [self.view addSubview:label]; - } - { - NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 120, 300, 35)]; - label.editable = NO; - label.bordered = NO; - label.stringValue = @"连接状态:未连接"; - label.backgroundColor = NSColor.clearColor; - self.connectLabel = label; - [self.view addSubview:label]; - } - { - NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 160, 300, 35)]; - label.editable = NO; - label.bordered = NO; - label.stringValue = @"鉴权状态:unknow"; - label.backgroundColor = NSColor.clearColor; - self.authLabel = label; - [self.view addSubview:label]; - } - { - NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectMake(180, self.view.bounds.size.height - 260, 300, 100)]; - label.editable = NO; - label.bordered = NO; - label.stringValue = @""; - label.backgroundColor = NSColor.clearColor; - self.deviceInfoLabel = label; - [self.view addSubview:label]; - } } #pragma mark - action -- (void)scanAction{ - ScanVC *vc = [[ScanVC alloc] initWithNibName:nil bundle:nil]; +- (void)frkAction{ + FrkVC *vc = [[FrkVC alloc] initWithNibName:nil bundle:nil]; [self presentViewControllerAsModalWindow:vc]; } - -/// 获取设备信息 -- (void)geviceInfoAction{ - [[DeviceManger shared] getDeviceInfo:^(AiMouseDeviceInfo *deviceInfo, NSError *error) { - if (deviceInfo) { - NSString *info = deviceInfo.description; - dispatch_async(dispatch_get_main_queue(), ^{ - self.deviceInfoLabel.stringValue = info; - }); - } - else{ - NSString *info = [NSString stringWithFormat:@"error == %@",error.description]; - NSLog(@"%@",info); - dispatch_async(dispatch_get_main_queue(), ^{ - self.deviceInfoLabel.stringValue = info; - }); - } - }]; -} -- (void)showAuthResult:(BOOL)isSuccess{ - dispatch_async(dispatch_get_main_queue(), ^{ - self.authLabel.stringValue = isSuccess?@"鉴权状态:鉴权成功":@"鉴权状态:鉴权失败"; - if(isSuccess){ - [self geviceInfoAction]; - } - }); -} -- (void)connectStateDidChange:(AiMouseConnectType)type connectState:(BOOL)isConnected{ - dispatch_async(dispatch_get_main_queue(), ^{ - if(isConnected){ - self.typeLabel.stringValue = type == AiMouseConnectType_bluetooth ?@"连接模式:蓝牙":@"连接模式:hid"; - self.connectLabel.stringValue = isConnected?@"连接状态:已连接":@"连接状态:未连接"; - [self geviceInfoAction]; - } - else{ - self.typeLabel.stringValue = @"连接模式:--"; - self.connectLabel.stringValue = @"连接状态:未连接"; - } - }); -} - -- (void)setupDeviceManger{ - DeviceManger *manager = [DeviceManger shared]; - __weak typeof(self) ws = self; - manager.authCallBack = ^(BOOL isSuccess) { - [ws showAuthResult:isSuccess]; - }; - manager.connectStateCallBack = ^(AiMouseConnectType type,BOOL isConnected) { - [ws connectStateDidChange:type connectState:isConnected]; - }; +- (void)tlwAction{ + [TlwHidManager shared]; + return; + TlwVC *vc = [[TlwVC alloc] initWithNibName:nil bundle:nil]; + [self presentViewControllerAsModalWindow:vc]; + } - @end