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
2022-09-23 21:41:07 -07:00
static let Altitude = PositionFlags ( rawValue : 1 )
static let AltitudeMsl = PositionFlags ( rawValue : 2 )
2022-09-30 17:10:03 -07:00
static let GeoidalSeparation = PositionFlags ( rawValue : 4 )
2022-09-23 21:41:07 -07:00
static let Dop = PositionFlags ( rawValue : 8 )
static let Hvdop = PositionFlags ( rawValue : 16 )
static let Satsinview = PositionFlags ( rawValue : 32 )
2022-10-04 18:10:40 -07:00
static let SeqNo = PositionFlags ( rawValue : 64 )
2022-09-23 21:41:07 -07:00
static let Timestamp = PositionFlags ( rawValue : 128 )
static let Speed = PositionFlags ( rawValue : 256 )
static let Heading = PositionFlags ( rawValue : 512 )
2022-09-01 11:54:39 -07:00
}
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 hasChanges = false
2022-10-04 18:10:40 -07:00
@ State var hasFlagChanges = 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-23 21:41:07 -07:00
@ State var includeAltitude = 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
2022-09-23 21:41:07 -07:00
@ State var includeAltitudeMsl = false
2022-07-13 08:32:34 -07:00
// / 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
2022-09-30 17:10:03 -07:00
@ State var includeGeoidalSeparation = false
2022-07-13 08:32:34 -07:00
// / 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
2022-09-23 21:41:07 -07:00
@ State var includeDop = false
2022-07-13 08:32:34 -07:00
// / 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
2022-09-23 21:41:07 -07:00
@ State var includeHvdop = false
2022-07-13 08:32:34 -07:00
// / 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
2022-09-23 21:41:07 -07:00
@ State var includeSatsinview = false
2022-07-13 08:32:34 -07:00
// / 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-09-30 17:10:03 -07:00
@ State var includeSeqNo = 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-23 21:41:07 -07:00
@ State var includeTimestamp = 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-09-23 21:41:07 -07:00
@ State var includeSpeed = 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-09-23 21:41:07 -07:00
@ State var includeHeading = false
2022-06-20 12:51:55 -07:00
2022-06-12 07:38:08 -07:00
var body : some View {
VStack {
Form {
2022-06-13 20:43:51 -07:00
Section ( header : Text ( " Device GPS " ) ) {
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 )
}
}
Text ( " How often should we try to get a GPS position. " )
. font ( . caption )
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 )
} 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 )
}
}
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-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeAltitude ) {
2022-06-20 12:51:55 -07:00
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-10-06 08:56:15 -07:00
if includeAltitude {
Toggle ( isOn : $ includeAltitudeMsl ) {
Label ( " Altitude is Mean Sea Level " , systemImage : " arrow.up.to.line.compact " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ includeGeoidalSeparation ) {
2022-10-25 19:46:51 -07:00
Label ( " Altitude Geoidal Separation " , systemImage : " globe.americas " )
2022-10-06 08:56:15 -07:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-09-01 18:37:41 -07:00
}
2022-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeSatsinview ) {
2022-06-20 12:51:55 -07:00
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
2022-09-30 17:10:03 -07:00
Toggle ( isOn : $ includeSeqNo ) { // 6 4
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-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeTimestamp ) { // 1 2 8
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-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeHeading ) { // 1 2 8
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-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeSpeed ) { // 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
2022-09-23 21:41:07 -07:00
Toggle ( isOn : $ includeDop ) {
2022-07-13 08:32:34 -07:00
Text ( " Dilution of precision (DOP) PDOP used by default " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-10-06 08:56:15 -07:00
if includeDop {
Toggle ( isOn : $ includeHvdop ) {
Text ( " If DOP is set use, HDOP / VDOP values instead of PDOP " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-07-13 08:32:34 -07:00
}
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 " ) ? " ) {
2022-10-22 07:35:55 -07:00
if fixedPosition {
let sendPosition = bleManager . sendPosition ( destNum : bleManager . connectedPeripheral . num , wantAck : true )
}
2022-06-21 13:10:30 -07:00
var pc = Config . PositionConfig ( )
2022-09-10 18:38:00 -07:00
pc . positionBroadcastSmartEnabled = smartPositionEnabled
pc . gpsEnabled = deviceGpsEnabled
2022-06-21 13:10:30 -07:00
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 = [ ]
2022-09-23 21:41:07 -07:00
if includeAltitude { pf . insert ( . Altitude ) }
if includeAltitudeMsl { pf . insert ( . AltitudeMsl ) }
2022-09-30 17:10:03 -07:00
if includeGeoidalSeparation { pf . insert ( . GeoidalSeparation ) }
2022-09-23 21:41:07 -07:00
if includeDop { pf . insert ( . Dop ) }
if includeHvdop { pf . insert ( . Hvdop ) }
if includeSatsinview { pf . insert ( . Satsinview ) }
2022-10-04 18:10:40 -07:00
if includeSeqNo { pf . insert ( . SeqNo ) }
2022-09-23 21:41:07 -07:00
if includeTimestamp { pf . insert ( . Timestamp ) }
if includeSpeed { pf . insert ( . Speed ) }
if includeHeading { pf . insert ( . Heading ) }
2022-09-01 11:54:39 -07:00
pc . positionFlags = UInt32 ( pf . rawValue )
2022-08-12 08:58:10 -07:00
let adminMessageId = bleManager . savePositionConfig ( config : pc , fromUser : node ! . user ! , toUser : node ! . user ! )
2022-10-17 21:18:57 -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
}
}
}
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-09-01 11:54:39 -07:00
2022-10-17 21:18:57 -07:00
self . bleManager . context = context
self . smartPositionEnabled = node ? . positionConfig ? . smartPositionEnabled ? ? true
self . deviceGpsEnabled = node ? . positionConfig ? . deviceGpsEnabled ? ? true
self . fixedPosition = node ? . positionConfig ? . fixedPosition ? ? false
self . gpsUpdateInterval = Int ( node ? . positionConfig ? . gpsUpdateInterval ? ? 30 )
self . gpsAttemptTime = Int ( node ? . positionConfig ? . gpsAttemptTime ? ? 30 )
self . positionBroadcastSeconds = Int ( node ? . positionConfig ? . positionBroadcastSeconds ? ? 900 )
self . positionFlags = Int ( node ? . positionConfig ? . positionFlags ? ? 3 )
let pf = PositionFlags ( rawValue : self . positionFlags )
if pf . contains ( . Altitude ) { self . includeAltitude = true } else { self . includeAltitude = false }
if pf . contains ( . AltitudeMsl ) { self . includeAltitudeMsl = true } else { self . includeAltitudeMsl = false }
if pf . contains ( . GeoidalSeparation ) { self . includeGeoidalSeparation = true } else { self . includeGeoidalSeparation = false }
if pf . contains ( . Dop ) { self . includeDop = true } else { self . includeDop = false }
if pf . contains ( . Hvdop ) { self . includeHvdop = true } else { self . includeHvdop = false }
if pf . contains ( . Satsinview ) { self . includeSatsinview = true } else { self . includeSatsinview = false }
if pf . contains ( . SeqNo ) { self . includeSeqNo = true } else { self . includeSeqNo = false }
if pf . contains ( . Timestamp ) { self . includeTimestamp = true } else { self . includeTimestamp = false }
if pf . contains ( . Speed ) { self . includeSpeed = true } else { self . includeSpeed = false }
if pf . contains ( . Heading ) { self . includeHeading = true } else { self . includeHeading = false }
self . hasChanges = false
2022-06-21 13:10:30 -07:00
}
2022-09-23 21:41:07 -07:00
. onChange ( of : deviceGpsEnabled ) { newDeviceGps in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . positionConfig != nil {
2022-09-23 21:41:07 -07:00
if newDeviceGps != node ! . positionConfig ! . deviceGpsEnabled { hasChanges = true }
2022-07-11 16:18:16 -07:00
}
2022-06-21 13:10:30 -07:00
}
2022-09-23 21:41:07 -07:00
. onChange ( of : gpsAttemptTime ) { newGpsAttemptTime in
2022-08-19 23:26:02 -07:00
if node != nil && node ! . positionConfig != nil {
2022-09-23 21:41:07 -07:00
if newGpsAttemptTime != node ! . positionConfig ! . gpsAttemptTime { hasChanges = true }
2022-08-19 23:26:02 -07:00
}
}
2022-09-23 21:41:07 -07:00
. onChange ( of : gpsUpdateInterval ) { newGpsUpdateInterval in
2022-07-11 16:18:16 -07:00
if node != nil && node ! . positionConfig != nil {
2022-09-23 21:41:07 -07:00
if newGpsUpdateInterval != node ! . positionConfig ! . gpsUpdateInterval { hasChanges = true }
2022-07-11 16:18:16 -07:00
}
2022-06-21 13:10:30 -07:00
}
2022-09-24 11:28:36 -07:00
. onChange ( of : smartPositionEnabled ) { newSmartPositionEnabled in
if node != nil && node ! . positionConfig != nil {
if newSmartPositionEnabled != node ! . positionConfig ! . smartPositionEnabled { 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-23 21:41:07 -07:00
. onChange ( of : positionBroadcastSeconds ) { newPositionBroadcastSeconds in
if node != nil && node ! . positionConfig != nil {
if newPositionBroadcastSeconds != node ! . positionConfig ! . positionBroadcastSeconds { hasChanges = true }
}
}
2022-10-04 18:10:40 -07:00
. onChange ( of : includeAltitude ) { altFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Altitude )
if existingValue != altFlag { hasChanges = true }
}
. onChange ( of : includeAltitudeMsl ) { altMslFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . AltitudeMsl )
if existingValue != altMslFlag { hasChanges = true }
}
. onChange ( of : includeSatsinview ) { satsFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Satsinview )
if existingValue != satsFlag { hasChanges = true }
}
. onChange ( of : includeSeqNo ) { seqFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . SeqNo )
if existingValue != seqFlag { hasChanges = true }
}
. onChange ( of : includeTimestamp ) { timestampFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Timestamp )
if existingValue != timestampFlag { hasChanges = true }
}
. onChange ( of : includeTimestamp ) { timestampFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Timestamp )
if existingValue != timestampFlag { hasChanges = true }
}
. onChange ( of : includeSpeed ) { speedFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Speed )
if existingValue != speedFlag { hasChanges = true }
}
. onChange ( of : includeHeading ) { headingFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Heading )
if existingValue != headingFlag { hasChanges = true }
}
. onChange ( of : includeGeoidalSeparation ) { geoSepFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . GeoidalSeparation )
if existingValue != geoSepFlag { hasChanges = true }
}
. onChange ( of : includeDop ) { dopFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Dop )
if existingValue != dopFlag { hasChanges = true }
}
. onChange ( of : includeHvdop ) { hvdopFlag in
let pf = PositionFlags ( rawValue : self . positionFlags )
let existingValue = pf . contains ( . Hvdop )
if existingValue != hvdopFlag { hasChanges = true }
2022-09-01 18:37:41 -07:00
}
2022-06-12 07:38:08 -07:00
}
}