summaryrefslogtreecommitdiff
path: root/LearnMapKit/Repositories
diff options
context:
space:
mode:
Diffstat (limited to 'LearnMapKit/Repositories')
-rw-r--r--LearnMapKit/Repositories/AircraftRepository.swift265
1 files changed, 265 insertions, 0 deletions
diff --git a/LearnMapKit/Repositories/AircraftRepository.swift b/LearnMapKit/Repositories/AircraftRepository.swift
new file mode 100644
index 0000000..6a6bc7e
--- /dev/null
+++ b/LearnMapKit/Repositories/AircraftRepository.swift
@@ -0,0 +1,265 @@
+//
+// AircraftRepository.swift
+// LearnMapKit
+//
+// Aircraft data management and state tracking
+//
+
+import Foundation
+import Combine
+
+// MARK: - Aircraft Model
+struct Aircraft: Identifiable, Equatable {
+ let id: Int // ICAO address
+ let icaoAddress: Int
+ let icaoName: String?
+ let position: Position?
+ let altitude: Int?
+ let lastSeen: Date
+ let flightInfo: FlightInfo?
+
+ struct Position: Equatable {
+ let latitude: Double
+ let longitude: Double
+ let accuracy: PositionAccuracy
+ let timestamp: Date
+
+ enum PositionAccuracy {
+ case unknown, low, medium, high
+ }
+ }
+
+ struct FlightInfo {
+ let from: String?
+ let to: String?
+ let route: String?
+ }
+
+ // Computed properties for UI
+ var isActive: Bool {
+ Date().timeIntervalSince(lastSeen) < 300 // 5 minutes
+ }
+
+ var displayName: String {
+ icaoName ?? "Unknown (\(String(format: "%06X", icaoAddress)))"
+ }
+}
+
+// MARK: - Aircraft Repository Protocol
+protocol AircraftRepository: AnyObject {
+ var aircraftStream: AnyPublisher<[Aircraft], Never> { get }
+ var aircraftCount: AnyPublisher<Int, Never> { get }
+
+ func getAllAircraft() -> [Aircraft]
+ func getAircraft(withId id: Int) -> Aircraft?
+ func updateAircraft(with event: ADSBDataEvent)
+ func removeInactiveAircraft()
+ func clearAll()
+}
+
+// MARK: - Default Implementation
+@MainActor
+class DefaultAircraftRepository: AircraftRepository {
+
+ // MARK: - Published Properties
+ var aircraftStream: AnyPublisher<[Aircraft], Never> {
+ aircraftSubject.eraseToAnyPublisher()
+ }
+
+ var aircraftCount: AnyPublisher<Int, Never> {
+ aircraftSubject
+ .map { $0.count }
+ .removeDuplicates()
+ .eraseToAnyPublisher()
+ }
+
+ // MARK: - Private Properties
+ private let aircraftSubject = CurrentValueSubject<[Aircraft], Never>([])
+ private var aircraftCache: [Int: Aircraft] = [:]
+
+ // Dependencies
+ private let tracker: AircraftTracker
+
+ init(tracker: AircraftTracker = DefaultAircraftTracker()) {
+ self.tracker = tracker
+ startCleanupTimer()
+ }
+
+ // MARK: - AircraftRepository Implementation
+ func getAllAircraft() -> [Aircraft] {
+ Array(aircraftCache.values)
+ .filter { $0.isActive }
+ .sorted { $0.lastSeen > $1.lastSeen }
+ }
+
+ func getAircraft(withId id: Int) -> Aircraft? {
+ aircraftCache[id]
+ }
+
+ func updateAircraft(with event: ADSBDataEvent) {
+ switch event {
+ case .aircraftIdentification(let address, let icaoName):
+ updateIdentification(address: address, icaoName: icaoName)
+
+ case .aircraftPosition(let address, let latitude, let longitude):
+ updatePosition(address: address, latitude: latitude, longitude: longitude)
+
+ case .aircraftAltitude(let address, let altitude):
+ updateAltitude(address: address, altitude: altitude)
+
+ case .rawMessage, .error:
+ break // Handle these separately if needed
+ }
+ }
+
+ func removeInactiveAircraft() {
+ let activeAircraft = aircraftCache.filter { _, aircraft in
+ aircraft.isActive
+ }
+
+ if activeAircraft.count != aircraftCache.count {
+ aircraftCache = activeAircraft
+ publishUpdate()
+ }
+ }
+
+ func clearAll() {
+ aircraftCache.removeAll()
+ publishUpdate()
+ }
+
+ // MARK: - Private Update Methods
+ private func updateIdentification(address: Int, icaoName: String) {
+ let aircraft = getOrCreateAircraft(address: address)
+ let updated = Aircraft(
+ id: aircraft.id,
+ icaoAddress: aircraft.icaoAddress,
+ icaoName: icaoName,
+ position: aircraft.position,
+ altitude: aircraft.altitude,
+ lastSeen: Date(),
+ flightInfo: aircraft.flightInfo
+ )
+
+ aircraftCache[address] = updated
+ publishUpdate()
+ }
+
+ private func updatePosition(address: Int, latitude: Double, longitude: Double) {
+ Task {
+ // Update the tracker for position calculation
+ await tracker.updatePosition(address: address, latitude: latitude, longitude: longitude)
+
+ // Get calculated position from tracker
+ if let calculatedPosition = await tracker.getPosition(address: address) {
+ let aircraft = getOrCreateAircraft(address: address)
+ let position = Aircraft.Position(
+ latitude: calculatedPosition.0,
+ longitude: calculatedPosition.1,
+ accuracy: .medium, // TODO: Determine accuracy from tracker
+ timestamp: Date()
+ )
+
+ let updated = Aircraft(
+ id: aircraft.id,
+ icaoAddress: aircraft.icaoAddress,
+ icaoName: aircraft.icaoName,
+ position: position,
+ altitude: aircraft.altitude,
+ lastSeen: Date(),
+ flightInfo: aircraft.flightInfo
+ )
+
+ aircraftCache[address] = updated
+ publishUpdate()
+ }
+ }
+ }
+
+ private func updateAltitude(address: Int, altitude: Int) {
+ let aircraft = getOrCreateAircraft(address: address)
+ let updated = Aircraft(
+ id: aircraft.id,
+ icaoAddress: aircraft.icaoAddress,
+ icaoName: aircraft.icaoName,
+ position: aircraft.position,
+ altitude: altitude,
+ lastSeen: Date(),
+ flightInfo: aircraft.flightInfo
+ )
+
+ aircraftCache[address] = updated
+ publishUpdate()
+ }
+
+ private func getOrCreateAircraft(address: Int) -> Aircraft {
+ if let existing = aircraftCache[address] {
+ return existing
+ }
+
+ let newAircraft = Aircraft(
+ id: address,
+ icaoAddress: address,
+ icaoName: nil,
+ position: nil,
+ altitude: nil,
+ lastSeen: Date(),
+ flightInfo: nil
+ )
+
+ return newAircraft
+ }
+
+ private func publishUpdate() {
+ let currentAircraft = getAllAircraft()
+ aircraftSubject.send(currentAircraft)
+ }
+
+ private func startCleanupTimer() {
+ Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
+ self?.removeInactiveAircraft()
+ }
+ }
+}
+
+// MARK: - Aircraft Tracker Protocol
+protocol AircraftTracker: Actor {
+ func updateIdentification(address: Int, icaoName: String) async
+ func updatePosition(address: Int, latitude: Double, longitude: Double) async
+ func updateAltitude(address: Int, altitude: Int) async
+ func getPosition(address: Int) async -> (Double, Double)?
+ func getAltitude(address: Int) async -> Int?
+ func getICAOName(address: Int) async -> String?
+}
+
+// MARK: - Default Aircraft Tracker (Adapter for existing AirPlaneTracker)
+actor DefaultAircraftTracker: AircraftTracker {
+ private let airplaneTracker = AirPlaneTracker()
+
+ func updateIdentification(address: Int, icaoName: String) async {
+ airplaneTracker.addDF17Indentification(address, icaoName)
+ }
+
+ func updatePosition(address: Int, latitude: Double, longitude: Double) async {
+ // Note: The original tracker expects CPR coordinates, not lat/long
+ // This is a simplified adapter - you might need to adjust based on your data flow
+ airplaneTracker.addDF17AirBornPosition(address, latitude, longitude, 0, true)
+ }
+
+ func updateAltitude(address: Int, altitude: Int) async {
+ // Altitude is typically updated with position in the original tracker
+ // This might need adjustment based on your specific needs
+ }
+
+ func getPosition(address: Int) async -> (Double, Double)? {
+ return airplaneTracker.getPosition(address)
+ }
+
+ func getAltitude(address: Int) async -> Int? {
+ return airplaneTracker.getAltitude(address)
+ }
+
+ func getICAOName(address: Int) async -> String? {
+ return airplaneTracker.getICAOname(address)
+ }
+} \ No newline at end of file