改了文件传协议,改为边录边传 record-transfer
authorliangzhenying <867174124@qq.com>
Fri, 7 Mar 2025 07:27:18 +0000 (15:27 +0800)
committerliangzhenying <867174124@qq.com>
Fri, 7 Mar 2025 07:27:18 +0000 (15:27 +0800)
Doc/最新/蓝牙手表AI协议.docx [new file with mode: 0644]
IOTBluetooth/IOTBluetooth/Example/Datas.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth.xcworkspace/xcuserdata/yaozuopan.xcuserdatad/UserInterfaceState.xcuserstate
IOTBluetooth/IOTBluetooth/IOTBluetooth.xcworkspace/xcuserdata/yaozuopan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBAbility.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBAiManager.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBChecker.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBNotify.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBParameter.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBReceiveManager.swift
IOTBluetooth/IOTBluetooth/IOTBluetooth/IBluetooth.swift

diff --git a/Doc/最新/蓝牙手表AI协议.docx b/Doc/最新/蓝牙手表AI协议.docx
new file mode 100644 (file)
index 0000000..311db31
Binary files /dev/null and "b/Doc/\346\234\200\346\226\260/\350\223\235\347\211\231\346\211\213\350\241\250AI\345\215\217\350\256\256.docx" differ
index 10563ad..37d4590 100644 (file)
@@ -114,6 +114,7 @@ let datas: [(ability: IBAbility, label: String, parameters: [IBParameter])] = [
     (.fetchFiles, "获取指定类型所有文件", [.file(.mp3)]),
     
     (.sendWeather, "发送天气数据", [.weather(IBWeather())]),
+    (.sendChatGptKey, "发送ChatGpt key", [.text("")]),
 ]
 
 class Config{
index f6204ab..e7e7e0b 100644 (file)
Binary files a/IOTBluetooth/IOTBluetooth/IOTBluetooth.xcworkspace/xcuserdata/yaozuopan.xcuserdatad/UserInterfaceState.xcuserstate and b/IOTBluetooth/IOTBluetooth/IOTBluetooth.xcworkspace/xcuserdata/yaozuopan.xcuserdatad/UserInterfaceState.xcuserstate differ
index 5008c74..94d7cbd 100644 (file)
             landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            uuid = "52D8FE9A-FFFA-4205-817F-5D2674C36860"
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Example/AbilityViewController.swift"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "81"
+            endingLineNumber = "81"
+            landmarkName = "tableView(_:didSelectRowAt:)"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
    </Breakpoints>
 </Bucket>
index c96c932..faa7a5a 100644 (file)
@@ -539,6 +539,10 @@ public extension IBAbility {
     /// 发送Ai回应内容
     /// - Parameter aiResponse: see ``IBAiResponse``
     internal static let aiResponse = IBAbility.long.id(0x1f).parser(IBGeneralParser.self).checker(IBAiReponseChecker.self)
+    
+    /// APP发送ChatGPT Key给手表
+    /// - Parameter text:String
+    static let sendChatGptKey =  IBAbility.long.id(0x23).parser(IBGeneralParser.self).checker(IBTextChecker.self)
 }
 
 extension IBAbility {
index cc113fc..6e3b371 100644 (file)
@@ -41,6 +41,7 @@ public class IBAiManager{
         
     var responseId = ""
     var reply = ""
+    var recordData = Data()
     
     var isLogin:Bool{
         return MixSdk.shared.auth.authorized
@@ -52,7 +53,23 @@ public class IBAiManager{
         setupChatCallBack()
     }
     
-    func aiValid(_ data:Data){
+    
+    /// 检查chatGPT key是否有效
+    /// - Parameters:
+    ///   - key: key
+    ///   - completeHandler: 结果回调
+    public func checkChatptValid(_ key:String ,completeHandler: @escaping MSChatGptCheckKeyCallBack){
+        guard let key = chatGptKey else{
+            Logger.log("请先设置chatGPT key")
+            completeHandler(false,nil)
+            return
+        }
+        MixSdk.shared.chatGpt.isValidKey(key, completeHandler: completeHandler)
+    }
+
+    
+    
+    func checkAiValid(_ data:Data){
         let result = IBGeneralParser.dataPacket(data)
         let platform = chatModeMap[result.int] ?? .doubao
         let key = platform == .chatGPT ? chatGptKey : nil
@@ -80,43 +97,17 @@ public class IBAiManager{
             }
         }
     }
-
-    public func checkChatptValid(_ key:String ,completeHandler: @escaping MSChatGptCheckKeyCallBack){
-        guard let key = chatGptKey else{
-            Logger.log("请先设置chatGPT key")
-            completeHandler(false,nil)
-            return
-        }
-        MixSdk.shared.chatGpt.isValidKey(key, completeHandler: completeHandler)
-    }
-    func sendAiState(isValid:Bool,gptKey:String?){
-        let model = IBAiValid()
-        model.isValid = isValid
-        model.key = gptKey
-        IBSender()
-            .ability(IBAbility.aiValid)
-            .parameters([.aiValid(model)])
-            .response { result in
-                
-            }
-    }
     
     //取消当前思考
     func cancelThinkAction(){
         MixSdk.shared.chat.reset()
         cancelThink?()
     }
-    func aiRequest(_ data:Data){
+    func startRecord(_ data:Data){
+        recordData = Data()
+        Logger.log("开始录音:")
         let result = IBGeneralParser.dataPacket(data)
         let code = result.int
-        IBSender()
-            .ability(IBAbility.aiReady)
-            .parameters([.value(1)])
-            .response {[weak self] result in
-                self?.fectchAiAudio()
-            }
-        
-
         let platform = chatModeMap[code] ?? .doubao
         Logger.log("使用模型:",platform.title)
         useMode?(platform)
@@ -129,29 +120,43 @@ public class IBAiManager{
             MixSdk.shared.setPlatform(platform,key: nil)
         }
     }
-    func fectchAiAudio(){
+    func stopRecord(_ data:Data){
+        Logger.log("结束录音:")
+        receiveFullData(sbcData: recordData)
+    }
+    func receiveRecordData(data:Data){
+        recordData.append(data)
+    }
+}
+extension IBAiManager{
+
+    func sendAiState(isValid:Bool,gptKey:String?){
+        let model = IBAiValid()
+        model.isValid = isValid
+        model.key = gptKey
         IBSender()
-            .ability(IBAbility.fetchAiAudio)
-            .parameters([])
-            .response {[weak self] result in
-                guard let self = self else {return}
-                responseId = "\(Date().timeIntervalSince1970)"
-                if result.isSuccess,let data = result.value as? Data{
-                    receiveAiData(sbcData: data)
-                }
-                else{
-                    Logger.log(result)
-                    let model = IBAiResponse(responseId)
-                    model.type = 0
-                    model.state = "end"
-                    model.content = "录音解析错误"
-                    sendRelpy(model: model)
-                }
+            .ability(IBAbility.aiValid)
+            .parameters([.aiValid(model)])
+            .response { result in
+                
             }
     }
+    
+    func saveRecordData(data:Data){
+        Logger.log("文件大小 \(data.count)")
+        let filePath = NSHomeDirectory() + "/Documents/\(Date().format(.long2)).sbc"
+        let url = URL(fileURLWithPath: filePath)
+        do {
+            try data.write(to: url)
+            print("保存路径 \(filePath)")
+        } catch let error {
+            print("\(error)")
+        }
+    }
 }
 extension IBAiManager{
-    func receiveAiData(sbcData:Data){
+    private func receiveFullData(sbcData:Data){
+        saveRecordData(data: sbcData)
         let pcmData =  MSAudioConvert.sbcTopcm(sbcData)
         if pcmData.count == 0{
             Logger.log("sbc2pcm失败")
@@ -192,7 +197,7 @@ extension IBAiManager{
         }
     }
     
-    func sendToChatGpt(content text:String){
+    private func sendToChatGpt(content text:String){
         reply = ""
         MixSdk.shared.chatGpt.send(content: text) {[weak self] response, error in
             guard let self  = self else {return}
@@ -225,7 +230,7 @@ extension IBAiManager{
     }
     
 
-    func setupChatCallBack(){
+    private func setupChatCallBack(){
         let chat = MixSdk.shared.chat
         chat.callBack = {[weak self] model in
             guard let self = self else {return}
@@ -251,11 +256,9 @@ extension IBAiManager{
                 sendRelpy(model: data)
             }
         }
-        
-        
     }
     
-    func sendRelpy(model:IBAiResponse){
+    private func sendRelpy(model:IBAiResponse){
         self.aiResponse?(model)
         IBSender()
             .ability(IBAbility.aiResponse)
@@ -266,7 +269,7 @@ extension IBAiManager{
     }
 }
 
-extension IBAiManager{
+ extension IBAiManager{
     func login(){
         let mac = IBBluetooth.shared.currentConnected.macAddress.split(separator: ":").joined().uppercased()
         login(deviceId:mac ) { token, error in
@@ -277,7 +280,7 @@ extension IBAiManager{
     /// - Parameters:
     ///   - deviceId: 设备ID
     ///   - completeHandler: 结果回调
-    func login(deviceId:String,completeHandler: @escaping MSLoginCallBack){
+     private func login(deviceId:String,completeHandler: @escaping MSLoginCallBack){
         if productId.isEmpty || productKey.isEmpty {
             Logger.log("请先设置productId和productKey!")
             return
@@ -291,7 +294,7 @@ extension IBAiManager{
         }
     }
     
-    func startLogin(deviceId:String,password:String,completeHandler: @escaping MSLoginCallBack) {
+     private func startLogin(deviceId:String,password:String,completeHandler: @escaping MSLoginCallBack) {
         MixSdk.shared.auth.login(productKey:productKey,deviceId: deviceId, password: password){[weak self] token,error in
             if let err = error{
                 if case  .reponseError(let code,_) = err,code == 50021{
@@ -305,7 +308,7 @@ extension IBAiManager{
         }
     }
 
-    func startRegister(deviceId:String,completeHandler: @escaping MSRegisterCallBack){
+     private func startRegister(deviceId:String,completeHandler: @escaping MSRegisterCallBack){
         MixSdk.shared.auth.register(productId: productId, productKey: productKey, deviceId: deviceId){[weak self] password,error in
             if let pwd = password{
                 UserDefaults.standard.setValue(password, forKey: deviceId)
index 1c8b58b..749dd88 100644 (file)
@@ -63,6 +63,16 @@ struct IBValueChecker: IBChecker {
         return [value.byte]
     }
 }
+struct IBTextChecker: IBChecker {
+    static func check(_ parameters: [IBParameter]) -> [UInt8] {
+        guard case let .text(value) = parameters[.text] else { fatalError("缺少参数: value") }
+        Logger.log(value)
+        if let value = value.data(using: .utf8),!value.isEmpty  {
+            return value.bytes
+        }
+        return []
+    }
+}
 
 struct IBHeartRateHighChecker: IBChecker {
     static func check(_ parameters: [IBParameter]) -> [UInt8] {
index 9f54dcd..2bc7b7a 100644 (file)
@@ -202,14 +202,14 @@ extension IBNotify {
         }
         //手表询问APP AI模型是否可用
         if(data.bytes[1] == 0x05 && data.bytes[2] == 0x06){
-            IBAiManager.shared.aiValid(data)
+            IBAiManager.shared.checkAiValid(data)
             return true
         }
         //AI录音已准备好
-        if(data.bytes[1] == 0x05 && data.bytes[2] == 0x05){
-            IBAiManager.shared.aiRequest(data)
-            return true
-        }
+//        if(data.bytes[1] == 0x05 && data.bytes[2] == 0x05){
+//            IBAiManager.shared.aiRequest(data)
+//            return true
+//        }
         //AI取消当前思考
         if(data.bytes[1] == 0x05 && data.bytes[2] == 0x07){
             IBAiManager.shared.cancelThinkAction()
index 87a20f4..bada05a 100644 (file)
@@ -13,6 +13,7 @@ enum IBKey: String {
     case motionMode = "motionMode"
     case language = "language"
     case value = "value"
+    case text = "text"
     case file = "file"
     case targetStep = "targetStep"
     case sleep = "sleep"
@@ -58,6 +59,8 @@ public enum IBParameter {
     case language(IBLanguage)
     /// 通用值设置
     case value(Int)
+    /// 通用值设置
+    case text(String)
     /// 文件
     case file(IBFile)
     /// 训练目标
@@ -126,6 +129,7 @@ public enum IBParameter {
         case .motionMode(_): return .motionMode
         case .language(_): return .language
         case .value(_): return .value
+        case .text(_): return .text
         case .file(_): return .file
         case .targetStep(_): return .targetStep
         case .sleep(_): return .sleep
index da68032..e419482 100644 (file)
@@ -25,7 +25,23 @@ class IBReceiveManager {
 
         guard let data = characteristic.value else { return }
         if data.count == 2 && data.int == 0 {
-            //            Logger.log("收到0000指令异常")
+            //Logger.log("收到0000指令异常")
+            return
+        }
+        
+        if characteristic.uuid.uuidString == recordCharacteristicUUID {
+            //手表发送录音开始
+            if(data.bytes[1] == 0x08 && data.bytes[2] == 0x01){
+                IBAiManager.shared.startRecord(data)
+            }
+            //手表发送录音结束
+            else if(data.bytes[1] == 0x08 && data.bytes[2] == 0x02){
+                IBAiManager.shared.stopRecord(data)
+            }
+            else{
+                //音频数据
+                IBAiManager.shared.receiveRecordData(data: data)
+            }
             return
         }
 
index d6dfa2e..343d7e1 100644 (file)
@@ -47,6 +47,8 @@ let writeBigCharacteristicUUID: String = "2AE4"
 /// Write 特征值(发送文件OAT、MP3等)
 let writeFileCharacteristicUUID: String = "2AE6"
 let writeFileResponseUUID: String = "2AE7"
+/// 录音uuid
+let recordCharacteristicUUID: String = "2AE8"
 
 class IBBluetooth: NSObject {
     static let shared = IBBluetooth()
@@ -69,6 +71,8 @@ class IBBluetooth: NSObject {
     private var writeBigCharacteristic: CBCharacteristic!
     /// 2AE0
     private var writeFileCharacteristic: CBCharacteristic!
+    /// 2AE8
+    private var recordCharacteristic: CBCharacteristic!
     
     /// 中央管理类,管理蓝牙的扫描连接
     var centralManager: CBCentralManager!
@@ -287,6 +291,12 @@ extension IBBluetooth: CBPeripheralDelegate {
                     peripheral.setNotifyValue(true, for: $0)
                     return
                 }
+                if $0.uuid.uuidString == recordCharacteristicUUID {
+                    recordCharacteristic = $0
+                    peripheral.setNotifyValue(true, for: $0)
+                    return
+                }
+                
             }
     }
     func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
@@ -297,7 +307,13 @@ extension IBBluetooth: CBPeripheralDelegate {
             Logger.log(error)
         }
         if let data = characteristic.value {
-            Logger.log( "[receive] \(characteristic.uuid.uuidString) " + data.joined(" "))
+//            Logger.log( "[receive] \(characteristic.uuid.uuidString) " + data.joined(""))
+            if data.count > 100 {
+                Logger.log( "[receive] \(characteristic.uuid.uuidString) " + "\(data.count)")
+            }
+            else{
+                Logger.log( "[receive] \(characteristic.uuid.uuidString) " + data.joined(""))
+            }
         }
         receiveManager.receice(characteristic: characteristic)
     }