mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Dont ever save positions at apple park
implement display mode
This commit is contained in:
parent
6b4240d7fb
commit
8141a36e06
6 changed files with 157 additions and 84 deletions
|
|
@ -104,6 +104,7 @@ enum OledTypes: Int, CaseIterable, Identifiable {
|
|||
case auto = 0
|
||||
case ssd1306 = 1
|
||||
case sh1106 = 2
|
||||
case sh1107 = 3
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
|
|
@ -115,6 +116,8 @@ enum OledTypes: Int, CaseIterable, Identifiable {
|
|||
return "SSD 1306"
|
||||
case .sh1106:
|
||||
return "SH 1106"
|
||||
case .sh1107:
|
||||
return "SH 1107"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,6 +130,46 @@ enum OledTypes: Int, CaseIterable, Identifiable {
|
|||
return Config.DisplayConfig.OledType.oledSsd1306
|
||||
case .sh1106:
|
||||
return Config.DisplayConfig.OledType.oledSh1106
|
||||
case .sh1107:
|
||||
return Config.DisplayConfig.OledType.oledSh1106
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default of 0 is auto
|
||||
enum DisplayModes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case defaultMode = 0
|
||||
case twoColor = 1
|
||||
case inverted = 2
|
||||
case color = 3
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .defaultMode:
|
||||
return "Default 128x64 screen layout"
|
||||
case .twoColor:
|
||||
return "Optimized for 2 color displays"
|
||||
case .inverted:
|
||||
return "Inverted top bar for 2 Color display"
|
||||
case .color:
|
||||
return "TFT Full Color Displays"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.DisplayMode {
|
||||
|
||||
switch self {
|
||||
case .defaultMode:
|
||||
return Config.DisplayConfig.DisplayMode.default
|
||||
case .twoColor:
|
||||
return Config.DisplayConfig.DisplayMode.twocolor
|
||||
case .inverted:
|
||||
return Config.DisplayConfig.DisplayMode.inverted
|
||||
case .color:
|
||||
return Config.DisplayConfig.DisplayMode.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
case .remoteHardwareApp:
|
||||
MeshLogger.log("🕸️ MESH PACKET received for Remote Hardware App UNHANDLED \(try! decodedInfo.packet.jsonString())")
|
||||
case .positionApp:
|
||||
positionPacket(packet: decodedInfo.packet, context: context!)
|
||||
upsertPositionPacket(packet: decodedInfo.packet, context: context!)
|
||||
case .waypointApp:
|
||||
waypointPacket(packet: decodedInfo.packet, context: context!)
|
||||
case .nodeinfoApp:
|
||||
|
|
|
|||
|
|
@ -589,7 +589,8 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newNode.user = newUser
|
||||
}
|
||||
|
||||
if nodeInfo.position.latitudeI > 0 || nodeInfo.position.longitudeI > 0 {
|
||||
if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000)
|
||||
{
|
||||
let position = PositionEntity(context: context)
|
||||
position.seqNo = Int32(nodeInfo.position.seqNumber)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
|
|
@ -656,14 +657,19 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
|
||||
if nodeInfo.hasPosition {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
position.longitudeI = nodeInfo.position.longitudeI
|
||||
position.altitude = nodeInfo.position.altitude
|
||||
position.satsInView = Int32(nodeInfo.position.satsInView)
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time)))
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
|
||||
if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
position.longitudeI = nodeInfo.position.longitudeI
|
||||
position.altitude = nodeInfo.position.altitude
|
||||
position.satsInView = Int32(nodeInfo.position.satsInView)
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time)))
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Look for a MyInfo
|
||||
|
|
@ -753,6 +759,8 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
|
||||
if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
|
||||
if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) {
|
||||
|
||||
if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) {
|
||||
|
|
@ -819,62 +827,6 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
}
|
||||
}
|
||||
|
||||
func positionPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.received %@", comment: "Position Packet received from node: %@"), String(packet.from))
|
||||
MeshLogger.log("📍 \(logString)")
|
||||
|
||||
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
do {
|
||||
|
||||
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
|
||||
// Don't save empty position packets
|
||||
if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 {
|
||||
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.snr = packet.rxSnr
|
||||
position.seqNo = Int32(positionMessage.seqNumber)
|
||||
position.latitudeI = positionMessage.latitudeI
|
||||
position.longitudeI = positionMessage.longitudeI
|
||||
position.altitude = positionMessage.altitude
|
||||
position.satsInView = Int32(positionMessage.satsInView)
|
||||
position.speed = Int32(positionMessage.groundSpeed)
|
||||
position.heading = Int32(positionMessage.groundTrack)
|
||||
if positionMessage.timestamp != 0 {
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.timestamp)))
|
||||
} else {
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
}
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("💥 Empty POSITION_APP Packet")
|
||||
print(try! packet.jsonString())
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("💥 Error Deserializing POSITION_APP packet.")
|
||||
}
|
||||
}
|
||||
|
||||
func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
if let routingMessage = try? Routing(serializedData: packet.decoded.payload) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22C65" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
|
|
@ -60,6 +60,7 @@
|
|||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManage
|
|||
|
||||
public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObjectContext) {
|
||||
do {
|
||||
let objects = channel.allPrivateMessages// try context.fetch(fetchChannelMessagesRequest) as! [NSManagedObject]
|
||||
let objects = channel.allPrivateMessages
|
||||
for object in objects {
|
||||
context.delete(object)
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObje
|
|||
public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext) {
|
||||
|
||||
do {
|
||||
let objects = user.messageList//try context.fetch(fetchUserMessagesRequest) as! [NSManagedObject]
|
||||
let objects = user.messageList
|
||||
for object in objects {
|
||||
context.delete(object)
|
||||
}
|
||||
|
|
@ -101,6 +101,64 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) {
|
|||
}
|
||||
}
|
||||
|
||||
func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.received %@", comment: "Position Packet received from node: %@"), String(packet.from))
|
||||
MeshLogger.log("📍 \(logString)")
|
||||
|
||||
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
do {
|
||||
|
||||
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
|
||||
|
||||
// Don't save empty position packets
|
||||
if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 && (positionMessage.latitudeI != 373346000 && positionMessage.longitudeI != -1220090000)
|
||||
{
|
||||
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.snr = packet.rxSnr
|
||||
position.seqNo = Int32(positionMessage.seqNumber)
|
||||
position.latitudeI = positionMessage.latitudeI
|
||||
position.longitudeI = positionMessage.longitudeI
|
||||
position.altitude = positionMessage.altitude
|
||||
position.satsInView = Int32(positionMessage.satsInView)
|
||||
position.speed = Int32(positionMessage.groundSpeed)
|
||||
position.heading = Int32(positionMessage.groundTrack)
|
||||
if positionMessage.timestamp != 0 {
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.timestamp)))
|
||||
} else {
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
}
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("💥 Empty POSITION_APP Packet")
|
||||
print(try! packet.jsonString())
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("💥 Error Deserializing POSITION_APP packet.")
|
||||
}
|
||||
}
|
||||
|
||||
func upsertBluetoothConfigPacket(config: Config, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.bluetooth.config %@", comment: "Bluetooth config received: %@"), String(nodeNum))
|
||||
|
|
@ -208,6 +266,7 @@ func upsertDisplayConfigPacket(config: Config, nodeNum: Int64, context: NSManage
|
|||
newDisplayConfig.compassNorthTop = config.display.compassNorthTop
|
||||
newDisplayConfig.flipScreen = config.display.flipScreen
|
||||
newDisplayConfig.oledType = Int32(config.display.oled.rawValue)
|
||||
newDisplayConfig.displayMode = Int32(config.display.displaymode.rawValue)
|
||||
fetchedNode[0].displayConfig = newDisplayConfig
|
||||
|
||||
} else {
|
||||
|
|
@ -218,6 +277,7 @@ func upsertDisplayConfigPacket(config: Config, nodeNum: Int64, context: NSManage
|
|||
fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop
|
||||
fetchedNode[0].displayConfig?.flipScreen = config.display.flipScreen
|
||||
fetchedNode[0].displayConfig?.oledType = Int32(config.display.oled.rawValue)
|
||||
fetchedNode[0].displayConfig?.displayMode = Int32(config.display.displaymode.rawValue)
|
||||
}
|
||||
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -24,29 +24,20 @@ struct DisplayConfig: View {
|
|||
@State var compassNorthTop = false
|
||||
@State var flipScreen = false
|
||||
@State var oledType = 0
|
||||
@State var displayMode = 0
|
||||
|
||||
var body: some View {
|
||||
|
||||
Form {
|
||||
Section(header: Text("Device Screen")) {
|
||||
|
||||
Picker("Screen on for", selection: $screenOnSeconds ) {
|
||||
ForEach(ScreenOnIntervals.allCases) { soi in
|
||||
Text(soi.description)
|
||||
Picker("Display Mode", selection: $displayMode ) {
|
||||
ForEach(DisplayModes.allCases) { dm in
|
||||
Text(dm.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Text("How long the screen remains on after the user button is pressed or messages are received.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Carousel Interval", selection: $screenCarouselInterval ) {
|
||||
ForEach(ScreenCarouselIntervals.allCases) { sci in
|
||||
Text(sci.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.")
|
||||
Text("Override automatic OLED screen detection.")
|
||||
.font(.caption)
|
||||
|
||||
Toggle(isOn: $compassNorthTop) {
|
||||
|
|
@ -64,6 +55,7 @@ struct DisplayConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("Flip screen vertically")
|
||||
.font(.caption)
|
||||
|
||||
Picker("OLED Type", selection: $oledType ) {
|
||||
ForEach(OledTypes.allCases) { ot in
|
||||
Text(ot.description)
|
||||
|
|
@ -74,7 +66,25 @@ struct DisplayConfig: View {
|
|||
.font(.caption)
|
||||
|
||||
}
|
||||
Section(header: Text("Format")) {
|
||||
Section(header: Text("Timing & Format")) {
|
||||
Picker("Screen on for", selection: $screenOnSeconds ) {
|
||||
ForEach(ScreenOnIntervals.allCases) { soi in
|
||||
Text(soi.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("How long the screen remains on after the user button is pressed or messages are received.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Carousel Interval", selection: $screenCarouselInterval ) {
|
||||
ForEach(ScreenCarouselIntervals.allCases) { sci in
|
||||
Text(sci.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("GPS Format", selection: $gpsFormat ) {
|
||||
ForEach(GpsFormats.allCases) { lu in
|
||||
Text(lu.description)
|
||||
|
|
@ -116,6 +126,7 @@ struct DisplayConfig: View {
|
|||
dc.compassNorthTop = compassNorthTop
|
||||
dc.flipScreen = flipScreen
|
||||
dc.oled = OledTypes(rawValue: oledType)!.protoEnumValue()
|
||||
dc.displaymode = DisplayModes(rawValue: displayMode)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
|
|
@ -143,6 +154,7 @@ struct DisplayConfig: View {
|
|||
self.compassNorthTop = node?.displayConfig?.compassNorthTop ?? false
|
||||
self.flipScreen = node?.displayConfig?.flipScreen ?? false
|
||||
self.oledType = Int(node?.displayConfig?.oledType ?? 0)
|
||||
self.displayMode = Int(node?.displayConfig?.displayMode ?? 0)
|
||||
self.hasChanges = false
|
||||
}
|
||||
.onChange(of: screenOnSeconds) { newScreenSecs in
|
||||
|
|
@ -175,5 +187,10 @@ struct DisplayConfig: View {
|
|||
if newOledType != node!.displayConfig!.oledType { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: displayMode) { newDisplayMode in
|
||||
if node != nil && node!.displayConfig != nil {
|
||||
if newDisplayMode != node!.displayConfig!.displayMode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue