2022-06-12 01:25:42 -07:00
//
// L o R a C o n f i g . s w i f t
// M e s h t a s t i c A p p l e
//
// C o p y r i g h t ( c ) b y G a r t h V a n d e r H o u w e n 6 / 1 1 / 2 2 .
//
import SwiftUI
2023-03-05 15:13:23 -08:00
import CoreData
2022-06-12 01:25:42 -07:00
struct LoRaConfig : View {
2023-03-06 10:33:18 -08:00
2023-03-05 15:13:23 -08:00
enum Field : Hashable {
case channelNum
2023-10-25 18:54:52 -07:00
case frequencyOverride
2023-03-05 15:13:23 -08:00
}
2023-03-05 23:01:09 -08:00
2023-03-05 05:42:25 -08:00
let formatter : NumberFormatter = {
let formatter = NumberFormatter ( )
formatter . numberStyle = . decimal
2023-03-05 23:39:06 -08:00
formatter . groupingSeparator = " "
2023-03-05 05:42:25 -08:00
return formatter
} ( )
2023-03-06 10:33:18 -08:00
2022-06-12 01:25:42 -07:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-12-09 18:19:00 -08:00
@ Environment ( \ . dismiss ) private var goBack
2023-03-05 15:13:23 -08:00
@ FocusState var focusedField : Field ?
2023-03-06 10:33:18 -08:00
2022-07-07 00:29:52 -07:00
var node : NodeInfoEntity ?
2023-03-06 10:33:18 -08:00
2022-07-26 09:24:35 -07:00
@ State var isPresentingSaveConfirm = false
2022-06-21 01:12:08 -07:00
@ State var hasChanges = false
2023-03-19 18:37:23 -07:00
@ State var region : Int = 0
2023-03-05 15:13:23 -08:00
@ State var modemPreset = 0
2023-04-22 22:55:58 -07:00
@ State var hopLimit = 3
2023-03-05 15:13:23 -08:00
@ State var txPower = 0
2022-09-18 08:56:08 -07:00
@ State var txEnabled = true
@ State var usePreset = true
2023-03-05 15:13:23 -08:00
@ State var channelNum = 0
2023-03-05 23:01:09 -08:00
@ State var bandwidth = 0
2023-03-05 15:13:23 -08:00
@ State var spreadFactor = 0
@ State var codingRate = 0
2023-03-08 21:14:46 -08:00
@ State var rxBoostedGain = false
2023-10-25 18:54:52 -07:00
@ State var overrideFrequency : Float = 0.0
let floatFormatter : NumberFormatter = {
let formatter = NumberFormatter ( )
formatter . numberStyle = . decimal
return formatter
} ( )
2023-03-06 10:33:18 -08:00
2022-06-12 01:25:42 -07:00
var body : some View {
2023-03-06 10:33:18 -08:00
2022-06-12 01:25:42 -07:00
VStack {
Form {
2023-03-13 19:17:43 -07:00
if node != nil && node ? . metadata = = nil && node ? . num ? ? 0 != bleManager . connectedPeripheral ? . num ? ? 0 {
2023-03-13 09:50:24 -07:00
Text ( " There has been no response to a request for device metadata over the admin channel for this node. " )
. font ( . callout )
. foregroundColor ( . orange )
2023-03-14 12:44:10 -07:00
2023-03-13 19:17:43 -07:00
} else if node != nil && node ? . num ? ? 0 != bleManager . connectedPeripheral ? . num ? ? 0 {
// L e t u s e r s k n o w w h a t i s g o i n g o n i f t h e y a r e u s i n g r e m o t e a d m i n a n d d o n ' t h a v e t h e c o n f i g y e t
2023-03-14 12:44:10 -07:00
if node ? . loRaConfig = = nil {
2023-03-13 19:17:43 -07:00
Text ( " LoRa config data was requested over the admin channel but no response has been returned from the remote node. You can check the status of admin message requests in the admin message log. " )
2023-03-11 09:26:52 -08:00
. font ( . callout )
. foregroundColor ( . orange )
} else {
Text ( " Remote administration for: \( node ? . user ? . longName ? ? " Unknown " ) " )
. font ( . title3 )
2023-03-19 18:37:23 -07:00
. onAppear {
setLoRaValues ( )
}
2023-03-11 09:26:52 -08:00
}
2023-03-14 12:44:10 -07:00
} else if node != nil && node ? . num ? ? 0 = = bleManager . connectedPeripheral ? . num ? ? 0 {
2023-03-11 09:26:52 -08:00
Text ( " Configuration for: \( node ? . user ? . longName ? ? " Unknown " ) " )
. font ( . title3 )
} else {
Text ( " Please connect to a radio to configure settings. " )
. font ( . callout )
. foregroundColor ( . orange )
}
2023-03-05 04:47:17 -08:00
Section ( header : Text ( " Options " ) ) {
2023-03-05 05:01:10 -08:00
2022-06-12 01:25:42 -07:00
Picker ( " Region " , selection : $ region ) {
ForEach ( RegionCodes . allCases ) { r in
Text ( r . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2023-03-05 15:13:23 -08:00
. fixedSize ( )
2023-03-06 10:33:18 -08:00
2022-06-20 12:51:55 -07:00
Text ( " The region where you will be using your radios. " )
2022-06-12 01:25:42 -07:00
. font ( . caption )
2023-03-06 10:33:18 -08:00
2023-03-05 04:47:17 -08:00
Toggle ( isOn : $ usePreset ) {
Label ( " Use Preset " , systemImage : " list.bullet.rectangle " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2023-03-06 10:33:18 -08:00
2023-03-05 04:47:17 -08:00
if usePreset {
Picker ( " Presets " , selection : $ modemPreset ) {
ForEach ( ModemPresets . allCases ) { m in
Text ( m . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2023-03-05 15:13:23 -08:00
. fixedSize ( )
2023-03-05 04:47:17 -08:00
Text ( " Available modem presets, default is Long Fast. " )
. font ( . caption )
2023-03-05 05:01:10 -08:00
}
}
Section ( header : Text ( " Advanced " ) ) {
2023-03-06 10:33:18 -08:00
2023-03-05 05:01:10 -08:00
Toggle ( isOn : $ txEnabled ) {
Label ( " Transmit Enabled " , systemImage : " waveform.path " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2023-03-06 10:33:18 -08:00
2023-03-05 05:01:10 -08:00
if ! usePreset {
2023-03-05 05:42:25 -08:00
HStack {
2023-04-18 00:09:13 -07:00
Picker ( " Bandwidth " , selection : $ bandwidth ) {
ForEach ( Bandwidths . allCases ) { bw in
Text ( bw . description )
. tag ( bw . rawValue = = 250 ? 0 : bw . rawValue )
}
2023-03-05 16:01:39 -08:00
}
2023-03-05 05:42:25 -08:00
}
HStack {
2023-03-05 16:01:39 -08:00
Picker ( " Spread Factor " , selection : $ spreadFactor ) {
ForEach ( 7. . < 13 ) {
Text ( " \( $0 ) " )
. tag ( $0 = = 12 ? 0 : $0 )
2023-03-05 15:13:23 -08:00
}
2023-03-05 16:01:39 -08:00
}
2023-03-05 05:42:25 -08:00
}
HStack {
2023-03-05 16:01:39 -08:00
Picker ( " Coding Rate " , selection : $ codingRate ) {
ForEach ( 5. . < 9 ) {
Text ( " \( $0 ) " )
. tag ( $0 = = 8 ? 0 : $0 )
2023-03-05 15:13:23 -08:00
}
2023-03-05 16:01:39 -08:00
}
2023-03-05 05:42:25 -08:00
}
2022-06-12 01:25:42 -07:00
}
2023-03-05 05:01:10 -08:00
2023-04-18 00:09:13 -07:00
Picker ( " Number of hops " , selection : $ hopLimit ) {
2023-03-05 23:39:06 -08:00
ForEach ( 1. . < 8 ) {
Text ( " \( $0 ) " )
2023-04-22 22:55:58 -07:00
. tag ( $0 = = 0 ? 3 : $0 )
2022-06-12 22:07:58 -07:00
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2023-08-14 14:41:26 -07:00
Text ( " Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. " )
2022-06-20 12:51:55 -07:00
. font ( . caption )
2023-03-06 10:33:18 -08:00
2023-03-05 05:42:25 -08:00
HStack {
Text ( " LoRa Channel Number " )
2023-03-05 15:13:23 -08:00
. fixedSize ( )
2023-03-05 05:42:25 -08:00
TextField ( " Channel Number " , value : $ channelNum , formatter : formatter )
2023-03-05 15:13:23 -08:00
. toolbar {
ToolbarItemGroup ( placement : . keyboard ) {
Button ( " dismiss.keyboard " ) {
focusedField = nil
}
. font ( . subheadline )
}
}
. keyboardType ( . decimalPad )
. scrollDismissesKeyboard ( . immediately )
. focused ( $ focusedField , equals : . channelNum )
2023-03-05 04:47:17 -08:00
}
2023-03-08 21:14:46 -08:00
Text ( " This determines the actual frequency you are transmitting on in the band. " )
2023-03-05 04:47:17 -08:00
. font ( . caption )
2023-03-08 21:14:46 -08:00
Toggle ( isOn : $ rxBoostedGain ) {
Label ( " RX Boosted Gain " , systemImage : " waveform.badge.plus " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2023-10-25 18:54:52 -07:00
HStack {
2023-11-08 22:01:24 -08:00
Label ( " Frequency Override " , systemImage : " waveform.path.ecg " )
2023-10-25 18:54:52 -07:00
Spacer ( )
TextField ( " Frequency Override " , value : $ overrideFrequency , formatter : floatFormatter )
. keyboardType ( . decimalPad )
. scrollDismissesKeyboard ( . immediately )
. focused ( $ focusedField , equals : . frequencyOverride )
}
2023-11-26 12:54:45 -08:00
HStack {
Image ( systemName : " antenna.radiowaves.left.and.right " )
. foregroundColor ( . accentColor )
Stepper ( " \( txPower ) db Transmit Power " , value : $ txPower , in : 1. . . 30 , step : 1 )
. padding ( 5 )
}
2022-06-12 22:07:58 -07:00
}
2022-06-12 01:25:42 -07:00
}
2023-01-31 22:08:03 -08:00
. disabled ( self . bleManager . connectedPeripheral = = nil || node ? . loRaConfig = = nil )
2023-03-06 10:33:18 -08:00
2022-06-20 00:13:04 -07:00
Button {
2022-06-20 12:51:55 -07:00
isPresentingSaveConfirm = true
2022-06-20 00:13:04 -07:00
} label : {
2022-12-12 20:35:38 -08:00
Label ( " save " , systemImage : " square.and.arrow.down " )
2022-06-20 00:13:04 -07:00
}
. disabled ( bleManager . connectedPeripheral = = nil || ! hasChanges )
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . large )
. padding ( )
2022-06-20 12:51:55 -07:00
. confirmationDialog (
2022-12-30 11:08:59 -08:00
" are.you.sure " ,
2022-09-23 21:41:07 -07:00
isPresented : $ isPresentingSaveConfirm ,
titleVisibility : . visible
2022-06-20 12:51:55 -07:00
) {
2023-05-05 09:27:24 -07:00
let nodeName = node ? . user ? . longName ? ? " unknown " . localized
let buttonText = String . localizedStringWithFormat ( " save.config %@ " . localized , nodeName )
2023-01-09 18:34:43 -08:00
Button ( buttonText ) {
2023-03-14 19:16:04 -07:00
let connectedNode = getNodeInfo ( id : bleManager . connectedPeripheral ? . num ? ? 0 , context : context )
2023-02-06 18:45:03 -08:00
if connectedNode != nil {
var lc = Config . LoRaConfig ( )
lc . hopLimit = UInt32 ( hopLimit )
lc . region = RegionCodes ( rawValue : region ) ! . protoEnumValue ( )
lc . modemPreset = ModemPresets ( rawValue : modemPreset ) ! . protoEnumValue ( )
2023-03-05 04:47:17 -08:00
lc . usePreset = usePreset
lc . txEnabled = txEnabled
2023-11-26 12:54:45 -08:00
lc . txPower = Int32 ( txPower )
2023-03-05 04:47:17 -08:00
lc . channelNum = UInt32 ( channelNum )
2023-03-05 05:42:25 -08:00
lc . bandwidth = UInt32 ( bandwidth )
lc . codingRate = UInt32 ( codingRate )
lc . spreadFactor = UInt32 ( spreadFactor )
2023-03-08 21:14:46 -08:00
lc . sx126XRxBoostedGain = rxBoostedGain
2023-11-08 22:01:24 -08:00
lc . overrideFrequency = overrideFrequency
2023-03-15 09:27:33 -07:00
let adminMessageId = bleManager . saveLoRaConfig ( config : lc , fromUser : connectedNode ! . user ! , toUser : node ! . user ! , adminIndex : connectedNode ? . myInfo ? . adminIndex ? ? 0 )
2023-02-06 18:45:03 -08:00
if adminMessageId > 0 {
// S h o u l d s h o w a s a v e d s u c c e s s f u l l y a l e r t o n c e I k n o w t h a t t o b e t r u e
// f o r n o w j u s t d i s a b l e t h e b u t t o n a f t e r a s u c c e s s f u l s a v e
hasChanges = false
goBack ( )
}
2022-06-20 12:51:55 -07:00
}
}
2022-09-23 21:41:07 -07:00
} message : {
2022-12-30 11:08:59 -08:00
Text ( " config.save.confirm " )
2022-06-20 12:51:55 -07:00
}
2022-06-12 01:25:42 -07:00
}
2022-12-13 07:49:46 -08:00
. navigationTitle ( " lora.config " )
2022-06-12 01:25:42 -07:00
. navigationBarItems ( trailing :
2023-03-05 04:47:17 -08:00
ZStack {
2023-09-02 17:37:35 -07:00
ConnectedDevice ( bluetoothOn : bleManager . isSwitchedOn , deviceConnected : bleManager . connectedPeripheral != nil , name : ( bleManager . connectedPeripheral != nil ) ? bleManager . connectedPeripheral . shortName : " ? " )
2022-06-12 01:25:42 -07:00
} )
. onAppear {
2023-03-06 10:33:18 -08:00
2022-07-26 09:24:35 -07:00
self . bleManager . context = context
2023-03-19 18:37:23 -07:00
setLoRaValues ( )
2023-03-06 10:33:18 -08:00
2023-01-20 19:14:49 -08:00
// N e e d t o r e q u e s t a L o R a C o n f i g f r o m t h e r e m o t e n o d e b e f o r e a l l o w i n g c h a n g e s
2023-01-31 10:50:17 -08:00
if bleManager . connectedPeripheral != nil && node ? . loRaConfig = = nil {
2023-01-23 17:56:04 -08:00
print ( " empty lora config " )
2023-01-31 10:50:17 -08:00
let connectedNode = getNodeInfo ( id : bleManager . connectedPeripheral . num , context : context )
2023-03-06 10:33:18 -08:00
if node != nil && connectedNode != nil {
2023-02-06 18:45:03 -08:00
_ = bleManager . requestLoRaConfig ( fromUser : connectedNode ! . user ! , toUser : node ! . user ! , adminIndex : connectedNode ? . myInfo ? . adminIndex ? ? 0 )
2023-01-31 10:50:17 -08:00
}
2023-01-20 19:14:49 -08:00
}
2022-06-12 01:25:42 -07:00
}
2022-06-20 22:48:38 -07:00
. onChange ( of : region ) { newRegion in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . loRaConfig != nil {
2022-07-07 00:29:52 -07:00
if newRegion != node ! . loRaConfig ! . regionCode { hasChanges = true }
2022-06-20 23:48:47 -07:00
}
2022-06-20 00:13:04 -07:00
}
2023-03-05 04:47:17 -08:00
. onChange ( of : usePreset ) { newUsePreset in
if node != nil && node ! . loRaConfig != nil {
if newUsePreset != node ! . loRaConfig ! . usePreset { hasChanges = true }
}
}
2022-06-20 00:13:04 -07:00
. onChange ( of : modemPreset ) { newModemPreset in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . loRaConfig != nil {
2022-07-07 00:29:52 -07:00
if newModemPreset != node ! . loRaConfig ! . modemPreset { hasChanges = true }
2022-06-20 23:48:47 -07:00
}
2022-06-20 00:13:04 -07:00
}
. onChange ( of : hopLimit ) { newHopLimit in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . loRaConfig != nil {
2022-07-07 00:29:52 -07:00
if newHopLimit != node ! . loRaConfig ! . hopLimit { hasChanges = true }
2022-06-20 23:48:47 -07:00
}
2022-06-20 00:13:04 -07:00
}
2023-03-05 04:47:17 -08:00
. onChange ( of : channelNum ) { newChannelNum in
if node != nil && node ! . loRaConfig != nil {
if newChannelNum != node ! . loRaConfig ! . channelNum { hasChanges = true }
}
}
2023-03-05 05:42:25 -08:00
. onChange ( of : bandwidth ) { newBandwidth in
if node != nil && node ! . loRaConfig != nil {
2023-03-05 23:01:09 -08:00
if newBandwidth != node ! . loRaConfig ! . bandwidth { hasChanges = true }
2023-03-05 05:42:25 -08:00
}
}
. onChange ( of : codingRate ) { newCodingRate in
if node != nil && node ! . loRaConfig != nil {
if newCodingRate != node ! . loRaConfig ! . codingRate { hasChanges = true }
}
}
. onChange ( of : spreadFactor ) { newSpreadFactor in
if node != nil && node ! . loRaConfig != nil {
if newSpreadFactor != node ! . loRaConfig ! . spreadFactor { hasChanges = true }
}
}
2023-03-08 21:14:46 -08:00
. onChange ( of : rxBoostedGain ) { newRxBoostedGain in
if node != nil && node ! . loRaConfig != nil {
if newRxBoostedGain != node ! . loRaConfig ! . sx126xRxBoostedGain { hasChanges = true }
}
}
2023-11-08 22:01:24 -08:00
. onChange ( of : overrideFrequency ) { newOverrideFrequency in
if node != nil && node ! . loRaConfig != nil {
if newOverrideFrequency != node ! . loRaConfig ! . overrideFrequency { hasChanges = true }
}
}
2023-11-26 12:54:45 -08:00
. onChange ( of : txPower ) { newTxPower in
if node != nil && node ! . loRaConfig != nil {
if newTxPower != node ! . loRaConfig ! . txPower { hasChanges = true }
}
}
2022-06-12 01:25:42 -07:00
}
2023-03-19 18:37:23 -07:00
func setLoRaValues ( ) {
2023-04-22 22:55:58 -07:00
self . hopLimit = Int ( node ? . loRaConfig ? . hopLimit ? ? 3 )
2023-03-19 18:37:23 -07:00
self . region = Int ( node ? . loRaConfig ? . regionCode ? ? 0 )
self . usePreset = node ? . loRaConfig ? . usePreset ? ? true
self . modemPreset = Int ( node ? . loRaConfig ? . modemPreset ? ? 0 )
self . txEnabled = node ? . loRaConfig ? . txEnabled ? ? true
self . txPower = Int ( node ? . loRaConfig ? . txPower ? ? 0 )
self . channelNum = Int ( node ? . loRaConfig ? . channelNum ? ? 0 )
self . bandwidth = Int ( node ? . loRaConfig ? . bandwidth ? ? 0 )
self . codingRate = Int ( node ? . loRaConfig ? . codingRate ? ? 0 )
self . spreadFactor = Int ( node ? . loRaConfig ? . spreadFactor ? ? 0 )
self . rxBoostedGain = node ? . loRaConfig ? . sx126xRxBoostedGain ? ? false
2023-11-08 22:01:24 -08:00
self . overrideFrequency = node ? . loRaConfig ? . overrideFrequency ? ? 0.0
2023-03-19 18:37:23 -07:00
self . hasChanges = false
}
2022-06-12 01:25:42 -07:00
}