mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Store and Forward updates
This commit is contained in:
parent
416d5e5f41
commit
c23e18316d
11 changed files with 156 additions and 73 deletions
|
|
@ -177,7 +177,7 @@
|
|||
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE5B4032B2279A700FCDD05 /* TraceRouteLog.swift */; };
|
||||
DDE5B4062B227E3200FCDD05 /* TraceRouteEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE5B4052B227E3200FCDD05 /* TraceRouteEntityExtension.swift */; };
|
||||
DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */; };
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForward.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */; };
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */; };
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; };
|
||||
DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */; };
|
||||
DDFFA7472B3A7F3C004730DB /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFFA7462B3A7F3C004730DB /* Bundle.swift */; };
|
||||
|
|
@ -422,7 +422,7 @@
|
|||
DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteRecorder.swift; sourceTree = "<group>"; };
|
||||
DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV4.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2462A9AEB9E00BA6931 /* MeshtasticDataModelV17.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV17.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForward.swift; sourceTree = "<group>"; };
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForwardConfig.swift; sourceTree = "<group>"; };
|
||||
DDF6B24B2A9C2FC800BA6931 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DDF924C926FBB953009FE055 /* ConnectedDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectedDevice.swift; sourceTree = "<group>"; };
|
||||
DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentConditionsCompact.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -625,7 +625,7 @@
|
|||
DD41582928585C32009B0E59 /* RangeTestConfig.swift */,
|
||||
DDC94FCD29CF55310082EA6E /* RtttlConfig.swift */,
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */,
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */,
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */,
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */,
|
||||
);
|
||||
path = Module;
|
||||
|
|
@ -1275,7 +1275,7 @@
|
|||
DD5E5210298EE33B00D21B61 /* telemetry.pb.swift in Sources */,
|
||||
DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */,
|
||||
DD5E5205298EE33B00D21B61 /* mesh.pb.swift in Sources */,
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForward.swift in Sources */,
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */,
|
||||
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */,
|
||||
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */,
|
||||
DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -2375,7 +2375,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
var sfPacket = StoreAndForward()
|
||||
sfPacket.rr = StoreAndForward.RequestResponse.clientHistory
|
||||
sfPacket.history.window = UInt32(toUser.userNode?.storeForwardConfig?.historyReturnWindow ?? 120)
|
||||
sfPacket.history.lastRequest = UInt32(toUser.userNode?.storeForwardConfig?.lastRequest?.timeIntervalSince1970 ?? Date().timeIntervalSince1970)
|
||||
sfPacket.history.lastRequest = UInt32(toUser.userNode?.storeForwardConfig?.lastRequest ?? 0)
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(toUser.num)
|
||||
meshPacket.from = UInt32(fromUser.num)
|
||||
|
|
@ -2456,10 +2456,10 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return
|
||||
}
|
||||
if routerNode.storeForwardConfig != nil {
|
||||
routerNode.storeForwardConfig?.lastRequest = Date(timeIntervalSince1970: TimeInterval(storeAndForwardMessage.history.lastRequest))
|
||||
routerNode.storeForwardConfig?.lastRequest = Int32(storeAndForwardMessage.history.lastRequest)
|
||||
} else {
|
||||
let newConfig = StoreForwardConfigEntity(context: context)
|
||||
newConfig.lastRequest = Date(timeIntervalSince1970: TimeInterval(storeAndForwardMessage.history.lastRequest))
|
||||
newConfig.lastRequest = Int32(storeAndForwardMessage.history.lastRequest)
|
||||
routerNode.storeForwardConfig = newConfig
|
||||
}
|
||||
|
||||
|
|
@ -2487,6 +2487,10 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
case .UNRECOGNIZED:
|
||||
textMessageAppPacket(packet: packet, connectedNode: connectedNodeNum, context: context)
|
||||
MeshLogger.log("📮 Store and Forward \(storeAndForwardMessage.rr) message received \(storeAndForwardMessage)")
|
||||
case .routerTextDirect:
|
||||
MeshLogger.log("💬 Store and Forward \(storeAndForwardMessage.rr) message received \(storeAndForwardMessage)")
|
||||
case .routerTextBroadcast:
|
||||
MeshLogger.log("✉️ Store and Forward \(storeAndForwardMessage.rr) message received \(storeAndForwardMessage)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -723,6 +723,10 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
|
||||
func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
//let storeForwardMessage = packet.decoded.payload.text/// String(bytes: packet.decoded.payload.text, encoding: .utf8)
|
||||
|
||||
|
||||
|
||||
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
|
||||
|
||||
MeshLogger.log("💬 \("mesh.log.textmessage.received".localized)")
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@
|
|||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
|
|
@ -412,4 +412,4 @@
|
|||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
</entity>
|
||||
</model>
|
||||
</model>
|
||||
|
|
@ -198,64 +198,63 @@ struct Config {
|
|||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// Client device role
|
||||
/// Description: App connected or stand alone messaging device.
|
||||
/// Technical Details: Default Role
|
||||
case client // = 0
|
||||
|
||||
///
|
||||
/// Client Mute device role
|
||||
/// Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.
|
||||
/// Description: Device that does not forward packets from other devices.
|
||||
case clientMute // = 1
|
||||
|
||||
///
|
||||
/// Router device role.
|
||||
/// Mesh packets will prefer to be routed over this node. This node will not be used by client apps.
|
||||
/// The wifi/ble radios and the oled screen will be put to sleep.
|
||||
/// Description: Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list.
|
||||
/// Technical Details: Mesh packets will prefer to be routed over this node. This node will not be used by client apps.
|
||||
/// The wifi radio and the oled screen will be put to sleep.
|
||||
/// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh.
|
||||
case router // = 2
|
||||
|
||||
///
|
||||
/// Router Client device role
|
||||
/// Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client.
|
||||
/// Description: Combination of both ROUTER and CLIENT. Not for mobile devices.
|
||||
case routerClient // = 3
|
||||
|
||||
///
|
||||
/// Repeater device role
|
||||
/// Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||
/// Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
||||
/// Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||
/// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
||||
case repeater // = 4
|
||||
|
||||
///
|
||||
/// Tracker device role
|
||||
/// Position Mesh packets will be prioritized higher and sent more frequently by default.
|
||||
/// Description: Broadcasts GPS position packets as priority.
|
||||
/// Technical Details: Position Mesh packets will be prioritized higher and sent more frequently by default.
|
||||
/// When used in conjunction with power.is_power_saving = true, nodes will wake up,
|
||||
/// send position, and then sleep for position.position_broadcast_secs seconds.
|
||||
case tracker // = 5
|
||||
|
||||
///
|
||||
/// Sensor device role
|
||||
/// Telemetry Mesh packets will be prioritized higher and sent more frequently by default.
|
||||
/// Description: Broadcasts telemetry packets as priority.
|
||||
/// Technical Details: Telemetry Mesh packets will be prioritized higher and sent more frequently by default.
|
||||
/// When used in conjunction with power.is_power_saving = true, nodes will wake up,
|
||||
/// send environment telemetry, and then sleep for telemetry.environment_update_interval seconds.
|
||||
case sensor // = 6
|
||||
|
||||
///
|
||||
/// TAK device role
|
||||
/// Used for nodes dedicated for connection to an ATAK EUD.
|
||||
/// Description: Optimized for ATAK system communication, reduces routine broadcasts.
|
||||
/// Technical Details: Used for nodes dedicated for connection to an ATAK EUD.
|
||||
/// Turns off many of the routine broadcasts to favor CoT packet stream
|
||||
/// from the Meshtastic ATAK plugin -> IMeshService -> Node
|
||||
case tak // = 7
|
||||
|
||||
///
|
||||
/// Client Hidden device role
|
||||
/// Used for nodes that "only speak when spoken to"
|
||||
/// Description: Device that only broadcasts as needed for stealth or power savings.
|
||||
/// Technical Details: Used for nodes that "only speak when spoken to"
|
||||
/// Turns all of the routine broadcasts but allows for ad-hoc communication
|
||||
/// Still rebroadcasts, but with local only rebroadcast mode (known meshes only)
|
||||
/// Can be used for clandestine operation or to dramatically reduce airtime / power consumption
|
||||
case clientHidden // = 8
|
||||
|
||||
///
|
||||
/// Lost and Found device role
|
||||
/// Used to automatically send a text message to the mesh
|
||||
/// Description: Broadcasts location as message to default channel regularly for to assist with device recovery.
|
||||
/// Technical Details: Used to automatically send a text message to the mesh
|
||||
/// with the current position of the device on a frequent interval:
|
||||
/// "I'm lost! Position: lat / long"
|
||||
case lostAndFound // = 9
|
||||
|
|
@ -419,6 +418,10 @@ struct Config {
|
|||
/// Set where GPS is enabled, disabled, or not present
|
||||
var gpsMode: Config.PositionConfig.GpsMode = .disabled
|
||||
|
||||
///
|
||||
/// Set GPS precision in bits per channel, or 0 for disabled
|
||||
var channelPrecision: [UInt32] = []
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -1838,6 +1841,7 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
11: .standard(proto: "broadcast_smart_minimum_interval_secs"),
|
||||
12: .standard(proto: "gps_en_gpio"),
|
||||
13: .standard(proto: "gps_mode"),
|
||||
14: .standard(proto: "channel_precision"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1859,6 +1863,7 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
case 11: try { try decoder.decodeSingularUInt32Field(value: &self.broadcastSmartMinimumIntervalSecs) }()
|
||||
case 12: try { try decoder.decodeSingularUInt32Field(value: &self.gpsEnGpio) }()
|
||||
case 13: try { try decoder.decodeSingularEnumField(value: &self.gpsMode) }()
|
||||
case 14: try { try decoder.decodeRepeatedUInt32Field(value: &self.channelPrecision) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1904,6 +1909,9 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
if self.gpsMode != .disabled {
|
||||
try visitor.visitSingularEnumField(value: self.gpsMode, fieldNumber: 13)
|
||||
}
|
||||
if !self.channelPrecision.isEmpty {
|
||||
try visitor.visitPackedUInt32Field(value: self.channelPrecision, fieldNumber: 14)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -1921,6 +1929,7 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
if lhs.broadcastSmartMinimumIntervalSecs != rhs.broadcastSmartMinimumIntervalSecs {return false}
|
||||
if lhs.gpsEnGpio != rhs.gpsEnGpio {return false}
|
||||
if lhs.gpsMode != rhs.gpsMode {return false}
|
||||
if lhs.channelPrecision != rhs.channelPrecision {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
|
||||
///
|
||||
/// Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT
|
||||
case heltecWirelessTracker // = 48
|
||||
/// Newer V1.1, version is written on the PCB near the display.
|
||||
case heltecWirelessTrackerV11 // = 48
|
||||
|
||||
///
|
||||
/// Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display
|
||||
|
|
@ -246,6 +247,11 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// Flex connector marking is FPC-7528B
|
||||
case heltecWirelessPaperV10 // = 57
|
||||
|
||||
///
|
||||
/// Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT
|
||||
/// Older "V1.0" Variant
|
||||
case heltecWirelessTrackerV10 // = 58
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
|
|
@ -301,7 +307,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 45: self = .betafpv2400Tx
|
||||
case 46: self = .betafpv900NanoTx
|
||||
case 47: self = .rpiPico
|
||||
case 48: self = .heltecWirelessTracker
|
||||
case 48: self = .heltecWirelessTrackerV11
|
||||
case 49: self = .heltecWirelessPaper
|
||||
case 50: self = .tDeck
|
||||
case 51: self = .tWatchS3
|
||||
|
|
@ -311,6 +317,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 55: self = .esp32S3Pico
|
||||
case 56: self = .chatter2
|
||||
case 57: self = .heltecWirelessPaperV10
|
||||
case 58: self = .heltecWirelessTrackerV10
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -360,7 +367,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .betafpv2400Tx: return 45
|
||||
case .betafpv900NanoTx: return 46
|
||||
case .rpiPico: return 47
|
||||
case .heltecWirelessTracker: return 48
|
||||
case .heltecWirelessTrackerV11: return 48
|
||||
case .heltecWirelessPaper: return 49
|
||||
case .tDeck: return 50
|
||||
case .tWatchS3: return 51
|
||||
|
|
@ -370,6 +377,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .esp32S3Pico: return 55
|
||||
case .chatter2: return 56
|
||||
case .heltecWirelessPaperV10: return 57
|
||||
case .heltecWirelessTrackerV10: return 58
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -424,7 +432,7 @@ extension HardwareModel: CaseIterable {
|
|||
.betafpv2400Tx,
|
||||
.betafpv900NanoTx,
|
||||
.rpiPico,
|
||||
.heltecWirelessTracker,
|
||||
.heltecWirelessTrackerV11,
|
||||
.heltecWirelessPaper,
|
||||
.tDeck,
|
||||
.tWatchS3,
|
||||
|
|
@ -434,6 +442,7 @@ extension HardwareModel: CaseIterable {
|
|||
.esp32S3Pico,
|
||||
.chatter2,
|
||||
.heltecWirelessPaperV10,
|
||||
.heltecWirelessTrackerV10,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -2613,7 +2622,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
45: .same(proto: "BETAFPV_2400_TX"),
|
||||
46: .same(proto: "BETAFPV_900_NANO_TX"),
|
||||
47: .same(proto: "RPI_PICO"),
|
||||
48: .same(proto: "HELTEC_WIRELESS_TRACKER"),
|
||||
48: .same(proto: "HELTEC_WIRELESS_TRACKER_V1_1"),
|
||||
49: .same(proto: "HELTEC_WIRELESS_PAPER"),
|
||||
50: .same(proto: "T_DECK"),
|
||||
51: .same(proto: "T_WATCH_S3"),
|
||||
|
|
@ -2623,6 +2632,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
55: .same(proto: "ESP32_S3_PICO"),
|
||||
56: .same(proto: "CHATTER_2"),
|
||||
57: .same(proto: "HELTEC_WIRELESS_PAPER_V1_0"),
|
||||
58: .same(proto: "HELTEC_WIRELESS_TRACKER_V1_0"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,14 @@ struct StoreAndForward {
|
|||
/// Router is responding to a request for stats.
|
||||
case routerStats // = 7
|
||||
|
||||
///
|
||||
/// Router sends a text message from its history that was a direct message.
|
||||
case routerTextDirect // = 8
|
||||
|
||||
///
|
||||
/// Router sends a text message from its history that was a broadcast.
|
||||
case routerTextBroadcast // = 9
|
||||
|
||||
///
|
||||
/// Client is an in error state.
|
||||
case clientError // = 64
|
||||
|
|
@ -200,6 +208,8 @@ struct StoreAndForward {
|
|||
case 5: self = .routerBusy
|
||||
case 6: self = .routerHistory
|
||||
case 7: self = .routerStats
|
||||
case 8: self = .routerTextDirect
|
||||
case 9: self = .routerTextBroadcast
|
||||
case 64: self = .clientError
|
||||
case 65: self = .clientHistory
|
||||
case 66: self = .clientStats
|
||||
|
|
@ -220,6 +230,8 @@ struct StoreAndForward {
|
|||
case .routerBusy: return 5
|
||||
case .routerHistory: return 6
|
||||
case .routerStats: return 7
|
||||
case .routerTextDirect: return 8
|
||||
case .routerTextBroadcast: return 9
|
||||
case .clientError: return 64
|
||||
case .clientHistory: return 65
|
||||
case .clientStats: return 66
|
||||
|
|
@ -341,6 +353,8 @@ extension StoreAndForward.RequestResponse: CaseIterable {
|
|||
.routerBusy,
|
||||
.routerHistory,
|
||||
.routerStats,
|
||||
.routerTextDirect,
|
||||
.routerTextBroadcast,
|
||||
.clientError,
|
||||
.clientHistory,
|
||||
.clientStats,
|
||||
|
|
@ -482,6 +496,8 @@ extension StoreAndForward.RequestResponse: SwiftProtobuf._ProtoNameProviding {
|
|||
5: .same(proto: "ROUTER_BUSY"),
|
||||
6: .same(proto: "ROUTER_HISTORY"),
|
||||
7: .same(proto: "ROUTER_STATS"),
|
||||
8: .same(proto: "ROUTER_TEXT_DIRECT"),
|
||||
9: .same(proto: "ROUTER_TEXT_BROADCAST"),
|
||||
64: .same(proto: "CLIENT_ERROR"),
|
||||
65: .same(proto: "CLIENT_HISTORY"),
|
||||
66: .same(proto: "CLIENT_STATS"),
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ struct NodeList: View {
|
|||
} label: {
|
||||
Label("Trace Route", systemImage: "signpost.right.and.left")
|
||||
}
|
||||
if true {//node?.storeForwardConfig != nil && node.storeForwardConfig?.isRouter ?? false {
|
||||
if node.isStoreForwardRouter {
|
||||
|
||||
Button {
|
||||
let success = bleManager.requestStoreAndForwardClientHistory(fromUser: connectedNode!.user!, toUser: node.user!)
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ struct LoRaConfig: View {
|
|||
.font(.caption)
|
||||
|
||||
HStack {
|
||||
Text("LoRa Frequency Slot")
|
||||
Text("Frequency Slot")
|
||||
.fixedSize()
|
||||
TextField("Frequency Slot", value: $channelNum, formatter: formatter)
|
||||
.toolbar {
|
||||
|
|
@ -175,6 +175,7 @@ struct LoRaConfig: View {
|
|||
.keyboardType(.decimalPad)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.focused($focusedField, equals: .channelNum)
|
||||
.disabled(overrideFrequency > 0.0)
|
||||
}
|
||||
Text("This determines the actual frequency you are transmitting on in the band. If set to 0 this value will be calculated automatically based on the primary channel name.")
|
||||
.font(.caption)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ struct StoreForwardConfig: View {
|
|||
@State var hasChanges: Bool = false
|
||||
/// Enable the Store and Forward Module
|
||||
@State var enabled = false
|
||||
/// Is a S&F Router
|
||||
@State var isRouter = false
|
||||
/// Send a Heartbeat
|
||||
@State var heartbeat: Bool = false
|
||||
/// Number of Records
|
||||
|
|
@ -56,38 +58,61 @@ struct StoreForwardConfig: View {
|
|||
.foregroundColor(.orange)
|
||||
}
|
||||
Section(header: Text("options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
Label("enabled", systemImage: "envelope.arrow.triangle.branch")
|
||||
Text("Enables the store and forward module.")
|
||||
.font(.caption)
|
||||
}
|
||||
Toggle(isOn: $heartbeat) {
|
||||
Label("storeforward.heartbeat", systemImage: "waveform.path.ecg")
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
if enabled {
|
||||
HStack {
|
||||
Picker(selection: $isRouter, label: Text("Role")) {
|
||||
Text("Client")
|
||||
.tag(false)
|
||||
Text("Router")
|
||||
.tag(true)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.padding(.top, 5)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
}
|
||||
Picker("Number of records", selection: $records) {
|
||||
Text("unset").tag(0)
|
||||
Text("25").tag(25)
|
||||
Text("50").tag(50)
|
||||
Text("75").tag(75)
|
||||
Text("100").tag(100)
|
||||
}
|
||||
|
||||
if isRouter {
|
||||
Section(header: Text("options")) {
|
||||
Toggle(isOn: $heartbeat) {
|
||||
Label("storeforward.heartbeat", systemImage: "waveform.path.ecg")
|
||||
}
|
||||
Picker("Number of records", selection: $records) {
|
||||
Text("unset").tag(0)
|
||||
Text("25").tag(25)
|
||||
Text("50").tag(50)
|
||||
Text("75").tag(75)
|
||||
Text("100").tag(100)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("History Return Max", selection: $historyReturnMax ) {
|
||||
Text("unset").tag(0)
|
||||
Text("25").tag(25)
|
||||
Text("50").tag(50)
|
||||
Text("75").tag(75)
|
||||
Text("100").tag(100)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("History Return Window", selection: $historyReturnWindow ) {
|
||||
Text("unset").tag(0)
|
||||
Text("One Minute").tag(60)
|
||||
Text("Five Minutes").tag(300)
|
||||
Text("Ten Minutes").tag(600)
|
||||
Text("Fifteen Minutes").tag(900)
|
||||
Text("Thirty Minutes").tag(1800)
|
||||
Text("One Hour").tag(3600)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("History Return Max", selection: $historyReturnMax ) {
|
||||
Text("unset").tag(0)
|
||||
Text("25").tag(25)
|
||||
Text("50").tag(50)
|
||||
Text("75").tag(75)
|
||||
Text("100").tag(100)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("History Return Window", selection: $historyReturnWindow ) {
|
||||
Text("unset").tag(0)
|
||||
Text("One Minute").tag(60)
|
||||
Text("Five Minutes").tag(300)
|
||||
Text("Ten Minutes").tag(600)
|
||||
Text("Fifteen Minutes").tag(900)
|
||||
Text("Thirty Minutes").tag(1800)
|
||||
Text("One Hour").tag(3600)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
|
|
@ -113,6 +138,18 @@ struct StoreForwardConfig: View {
|
|||
let nodeName = node?.user?.longName ?? "unknown".localized
|
||||
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
|
||||
Button(buttonText) {
|
||||
|
||||
/// Let the user set isRouter for the connected node, for nodes on the mesh set isRouter based
|
||||
/// on receipt of a primary heartbeat
|
||||
if connectedNode?.num ?? 0 == node?.num ?? -1 {
|
||||
connectedNode?.storeForwardConfig?.isRouter = isRouter
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
print("Failed to save isRouter")
|
||||
}
|
||||
}
|
||||
|
||||
var sfc = ModuleConfig.StoreForwardConfig()
|
||||
sfc.enabled = self.enabled
|
||||
sfc.heartbeat = self.heartbeat
|
||||
|
|
@ -125,7 +162,8 @@ struct StoreForwardConfig: View {
|
|||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
goBack()
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message: {
|
||||
|
|
@ -178,6 +216,7 @@ struct StoreForwardConfig: View {
|
|||
}
|
||||
func setStoreAndForwardValues() {
|
||||
self.enabled = (node?.storeForwardConfig?.enabled ?? false)
|
||||
self.isRouter = (node?.storeForwardConfig?.isRouter ?? false)
|
||||
self.heartbeat = (node?.storeForwardConfig?.heartbeat ?? true)
|
||||
self.records = Int(node?.storeForwardConfig?.records ?? 50)
|
||||
self.historyReturnMax = Int(node?.storeForwardConfig?.historyReturnMax ?? 100)
|
||||
|
|
@ -69,16 +69,16 @@
|
|||
"device.config"="Device Config";
|
||||
"device.metrics.delete"="Delete all device metrics?";
|
||||
"device.metrics.log"="Device Metrics Log";
|
||||
"device.role.client"="App connected or stand alone messaging client.";
|
||||
"device.role.clientmute"="Client that does not forward packets from other devices.";
|
||||
"device.role.clienthidden"="Client that only broadcasts as needed for stealth or power savings.";
|
||||
"device.role.tracker"="Prioritizes broadcasting GPS position packets.";
|
||||
"device.role.lostandfound"="Broadcasts location as message to default channel regularly for to assist with node recovery.";
|
||||
"device.role.sensor"="Prioritizes broadcasting telemetry packets.";
|
||||
"device.role.client"="App connected or stand alone messaging device.";
|
||||
"device.role.clientmute"="Device that does not forward packets from other devices.";
|
||||
"device.role.clienthidden"="Device that only broadcasts as needed for stealth or power savings.";
|
||||
"device.role.tracker"="Broadcasts GPS position packets as priority.";
|
||||
"device.role.lostandfound"="Broadcasts location as message to default channel regularly for to assist with device recovery.";
|
||||
"device.role.sensor"="Broadcasts telemetry packets as priority.";
|
||||
"device.role.tak"="Optimized for ATAK system communication, reduces routine broadcasts.";
|
||||
"device.role.repeater"="Infrastructure node for extending mesh network coverage by relaying messages with minimal overhead. Not visible in Nodes list. Best positioned in strategic locations to maximize the network's overall coverage. Device is not shown in topology.";
|
||||
"device.role.router"="Infrastructure node for on extending mesh network coverage by relaying messages. Visible in Nodes list. Best positioned in strategic locations to maximize the network's overall coverage. Device is shown in topology.";
|
||||
"device.role.routerclient"="Combination of both ROUTER and CLIENT. Not for mobile nodes.";
|
||||
"device.role.repeater"="Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.";
|
||||
"device.role.router"="Infrastructure node for extending network coverage by relaying messages. Visible in Nodes list.";
|
||||
"device.role.routerclient"="Combination of both ROUTER and CLIENT. Not for mobile devices.";
|
||||
"direct.messages"="Direct Messages";
|
||||
"dismiss.keyboard"="Dismiss";
|
||||
"display"="Display (Device Screen)";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue