CommandRouter.swift 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import Foundation
  2. // ══════════════════════════════════════════════════════════════════
  3. // Phase 2: Userspace API & Command Router
  4. // ══════════════════════════════════════════════════════════════════
  5. // ── Developers Public API Namespaces ──
  6. public struct ark {
  7. public struct system {
  8. public struct time {
  9. public static func getCurrentTime() -> String {
  10. let formatter = DateFormatter()
  11. formatter.timeStyle = .medium
  12. formatter.dateStyle = .medium
  13. return formatter.string(from: Date())
  14. }
  15. public static func getUptime() -> Double {
  16. if let data = try? String(contentsOfFile: "/proc/uptime") {
  17. let parts = data.split(separator: " ")
  18. if let first = parts.first, let uptimeVal = Double(first) {
  19. return uptimeVal
  20. }
  21. }
  22. return 0.0
  23. }
  24. }
  25. public struct network {
  26. public static func getIPAddress() -> String {
  27. return NetworkManager.getInterfaceIP()
  28. }
  29. public static func getWiFiNetworks() -> [String] {
  30. return NetworkManager.scanWiFi()
  31. }
  32. }
  33. public struct input {
  34. public static func readKey() -> Character? {
  35. return InputManager.readKeyStroke()
  36. }
  37. }
  38. }
  39. }
  40. // ── Header-Based Command Router ──
  41. public struct CommandRouter {
  42. // Command IDs
  43. public static let CMD_GET_TIME: UInt16 = 101
  44. public static let CMD_GET_IP: UInt16 = 102
  45. public static let CMD_GET_BATTERY: UInt16 = 103
  46. public static let CMD_GET_BLUETOOTH: UInt16 = 104
  47. public static let CMD_SHUTDOWN: UInt16 = 105
  48. public static let CMD_DUMP_LOGS: UInt16 = 106
  49. // Strict binary routing header format:
  50. // [Command_ID (2 Bytes)][Payload_Length (4 Bytes)][Payload_Data]
  51. public static func route(requestBuffer: UnsafeRawBufferPointer) -> Data {
  52. guard requestBuffer.count >= 6 else {
  53. LogManager.log("Router: Invalid request header (less than 6 bytes).")
  54. return makeResponse(cmdId: 0, payload: "ERROR: INVALID_HEADER")
  55. }
  56. // Zero-copy loading of header fields
  57. let cmdId = requestBuffer.load(fromByteOffset: 0, as: UInt16.self).bigEndian
  58. let payloadLen = requestBuffer.load(fromByteOffset: 2, as: UInt32.self).bigEndian
  59. guard requestBuffer.count >= 6 + Int(payloadLen) else {
  60. LogManager.log("Router: Payload size mismatch (expected \(payloadLen) bytes).")
  61. return makeResponse(cmdId: cmdId, payload: "ERROR: SIZE_MISMATCH")
  62. }
  63. // Zero-copy payload slicing
  64. let payloadBytes = UnsafeRawBufferPointer(rebasing: requestBuffer[6..<(6 + Int(payloadLen))])
  65. let payloadString = String(decoding: payloadBytes, as: UTF8.self)
  66. LogManager.log("Router: Received command \(cmdId), payload size \(payloadLen), payload: \(payloadString)")
  67. // Handle commands
  68. var responseString = ""
  69. switch cmdId {
  70. case CMD_GET_TIME:
  71. responseString = ark.system.time.getCurrentTime()
  72. case CMD_GET_IP:
  73. responseString = ark.system.network.getIPAddress()
  74. case CMD_GET_BATTERY:
  75. responseString = "\(PowerManager.getBatteryPercentage())%"
  76. case CMD_GET_BLUETOOTH:
  77. responseString = BluetoothManager.isEnabled() ? "ACTIVE" : "INACTIVE"
  78. case CMD_SHUTDOWN:
  79. PowerManager.shutdown()
  80. responseString = "SHUTTING_DOWN"
  81. case CMD_DUMP_LOGS:
  82. responseString = LogManager.dumpLogs().joined(separator: "\n")
  83. default:
  84. LogManager.log("Router: Unknown Command_ID \(cmdId)")
  85. responseString = "ERROR: UNKNOWN_COMMAND"
  86. }
  87. return makeResponse(cmdId: cmdId, payload: responseString)
  88. }
  89. // Construct response header and payload
  90. private static func makeResponse(cmdId: UInt16, payload: String) -> Data {
  91. let payloadData = payload.data(using: .utf8) ?? Data()
  92. var response = Data()
  93. response.reserveCapacity(6 + payloadData.count)
  94. var bigEndianCmd = cmdId.bigEndian
  95. var bigEndianLen = UInt32(payloadData.count).bigEndian
  96. withUnsafeBytes(of: &bigEndianCmd) { response.append(contentsOf: $0) }
  97. withUnsafeBytes(of: &bigEndianLen) { response.append(contentsOf: $0) }
  98. response.append(payloadData)
  99. return response
  100. }
  101. }