From 458e698a2d7880b70e9ac0af942c8296b9582360 Mon Sep 17 00:00:00 2001 From: Mike Kinney Date: Mon, 29 Nov 2021 21:11:27 -0800 Subject: [PATCH 1/2] update protobufs from master 0de53c84a26a42ce9405109e392ca1143de86f58 --- .swiftlint.yml | 4 +- MeshtasticClient/MeshtasticClientApp.swift | 6 +- MeshtasticClient/Protobufs/admin.pb.swift | 5 +- MeshtasticClient/Protobufs/channel.pb.swift | 8 +- .../Protobufs/deviceonly.pb.swift | 28 +- MeshtasticClient/Protobufs/mesh.pb.swift | 745 ++++++++++++++++-- MeshtasticClient/Protobufs/mqtt.pb.swift | 8 +- MeshtasticClient/Protobufs/portnums.pb.swift | 10 + .../Protobufs/radioconfig.pb.swift | 273 ++++++- .../Protobufs/storeforward.pb.swift | 412 ++++++++++ README.md | 8 + gen_protos.sh | 25 + 12 files changed, 1452 insertions(+), 80 deletions(-) create mode 100644 MeshtasticClient/Protobufs/storeforward.pb.swift create mode 100755 gen_protos.sh diff --git a/.swiftlint.yml b/.swiftlint.yml index b6d6ee29..9f3ff810 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -19,8 +19,8 @@ force_try: # TODO: should review file_length: - warning: 2500 - error: 3000 + warning: 3500 + error: 4000 # TODO: should review cyclomatic_complexity: diff --git a/MeshtasticClient/MeshtasticClientApp.swift b/MeshtasticClient/MeshtasticClientApp.swift index 874cb6c6..e4e6e6b1 100644 --- a/MeshtasticClient/MeshtasticClientApp.swift +++ b/MeshtasticClient/MeshtasticClientApp.swift @@ -3,12 +3,12 @@ import CoreData @main struct MeshtasticClientApp: App { - + @ObservedObject private var bleManager: BLEManager = BLEManager() @ObservedObject private var userSettings: UserSettings = UserSettings() - + @Environment(\.scenePhase) var scenePhase - + var body: some Scene { WindowGroup { ContentView() diff --git a/MeshtasticClient/Protobufs/admin.pb.swift b/MeshtasticClient/Protobufs/admin.pb.swift index 5e48f5ba..0985262d 100644 --- a/MeshtasticClient/Protobufs/admin.pb.swift +++ b/MeshtasticClient/Protobufs/admin.pb.swift @@ -387,8 +387,9 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat func traverse(visitor: inout V) throws { // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 switch self.variant { case .setRadio?: try { guard case .setRadio(let v)? = self.variant else { preconditionFailure() } diff --git a/MeshtasticClient/Protobufs/channel.pb.swift b/MeshtasticClient/Protobufs/channel.pb.swift index 6fa4799f..dff3f2a7 100644 --- a/MeshtasticClient/Protobufs/channel.pb.swift +++ b/MeshtasticClient/Protobufs/channel.pb.swift @@ -461,12 +461,16 @@ extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if self.index != 0 { try visitor.visitSingularInt32Field(value: self.index, fieldNumber: 1) } - if let v = self._settings { + try { if let v = self._settings { try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } + } }() if self.role != .disabled { try visitor.visitSingularEnumField(value: self.role, fieldNumber: 3) } diff --git a/MeshtasticClient/Protobufs/deviceonly.pb.swift b/MeshtasticClient/Protobufs/deviceonly.pb.swift index 5cca37f9..77a21df1 100644 --- a/MeshtasticClient/Protobufs/deviceonly.pb.swift +++ b/MeshtasticClient/Protobufs/deviceonly.pb.swift @@ -196,9 +196,13 @@ extension LegacyRadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem } func traverse(visitor: inout V) throws { - if let v = self._preferences { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._preferences { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -315,24 +319,28 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - if let v = _storage._legacyRadio { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = _storage._legacyRadio { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } - if let v = _storage._myNode { + } }() + try { if let v = _storage._myNode { try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } - if let v = _storage._owner { + } }() + try { if let v = _storage._owner { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } + } }() if !_storage._nodeDb.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._nodeDb, fieldNumber: 4) } if !_storage._receiveQueue.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._receiveQueue, fieldNumber: 5) } - if let v = _storage._rxTextMessage { + try { if let v = _storage._rxTextMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 7) - } + } }() if _storage._version != 0 { try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 8) } diff --git a/MeshtasticClient/Protobufs/mesh.pb.swift b/MeshtasticClient/Protobufs/mesh.pb.swift index 83cd53fb..2f09f5f1 100644 --- a/MeshtasticClient/Protobufs/mesh.pb.swift +++ b/MeshtasticClient/Protobufs/mesh.pb.swift @@ -74,6 +74,10 @@ enum HardwareModel: SwiftProtobuf.Enum { /// /// The simulator built into the android app case androidSim // = 38 + + /// + /// Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics + case diyV1 // = 39 case UNRECOGNIZED(Int) init() { @@ -100,6 +104,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case 36: self = .nrf52Unknown case 37: self = .portduino case 38: self = .androidSim + case 39: self = .diyV1 default: self = .UNRECOGNIZED(rawValue) } } @@ -124,6 +129,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case .nrf52Unknown: return 36 case .portduino: return 37 case .androidSim: return 38 + case .diyV1: return 39 case .UNRECOGNIZED(let i): return i } } @@ -152,7 +158,105 @@ extension HardwareModel: CaseIterable { .genieblocks, .nrf52Unknown, .portduino, - .androidSim + .androidSim, + .diyV1 + ] +} + +#endif // swift(>=4.2) + +/// +/// The team colors are based on the names of "friendly teams" in ATAK: +/// https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/atak/ATAK/app/src/main/assets/filters/team_filters.xml +enum Team: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// the default (unset) is "achromatic" (unaffiliated) + case clear // = 0 + case cyan // = 1 + case white // = 2 + case yellow // = 3 + case orange // = 4 + case magenta // = 5 + case red // = 6 + case maroon // = 7 + case purple // = 8 + case darkBlue // = 9 + case blue // = 10 + case teal // = 11 + case green // = 12 + case darkGreen // = 13 + case brown // = 14 + case UNRECOGNIZED(Int) + + init() { + self = .clear + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .clear + case 1: self = .cyan + case 2: self = .white + case 3: self = .yellow + case 4: self = .orange + case 5: self = .magenta + case 6: self = .red + case 7: self = .maroon + case 8: self = .purple + case 9: self = .darkBlue + case 10: self = .blue + case 11: self = .teal + case 12: self = .green + case 13: self = .darkGreen + case 14: self = .brown + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .clear: return 0 + case .cyan: return 1 + case .white: return 2 + case .yellow: return 3 + case .orange: return 4 + case .magenta: return 5 + case .red: return 6 + case .maroon: return 7 + case .purple: return 8 + case .darkBlue: return 9 + case .blue: return 10 + case .teal: return 11 + case .green: return 12 + case .darkGreen: return 13 + case .brown: return 14 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension Team: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Team] = [ + .clear, + .cyan, + .white, + .yellow, + .orange, + .magenta, + .red, + .maroon, + .purple, + .darkBlue, + .blue, + .teal, + .green, + .darkGreen, + .brown ] } @@ -339,17 +443,29 @@ struct Position { /// /// The new preferred location encoding, divide by 1e-7 to get degrees /// in floating point - var latitudeI: Int32 = 0 + var latitudeI: Int32 { + get {return _storage._latitudeI} + set {_uniqueStorage()._latitudeI = newValue} + } - var longitudeI: Int32 = 0 + var longitudeI: Int32 { + get {return _storage._longitudeI} + set {_uniqueStorage()._longitudeI = newValue} + } /// - /// In meters above MSL - var altitude: Int32 = 0 + /// In meters above MSL (but see issue #359) + var altitude: Int32 { + get {return _storage._altitude} + set {_uniqueStorage()._altitude = newValue} + } /// /// 1-100 (0 means not provided) - var batteryLevel: Int32 = 0 + var batteryLevel: Int32 { + get {return _storage._batteryLevel} + set {_uniqueStorage()._batteryLevel = newValue} + } /// /// This is usually not sent over the mesh (to save space), but it is sent @@ -357,13 +473,253 @@ struct Position { /// the mesh (because there are devices on the mesh without GPS), it will only /// be sent by devices which has a hardware GPS clock. /// seconds since 1970 - var time: UInt32 = 0 + var time: UInt32 { + get {return _storage._time} + set {_uniqueStorage()._time = newValue} + } + + var locationSource: Position.LocSource { + get {return _storage._locationSource} + set {_uniqueStorage()._locationSource = newValue} + } + + var altitudeSource: Position.AltSource { + get {return _storage._altitudeSource} + set {_uniqueStorage()._altitudeSource = newValue} + } + + /// + /// Positional timestamp (actual timestamp of GPS solution) in integer epoch seconds + var posTimestamp: UInt32 { + get {return _storage._posTimestamp} + set {_uniqueStorage()._posTimestamp = newValue} + } + + /// + /// Pos. timestamp milliseconds adjustment (rarely available or required) + var posTimeMillis: Int32 { + get {return _storage._posTimeMillis} + set {_uniqueStorage()._posTimeMillis = newValue} + } + + /// + /// HAE altitude in meters - can be used instead of MSL altitude + var altitudeHae: Int32 { + get {return _storage._altitudeHae} + set {_uniqueStorage()._altitudeHae = newValue} + } + + /// + /// Geoidal separation in meters + var altGeoidSep: Int32 { + get {return _storage._altGeoidSep} + set {_uniqueStorage()._altGeoidSep = newValue} + } + + /// + /// Horizontal, Vertical and Position Dilution of Precision, in 1/100 units + /// - PDOP is sufficient for most cases + /// - for higher precision scenarios, HDOP and VDOP can be used instead, + /// in which case PDOP becomes redundant (PDOP=sqrt(HDOP^2 + VDOP^2)) + var pdop: UInt32 { + get {return _storage._pdop} + set {_uniqueStorage()._pdop = newValue} + } + + var hdop: UInt32 { + get {return _storage._hdop} + set {_uniqueStorage()._hdop = newValue} + } + + var vdop: UInt32 { + get {return _storage._vdop} + set {_uniqueStorage()._vdop = newValue} + } + + /// + /// GPS accuracy (a hardware specific constant) in mm + /// multiplied with DOP to calculate positional accuracy + /// Default: "'bout three meters-ish" :) + var gpsAccuracy: UInt32 { + get {return _storage._gpsAccuracy} + set {_uniqueStorage()._gpsAccuracy = newValue} + } + + /// + /// Ground speed in m/s and True North TRACK in 1/100 degrees + /// + /// Clarification of terms: + /// - "track" is the direction of motion (measured in horizontal plane) + /// - "heading" is where the fuselage points (measured in horizontal plane) + /// - "yaw" indicates a relative rotation about the vertical axis + var groundSpeed: UInt32 { + get {return _storage._groundSpeed} + set {_uniqueStorage()._groundSpeed = newValue} + } + + var groundTrack: UInt32 { + get {return _storage._groundTrack} + set {_uniqueStorage()._groundTrack = newValue} + } + + /// + /// GPS fix quality (from NMEA GxGGA statement or similar) + var fixQuality: UInt32 { + get {return _storage._fixQuality} + set {_uniqueStorage()._fixQuality = newValue} + } + + /// + /// GPS fix type 2D/3D (from NMEA GxGSA statement) + var fixType: UInt32 { + get {return _storage._fixType} + set {_uniqueStorage()._fixType = newValue} + } + + /// + /// GPS "Satellites in View" number + var satsInView: UInt32 { + get {return _storage._satsInView} + set {_uniqueStorage()._satsInView = newValue} + } + + /// + /// Sensor ID - in case multiple positioning sensors are being used + var sensorID: UInt32 { + get {return _storage._sensorID} + set {_uniqueStorage()._sensorID = newValue} + } + + /// + /// Estimated/expected time (in seconds) until next update: + /// - if we update at fixed intervals of X seconds, use X + /// - if we update at dynamic intervals (based on relative movement etc), + /// but "AT LEAST every Y seconds", use Y + var posNextUpdate: UInt32 { + get {return _storage._posNextUpdate} + set {_uniqueStorage()._posNextUpdate = newValue} + } + + /// + /// A sequence number, incremented with each Position message to help + /// detect lost updates if needed + var posSeqNumber: UInt32 { + get {return _storage._posSeqNumber} + set {_uniqueStorage()._posSeqNumber = newValue} + } var unknownFields = SwiftProtobuf.UnknownStorage() + /// + /// How the location was acquired: manual, onboard GPS, external (EUD) GPS + enum LocSource: SwiftProtobuf.Enum { + typealias RawValue = Int + case locsrcUnspecified // = 0 + case locsrcManualEntry // = 1 + case locsrcGpsInternal // = 2 + + /// + /// More location sources can be added here when available: + /// GSM, radio beacons (BLE etc), location fingerprinting etc + case locsrcGpsExternal // = 3 + case UNRECOGNIZED(Int) + + init() { + self = .locsrcUnspecified + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .locsrcUnspecified + case 1: self = .locsrcManualEntry + case 2: self = .locsrcGpsInternal + case 3: self = .locsrcGpsExternal + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .locsrcUnspecified: return 0 + case .locsrcManualEntry: return 1 + case .locsrcGpsInternal: return 2 + case .locsrcGpsExternal: return 3 + case .UNRECOGNIZED(let i): return i + } + } + + } + + /// + /// How the altitude was acquired: manual, GPS int/ext, etc + /// Default: same as location_source if present + enum AltSource: SwiftProtobuf.Enum { + typealias RawValue = Int + case altsrcUnspecified // = 0 + case altsrcManualEntry // = 1 + case altsrcGpsInternal // = 2 + case altsrcGpsExternal // = 3 + case altsrcBarometric // = 4 + case UNRECOGNIZED(Int) + + init() { + self = .altsrcUnspecified + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .altsrcUnspecified + case 1: self = .altsrcManualEntry + case 2: self = .altsrcGpsInternal + case 3: self = .altsrcGpsExternal + case 4: self = .altsrcBarometric + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .altsrcUnspecified: return 0 + case .altsrcManualEntry: return 1 + case .altsrcGpsInternal: return 2 + case .altsrcGpsExternal: return 3 + case .altsrcBarometric: return 4 + case .UNRECOGNIZED(let i): return i + } + } + + } + init() {} + + fileprivate var _storage = _StorageClass.defaultInstance } +#if swift(>=4.2) + +extension Position.LocSource: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Position.LocSource] = [ + .locsrcUnspecified, + .locsrcManualEntry, + .locsrcGpsInternal, + .locsrcGpsExternal + ] +} + +extension Position.AltSource: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Position.AltSource] = [ + .altsrcUnspecified, + .altsrcManualEntry, + .altsrcGpsInternal, + .altsrcGpsExternal, + .altsrcBarometric + ] +} + +#endif // swift(>=4.2) + /// /// Broadcast when a newly powered mesh node wants to find a node num it can use /// Sent from the phone over bluetooth to set the user id for the owner of this node. @@ -428,6 +784,31 @@ struct User { /// Also, "long_name" should be their licence number. var isLicensed: Bool = false + /// + /// Participants in the same network can self-group into different teams. + /// Short-term this can be used to visually differentiate them on the map; + /// in the longer term it could also help users to semi-automatically + /// select or ignore messages according to team affiliation. + /// In total, 14 teams are defined (encoded in 4 bits) + var team: Team = .clear + + /// + /// Transmit power at antenna connector, in decibel-milliwatt + /// An optional self-reported value useful in network planning, discovery + /// and positioning - along with ant_gain_dbi and ant_azimuth below + var txPowerDbm: UInt32 = 0 + + /// + /// Antenna gain (applicable to both Tx and Rx), in decibel-isotropic + var antGainDbi: UInt32 = 0 + + /// + /// Directional antenna true azimuth *if applicable*, in degrees (0-360) + /// Only applicable in case of stationary nodes with a directional antenna + /// Zero = not applicable (mobile or omni) or not specified + /// (use a value of 360 to indicate an antenna azimuth of zero degrees) + var antAzimuth: UInt32 = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1495,7 +1876,28 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 35: .same(proto: "GENIEBLOCKS"), 36: .same(proto: "NRF52_UNKNOWN"), 37: .same(proto: "PORTDUINO"), - 38: .same(proto: "ANDROID_SIM") + 38: .same(proto: "ANDROID_SIM"), + 39: .same(proto: "DIY_V1") + ] +} + +extension Team: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "CLEAR"), + 1: .same(proto: "CYAN"), + 2: .same(proto: "WHITE"), + 3: .same(proto: "YELLOW"), + 4: .same(proto: "ORANGE"), + 5: .same(proto: "MAGENTA"), + 6: .same(proto: "RED"), + 7: .same(proto: "MAROON"), + 8: .same(proto: "PURPLE"), + 9: .same(proto: "DARK_BLUE"), + 10: .same(proto: "BLUE"), + 11: .same(proto: "TEAL"), + 12: .same(proto: "GREEN"), + 13: .same(proto: "DARK_GREEN"), + 14: .same(proto: "BROWN") ] } @@ -1530,55 +1932,258 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 2: .standard(proto: "longitude_i"), 3: .same(proto: "altitude"), 4: .standard(proto: "battery_level"), - 9: .same(proto: "time") + 9: .same(proto: "time"), + 10: .standard(proto: "location_source"), + 11: .standard(proto: "altitude_source"), + 12: .standard(proto: "pos_timestamp"), + 13: .standard(proto: "pos_time_millis"), + 14: .standard(proto: "altitude_hae"), + 15: .standard(proto: "alt_geoid_sep"), + 16: .same(proto: "PDOP"), + 17: .same(proto: "HDOP"), + 18: .same(proto: "VDOP"), + 19: .standard(proto: "gps_accuracy"), + 20: .standard(proto: "ground_speed"), + 21: .standard(proto: "ground_track"), + 22: .standard(proto: "fix_quality"), + 23: .standard(proto: "fix_type"), + 24: .standard(proto: "sats_in_view"), + 25: .standard(proto: "sensor_id"), + 40: .standard(proto: "pos_next_update"), + 41: .standard(proto: "pos_seq_number") ] + fileprivate class _StorageClass { + var _latitudeI: Int32 = 0 + var _longitudeI: Int32 = 0 + var _altitude: Int32 = 0 + var _batteryLevel: Int32 = 0 + var _time: UInt32 = 0 + var _locationSource: Position.LocSource = .locsrcUnspecified + var _altitudeSource: Position.AltSource = .altsrcUnspecified + var _posTimestamp: UInt32 = 0 + var _posTimeMillis: Int32 = 0 + var _altitudeHae: Int32 = 0 + var _altGeoidSep: Int32 = 0 + var _pdop: UInt32 = 0 + var _hdop: UInt32 = 0 + var _vdop: UInt32 = 0 + var _gpsAccuracy: UInt32 = 0 + var _groundSpeed: UInt32 = 0 + var _groundTrack: UInt32 = 0 + var _fixQuality: UInt32 = 0 + var _fixType: UInt32 = 0 + var _satsInView: UInt32 = 0 + var _sensorID: UInt32 = 0 + var _posNextUpdate: UInt32 = 0 + var _posSeqNumber: UInt32 = 0 + + static let defaultInstance = _StorageClass() + + private init() {} + + init(copying source: _StorageClass) { + _latitudeI = source._latitudeI + _longitudeI = source._longitudeI + _altitude = source._altitude + _batteryLevel = source._batteryLevel + _time = source._time + _locationSource = source._locationSource + _altitudeSource = source._altitudeSource + _posTimestamp = source._posTimestamp + _posTimeMillis = source._posTimeMillis + _altitudeHae = source._altitudeHae + _altGeoidSep = source._altGeoidSep + _pdop = source._pdop + _hdop = source._hdop + _vdop = source._vdop + _gpsAccuracy = source._gpsAccuracy + _groundSpeed = source._groundSpeed + _groundTrack = source._groundTrack + _fixQuality = source._fixQuality + _fixType = source._fixType + _satsInView = source._satsInView + _sensorID = source._sensorID + _posNextUpdate = source._posNextUpdate + _posSeqNumber = source._posSeqNumber + } + } + + fileprivate mutating func _uniqueStorage() -> _StorageClass { + if !isKnownUniquelyReferenced(&_storage) { + _storage = _StorageClass(copying: _storage) + } + return _storage + } + mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularSFixed32Field(value: &self.latitudeI) }() - case 2: try { try decoder.decodeSingularSFixed32Field(value: &self.longitudeI) }() - case 3: try { try decoder.decodeSingularInt32Field(value: &self.altitude) }() - case 4: try { try decoder.decodeSingularInt32Field(value: &self.batteryLevel) }() - case 9: try { try decoder.decodeSingularFixed32Field(value: &self.time) }() - default: break + _ = _uniqueStorage() + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularSFixed32Field(value: &_storage._latitudeI) }() + case 2: try { try decoder.decodeSingularSFixed32Field(value: &_storage._longitudeI) }() + case 3: try { try decoder.decodeSingularInt32Field(value: &_storage._altitude) }() + case 4: try { try decoder.decodeSingularInt32Field(value: &_storage._batteryLevel) }() + case 9: try { try decoder.decodeSingularFixed32Field(value: &_storage._time) }() + case 10: try { try decoder.decodeSingularEnumField(value: &_storage._locationSource) }() + case 11: try { try decoder.decodeSingularEnumField(value: &_storage._altitudeSource) }() + case 12: try { try decoder.decodeSingularFixed32Field(value: &_storage._posTimestamp) }() + case 13: try { try decoder.decodeSingularInt32Field(value: &_storage._posTimeMillis) }() + case 14: try { try decoder.decodeSingularSInt32Field(value: &_storage._altitudeHae) }() + case 15: try { try decoder.decodeSingularSInt32Field(value: &_storage._altGeoidSep) }() + case 16: try { try decoder.decodeSingularUInt32Field(value: &_storage._pdop) }() + case 17: try { try decoder.decodeSingularUInt32Field(value: &_storage._hdop) }() + case 18: try { try decoder.decodeSingularUInt32Field(value: &_storage._vdop) }() + case 19: try { try decoder.decodeSingularUInt32Field(value: &_storage._gpsAccuracy) }() + case 20: try { try decoder.decodeSingularUInt32Field(value: &_storage._groundSpeed) }() + case 21: try { try decoder.decodeSingularUInt32Field(value: &_storage._groundTrack) }() + case 22: try { try decoder.decodeSingularUInt32Field(value: &_storage._fixQuality) }() + case 23: try { try decoder.decodeSingularUInt32Field(value: &_storage._fixType) }() + case 24: try { try decoder.decodeSingularUInt32Field(value: &_storage._satsInView) }() + case 25: try { try decoder.decodeSingularUInt32Field(value: &_storage._sensorID) }() + case 40: try { try decoder.decodeSingularUInt32Field(value: &_storage._posNextUpdate) }() + case 41: try { try decoder.decodeSingularUInt32Field(value: &_storage._posSeqNumber) }() + default: break + } } } } func traverse(visitor: inout V) throws { - if self.latitudeI != 0 { - try visitor.visitSingularSFixed32Field(value: self.latitudeI, fieldNumber: 1) - } - if self.longitudeI != 0 { - try visitor.visitSingularSFixed32Field(value: self.longitudeI, fieldNumber: 2) - } - if self.altitude != 0 { - try visitor.visitSingularInt32Field(value: self.altitude, fieldNumber: 3) - } - if self.batteryLevel != 0 { - try visitor.visitSingularInt32Field(value: self.batteryLevel, fieldNumber: 4) - } - if self.time != 0 { - try visitor.visitSingularFixed32Field(value: self.time, fieldNumber: 9) + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + if _storage._latitudeI != 0 { + try visitor.visitSingularSFixed32Field(value: _storage._latitudeI, fieldNumber: 1) + } + if _storage._longitudeI != 0 { + try visitor.visitSingularSFixed32Field(value: _storage._longitudeI, fieldNumber: 2) + } + if _storage._altitude != 0 { + try visitor.visitSingularInt32Field(value: _storage._altitude, fieldNumber: 3) + } + if _storage._batteryLevel != 0 { + try visitor.visitSingularInt32Field(value: _storage._batteryLevel, fieldNumber: 4) + } + if _storage._time != 0 { + try visitor.visitSingularFixed32Field(value: _storage._time, fieldNumber: 9) + } + if _storage._locationSource != .locsrcUnspecified { + try visitor.visitSingularEnumField(value: _storage._locationSource, fieldNumber: 10) + } + if _storage._altitudeSource != .altsrcUnspecified { + try visitor.visitSingularEnumField(value: _storage._altitudeSource, fieldNumber: 11) + } + if _storage._posTimestamp != 0 { + try visitor.visitSingularFixed32Field(value: _storage._posTimestamp, fieldNumber: 12) + } + if _storage._posTimeMillis != 0 { + try visitor.visitSingularInt32Field(value: _storage._posTimeMillis, fieldNumber: 13) + } + if _storage._altitudeHae != 0 { + try visitor.visitSingularSInt32Field(value: _storage._altitudeHae, fieldNumber: 14) + } + if _storage._altGeoidSep != 0 { + try visitor.visitSingularSInt32Field(value: _storage._altGeoidSep, fieldNumber: 15) + } + if _storage._pdop != 0 { + try visitor.visitSingularUInt32Field(value: _storage._pdop, fieldNumber: 16) + } + if _storage._hdop != 0 { + try visitor.visitSingularUInt32Field(value: _storage._hdop, fieldNumber: 17) + } + if _storage._vdop != 0 { + try visitor.visitSingularUInt32Field(value: _storage._vdop, fieldNumber: 18) + } + if _storage._gpsAccuracy != 0 { + try visitor.visitSingularUInt32Field(value: _storage._gpsAccuracy, fieldNumber: 19) + } + if _storage._groundSpeed != 0 { + try visitor.visitSingularUInt32Field(value: _storage._groundSpeed, fieldNumber: 20) + } + if _storage._groundTrack != 0 { + try visitor.visitSingularUInt32Field(value: _storage._groundTrack, fieldNumber: 21) + } + if _storage._fixQuality != 0 { + try visitor.visitSingularUInt32Field(value: _storage._fixQuality, fieldNumber: 22) + } + if _storage._fixType != 0 { + try visitor.visitSingularUInt32Field(value: _storage._fixType, fieldNumber: 23) + } + if _storage._satsInView != 0 { + try visitor.visitSingularUInt32Field(value: _storage._satsInView, fieldNumber: 24) + } + if _storage._sensorID != 0 { + try visitor.visitSingularUInt32Field(value: _storage._sensorID, fieldNumber: 25) + } + if _storage._posNextUpdate != 0 { + try visitor.visitSingularUInt32Field(value: _storage._posNextUpdate, fieldNumber: 40) + } + if _storage._posSeqNumber != 0 { + try visitor.visitSingularUInt32Field(value: _storage._posSeqNumber, fieldNumber: 41) + } } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: Position, rhs: Position) -> Bool { - if lhs.latitudeI != rhs.latitudeI {return false} - if lhs.longitudeI != rhs.longitudeI {return false} - if lhs.altitude != rhs.altitude {return false} - if lhs.batteryLevel != rhs.batteryLevel {return false} - if lhs.time != rhs.time {return false} + if lhs._storage !== rhs._storage { + let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in + let _storage = _args.0 + let rhs_storage = _args.1 + if _storage._latitudeI != rhs_storage._latitudeI {return false} + if _storage._longitudeI != rhs_storage._longitudeI {return false} + if _storage._altitude != rhs_storage._altitude {return false} + if _storage._batteryLevel != rhs_storage._batteryLevel {return false} + if _storage._time != rhs_storage._time {return false} + if _storage._locationSource != rhs_storage._locationSource {return false} + if _storage._altitudeSource != rhs_storage._altitudeSource {return false} + if _storage._posTimestamp != rhs_storage._posTimestamp {return false} + if _storage._posTimeMillis != rhs_storage._posTimeMillis {return false} + if _storage._altitudeHae != rhs_storage._altitudeHae {return false} + if _storage._altGeoidSep != rhs_storage._altGeoidSep {return false} + if _storage._pdop != rhs_storage._pdop {return false} + if _storage._hdop != rhs_storage._hdop {return false} + if _storage._vdop != rhs_storage._vdop {return false} + if _storage._gpsAccuracy != rhs_storage._gpsAccuracy {return false} + if _storage._groundSpeed != rhs_storage._groundSpeed {return false} + if _storage._groundTrack != rhs_storage._groundTrack {return false} + if _storage._fixQuality != rhs_storage._fixQuality {return false} + if _storage._fixType != rhs_storage._fixType {return false} + if _storage._satsInView != rhs_storage._satsInView {return false} + if _storage._sensorID != rhs_storage._sensorID {return false} + if _storage._posNextUpdate != rhs_storage._posNextUpdate {return false} + if _storage._posSeqNumber != rhs_storage._posSeqNumber {return false} + return true + } + if !storagesAreEqual {return false} + } if lhs.unknownFields != rhs.unknownFields {return false} return true } } +extension Position.LocSource: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "LOCSRC_UNSPECIFIED"), + 1: .same(proto: "LOCSRC_MANUAL_ENTRY"), + 2: .same(proto: "LOCSRC_GPS_INTERNAL"), + 3: .same(proto: "LOCSRC_GPS_EXTERNAL") + ] +} + +extension Position.AltSource: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "ALTSRC_UNSPECIFIED"), + 1: .same(proto: "ALTSRC_MANUAL_ENTRY"), + 2: .same(proto: "ALTSRC_GPS_INTERNAL"), + 3: .same(proto: "ALTSRC_GPS_EXTERNAL"), + 4: .same(proto: "ALTSRC_BAROMETRIC") + ] +} + extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "User" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1587,7 +2192,11 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, 3: .standard(proto: "short_name"), 4: .same(proto: "macaddr"), 6: .standard(proto: "hw_model"), - 7: .standard(proto: "is_licensed") + 7: .standard(proto: "is_licensed"), + 8: .same(proto: "team"), + 10: .standard(proto: "tx_power_dbm"), + 11: .standard(proto: "ant_gain_dbi"), + 12: .standard(proto: "ant_azimuth") ] mutating func decodeMessage(decoder: inout D) throws { @@ -1602,6 +2211,10 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, case 4: try { try decoder.decodeSingularBytesField(value: &self.macaddr) }() case 6: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }() case 7: try { try decoder.decodeSingularBoolField(value: &self.isLicensed) }() + case 8: try { try decoder.decodeSingularEnumField(value: &self.team) }() + case 10: try { try decoder.decodeSingularUInt32Field(value: &self.txPowerDbm) }() + case 11: try { try decoder.decodeSingularUInt32Field(value: &self.antGainDbi) }() + case 12: try { try decoder.decodeSingularUInt32Field(value: &self.antAzimuth) }() default: break } } @@ -1626,6 +2239,18 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, if self.isLicensed != false { try visitor.visitSingularBoolField(value: self.isLicensed, fieldNumber: 7) } + if self.team != .clear { + try visitor.visitSingularEnumField(value: self.team, fieldNumber: 8) + } + if self.txPowerDbm != 0 { + try visitor.visitSingularUInt32Field(value: self.txPowerDbm, fieldNumber: 10) + } + if self.antGainDbi != 0 { + try visitor.visitSingularUInt32Field(value: self.antGainDbi, fieldNumber: 11) + } + if self.antAzimuth != 0 { + try visitor.visitSingularUInt32Field(value: self.antAzimuth, fieldNumber: 12) + } try unknownFields.traverse(visitor: &visitor) } @@ -1636,6 +2261,10 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, if lhs.macaddr != rhs.macaddr {return false} if lhs.hwModel != rhs.hwModel {return false} if lhs.isLicensed != rhs.isLicensed {return false} + if lhs.team != rhs.team {return false} + if lhs.txPowerDbm != rhs.txPowerDbm {return false} + if lhs.antGainDbi != rhs.antGainDbi {return false} + if lhs.antAzimuth != rhs.antAzimuth {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -1728,8 +2357,9 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa func traverse(visitor: inout V) throws { // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 switch self.variant { case .routeRequest?: try { guard case .routeRequest(let v)? = self.variant else { preconditionFailure() } @@ -1893,6 +2523,10 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if self.from != 0 { try visitor.visitSingularFixed32Field(value: self.from, fieldNumber: 1) } @@ -1902,9 +2536,6 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if self.channel != 0 { try visitor.visitSingularUInt32Field(value: self.channel, fieldNumber: 3) } - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch self.payloadVariant { case .decoded?: try { guard case .decoded(let v)? = self.payloadVariant else { preconditionFailure() } @@ -1996,15 +2627,19 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if self.num != 0 { try visitor.visitSingularUInt32Field(value: self.num, fieldNumber: 1) } - if let v = self._user { + try { if let v = self._user { try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } - if let v = self._position { + } }() + try { if let v = self._position { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } + } }() if self.lastHeard != 0 { try visitor.visitSingularFixed32Field(value: self.lastHeard, fieldNumber: 4) } @@ -2309,12 +2944,13 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if _storage._num != 0 { try visitor.visitSingularUInt32Field(value: _storage._num, fieldNumber: 1) } - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch _storage._payloadVariant { case .myInfo?: try { guard case .myInfo(let v)? = _storage._payloadVariant else { preconditionFailure() } @@ -2426,8 +3062,9 @@ extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa func traverse(visitor: inout V) throws { // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 switch self.payloadVariant { case .packet?: try { guard case .packet(let v)? = self.payloadVariant else { preconditionFailure() } diff --git a/MeshtasticClient/Protobufs/mqtt.pb.swift b/MeshtasticClient/Protobufs/mqtt.pb.swift index 3c1ece18..1dca74e1 100644 --- a/MeshtasticClient/Protobufs/mqtt.pb.swift +++ b/MeshtasticClient/Protobufs/mqtt.pb.swift @@ -113,9 +113,13 @@ extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - if let v = _storage._packet { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = _storage._packet { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } + } }() if !_storage._channelID.isEmpty { try visitor.visitSingularStringField(value: _storage._channelID, fieldNumber: 2) } diff --git a/MeshtasticClient/Protobufs/portnums.pb.swift b/MeshtasticClient/Protobufs/portnums.pb.swift index 10a790df..89aea9f5 100644 --- a/MeshtasticClient/Protobufs/portnums.pb.swift +++ b/MeshtasticClient/Protobufs/portnums.pb.swift @@ -112,6 +112,12 @@ enum PortNum: SwiftProtobuf.Enum { /// Maintained by Charles Crossan (crossan007) : crossan007@gmail.com case environmentalMeasurementApp // = 67 + /// + /// Experimental tools for estimating node position without a GPS + /// Maintained by Github user a-f-G-U-C (a Meshtastic contributor) + /// Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS + case zpsApp // = 68 + /// /// Private applications should use portnums >= 256. /// To simplify initial development and testing you can use "PRIVATE_APP" @@ -146,6 +152,7 @@ enum PortNum: SwiftProtobuf.Enum { case 65: self = .storeForwardApp case 66: self = .rangeTestApp case 67: self = .environmentalMeasurementApp + case 68: self = .zpsApp case 256: self = .privateApp case 257: self = .atakForwarder case 511: self = .max @@ -168,6 +175,7 @@ enum PortNum: SwiftProtobuf.Enum { case .storeForwardApp: return 65 case .rangeTestApp: return 66 case .environmentalMeasurementApp: return 67 + case .zpsApp: return 68 case .privateApp: return 256 case .atakForwarder: return 257 case .max: return 511 @@ -195,6 +203,7 @@ extension PortNum: CaseIterable { .storeForwardApp, .rangeTestApp, .environmentalMeasurementApp, + .zpsApp, .privateApp, .atakForwarder, .max @@ -220,6 +229,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding { 65: .same(proto: "STORE_FORWARD_APP"), 66: .same(proto: "RANGE_TEST_APP"), 67: .same(proto: "ENVIRONMENTAL_MEASUREMENT_APP"), + 68: .same(proto: "ZPS_APP"), 256: .same(proto: "PRIVATE_APP"), 257: .same(proto: "ATAK_FORWARDER"), 511: .same(proto: "MAX") diff --git a/MeshtasticClient/Protobufs/radioconfig.pb.swift b/MeshtasticClient/Protobufs/radioconfig.pb.swift index decb9ff7..34fc6aa6 100644 --- a/MeshtasticClient/Protobufs/radioconfig.pb.swift +++ b/MeshtasticClient/Protobufs/radioconfig.pb.swift @@ -436,6 +436,105 @@ extension LocationSharing: CaseIterable { #endif // swift(>=4.2) +/// +/// Bit field of boolean configuration options, indicating which optional +/// fields to include when assembling POSITION messages +/// Longitude and latitude are always included (also time if GPS-synced) +/// +/// NOTE: the more fields are included, the larger the message will be - +/// leading to longer airtime and a higher risk of packet loss +enum PositionFlags: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// Required for compilation + case posUndefined // = 0 + + /// Include an altitude value (if available) + case posAltitude // = 1 + + /// Altitude value is MSL + case posAltMsl // = 2 + + /// Include geoidal separation + case posGeoSep // = 4 + + /// Include the DOP value ; PDOP used by default, see below + case posDop // = 8 + + /// If POS_DOP set, send separate HDOP / VDOP values instead of PDOP + case posHvdop // = 16 + + /// Include battery level + case posBattery // = 32 + + /// Include number of "satellites in view" + case posSatinview // = 64 + + /// Include a sequence number incremented per packet + case posSeqNos // = 128 + + /// Include positional timestamp (from GPS solution) + case posTimestamp // = 256 + case UNRECOGNIZED(Int) + + init() { + self = .posUndefined + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .posUndefined + case 1: self = .posAltitude + case 2: self = .posAltMsl + case 4: self = .posGeoSep + case 8: self = .posDop + case 16: self = .posHvdop + case 32: self = .posBattery + case 64: self = .posSatinview + case 128: self = .posSeqNos + case 256: self = .posTimestamp + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .posUndefined: return 0 + case .posAltitude: return 1 + case .posAltMsl: return 2 + case .posGeoSep: return 4 + case .posDop: return 8 + case .posHvdop: return 16 + case .posBattery: return 32 + case .posSatinview: return 64 + case .posSeqNos: return 128 + case .posTimestamp: return 256 + case .UNRECOGNIZED(let i): return i + } + } + +} + +#if swift(>=4.2) + +extension PositionFlags: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [PositionFlags] = [ + .posUndefined, + .posAltitude, + .posAltMsl, + .posGeoSep, + .posDop, + .posHvdop, + .posBattery, + .posSatinview, + .posSeqNos, + .posTimestamp + ] +} + +#endif // swift(>=4.2) + /// /// The entire set of user settable/readable settings for our radio device. /// Includes both the current channel settings and any preferences the user has @@ -471,6 +570,14 @@ struct RadioConfig { set {_uniqueStorage()._positionBroadcastSecs = newValue} } + /// + /// We should send our position this often (but only if it has changed significantly) + /// Defaults to 15 minutes + var positionBroadcastSmart: Bool { + get {return _storage._positionBroadcastSmart} + set {_uniqueStorage()._positionBroadcastSmart = newValue} + } + /// /// Send our owner info at least this often (also we always send once at boot - to rejoin the mesh) var sendOwnerInterval: UInt32 { @@ -659,6 +766,27 @@ struct RadioConfig { set {_uniqueStorage()._gpsAttemptTime = newValue} } + /// + /// Shall we accept 2D GPS fixes? By default, only 3D fixes are accepted + /// (during a 2D fix, altitude values are unreliable and will be excluded) + var gpsAccept2D: Bool { + get {return _storage._gpsAccept2D} + set {_uniqueStorage()._gpsAccept2D = newValue} + } + + /// + /// GPS maximum DOP accepted (dilution of precision) + /// Set a rejection threshold for GPS readings based on their precision, + /// relative to the GPS rated accuracy (which is typically ~3m) + /// Solutions above this value will be treated as retryable errors! + /// + /// Useful range is between 1 - 64 (3m - <~200m) + /// By default (if zero), accept all GPS readings + var gpsMaxDop: UInt32 { + get {return _storage._gpsMaxDop} + set {_uniqueStorage()._gpsMaxDop = newValue} + } + /// /// This parameter is for advanced users with advanced test equipment, we do not recommend most users use it. /// A frequency offset that is added to to the calculated band center frequency. @@ -814,11 +942,26 @@ struct RadioConfig { set {_uniqueStorage()._storeForwardPluginEnabled = newValue} } + var storeForwardPluginHeartbeat: Bool { + get {return _storage._storeForwardPluginHeartbeat} + set {_uniqueStorage()._storeForwardPluginHeartbeat = newValue} + } + var storeForwardPluginRecords: UInt32 { get {return _storage._storeForwardPluginRecords} set {_uniqueStorage()._storeForwardPluginRecords = newValue} } + var storeForwardPluginHistoryReturnMax: UInt32 { + get {return _storage._storeForwardPluginHistoryReturnMax} + set {_uniqueStorage()._storeForwardPluginHistoryReturnMax = newValue} + } + + var storeForwardPluginHistoryReturnWindow: UInt32 { + get {return _storage._storeForwardPluginHistoryReturnWindow} + set {_uniqueStorage()._storeForwardPluginHistoryReturnWindow = newValue} + } + /// /// Preferences for the EnvironmentalMeasurement Plugin /// FIXME - Move this out of UserPreferences and into a section for plugin configuration. @@ -883,11 +1026,36 @@ struct RadioConfig { set {_uniqueStorage()._environmentalMeasurementPluginSensorPin = newValue} } + /// + /// Bit field of boolean configuration options for POSITION messages + /// (bitwise OR of PositionFlags) + var positionFlags: UInt32 { + get {return _storage._positionFlags} + set {_uniqueStorage()._positionFlags = newValue} + } + + /// + /// Circumvents the logic block for determining whether the device is powered or not. + /// Useful for devices with finicky ADC issues on the battery sense pins. + var isAlwaysPowered: Bool { + get {return _storage._isAlwaysPowered} + set {_uniqueStorage()._isAlwaysPowered = newValue} + } + + /// + /// Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. + /// Potentially useful for devices without user buttons. + var autoScreenCarouselSecs: UInt32 { + get {return _storage._autoScreenCarouselSecs} + set {_uniqueStorage()._autoScreenCarouselSecs = newValue} + } + var unknownFields = SwiftProtobuf.UnknownStorage() enum EnvironmentalMeasurementSensorType: SwiftProtobuf.Enum { typealias RawValue = Int case dht11 // = 0 + case ds18B20 // = 1 case UNRECOGNIZED(Int) init() { @@ -897,6 +1065,7 @@ struct RadioConfig { init?(rawValue: Int) { switch rawValue { case 0: self = .dht11 + case 1: self = .ds18B20 default: self = .UNRECOGNIZED(rawValue) } } @@ -904,6 +1073,7 @@ struct RadioConfig { var rawValue: Int { switch self { case .dht11: return 0 + case .ds18B20: return 1 case .UNRECOGNIZED(let i): return i } } @@ -925,7 +1095,8 @@ struct RadioConfig { extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType] = [ - .dht11 + .dht11, + .ds18B20 ] } @@ -999,6 +1170,21 @@ extension LocationSharing: SwiftProtobuf._ProtoNameProviding { ] } +extension PositionFlags: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "POS_UNDEFINED"), + 1: .same(proto: "POS_ALTITUDE"), + 2: .same(proto: "POS_ALT_MSL"), + 4: .same(proto: "POS_GEO_SEP"), + 8: .same(proto: "POS_DOP"), + 16: .same(proto: "POS_HVDOP"), + 32: .same(proto: "POS_BATTERY"), + 64: .same(proto: "POS_SATINVIEW"), + 128: .same(proto: "POS_SEQ_NOS"), + 256: .same(proto: "POS_TIMESTAMP") + ] +} + extension RadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "RadioConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1018,9 +1204,13 @@ extension RadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati } func traverse(visitor: inout V) throws { - if let v = self._preferences { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._preferences { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -1035,6 +1225,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes static let protoMessageName: String = RadioConfig.protoMessageName + ".UserPreferences" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .standard(proto: "position_broadcast_secs"), + 17: .standard(proto: "position_broadcast_smart"), 2: .standard(proto: "send_owner_interval"), 4: .standard(proto: "wait_bluetooth_secs"), 5: .standard(proto: "screen_on_secs"), @@ -1057,6 +1248,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes 33: .standard(proto: "gps_operation"), 34: .standard(proto: "gps_update_interval"), 36: .standard(proto: "gps_attempt_time"), + 45: .standard(proto: "gps_accept_2d"), + 46: .standard(proto: "gps_max_dop"), 41: .standard(proto: "frequency_offset"), 42: .standard(proto: "mqtt_server"), 43: .standard(proto: "mqtt_disabled"), @@ -1080,7 +1273,10 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes 133: .standard(proto: "range_test_plugin_sender"), 134: .standard(proto: "range_test_plugin_save"), 148: .standard(proto: "store_forward_plugin_enabled"), + 149: .standard(proto: "store_forward_plugin_heartbeat"), 137: .standard(proto: "store_forward_plugin_records"), + 138: .standard(proto: "store_forward_plugin_history_return_max"), + 139: .standard(proto: "store_forward_plugin_history_return_window"), 140: .standard(proto: "environmental_measurement_plugin_measurement_enabled"), 141: .standard(proto: "environmental_measurement_plugin_screen_enabled"), 142: .standard(proto: "environmental_measurement_plugin_read_error_count_threshold"), @@ -1088,11 +1284,15 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes 144: .standard(proto: "environmental_measurement_plugin_recovery_interval"), 145: .standard(proto: "environmental_measurement_plugin_display_farenheit"), 146: .standard(proto: "environmental_measurement_plugin_sensor_type"), - 147: .standard(proto: "environmental_measurement_plugin_sensor_pin") + 147: .standard(proto: "environmental_measurement_plugin_sensor_pin"), + 150: .standard(proto: "position_flags"), + 151: .standard(proto: "is_always_powered"), + 152: .standard(proto: "auto_screen_carousel_secs") ] fileprivate class _StorageClass { var _positionBroadcastSecs: UInt32 = 0 + var _positionBroadcastSmart: Bool = false var _sendOwnerInterval: UInt32 = 0 var _waitBluetoothSecs: UInt32 = 0 var _screenOnSecs: UInt32 = 0 @@ -1115,6 +1315,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes var _gpsOperation: GpsOperation = .gpsOpUnset var _gpsUpdateInterval: UInt32 = 0 var _gpsAttemptTime: UInt32 = 0 + var _gpsAccept2D: Bool = false + var _gpsMaxDop: UInt32 = 0 var _frequencyOffset: Float = 0 var _mqttServer: String = String() var _mqttDisabled: Bool = false @@ -1138,7 +1340,10 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes var _rangeTestPluginSender: UInt32 = 0 var _rangeTestPluginSave: Bool = false var _storeForwardPluginEnabled: Bool = false + var _storeForwardPluginHeartbeat: Bool = false var _storeForwardPluginRecords: UInt32 = 0 + var _storeForwardPluginHistoryReturnMax: UInt32 = 0 + var _storeForwardPluginHistoryReturnWindow: UInt32 = 0 var _environmentalMeasurementPluginMeasurementEnabled: Bool = false var _environmentalMeasurementPluginScreenEnabled: Bool = false var _environmentalMeasurementPluginReadErrorCountThreshold: UInt32 = 0 @@ -1147,6 +1352,9 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes var _environmentalMeasurementPluginDisplayFarenheit: Bool = false var _environmentalMeasurementPluginSensorType: RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType = .dht11 var _environmentalMeasurementPluginSensorPin: UInt32 = 0 + var _positionFlags: UInt32 = 0 + var _isAlwaysPowered: Bool = false + var _autoScreenCarouselSecs: UInt32 = 0 static let defaultInstance = _StorageClass() @@ -1154,6 +1362,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes init(copying source: _StorageClass) { _positionBroadcastSecs = source._positionBroadcastSecs + _positionBroadcastSmart = source._positionBroadcastSmart _sendOwnerInterval = source._sendOwnerInterval _waitBluetoothSecs = source._waitBluetoothSecs _screenOnSecs = source._screenOnSecs @@ -1176,6 +1385,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes _gpsOperation = source._gpsOperation _gpsUpdateInterval = source._gpsUpdateInterval _gpsAttemptTime = source._gpsAttemptTime + _gpsAccept2D = source._gpsAccept2D + _gpsMaxDop = source._gpsMaxDop _frequencyOffset = source._frequencyOffset _mqttServer = source._mqttServer _mqttDisabled = source._mqttDisabled @@ -1199,7 +1410,10 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes _rangeTestPluginSender = source._rangeTestPluginSender _rangeTestPluginSave = source._rangeTestPluginSave _storeForwardPluginEnabled = source._storeForwardPluginEnabled + _storeForwardPluginHeartbeat = source._storeForwardPluginHeartbeat _storeForwardPluginRecords = source._storeForwardPluginRecords + _storeForwardPluginHistoryReturnMax = source._storeForwardPluginHistoryReturnMax + _storeForwardPluginHistoryReturnWindow = source._storeForwardPluginHistoryReturnWindow _environmentalMeasurementPluginMeasurementEnabled = source._environmentalMeasurementPluginMeasurementEnabled _environmentalMeasurementPluginScreenEnabled = source._environmentalMeasurementPluginScreenEnabled _environmentalMeasurementPluginReadErrorCountThreshold = source._environmentalMeasurementPluginReadErrorCountThreshold @@ -1208,6 +1422,9 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes _environmentalMeasurementPluginDisplayFarenheit = source._environmentalMeasurementPluginDisplayFarenheit _environmentalMeasurementPluginSensorType = source._environmentalMeasurementPluginSensorType _environmentalMeasurementPluginSensorPin = source._environmentalMeasurementPluginSensorPin + _positionFlags = source._positionFlags + _isAlwaysPowered = source._isAlwaysPowered + _autoScreenCarouselSecs = source._autoScreenCarouselSecs } } @@ -1241,6 +1458,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes case 14: try { try decoder.decodeSingularBoolField(value: &_storage._wifiApMode) }() case 15: try { try decoder.decodeSingularEnumField(value: &_storage._region) }() case 16: try { try decoder.decodeSingularEnumField(value: &_storage._chargeCurrent) }() + case 17: try { try decoder.decodeSingularBoolField(value: &_storage._positionBroadcastSmart) }() case 32: try { try decoder.decodeSingularEnumField(value: &_storage._locationShare) }() case 33: try { try decoder.decodeSingularEnumField(value: &_storage._gpsOperation) }() case 34: try { try decoder.decodeSingularUInt32Field(value: &_storage._gpsUpdateInterval) }() @@ -1253,6 +1471,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes case 42: try { try decoder.decodeSingularStringField(value: &_storage._mqttServer) }() case 43: try { try decoder.decodeSingularBoolField(value: &_storage._mqttDisabled) }() case 44: try { try decoder.decodeSingularEnumField(value: &_storage._gpsFormat) }() + case 45: try { try decoder.decodeSingularBoolField(value: &_storage._gpsAccept2D) }() + case 46: try { try decoder.decodeSingularUInt32Field(value: &_storage._gpsMaxDop) }() case 100: try { try decoder.decodeSingularBoolField(value: &_storage._factoryReset) }() case 101: try { try decoder.decodeSingularBoolField(value: &_storage._debugLogEnabled) }() case 103: try { try decoder.decodeRepeatedUInt32Field(value: &_storage._ignoreIncoming) }() @@ -1272,6 +1492,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes case 133: try { try decoder.decodeSingularUInt32Field(value: &_storage._rangeTestPluginSender) }() case 134: try { try decoder.decodeSingularBoolField(value: &_storage._rangeTestPluginSave) }() case 137: try { try decoder.decodeSingularUInt32Field(value: &_storage._storeForwardPluginRecords) }() + case 138: try { try decoder.decodeSingularUInt32Field(value: &_storage._storeForwardPluginHistoryReturnMax) }() + case 139: try { try decoder.decodeSingularUInt32Field(value: &_storage._storeForwardPluginHistoryReturnWindow) }() case 140: try { try decoder.decodeSingularBoolField(value: &_storage._environmentalMeasurementPluginMeasurementEnabled) }() case 141: try { try decoder.decodeSingularBoolField(value: &_storage._environmentalMeasurementPluginScreenEnabled) }() case 142: try { try decoder.decodeSingularUInt32Field(value: &_storage._environmentalMeasurementPluginReadErrorCountThreshold) }() @@ -1281,6 +1503,10 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes case 146: try { try decoder.decodeSingularEnumField(value: &_storage._environmentalMeasurementPluginSensorType) }() case 147: try { try decoder.decodeSingularUInt32Field(value: &_storage._environmentalMeasurementPluginSensorPin) }() case 148: try { try decoder.decodeSingularBoolField(value: &_storage._storeForwardPluginEnabled) }() + case 149: try { try decoder.decodeSingularBoolField(value: &_storage._storeForwardPluginHeartbeat) }() + case 150: try { try decoder.decodeSingularUInt32Field(value: &_storage._positionFlags) }() + case 151: try { try decoder.decodeSingularBoolField(value: &_storage._isAlwaysPowered) }() + case 152: try { try decoder.decodeSingularUInt32Field(value: &_storage._autoScreenCarouselSecs) }() default: break } } @@ -1334,6 +1560,9 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._chargeCurrent != .maunset { try visitor.visitSingularEnumField(value: _storage._chargeCurrent, fieldNumber: 16) } + if _storage._positionBroadcastSmart != false { + try visitor.visitSingularBoolField(value: _storage._positionBroadcastSmart, fieldNumber: 17) + } if _storage._locationShare != .locUnset { try visitor.visitSingularEnumField(value: _storage._locationShare, fieldNumber: 32) } @@ -1370,6 +1599,12 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._gpsFormat != .gpsFormatDec { try visitor.visitSingularEnumField(value: _storage._gpsFormat, fieldNumber: 44) } + if _storage._gpsAccept2D != false { + try visitor.visitSingularBoolField(value: _storage._gpsAccept2D, fieldNumber: 45) + } + if _storage._gpsMaxDop != 0 { + try visitor.visitSingularUInt32Field(value: _storage._gpsMaxDop, fieldNumber: 46) + } if _storage._factoryReset != false { try visitor.visitSingularBoolField(value: _storage._factoryReset, fieldNumber: 100) } @@ -1427,6 +1662,12 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._storeForwardPluginRecords != 0 { try visitor.visitSingularUInt32Field(value: _storage._storeForwardPluginRecords, fieldNumber: 137) } + if _storage._storeForwardPluginHistoryReturnMax != 0 { + try visitor.visitSingularUInt32Field(value: _storage._storeForwardPluginHistoryReturnMax, fieldNumber: 138) + } + if _storage._storeForwardPluginHistoryReturnWindow != 0 { + try visitor.visitSingularUInt32Field(value: _storage._storeForwardPluginHistoryReturnWindow, fieldNumber: 139) + } if _storage._environmentalMeasurementPluginMeasurementEnabled != false { try visitor.visitSingularBoolField(value: _storage._environmentalMeasurementPluginMeasurementEnabled, fieldNumber: 140) } @@ -1454,6 +1695,18 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._storeForwardPluginEnabled != false { try visitor.visitSingularBoolField(value: _storage._storeForwardPluginEnabled, fieldNumber: 148) } + if _storage._storeForwardPluginHeartbeat != false { + try visitor.visitSingularBoolField(value: _storage._storeForwardPluginHeartbeat, fieldNumber: 149) + } + if _storage._positionFlags != 0 { + try visitor.visitSingularUInt32Field(value: _storage._positionFlags, fieldNumber: 150) + } + if _storage._isAlwaysPowered != false { + try visitor.visitSingularBoolField(value: _storage._isAlwaysPowered, fieldNumber: 151) + } + if _storage._autoScreenCarouselSecs != 0 { + try visitor.visitSingularUInt32Field(value: _storage._autoScreenCarouselSecs, fieldNumber: 152) + } } try unknownFields.traverse(visitor: &visitor) } @@ -1464,6 +1717,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes let _storage = _args.0 let rhs_storage = _args.1 if _storage._positionBroadcastSecs != rhs_storage._positionBroadcastSecs {return false} + if _storage._positionBroadcastSmart != rhs_storage._positionBroadcastSmart {return false} if _storage._sendOwnerInterval != rhs_storage._sendOwnerInterval {return false} if _storage._waitBluetoothSecs != rhs_storage._waitBluetoothSecs {return false} if _storage._screenOnSecs != rhs_storage._screenOnSecs {return false} @@ -1486,6 +1740,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._gpsOperation != rhs_storage._gpsOperation {return false} if _storage._gpsUpdateInterval != rhs_storage._gpsUpdateInterval {return false} if _storage._gpsAttemptTime != rhs_storage._gpsAttemptTime {return false} + if _storage._gpsAccept2D != rhs_storage._gpsAccept2D {return false} + if _storage._gpsMaxDop != rhs_storage._gpsMaxDop {return false} if _storage._frequencyOffset != rhs_storage._frequencyOffset {return false} if _storage._mqttServer != rhs_storage._mqttServer {return false} if _storage._mqttDisabled != rhs_storage._mqttDisabled {return false} @@ -1509,7 +1765,10 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._rangeTestPluginSender != rhs_storage._rangeTestPluginSender {return false} if _storage._rangeTestPluginSave != rhs_storage._rangeTestPluginSave {return false} if _storage._storeForwardPluginEnabled != rhs_storage._storeForwardPluginEnabled {return false} + if _storage._storeForwardPluginHeartbeat != rhs_storage._storeForwardPluginHeartbeat {return false} if _storage._storeForwardPluginRecords != rhs_storage._storeForwardPluginRecords {return false} + if _storage._storeForwardPluginHistoryReturnMax != rhs_storage._storeForwardPluginHistoryReturnMax {return false} + if _storage._storeForwardPluginHistoryReturnWindow != rhs_storage._storeForwardPluginHistoryReturnWindow {return false} if _storage._environmentalMeasurementPluginMeasurementEnabled != rhs_storage._environmentalMeasurementPluginMeasurementEnabled {return false} if _storage._environmentalMeasurementPluginScreenEnabled != rhs_storage._environmentalMeasurementPluginScreenEnabled {return false} if _storage._environmentalMeasurementPluginReadErrorCountThreshold != rhs_storage._environmentalMeasurementPluginReadErrorCountThreshold {return false} @@ -1518,6 +1777,9 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._environmentalMeasurementPluginDisplayFarenheit != rhs_storage._environmentalMeasurementPluginDisplayFarenheit {return false} if _storage._environmentalMeasurementPluginSensorType != rhs_storage._environmentalMeasurementPluginSensorType {return false} if _storage._environmentalMeasurementPluginSensorPin != rhs_storage._environmentalMeasurementPluginSensorPin {return false} + if _storage._positionFlags != rhs_storage._positionFlags {return false} + if _storage._isAlwaysPowered != rhs_storage._isAlwaysPowered {return false} + if _storage._autoScreenCarouselSecs != rhs_storage._autoScreenCarouselSecs {return false} return true } if !storagesAreEqual {return false} @@ -1529,6 +1791,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DHT11") + 0: .same(proto: "DHT11"), + 1: .same(proto: "DS18B20") ] } diff --git a/MeshtasticClient/Protobufs/storeforward.pb.swift b/MeshtasticClient/Protobufs/storeforward.pb.swift new file mode 100644 index 00000000..3c05a43d --- /dev/null +++ b/MeshtasticClient/Protobufs/storeforward.pb.swift @@ -0,0 +1,412 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: storeforward.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +struct StoreAndForwardMessage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var rr: StoreAndForwardMessage.RequestResponse = .unset + + var stats: StoreAndForwardMessage.Statistics { + get {return _stats ?? StoreAndForwardMessage.Statistics()} + set {_stats = newValue} + } + /// Returns true if `stats` has been explicitly set. + var hasStats: Bool {return self._stats != nil} + /// Clears the value of `stats`. Subsequent reads from it will return its default value. + mutating func clearStats() {self._stats = nil} + + var history: StoreAndForwardMessage.History { + get {return _history ?? StoreAndForwardMessage.History()} + set {_history = newValue} + } + /// Returns true if `history` has been explicitly set. + var hasHistory: Bool {return self._history != nil} + /// Clears the value of `history`. Subsequent reads from it will return its default value. + mutating func clearHistory() {self._history = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + /// + /// 1 - 99 = From Router + /// 101 - 199 = From Client + enum RequestResponse: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// + /// Unset/unused + case unset // = 0 + case routerError // = 1 + case routerHeartbeat // = 2 + + /// + /// Router has requested the client respond. This can work as a + /// "are you there" message. + case routerPing // = 3 + + /// + /// The response to a "Ping" + case routerPong // = 4 + + /// + /// Router is currently busy. Please try again later. + case routerBusy // = 5 + case clientError // = 101 + + /// + /// Client has requested a replay from the router. + case clientHistory // = 102 + + /// + /// Client has requested stats from the router. + case clientStats // = 103 + + /// + /// Client has requested the router respond. This can work as a + /// "are you there" message. + case clientPing // = 104 + + /// + /// The response to a "Ping" + case clientPong // = 105 + case max // = 255 + case UNRECOGNIZED(Int) + + init() { + self = .unset + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .unset + case 1: self = .routerError + case 2: self = .routerHeartbeat + case 3: self = .routerPing + case 4: self = .routerPong + case 5: self = .routerBusy + case 101: self = .clientError + case 102: self = .clientHistory + case 103: self = .clientStats + case 104: self = .clientPing + case 105: self = .clientPong + case 255: self = .max + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .unset: return 0 + case .routerError: return 1 + case .routerHeartbeat: return 2 + case .routerPing: return 3 + case .routerPong: return 4 + case .routerBusy: return 5 + case .clientError: return 101 + case .clientHistory: return 102 + case .clientStats: return 103 + case .clientPing: return 104 + case .clientPong: return 105 + case .max: return 255 + case .UNRECOGNIZED(let i): return i + } + } + + } + + struct Statistics { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Number of messages we have ever seen + var messagesTotal: UInt32 = 0 + + /// + /// Number of messages we have currently saved our history. + var messagesSaved: UInt32 = 0 + + /// + /// Maximum number of messages we will save + var messagesMax: UInt32 = 0 + + /// + /// Router uptime in seconds + var upTime: UInt32 = 0 + + /// + /// Number of times any client sent a request to the S&F. + var requests: UInt32 = 0 + + /// + /// Number of times the history was requested. + var requestsHistory: UInt32 = 0 + + /// + /// Is the heartbeat enabled on the server? + var heartbeat: Bool = false + + /// + /// Is the heartbeat enabled on the server? + var returnMax: UInt32 = 0 + + /// + /// Is the heartbeat enabled on the server? + var returnWindow: UInt32 = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + } + + struct History { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Number of that will be sent to the client + var historyMessages: UInt32 = 0 + + /// + /// The window of messages that was used to filter the history client requested + var window: UInt32 = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + } + + init() {} + + fileprivate var _stats: StoreAndForwardMessage.Statistics? + fileprivate var _history: StoreAndForwardMessage.History? +} + +#if swift(>=4.2) + +extension StoreAndForwardMessage.RequestResponse: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [StoreAndForwardMessage.RequestResponse] = [ + .unset, + .routerError, + .routerHeartbeat, + .routerPing, + .routerPong, + .routerBusy, + .clientError, + .clientHistory, + .clientStats, + .clientPing, + .clientPong, + .max + ] +} + +#endif // swift(>=4.2) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +extension StoreAndForwardMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "StoreAndForwardMessage" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "rr"), + 2: .same(proto: "stats"), + 3: .same(proto: "history") + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.rr) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._stats) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._history) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.rr != .unset { + try visitor.visitSingularEnumField(value: self.rr, fieldNumber: 1) + } + try { if let v = self._stats { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try { if let v = self._history { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: StoreAndForwardMessage, rhs: StoreAndForwardMessage) -> Bool { + if lhs.rr != rhs.rr {return false} + if lhs._stats != rhs._stats {return false} + if lhs._history != rhs._history {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension StoreAndForwardMessage.RequestResponse: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "UNSET"), + 1: .same(proto: "ROUTER_ERROR"), + 2: .same(proto: "ROUTER_HEARTBEAT"), + 3: .same(proto: "ROUTER_PING"), + 4: .same(proto: "ROUTER_PONG"), + 5: .same(proto: "ROUTER_BUSY"), + 101: .same(proto: "CLIENT_ERROR"), + 102: .same(proto: "CLIENT_HISTORY"), + 103: .same(proto: "CLIENT_STATS"), + 104: .same(proto: "CLIENT_PING"), + 105: .same(proto: "CLIENT_PONG"), + 255: .same(proto: "MAX") + ] +} + +extension StoreAndForwardMessage.Statistics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = StoreAndForwardMessage.protoMessageName + ".Statistics" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "MessagesTotal"), + 2: .same(proto: "MessagesSaved"), + 3: .same(proto: "MessagesMax"), + 4: .same(proto: "UpTime"), + 5: .same(proto: "Requests"), + 6: .same(proto: "RequestsHistory"), + 7: .same(proto: "Heartbeat"), + 8: .same(proto: "ReturnMax"), + 9: .same(proto: "ReturnWindow") + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.messagesTotal) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.messagesSaved) }() + case 3: try { try decoder.decodeSingularUInt32Field(value: &self.messagesMax) }() + case 4: try { try decoder.decodeSingularUInt32Field(value: &self.upTime) }() + case 5: try { try decoder.decodeSingularUInt32Field(value: &self.requests) }() + case 6: try { try decoder.decodeSingularUInt32Field(value: &self.requestsHistory) }() + case 7: try { try decoder.decodeSingularBoolField(value: &self.heartbeat) }() + case 8: try { try decoder.decodeSingularUInt32Field(value: &self.returnMax) }() + case 9: try { try decoder.decodeSingularUInt32Field(value: &self.returnWindow) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.messagesTotal != 0 { + try visitor.visitSingularUInt32Field(value: self.messagesTotal, fieldNumber: 1) + } + if self.messagesSaved != 0 { + try visitor.visitSingularUInt32Field(value: self.messagesSaved, fieldNumber: 2) + } + if self.messagesMax != 0 { + try visitor.visitSingularUInt32Field(value: self.messagesMax, fieldNumber: 3) + } + if self.upTime != 0 { + try visitor.visitSingularUInt32Field(value: self.upTime, fieldNumber: 4) + } + if self.requests != 0 { + try visitor.visitSingularUInt32Field(value: self.requests, fieldNumber: 5) + } + if self.requestsHistory != 0 { + try visitor.visitSingularUInt32Field(value: self.requestsHistory, fieldNumber: 6) + } + if self.heartbeat != false { + try visitor.visitSingularBoolField(value: self.heartbeat, fieldNumber: 7) + } + if self.returnMax != 0 { + try visitor.visitSingularUInt32Field(value: self.returnMax, fieldNumber: 8) + } + if self.returnWindow != 0 { + try visitor.visitSingularUInt32Field(value: self.returnWindow, fieldNumber: 9) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: StoreAndForwardMessage.Statistics, rhs: StoreAndForwardMessage.Statistics) -> Bool { + if lhs.messagesTotal != rhs.messagesTotal {return false} + if lhs.messagesSaved != rhs.messagesSaved {return false} + if lhs.messagesMax != rhs.messagesMax {return false} + if lhs.upTime != rhs.upTime {return false} + if lhs.requests != rhs.requests {return false} + if lhs.requestsHistory != rhs.requestsHistory {return false} + if lhs.heartbeat != rhs.heartbeat {return false} + if lhs.returnMax != rhs.returnMax {return false} + if lhs.returnWindow != rhs.returnWindow {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension StoreAndForwardMessage.History: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = StoreAndForwardMessage.protoMessageName + ".History" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "HistoryMessages"), + 2: .same(proto: "Window") + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.historyMessages) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.window) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.historyMessages != 0 { + try visitor.visitSingularUInt32Field(value: self.historyMessages, fieldNumber: 1) + } + if self.window != 0 { + try visitor.visitSingularUInt32Field(value: self.window, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: StoreAndForwardMessage.History, rhs: StoreAndForwardMessage.History) -> Bool { + if lhs.historyMessages != rhs.historyMessages {return false} + if lhs.window != rhs.window {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/README.md b/README.md index fc64ac8b..913b8c2c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ # Meshtastic Client - Requires SwiftLint - see https://github.com/realm/SwiftLint + +## To update protobufs: + +- install swift-protobuf: *brew install swift-protobuf* +- check out the latest commit from master branch from here: https://github.com/meshtastic/Meshtastic-protobufs in a directory that is at the same level as this project +- run: *./gen_proto.sh* +- build, test, commit changes +- You may need to run *swiftlint --fix* diff --git a/gen_protos.sh b/gen_protos.sh new file mode 100755 index 00000000..abdb0cdf --- /dev/null +++ b/gen_protos.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# simple sanity checking for repo +if [ ! -d "../Meshtastic-protobufs" ]; then + echo "Please check out the https://github.com/meshtastic/Meshtastic-protobufs the parent directory." + exit +fi + +# simple sanity checking for executable +if [ ! -x "`which protoc`" ]; then + echo "Please install switf-protobuf by running: brew install switf-proto" + exit +fi + +pdir=$(realpath "../Meshtastic-protobufs") +sdir=$(realpath "./MeshtasticClient/Protobufs") +echo "pdir:$pdir sdir:$sdir" +pfiles="radioconfig.proto channel.proto deviceonly.proto portnums.proto remote_hardware.proto environmental_measurement.proto admin.proto mqtt.proto mesh.proto storeforward.proto" +for pf in $pfiles +do + echo "Generating $pf..." + protoc --swift_out=${sdir} --proto_path=${pdir} $pf +done +echo "Done generating the swift files from the proto files." +echo "Build, test, and commit changes." From 837de346a7936cd83a44020d09384e29172b5bb3 Mon Sep 17 00:00:00 2001 From: Mike Kinney Date: Mon, 29 Nov 2021 21:35:23 -0800 Subject: [PATCH 2/2] fix comments to match filenames --- MeshtasticClient/Views/Bluetooth/Connect.swift | 2 +- MeshtasticClient/Views/Nodes/NodeList.swift | 2 +- MeshtasticClient/Views/Nodes/NodeMap.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index 637ba3cb..cc3d11d0 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -1,5 +1,5 @@ // -// DeviceBLE.swift +// Connect.swift // MeshtasticClient // // Created by Garth Vander Houwen on 8/18/21. diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index 3ad7997e..f80a5ee2 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -1,5 +1,5 @@ // -// DeviceHome.swift +// NodeList.swift // MeshtasticClient // // Created by Garth Vander Houwen on 8/7/21. diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index 665639d4..c3007cae 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -1,5 +1,5 @@ // -// DeviceMap.swift +// NodeMap.swift // MeshtasticClient // // Created by Garth Vander Houwen on 8/7/21.