summaryrefslogtreecommitdiff
path: root/LearnMapKit/DataSources/ADSBDataSource.swift
blob: b30e344a5eafa893d0dea792621d2b5d3e41563d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//
//  ADSBDataSource.swift
//  LearnMapKit
//
//  Created for MVVM refactoring
//

import Foundation
import Combine

// MARK: - Data Source Protocol
protocol ADSBDataSource: AnyObject {
    var dataStream: AnyPublisher<ADSBDataEvent, Never> { get }
    var connectionState: AnyPublisher<DataSourceState, Never> { get }
    var configuration: DataSourceConfiguration { get }

    func configure(with config: DataSourceConfiguration) async
    func start() async throws
    func stop() async
    func reconnect() async throws
}

// MARK: - Data Events
enum ADSBDataEvent {
    case aircraftIdentification(address: Int, icaoName: String)
    case aircraftPosition(address: Int, latitude: Double, longitude: Double)
    case aircraftAltitude(address: Int, altitude: Int)
    case rawMessage(String)
    case error(Error)
}

// MARK: - Data Source State
enum DataSourceState: Equatable {
    case idle
    case configuring
    case connecting
    case connected
    case disconnected
    case error(String)

    static func == (lhs: DataSourceState, rhs: DataSourceState) -> Bool {
        switch (lhs, rhs) {
        case (.idle, .idle), (.configuring, .configuring),
             (.connecting, .connecting), (.connected, .connected),
             (.disconnected, .disconnected):
            return true
        case let (.error(lhsError), .error(rhsError)):
            return lhsError == rhsError
        default:
            return false
        }
    }
}

// MARK: - Configuration
protocol DataSourceConfiguration {
    var sourceType: DataSourceType { get }
}

enum DataSourceType {
    case file(path: String)
    case network(host: String, port: Int)
    case test(mockData: [String])
}

struct FileDataSourceConfiguration: DataSourceConfiguration {
    let sourceType: DataSourceType
    let filePath: String
    let processRate: Int // messages per second

    init(filePath: String, processRate: Int = 120) {
        self.filePath = filePath
        self.processRate = processRate
        self.sourceType = .file(path: filePath)
    }
}

struct NetworkDataSourceConfiguration: DataSourceConfiguration {
    let sourceType: DataSourceType
    let hostname: String
    let port: Int
    let reconnectAttempts: Int
    let timeoutInterval: TimeInterval

    init(hostname: String, port: Int, reconnectAttempts: Int = 3, timeoutInterval: TimeInterval = 10.0) {
        self.hostname = hostname
        self.port = port
        self.reconnectAttempts = reconnectAttempts
        self.timeoutInterval = timeoutInterval
        self.sourceType = .network(host: hostname, port: port)
    }
}