Display Settings

This commit is contained in:
Garth Vander Houwen 2022-06-21 10:02:05 -07:00
parent c7b98cb7f4
commit 0d3b6276da
5 changed files with 217 additions and 36 deletions

View file

@ -886,6 +886,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func saveDisplayConfig(config: Config.DisplayConfig, destNum: Int64, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()
adminPacket.setConfig.display = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
return true
}
return false
}
public func saveLoRaConfig(config: Config.LoRaConfig, destNum: Int64, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()

View file

@ -14,25 +14,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
// We don't care about any of the Power settings
// We don't want to manage wifi from the phone app and disconnect our device
//if meshlogging { MeshLogger.log(" Local Config version \(config.version) received for \(nodeLongName)") }
// if (try! config.device.jsonString()) == "{}" {
//
// print("📟 Default Device config")
//
// } else {
//
// print("📟 Has Device config")
// }
//
// if (try! config.position.jsonString()) == "{}" {
//
// print("📍 Default Position config")
//
// } else {
//
// print("📍 Has Position config")
// }
//
// if (try! config.power.jsonString() == "{\"lsSecs\":300}") {
//
// print("📍 Default Power config")
@ -44,14 +26,6 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
// print(try! config.power.jsonString())
// }
//
// if (try! config.display.jsonString()) == "{}" {
//
// print("🖥 Default Display config")
//
// } else {
//
// print("🖥 Has Display config")
// }
if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) {
var isDefault = false
@ -59,6 +33,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
if (try! config.device.jsonString()) == "{}" {
isDefault = true
print("📟 Default Device config")
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -117,7 +92,79 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
context.rollback()
let nsError = error as NSError
print("💥 Error Updating Core Data MyInfoEntity: \(nsError)")
print("💥 Error Updating Core Data DeviceConfigEntity: \(nsError)")
}
}
} catch {
}
}
if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) {
var isDefault = false
if (try! config.display.jsonString()) == "{}" {
isDefault = true
print("🖥️ Default Display config")
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
do {
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
// Found a node, save Device Config
if !fetchedNode.isEmpty {
if fetchedNode[0].displayConfig == nil {
let newDisplayConfig = DisplayConfigEntity(context: context)
if isDefault {
newDisplayConfig.screenOnSeconds = 0
newDisplayConfig.screenCarouselInterval = 0
newDisplayConfig.gpsFormat = 0
} else {
newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue)
newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs)
newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
}
fetchedNode[0].displayConfig = newDisplayConfig
} else {
if isDefault {
fetchedNode[0].displayConfig?.screenOnSeconds = 0
fetchedNode[0].displayConfig?.screenCarouselInterval = 0
fetchedNode[0].displayConfig?.gpsFormat = 0
} else {
fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue)
fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs)
fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
}
}
do {
try context.save()
if meshlogging { MeshLogger.log("💾 Updated Display Config for node number: \(String(nodeNum))") }
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)")
}
}

View file

@ -2,15 +2,22 @@
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
@ -61,6 +68,7 @@
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
@ -112,10 +120,11 @@
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="215"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="194"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="209"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="194"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="200"/>
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
</elements>
</model>

View file

@ -7,7 +7,7 @@
import SwiftUI
enum GpsFormat: Int, CaseIterable, Identifiable {
enum GpsFormats: Int, CaseIterable, Identifiable {
case gpsFormatDec = 0
case gpsFormatDms = 1
@ -35,6 +35,24 @@ enum GpsFormat: Int, CaseIterable, Identifiable {
}
}
}
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
switch self {
case .gpsFormatDec:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
case .gpsFormatDms:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
case .gpsFormatUtm:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
case .gpsFormatMgrs:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
case .gpsFormatOlc:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
case .gpsFormatOsgr:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
}
}
}
// Default of 0 is One Minute
@ -109,6 +127,11 @@ struct DisplayConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
var node: NodeInfoEntity
@State private var isPresentingSaveConfirm: Bool = false
@State var initialLoad: Bool = true
@State var hasChanges = false
@State var screenOnSeconds = 0
@State var screenCarouselInterval = 0
@ -146,7 +169,7 @@ struct DisplayConfig: View {
}
Section(header: Text("Format")) {
Picker("GPS Format", selection: $gpsFormat ) {
ForEach(GpsFormat.allCases) { lu in
ForEach(GpsFormats.allCases) { lu in
Text(lu.description)
}
}
@ -157,6 +180,43 @@ struct DisplayConfig: View {
.listRowSeparator(.visible)
}
}
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?",
isPresented: $isPresentingSaveConfirm
) {
Button("Save Display Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
var dc = Config.DisplayConfig()
dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue()
dc.screenOnSecs = UInt32(screenOnSeconds)
dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval)
if bleManager.saveDisplayConfig(config: dc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
} else {
}
}
}
}
.navigationTitle("Display Config")
.navigationBarItems(trailing:
@ -167,7 +227,37 @@ struct DisplayConfig: View {
})
.onAppear {
self.bleManager.context = context
if self.initialLoad{
self.bleManager.context = context
self.gpsFormat = Int(node.displayConfig?.gpsFormat ?? 0)
self.screenOnSeconds = Int(node.displayConfig?.screenOnSeconds ?? 0)
self.screenCarouselInterval = Int(node.displayConfig?.screenCarouselInterval ?? 0)
self.hasChanges = false
self.initialLoad = false
}
}
.onChange(of: screenOnSeconds) { newScreenSecs in
if newScreenSecs != node.displayConfig!.screenOnSeconds {
hasChanges = true
}
}
.onChange(of: screenCarouselInterval) { newCarouselSecs in
if newCarouselSecs != node.displayConfig!.screenCarouselInterval {
hasChanges = true
}
}
.onChange(of: gpsFormat) { newGpsFormat in
if newGpsFormat != node.displayConfig!.gpsFormat {
hasChanges = true
}
}
.navigationViewStyle(StackNavigationViewStyle())
}

View file

@ -58,7 +58,7 @@ struct Settings: View {
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
DisplayConfig()
DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
Image(systemName: "display")
@ -93,7 +93,7 @@ struct Settings: View {
Section("Module Configuration") {
NavigationLink {
DisplayConfig()
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
Image(systemName: "list.bullet.rectangle.fill")