Commit fafe30be authored by Philip Brechler's avatar Philip Brechler

Add support for uploading eggs from the Hatchery to modified firmware

parent e2dc55b5
......@@ -94,8 +94,8 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
/// File transfer service. The two channels are seen from the Central perspective (iOS device)
/// and hence named Central TX and Central RX.
let card10RxTxServiceUUID = CBUUID(string: "42230100-2342-2342-2342-234223422342")
let card10TXWriteCharacteristicUUID = CBUUID(string: "01422342-2342-2342-2342-234223422342")
let card10RXReadCharacteristicUUID = CBUUID(string: "02422342-2342-2342-2342-234223422342")
let card10TXWriteCharacteristicUUID = CBUUID(string: "42230101-2342-2342-2342-234223422342")
let card10RXReadCharacteristicUUID = CBUUID(string: "42230102-2342-2342-2342-234223422342")
private(set) public var foundPeripheral: Array<FoundPeripheral>
public var connectedPeripheral: CBPeripheral?
......@@ -168,6 +168,12 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
}
public func sendFileData(data: Data, fileName: String) {
guard self.connectedPeripheral != nil else {
subscribers.forEach { $0.value?.didFailToSendFile() }
return
}
self.dataTransferManager = DataTransferManager(self)
self.subscribeToRxChannel()
......@@ -194,7 +200,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
let time = UInt64(bigEndian: badgeTimeInMilliseconds)
print("setTimeOnPeripheral \(peripheral.identifier) to \(currentDate) (\(badgeTimeInMilliseconds))")
self.connectedPeripheral?.writeValue(time.data, for: characteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(time.data, for: characteristic, type: .withoutResponse)
}
public func setVibrate(milliseconds: UInt16) {
......@@ -206,7 +212,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
}
print("setVibrate \(milliseconds) ms on \(peripheral.identifier)")
self.connectedPeripheral?.writeValue(milliseconds.data, for: characteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(milliseconds.data, for: characteristic, type: .withoutResponse)
}
public func illuminateRocketsWithBrightness(rocketOne: UInt8, rocketTwo: UInt8, rocketThree: UInt8) {
......@@ -218,7 +224,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
}
print("set rockets to \(rocketOne), \(rocketTwo), \(rocketThree) on \(peripheral.identifier)")
self.connectedPeripheral?.writeValue(Data([rocketOne, rocketTwo, rocketThree]), for: characteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(Data([rocketOne, rocketTwo, rocketThree]), for: characteristic, type: .withoutResponse)
}
public func setBackgroundLEDs (topLeft: RGBLED, topRight: RGBLED, bottomRight: RGBLED, bottomLeft: RGBLED) {
......@@ -240,10 +246,10 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
return
}
self.connectedPeripheral?.writeValue(topLeft.data, for: tlCharacteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(topRight.data, for: trCharacteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(bottomRight.data, for: brCharacteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(bottomLeft.data, for: blCharacteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(topLeft.data, for: tlCharacteristic, type: .withoutResponse)
self.connectedPeripheral?.writeValue(topRight.data, for: trCharacteristic, type: .withoutResponse)
self.connectedPeripheral?.writeValue(bottomRight.data, for: brCharacteristic, type: .withoutResponse)
self.connectedPeripheral?.writeValue(bottomLeft.data, for: blCharacteristic, type: .withoutResponse)
}
/// Set's the 11 RGB leds at the top. Parameters are from left to right.
......@@ -269,7 +275,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
self.connectedPeripheral?.writeValue(mergeLedsAboveIntoData(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, i: i, j: j, k: k),
for: aboveCharacteristic,
type: .withResponse)
type: .withoutResponse)
}
private func mergeLedsAboveIntoData(a: RGBLED,
......@@ -418,11 +424,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
if characteristic.uuid.isEqual(card10RXReadCharacteristicUUID) {
guard let data = characteristic.value else { return }
guard let nextChunk = String(data: data, encoding: String.Encoding.utf8) else {
print("Next chunk of data is nil.")
return
}
self.dataTransferManager?.receivedNewPackage(nextChunk)
self.dataTransferManager?.receivedNewPackage(data)
}
}
......@@ -440,7 +442,7 @@ class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
guard let characteristic = self.findCharacteristic(on: peripheral, forServiceUUID: card10RxTxServiceUUID, forCharacteristicUUID: card10TXWriteCharacteristicUUID) else { return }
self.connectedPeripheral?.writeValue(package, for: characteristic, type: .withResponse)
self.connectedPeripheral?.writeValue(package, for: characteristic, type: .withoutResponse)
}
func didUpdateState(_ state: TransferState) {
......
......@@ -10,7 +10,7 @@ import UIKit
class ChunkedDataReader: NSObject {
var dataToChunk: Data
let packageSize = 95
let packageSize = 20
public var currentOffset: Int = 0
init(_ data: Data) {
......@@ -24,8 +24,8 @@ class ChunkedDataReader: NSObject {
guard dataToReturn!.count > 0 else { return nil }
currentOffset += 95
currentOffset += packageSize
return dataToReturn
}
......@@ -38,7 +38,7 @@ class ChunkedDataReader: NSObject {
var range: Range<Data.Index>
// Create a range based on the length of data to return
if dataToChunk.count >= offset+length {
range = offset..<length
range = offset..<offset+length
} else {
range = offset..<dataToChunk.count
}
......
......@@ -55,8 +55,9 @@ class DataTransferManager: NSObject {
self.transferState = .IDLE
}
public func receivedNewPackage(_ package: String) {
let firstChar = String(package.prefix(1))
public func receivedNewPackage(_ package: Data) {
let stringPackaage = String(data: package, encoding: String.Encoding.ascii)
let firstChar = String(stringPackaage!.prefix(1))
let packageType = PackageType(rawValue: firstChar)
switch packageType {
case .START?:
......@@ -64,32 +65,34 @@ class DataTransferManager: NSObject {
case .START_ACK?:
self.transferState = .READY_TO_SEND
let chunkToSend = self.chunkedReader?.nextChunk()
guard chunkToSend!.count > 0 else { self.sendPackage(type: .ERROR, payload: nil); return }
guard chunkToSend!.count > 0 else { self.sendPackage(type: .ERROR, payload: nil, offset: 0); return }
self.currentChunkToSend = chunkToSend
sendPackage(type: .CHUNK, payload: chunkToSend)
sendPackage(type: .CHUNK, payload: chunkToSend, offset: 0)
break
case .CHUNK?:
break
case .CHUNK_ACK?:
self.transferState = .CHUNK_SENT
// calculate CRC and send new chunk
let crc = String(package.suffix(package.count-1))
let range: Range<Data.Index> = 0..<1
let crc = package.subdata(in: range)
updateProgress()
if (checkChunkWithResponse(crc)) {
if (currentChunkToSend!.count < chunkedReader!.packageSize) {
// The last package arrived, we are done here
sendPackage(type: .FINISH, payload: nil)
sendPackage(type: .FINISH, payload: nil, offset: 0)
break
} else {
let chunkToSend = self.chunkedReader?.nextChunk()
guard chunkToSend!.count > 0 else { self.sendPackage(type: .ERROR, payload: nil); return }
guard chunkToSend != nil else { self.sendPackage(type: .ERROR, payload: nil, offset: 0); return }
self.currentChunkToSend = chunkToSend
sendPackage(type: .CHUNK, payload: chunkToSend)
sendPackage(type: .CHUNK, payload: chunkToSend, offset: self.chunkedReader!.currentOffset-20)
}
} else if (retryCount < 9) {
sendPackage(type: .CHUNK, payload: self.currentChunkToSend)
sendPackage(type: .CHUNK, payload: self.currentChunkToSend, offset: self.chunkedReader!.currentOffset-20)
retryCount += 1
} else {
self.sendPackage(type: .ERROR, payload: nil)
self.sendPackage(type: .ERROR, payload: nil,offset: 0)
}
break
case .FINISH?:
......@@ -103,6 +106,9 @@ class DataTransferManager: NSObject {
break
case .ERROR?:
// fucked up, send error ack
print("Error from card10, message ",package)
self.sendPackage(type: .ERROR_ACK, payload: nil, offset: 0)
delegate?.didFailToSendFile()
break
case .ERROR_ACK?:
// device knows we aborted
......@@ -122,11 +128,11 @@ class DataTransferManager: NSObject {
self.dataToSend = data
self.transferState = .IDLE
sendPackage(type: .START, payload: fileNameData)
sendPackage(type: .START, payload: fileNameData, offset: 0)
self.transferState = .START_SENT
}
func sendPackage(type: PackageType, payload: Data?) {
func sendPackage(type: PackageType, payload: Data?, offset: Int) {
var payloadToSend: Data?
......@@ -140,6 +146,8 @@ class DataTransferManager: NSObject {
break
case .CHUNK:
payloadToSend = PackageType.CHUNK.rawValue.data(using: .ascii)
var int = UInt32(bigEndian: UInt32(offset))
payloadToSend?.append(Data(bytes: &int, count: MemoryLayout.size(ofValue: UInt32())))
payloadToSend?.append(payload!)
lastPackageSentAt = NSDate.init().timeIntervalSinceNow
break
......@@ -164,12 +172,15 @@ class DataTransferManager: NSObject {
}
}
func checkChunkWithResponse(_ response: String) -> Bool {
let responseData = response.data(using: .ascii)
let responseInUInt32 = responseData!.uint32
func checkChunkWithResponse(_ response: Data) -> Bool {
return true
//FIXME: This CRC32 implementation doesn't work. Disabled for now
let responseInUInt32 = response.withUnsafeBytes{ $0.load(as: UInt8.self) }
let chunkInUInt8 = [UInt8](self.currentChunkToSend!)
if (responseInUInt32 == CRC32.checksum(bytes: chunkInUInt8)) {
let checksum = CRC32.checksum(bytes: chunkInUInt8)
if (responseInUInt32 == checksum) {
return true
}
return false
......@@ -179,6 +190,7 @@ class DataTransferManager: NSObject {
guard chunkedReader != nil && dataToSend != nil else { return }
let progress = Double(chunkedReader!.currentOffset) / Double(self.dataToSend!.count)
print("Progress sending file: ",progress)
delegate?.didUpdateProgress(progress)
}
}
......@@ -112,7 +112,12 @@ extension ConnectedViewController {
}
@IBAction func disconnectButtonAction(sender: UIButton) {
BluetoothManager.sharedInstance().disconnectFromCard10Badge()
pollingTimer?.cancel()
let rgbOff = RGBLED.init(r: 0, g: 0, b: 0)
BluetoothManager.sharedInstance().setBackgroundLEDs(topLeft: rgbOff, topRight: rgbOff, bottomRight: rgbOff, bottomLeft: rgbOff)
BluetoothManager.sharedInstance().setLEDsAbove(a: rgbOff, b: rgbOff, c: rgbOff, d: rgbOff, e: rgbOff, f: rgbOff, g: rgbOff, h: rgbOff, i: rgbOff, j: rgbOff, k: rgbOff)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { BluetoothManager.sharedInstance().disconnectFromCard10Badge()
}
}
}
......@@ -157,7 +162,7 @@ extension ConnectedViewController {
pollingTimer?.setEventHandler { [weak self] in
self?.pollValues()
// self?.setRandomBackgroundLedColors()
self?.setRandomBackgroundLedColors()
}
pollingTimer?.resume()
}
......
......@@ -8,10 +8,18 @@
import UIKit
class HatcheryDetailViewController: UIViewController, HatcheryClientDelegate {
fileprivate struct DataToTransfer: Hashable {
let fileName: String
let data: Data
}
class HatcheryDetailViewController: UIViewController, HatcheryClientDelegate, BluetoothManagerDelegate {
public var eggToShow: HatcheryEgg?
private var client: HatcheryClient?
private var filesToTransfer: Array<DataToTransfer> = []
private var uploadAlert: UIAlertController?
private var progressBar: UIProgressView?
@IBOutlet weak var descriptionTextView: UITextView?
@IBOutlet weak var metaDataLabel: UILabel?
......@@ -46,14 +54,73 @@ class HatcheryDetailViewController: UIViewController, HatcheryClientDelegate {
// MARK: - HatcheryClientDelegate
func didDownloadEggToPath(_ path: URL) {
//TODO: Call BluetoothManager to upload? Just tell the user and they do it Control?
let folderName = FileManager.default.displayName(atPath: path.path)
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil)
for fileURL in fileURLs {
let fileContent = FileManager.default.contents(atPath: fileURL.path)
let fileName = FileManager.default.displayName(atPath: fileURL.path)
guard fileContent != nil else { return }
self.filesToTransfer.append(DataToTransfer(fileName: String.init(format: "apps/%@/%@",folderName,fileName), data: fileContent!))
}
BluetoothManager.sharedInstance().subscribe(self)
uploadAlert = UIAlertController(title: "Uploading", message: "Please wait", preferredStyle: .alert)
progressBar = UIProgressView(progressViewStyle: .default)
progressBar?.setProgress(0.0, animated: true)
progressBar?.frame = CGRect(x: 10, y: 70, width: 250, height: 0)
uploadAlert?.view.addSubview(progressBar!)
self.present(uploadAlert!, animated: true, completion: nil)
self.transferNextFile()
} catch {
print("Error while enumerating files \(path.path): \(error.localizedDescription)")
}
}
func didFailToDownloadEgg() {
let failedAlert = UIAlertController(title: "Error", message: "Failed to download egg", preferredStyle: .alert)
failedAlert.addAction(UIAlertAction.init(title: "OK", style: .default, handler: nil))
self.present(failedAlert, animated: true)
}
private func transferNextFile() {
if filesToTransfer.count > 0 {
let fileToTransfer = self.filesToTransfer[0]
BluetoothManager.sharedInstance().sendFileData(data: fileToTransfer.data, fileName: fileToTransfer.fileName)
} else {
self.uploadAlert?.dismiss(animated: true, completion: {
let finishedController = UIAlertController .init(title: "Uploaded", message: "Uploaded all files", preferredStyle: .alert)
finishedController.addAction(UIAlertAction.init(title: "OK", style: .default, handler: nil))
self.present(finishedController, animated: true, completion: nil)
})
}
}
//MARK: - BluetoothManagerDelegate
func didFinishToSendFile() {
guard self.filesToTransfer.count > 0 else {
return
}
self.filesToTransfer.removeFirst()
self.transferNextFile()
}
func didFailToSendFile() {
self.filesToTransfer.removeAll()
BluetoothManager.sharedInstance().unsubscribe(self)
self.uploadAlert?.dismiss(animated: true, completion: {
let finishedController = UIAlertController .init(title: "Error", message: "Failed to upload files. Please check if you are connected and if you are using the forked firmware", preferredStyle: .alert)
finishedController.addAction(UIAlertAction.init(title: "OK", style: .default, handler: nil))
self.present(finishedController, animated: true, completion: nil)
})
}
func didUpdateProgressOnFile(_ progress: Double) {
self.progressBar?.setProgress(Float(progress), animated: true)
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment