2022-06-12 07:38:08 -07:00
//
// P o s i t i o n 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 ) G a r t h V a n d e r H o u w e n 6 / 1 1 / 2 2 .
//
import SwiftUI
2022-09-01 11:54:39 -07:00
struct PositionFlags : OptionSet
{
let rawValue : Int
static let posAltitude = PositionFlags ( rawValue : 1 )
static let posAltMsl = PositionFlags ( rawValue : 2 )
static let posGeoSep = PositionFlags ( rawValue : 4 )
static let posDop = PositionFlags ( rawValue : 8 )
static let posHvdop = PositionFlags ( rawValue : 16 )
static let posSatsinview = PositionFlags ( rawValue : 32 )
static let posSeqNos = PositionFlags ( rawValue : 64 )
static let posTimestamp = PositionFlags ( rawValue : 128 )
static let posSpeed = PositionFlags ( rawValue : 256 )
static let posHeading = PositionFlags ( rawValue : 512 )
}
2022-06-12 07:38:08 -07:00
struct PositionConfig : View {
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-07-07 00:29:52 -07:00
var node : NodeInfoEntity ?
2022-06-21 10:15:47 -07:00
@ State private var isPresentingSaveConfirm : Bool = false
@ State var initialLoad : Bool = true
@ State var hasChanges = false
2022-06-21 01:12:08 -07:00
2022-06-12 10:28:26 -07:00
@ State var smartPositionEnabled = true
@ State var deviceGpsEnabled = true
@ State var fixedPosition = false
2022-06-12 22:07:58 -07:00
@ State var gpsUpdateInterval = 0
@ State var gpsAttemptTime = 0
@ State var positionBroadcastSeconds = 0
2022-09-01 11:54:39 -07:00
@ State var positionFlags = 3
2022-06-12 10:28:26 -07:00
2022-07-13 08:32:34 -07:00
// / P o s i t i o n F l a g s
// / A l t i t u d e v a l u e - 1
2022-09-01 11:54:39 -07:00
@ State var includePosAltitude = false
2022-07-13 08:32:34 -07:00
// / A l t i t u d e v a l u e i s M S L - 2
@ State var includePosAltMsl = false
// / I n c l u d e g e o i d a l s e p a r a t i o n - 4
@ State var includePosGeoSep = false
// / I n c l u d e t h e D O P v a l u e ; P D O P u s e d b y d e f a u l t , s e e b e l o w - 8
@ State var includePosDop = false
// / I f P O S _ D O P s e t , s e n d s e p a r a t e H D O P / V D O P v a l u e s i n s t e a d o f P D O P - 1 6
@ State var includePosHvdop = false
// / I n c l u d e n u m b e r o f " s a t e l l i t e s i n v i e w " - 3 2
@ State var includePosSatsinview = false
// / I n c l u d e a s e q u e n c e n u m b e r i n c r e m e n t e d p e r p a c k e t - 6 4
2022-06-20 12:51:55 -07:00
@ State var includePosSeqNos = false
2022-07-13 08:32:34 -07:00
// / I n c l u d e p o s i t i o n a l t i m e s t a m p ( f r o m G P S s o l u t i o n ) - 1 2 8
2022-09-01 11:54:39 -07:00
@ State var includePosTimestamp = false
2022-07-13 08:32:34 -07:00
// / I n c l u d e p o s i t i o n a l h e a d i n g - 2 5 6
// / I n t e n d e d f o r u s e w i t h v e h i c l e n o t w a l k i n g s p e e d s
// / w a l k i n g s p e e d s a r e l i k e l y t o b e e r r o r p r o n e l i k e t h e c o m p a s s
2022-06-20 12:51:55 -07:00
@ State var includePosSpeed = false
2022-07-13 08:32:34 -07:00
// / I n c l u d e p o s i t i o n a l s p e e d - 5 1 2
// / I n t e n d e d f o r u s e w i t h v e h i c l e n o t w a l k i n g s p e e d s
// / w a l k i n g s p e e d s a r e l i k e l y t o b e e r r o r p r o n e l i k e t h e c o m p a s s
2022-06-20 12:51:55 -07:00
@ State var includePosHeading = false
2022-06-12 07:38:08 -07:00
var body : some View {
VStack {
Form {
2022-06-12 10:28:26 -07:00
2022-06-13 20:43:51 -07:00
Section ( header : Text ( " Device GPS " ) ) {
2022-06-12 07:38:08 -07:00
2022-06-12 10:28:26 -07:00
Toggle ( isOn : $ deviceGpsEnabled ) {
Label ( " Device GPS Enabled " , systemImage : " location " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
if deviceGpsEnabled {
2022-06-13 21:37:39 -07:00
Picker ( " Update Interval " , selection : $ gpsUpdateInterval ) {
2022-06-12 10:28:26 -07:00
ForEach ( GpsUpdateIntervals . allCases ) { ui in
Text ( ui . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Text ( " How often should we try to get a GPS position. " )
. font ( . caption )
. listRowSeparator ( . visible )
2022-06-13 21:37:39 -07:00
Picker ( " Attempt Time " , selection : $ gpsAttemptTime ) {
2022-06-12 10:28:26 -07:00
ForEach ( GpsAttemptTimes . allCases ) { at in
Text ( at . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Text ( " How long should we try to get our position during each GPS Update Interval attempt? " )
. font ( . caption )
. listRowSeparator ( . visible )
} else {
Toggle ( isOn : $ fixedPosition ) {
Label ( " Fixed Position " , systemImage : " location.square.fill " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-09-05 18:32:27 -07:00
Text ( " If enabled your current location will be set as a fixed position. " )
2022-06-12 10:28:26 -07:00
. font ( . caption )
. listRowSeparator ( . visible )
}
}
2022-06-21 01:12:08 -07:00
2022-06-13 20:43:51 -07:00
Section ( header : Text ( " Position Packet " ) ) {
2022-06-12 07:38:08 -07:00
2022-06-12 10:28:26 -07:00
Toggle ( isOn : $ smartPositionEnabled ) {
Label ( " Smart Position Broadcast " , systemImage : " location.fill.viewfinder " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-28 06:56:50 -07:00
Picker ( " Position Broadcast Interval " , selection : $ positionBroadcastSeconds ) {
ForEach ( PositionBroadcastIntervals . allCases ) { at in
Text ( at . description )
2022-06-12 10:28:26 -07:00
}
}
2022-06-28 06:56:50 -07:00
. pickerStyle ( DefaultPickerStyle ( ) )
Text ( " We should send our position this often (but only if it has changed significantly) " )
. font ( . caption )
2022-06-12 10:28:26 -07:00
}
2022-09-01 11:54:39 -07:00
Section ( header : Text ( " Position Flags " ) ) {
2022-06-14 16:45:43 -07:00
Text ( " Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss " )
. font ( . caption )
. listRowSeparator ( . visible )
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosAltitude ) {
Label ( " Altitude " , systemImage : " arrow.up " )
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-20 12:51:55 -07:00
2022-09-01 18:37:41 -07:00
Toggle ( isOn : $ includePosAltMsl ) {
Label ( " Altitude is Mean Sea Level " , systemImage : " arrow.up.to.line.compact " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosSatsinview ) {
Label ( " Number of satellites " , systemImage : " skew " )
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosSeqNos ) { // 6 4
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Label ( " Sequence number " , systemImage : " number " )
2022-06-14 16:45:43 -07:00
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosTimestamp ) { // 1 2 8
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Label ( " Timestamp " , systemImage : " clock " )
2022-06-14 16:45:43 -07:00
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosHeading ) { // 1 2 8
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Label ( " Vehicle heading " , systemImage : " location.circle " )
2022-06-14 16:45:43 -07:00
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Toggle ( isOn : $ includePosSpeed ) { // 1 2 8
2022-06-14 16:45:43 -07:00
2022-06-20 12:51:55 -07:00
Label ( " Vehicle speed " , systemImage : " speedometer " )
2022-06-14 16:45:43 -07:00
}
2022-06-21 01:12:08 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-07-13 08:32:34 -07:00
}
2022-09-01 11:54:39 -07:00
Section ( header : Text ( " Advanced Position Flags " ) ) {
2022-07-13 08:32:34 -07:00
Toggle ( isOn : $ includePosGeoSep ) {
Text ( " Geoidal Seperation " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ includePosDop ) {
Text ( " Dilution of precision (DOP) PDOP used by default " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ includePosHvdop ) {
Text ( " If DOP is set use, HDOP / VDOP values instead of PDOP " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-12 07:38:08 -07:00
}
}
2022-06-21 13:34:57 -07:00
. disabled ( bleManager . connectedPeripheral = = nil )
2022-06-21 13:10:30 -07:00
Button {
isPresentingSaveConfirm = true
} label : {
Label ( " Save " , systemImage : " square.and.arrow.down " )
}
. disabled ( bleManager . connectedPeripheral = = nil || ! hasChanges )
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . large )
. padding ( )
. confirmationDialog (
" Are you sure? " ,
2022-07-13 08:32:34 -07:00
isPresented : $ isPresentingSaveConfirm ,
titleVisibility : . visible
2022-06-21 13:10:30 -07:00
) {
Button ( " Save Position Config to \( bleManager . connectedPeripheral != nil ? bleManager . connectedPeripheral . longName : " Unknown " ) ? " ) {
var pc = Config . PositionConfig ( )
pc . positionBroadcastSmartDisabled = ! smartPositionEnabled
pc . gpsDisabled = ! deviceGpsEnabled
pc . fixedPosition = fixedPosition
pc . gpsUpdateInterval = UInt32 ( gpsUpdateInterval )
pc . gpsAttemptTime = UInt32 ( gpsAttemptTime )
pc . positionBroadcastSecs = UInt32 ( positionBroadcastSeconds )
2022-09-01 11:54:39 -07:00
var pf : PositionFlags = [ ]
if includePosAltitude { pf . insert ( . posAltitude ) }
if includePosAltMsl { pf . insert ( . posAltMsl ) }
if includePosGeoSep { pf . insert ( . posGeoSep ) }
if includePosDop { pf . insert ( . posDop ) }
if includePosHvdop { pf . insert ( . posHvdop ) }
if includePosSatsinview { pf . insert ( . posSatsinview ) }
if includePosSeqNos { pf . insert ( . posSeqNos ) }
if includePosTimestamp { pf . insert ( . posTimestamp ) }
if includePosSpeed { pf . insert ( . posSpeed ) }
if includePosHeading { pf . insert ( . posHeading ) }
pc . positionFlags = UInt32 ( pf . rawValue )
2022-09-05 18:32:27 -07:00
if fixedPosition {
let sendPosition = bleManager . sendPosition ( destNum : bleManager . connectedPeripheral . num , wantAck : true )
}
2022-09-01 11:54:39 -07:00
2022-08-12 08:58:10 -07:00
let adminMessageId = bleManager . savePositionConfig ( config : pc , fromUser : node ! . user ! , toUser : node ! . user ! )
2022-07-02 19:50:08 -07:00
if adminMessageId > 0 {
2022-06-21 13:10:30 -07:00
// 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
} else {
}
}
}
2022-06-12 07:38:08 -07:00
}
. navigationTitle ( " Position Config " )
. navigationBarItems ( trailing :
ZStack {
2022-07-01 19:44:25 -07:00
ConnectedDevice ( bluetoothOn : bleManager . isSwitchedOn , deviceConnected : bleManager . connectedPeripheral != nil , name : ( bleManager . connectedPeripheral != nil ) ? bleManager . connectedPeripheral . shortName : " ???? " )
2022-06-12 07:38:08 -07:00
} )
. onAppear {
2022-06-21 13:10:30 -07:00
if self . initialLoad {
self . bleManager . context = context
2022-07-07 00:29:52 -07:00
self . smartPositionEnabled = node ! . positionConfig ? . smartPositionEnabled ? ? true
self . deviceGpsEnabled = node ! . positionConfig ? . deviceGpsEnabled ? ? true
self . fixedPosition = node ! . positionConfig ? . fixedPosition ? ? false
self . gpsUpdateInterval = Int ( node ! . positionConfig ? . gpsUpdateInterval ? ? 0 )
self . gpsAttemptTime = Int ( node ! . positionConfig ? . gpsAttemptTime ? ? 0 )
self . positionBroadcastSeconds = Int ( node ! . positionConfig ? . positionBroadcastSeconds ? ? 0 )
2022-09-01 11:54:39 -07:00
self . positionFlags = Int ( node ! . positionConfig ? . positionFlags ? ? 3 )
let pf = PositionFlags ( rawValue : self . positionFlags )
if pf . contains ( . posAltitude ) { self . includePosAltitude = true } else { self . includePosAltitude = false }
if pf . contains ( . posAltMsl ) { self . includePosAltMsl = true } else { self . includePosAltMsl = false }
if pf . contains ( . posGeoSep ) { self . includePosGeoSep = true } else { self . includePosGeoSep = false }
if pf . contains ( . posDop ) { self . includePosDop = true } else { self . includePosDop = false }
if pf . contains ( . posHvdop ) { self . includePosHvdop = true } else { self . includePosHvdop = false }
if pf . contains ( . posSatsinview ) { self . includePosSatsinview = true } else { self . includePosSatsinview = false }
if pf . contains ( . posSeqNos ) { self . includePosSeqNos = true } else { self . includePosSeqNos = false }
if pf . contains ( . posTimestamp ) { self . includePosTimestamp = true } else { self . includePosTimestamp = false }
if pf . contains ( . posSpeed ) { self . includePosSpeed = true } else { self . includePosSpeed = false }
if pf . contains ( . posHeading ) { self . includePosHeading = true } else { self . includePosHeading = false }
2022-06-21 13:10:30 -07:00
self . hasChanges = false
self . initialLoad = false
2022-09-01 11:54:39 -07:00
2022-06-21 13:10:30 -07:00
}
}
. onChange ( of : smartPositionEnabled ) { newSmartPosition in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . positionConfig != nil {
if newSmartPosition != node ! . positionConfig ! . smartPositionEnabled { hasChanges = true }
}
2022-06-21 13:10:30 -07:00
}
2022-08-19 23:26:02 -07:00
. onChange ( of : positionBroadcastSeconds ) { newPositionBroadcastSeconds in
if node != nil && node ! . positionConfig != nil {
if newPositionBroadcastSeconds != node ! . positionConfig ! . positionBroadcastSeconds { hasChanges = true }
}
}
2022-06-21 13:10:30 -07:00
. onChange ( of : deviceGpsEnabled ) { newDeviceGps in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . positionConfig != nil {
if newDeviceGps != node ! . positionConfig ! . deviceGpsEnabled { hasChanges = true }
}
2022-06-21 13:10:30 -07:00
}
. onChange ( of : fixedPosition ) { newFixed in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . positionConfig != nil {
if newFixed != node ! . positionConfig ! . fixedPosition { hasChanges = true }
}
2022-06-12 07:38:08 -07:00
}
2022-09-01 18:37:41 -07:00
. onChange ( of : includePosAltitude || includePosAltMsl || includePosGeoSep || includePosDop || includePosHvdop || includePosSatsinview || includePosSeqNos || includePosTimestamp || includePosSpeed || includePosHeading ) { newFlags in
hasChanges = true
}
2022-06-12 07:38:08 -07:00
. navigationViewStyle ( StackNavigationViewStyle ( ) )
}
}