如何使一个函数与 NFC 调用同步?

huangapple go评论65阅读模式
英文:

How to make a function synchronous with NFC calls?

问题

在您的函数中,由于异步操作,导致结果在函数返回之前未准备好。您尝试使用信号量来解决这个问题,但遇到了崩溃。解决这个问题的一种方法是使用DispatchGroup来等待异步操作完成,而不是信号量或usleep

首先,在函数内创建一个DispatchGroup,然后在每个异步闭包开始时进入组,在每个异步闭包结束时离开组。最后,等待组的完成,然后返回结果。这将确保所有异步操作都完成后再返回结果。

以下是您的sendI2CCommand函数的修改版本:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String {
    var res: String = ""
    let group = DispatchGroup() // 创建 DispatchGroup

    group.enter() // 进入组

    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command])) { result in
        switch result {
        case .success((let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
            // 处理成功情况
            // ...

        case .failure(_):
            // 处理失败情况
            // ...

        default:
            // 默认情况
            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00])) { result in
                switch result {
                case .success((let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                    // 处理成功情况
                    // ...

                case .failure(_):
                    // 处理失败情况
                    // ...

                default:
                    // 默认情况
                    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06])) { result in
                        switch result {
                        case .success((let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            // 处理成功情况
                            // ...

                        case .failure(_):
                            // 处理失败情况
                            // ...

                        default:
                            // 默认情况
                            break
                        }

                        group.leave() // 离开组
                    }
                }
            }
        }
    }

    group.wait() // 等待所有异步操作完成

    return res
}

通过这种方式,您可以等待所有异步操作完成,而无需使用usleep。这应该解决您的问题。

英文:

I'm developing an iOS NFC application interacting with ISO15693 chips.

I need to send multiple consecutive commands through NFC and those commands shall be executed one by one and I need to wait for the result of the previous command to execute the next one.

As of today I'm using:

DispatchQueue.global().async {
    DispatchQueue.global().sync {
        var fw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
        print("Read FirmwareID \(fw_id)")
        var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
        print("Read SotfwareID \(sw_id)")
        var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
        print("Read ExternalID \(ex_id)")
    }
}

The commands are correctly executed one by one.

But inside the sendI2CCommand:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String{
    var res:String = ""
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    { result in
        switch result {
            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
                break
            case .failure(_):
                break
            default:
                tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
                { result in
                    switch result {
                        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            break
                    case .failure(_):
                            break
                        default:
                            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                            { result in
                                switch result {
                                    case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                                        var foo = response2
                                        foo.insert(responseFlag.rawValue, at: 0)
                                        let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                                        res = responseHex
                                        break
                                    case .failure(_):
                                        break
                                    default:
                                        break
                                }
                            }
                            break
                    }
                }
                break
        }
    }
    usleep(50000)
    return res
}

the sendI2CCommand is sending two consecutive NFC commands. But even if the call is inside the DispatchQueue sync, the result of the function is not waiting for the end of the execution and I need to add a very ugly
usleep(50000)
before returning the value, otherwise the result is empty and is not returning any data as the APDU commands takes some time to be executed.

I also tried to use semaphore inside the function but it blocks my APDU commands:

let semaphore = DispatchSemaphore(value: 0)

at the beginning of the function

semaphore.signal()

when my processing is finished.

And

semaphore.wait()

just before returning the result but it crashes during processing.

So how can I avoid using usleep function to wait for the end of the processing before returning the result in my function?

答案1

得分: 1

以下是翻译好的内容:

你不能像这样描述的那样操作。你会阻塞那些你没有权限阻塞的共享线程。这正是async/await和CheckedContinuation设计的目的。

// 在这里添加 async throws
public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) async throws -> String {
// 包装在CheckedThrowingContinuation中
try await withCheckedThrowingContinuation { continuation in
// 调用你的异步方法
tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command])) { result in

        // ... 当你要返回一个值时:
        continuation.resume(returning: responseHex)

        // ... 当有错误时:
        continuation.resume(throwing: error)
    }
}

}

你必须确保精确调用.resume一次。

然后你的顶级代码变成:

Task {
do {
var fw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
print("Read FirmwareID (fw_id)")
var sw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
print("Read SotfwareID (sw_id)")
var ex_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
print("Read ExternalID (ex_id)")
} catch {
// 处理错误
}
}

英文:

You cannot do what you're describing like this. You will block shared threads that you're not allowed to block. This is precisely what async/await and CheckedContinuation is designed for.

// Add `async throws` here
public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) async throws -> String {
    // Wrap in a CheckedThrowingContinuation
    try await withCheckedThrowingContinuation { continuation in
        // Call your async method
        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
        { result in

           // ... When you would return a value:
                                continuation.resume(returning: responseHex)

           // ... When there is an error:
           continuation.resume(throwing: error)
        }
    }
}

You must make sure to call .resume precisely one time.

Then your top level code becomes:

Task {
    do {
        var fw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
        print("Read FirmwareID \(fw_id)")
        var sw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
        print("Read SotfwareID \(sw_id)")
        var ex_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
        print("Read ExternalID \(ex_id)")
    } catch {
        // handle error
    }
}

答案2

得分: 0

Sure, here are the translated parts:

Original code:

If you don't want `sendI2CCommand` to return until it actually has a result then you can use a `DispatchGroup` as follows:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String{
    var res:String = ""
    var group = DispatchGroup()
    group.enter()
    DispatchQueue.global().async {
        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
        { result in
            switch result {
            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
                group.leave()
            case .failure(_):
                group.leave()
            default:
                tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
                { result in
                    switch result {
                    case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                        group.leave()
                    case .failure(_):
                        group.leave()
                    default:
                        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                        { result in
                            switch result {
                            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                                var foo = response2
                                foo.insert(responseFlag.rawValue, at: 0)
                                let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                                res = responseHex
                            case .failure(_):
                                break
                            default:
                                break
                            }
                            group.leave()
                        }
                    }
                }
            }
        }
    }

    group.wait()

    return res
}

Modified code:

You can also remove the `DispatchGroup.global().sync` from the calling code:

DispatchQueue.global().async {
    var fw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
    print("Read FirmwareID \(fw_id)")
    var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
    print("Read SoftwareID \(sw_id)")
    var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
    print("Read ExternalID \(ex_id)")
}

Another option is to modify sendI2CCommand to take a completion handler:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8, completion: (String) -> Void) -> String{
    var res = ""
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    { result in
        switch result {
        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
            completion(res)
        case .failure(_):
            completion(res)
        default:
            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
            { result in
                switch result {
                case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                    completion(res)
                case .failure(_):
                    completion(res)
                default:
                    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                    { result in
                        switch result {
                        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            var foo = response2
                            foo.insert(responseFlag.rawValue, at: 0)
                            let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                            res = responseHex
                        case .failure(_):
                            break
                        default:
                            break
                        }
                        completion(res)
                    }
                }
            }
        }
    }
}

Calling code:

DispatchQueue.global().async {
    NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID) { fw_id in
        print("Read FirmwareID \(fw_id)")
        var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID) { sw_id in
            print("Read SoftwareID \(sw_id)")
            var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID) { ex_id in
                print("Read ExternalID \(ex_id)")
            }
        }
    }
}
英文:

If you don't want sendI2CCommand to return until it actually has a result then you can use a DispatchGroup as follows:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -&gt; String{
    var res:String = &quot;&quot;
    var group = DispatchGroup()
    group.enter()
    DispatchQueue.global().async {
        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
        { result in
            switch result {
            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
                group.leave()
            case .failure(_):
                group.leave()
            default:
                tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
                { result in
                    switch result {
                    case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                        group.leave()
                    case .failure(_):
                        group.leave()
                    default:
                        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                        { result in
                            switch result {
                            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                                var foo = response2
                                foo.insert(responseFlag.rawValue, at: 0)
                                let responseHex = foo.hexadecimalCompressed.substring(with: 2..&lt;4)
                                res = responseHex
                            case .failure(_):
                                break
                            default:
                                break
                            }
                            group.leave()
                        }
                    }
                }
            }
        }
    }

    group.wait()

    return res
}

You can also remove the DispatchGroup.global().sync from the calling code:

DispatchQueue.global().async {
    var fw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
    print(&quot;Read FirmwareID \(fw_id)&quot;)
    var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
    print(&quot;Read SotfwareID \(sw_id)&quot;)
    var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
    print(&quot;Read ExternalID \(ex_id)&quot;)
}

Another option is to modify sendI2CCommand to take a completion handler:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8, completion: (String) -&gt; Void) -&gt; String{
    var res = &quot;&quot;
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    { result in
        switch result {
        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
            completion(res)
        case .failure(_):
            completion(res)
        default:
            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
            { result in
                switch result {
                case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                    completion(res)
                case .failure(_):
                    completion(res)
                default:
                    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                    { result in
                        switch result {
                        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            var foo = response2
                            foo.insert(responseFlag.rawValue, at: 0)
                            let responseHex = foo.hexadecimalCompressed.substring(with: 2..&lt;4)
                            res = responseHex
                        case .failure(_):
                            break
                        default:
                            break
                        }
                        completion(res)
                    }
                }
            }
        }
    }
}

Then the calling code looks like this:

DispatchQueue.global().async {
    NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID) { fw_id in
        print(&quot;Read FirmwareID \(fw_id)&quot;)
        var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID) { sw_id in
            print(&quot;Read SotfwareID \(sw_id)&quot;)
            var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID) { ex_id in
                print(&quot;Read ExternalID \(ex_id)&quot;)
            }
        }
    }
}

huangapple
  • 本文由 发表于 2023年4月17日 22:36:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76036320.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定