You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
4.1 KiB

import Foundation
#if canImport(JL_BLEKit)
import JL_BLEKit
#endif
#if canImport(JL_OTALib)
import JL_OTALib
#endif
/// OTA via JL_OTALib + `JL_BLEMultiple.otaFunc...`. Mirrors Android
/// `feature/ota/OtaFeature.kt`.
///
/// Drives `JL_BLEMultiple.otaFunc(withEntityM:withFilePath:Result:)` (the
/// "simple" path used by the iOS demo) and remaps the `JL_OTAResult` /
/// progress callback into the same `otaState` / `otaError` event shapes
/// the Android side publishes — Dart `_parseOtaState(...)` keeps working
/// unchanged.
class OtaFeature {
weak var server: JieliHomeServer?
init(server: JieliHomeServer) { self.server = server }
private var running: Bool = false
func isRunning() -> Bool { running }
func start(
address: String?,
firmwareFilePath: String,
blockSize: Int,
fileFlag: Data?
) {
if running { return }
#if canImport(JL_BLEKit)
guard let server = server,
let entity = server.connectFeature.currentEntity() as? JL_EntityM else {
server?.dispatcher.send([
"type": "otaError",
"code": -1,
"message": "no active session",
])
return
}
running = true
server.dispatcher.send([
"type": "otaState",
"state": "INQUIRING",
"sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(),
])
// JL_OTA_RT 是 nullable closure (`JL_OTA_RT __nullable`),Swift 不接受
// trailing closure 给 nullable closure 参数 —— 显式拼出 closure 字面量。
let cb: JL_OTA_RT = { [weak self] result, progress in
self?.onOtaCallback(result: result, progress: progress)
}
server.bleMultiple.otaFunc(
withEntityM: entity,
withFilePath: firmwareFilePath,
result: cb
)
#else
server?.dispatcher.send([
"type": "otaError",
"code": -1,
"message": "JL_OTALib not linked",
])
#endif
}
func cancel() {
if !running { return }
running = false
server?.dispatcher.send([
"type": "otaState",
"state": "CANCELLED",
"sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(),
])
}
#if canImport(JL_OTALib)
/// Map `JL_OTAResult` (~30 distinct cases) to the smaller Android-style
/// state machine used by Dart. We funnel "in-progress / preparing /
/// upgrading" into TRANSFERRING with the float `progress` (0..1) scaled
/// to 0..100, terminal success → DONE, anything else → FAILED.
private func onOtaCallback(result: JL_OTAResult, progress: Float) {
guard let server = server else { return }
switch result {
case .preparing, .prepared:
server.dispatcher.send([
"type": "otaState", "state": "ENTERING",
"sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(),
])
case .upgrading:
let percent = max(0, min(100, Int(progress * 100)))
server.dispatcher.send([
"type": "otaState", "state": "TRANSFERRING",
"sent": 0, "total": 0, "percent": percent, "tsMs": Self.tsMs(),
])
case .reboot, .reconnect, .reconnectWithMacAddr, .reconnectUpdateSource:
server.dispatcher.send([
"type": "otaState", "state": "REBOOTING",
"sent": 0, "total": 0, "percent": 100, "tsMs": Self.tsMs(),
])
case .success:
running = false
server.dispatcher.send([
"type": "otaState", "state": "DONE",
"sent": 0, "total": 0, "percent": 100, "tsMs": Self.tsMs(),
])
case .cancel:
running = false
server.dispatcher.send([
"type": "otaState", "state": "CANCELLED",
"sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(),
])
default:
// Anything else from the JL_OTAResult enum is a terminal failure.
running = false
server.dispatcher.send([
"type": "otaState", "state": "FAILED",
"sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(),
])
server.dispatcher.send([
"type": "otaError",
"code": Int(result.rawValue),
"message": "JL_OTAResult=\(result.rawValue)",
])
}
}
#endif
private static func tsMs() -> Int {
Int(Date().timeIntervalSince1970 * 1000)
}
}