mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
More waypoints
This commit is contained in:
parent
dbe61f94c4
commit
50943ea5fc
14 changed files with 461 additions and 85 deletions
|
|
@ -68,6 +68,7 @@
|
|||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
|
||||
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
|
||||
DD964FBF296E76EF007C176F /* WaypointFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormView.swift */; };
|
||||
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */; };
|
||||
DD97E96628EFD9820056DDA4 /* MeshtasticLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */; };
|
||||
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96728EFE9A00056DDA4 /* About.swift */; };
|
||||
DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD994B68295F88B60013760A /* IntervalEnums.swift */; };
|
||||
|
|
@ -192,6 +193,8 @@
|
|||
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
|
||||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiOnlyTextField.swift; sourceTree = "<group>"; };
|
||||
DD964FBE296E76EF007C176F /* WaypointFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormView.swift; sourceTree = "<group>"; };
|
||||
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticLogo.swift; sourceTree = "<group>"; };
|
||||
DD97E96728EFE9A00056DDA4 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
|
||||
DD994B68295F88B60013760A /* IntervalEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntervalEnums.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -565,6 +568,7 @@
|
|||
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */,
|
||||
DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */,
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */,
|
||||
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */,
|
||||
);
|
||||
path = Persistence;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -777,6 +781,7 @@
|
|||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
|
|
@ -1226,13 +1231,14 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */,
|
||||
DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */,
|
||||
DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */,
|
||||
DDCDC69A29467643004C1DDA /* MeshtasticDataModelV3.xcdatamodel */,
|
||||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */;
|
||||
currentVersion = DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
case .positionApp:
|
||||
positionPacket(packet: decodedInfo.packet, context: context!)
|
||||
case .waypointApp:
|
||||
MeshLogger.log("🕸️ MESH PACKET received for Waypoint App UNHANDLED \(try! decodedInfo.packet.jsonString())")
|
||||
waypointPacket(packet: decodedInfo.packet, context: context!)
|
||||
case .nodeinfoApp:
|
||||
if !invalidVersion { nodeInfoAppPacket(packet: decodedInfo.packet, context: context!) }
|
||||
case .routingApp:
|
||||
|
|
@ -627,7 +627,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
|
||||
connectTo(peripheral: preferredPeripheral!.peripheral)
|
||||
}
|
||||
let nodeName = connectedPeripheral!.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown"))
|
||||
let nodeName = connectedPeripheral?.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown"))
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.textmessage.send.failed %@",
|
||||
comment: "Message Send Failed, not properly connected to %@"), nodeName)
|
||||
MeshLogger.log("🚫 \(logString)")
|
||||
|
|
|
|||
|
|
@ -1311,3 +1311,68 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from))
|
||||
MeshLogger.log("📍 \(logString)")
|
||||
|
||||
let fetchWaypointRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "WaypointEntity")
|
||||
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id))
|
||||
|
||||
do {
|
||||
|
||||
if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) {
|
||||
// Don't save empty waypoint packets
|
||||
if waypointMessage.longitudeI > 0 || waypointMessage.latitudeI > 0 {
|
||||
let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as! [WaypointEntity]
|
||||
if fetchedWaypoint.isEmpty {
|
||||
let waypoint = WaypointEntity(context: context)
|
||||
|
||||
waypoint.id = Int64(packet.id)
|
||||
waypoint.name = waypointMessage.name
|
||||
waypoint.longDescription = waypointMessage.description_p
|
||||
waypoint.latitudeI = waypointMessage.latitudeI
|
||||
waypoint.longitudeI = waypointMessage.longitudeI
|
||||
//waypoint.icon = Int32(waypointMessage.icon)
|
||||
waypoint.locked = waypointMessage.locked
|
||||
if waypointMessage.expire != 0 {
|
||||
waypoint.expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire)))
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Node Waypoint App Packet For: \(waypoint.id)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving WaypointEntity from WAYPOINT_APP \(nsError)")
|
||||
}
|
||||
} else {
|
||||
fetchedWaypoint[0].id = Int64(packet.id)
|
||||
fetchedWaypoint[0].name = waypointMessage.name
|
||||
fetchedWaypoint[0].longDescription = waypointMessage.description_p
|
||||
fetchedWaypoint[0].latitudeI = waypointMessage.latitudeI
|
||||
fetchedWaypoint[0].longitudeI = waypointMessage.longitudeI
|
||||
//fetchedWaypoint[0].icon = Int32(waypointMessage.icon)
|
||||
fetchedWaypoint[0].locked = waypointMessage.locked
|
||||
if waypointMessage.expire != 0 {
|
||||
fetchedWaypoint[0].expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire)))
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Node Waypoint App Packet For: \(fetchedWaypoint[0].id)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving WaypointEntity from WAYPOINT_APP \(nsError)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("💥 Empty WAYPOINT_APP Packet")
|
||||
print(try! packet.jsonString())
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("💥 Error Deserializing WAYPOINT_APP packet.")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV5.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV6.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -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="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22C65" 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"/>
|
||||
|
|
@ -269,6 +269,7 @@
|
|||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="expire" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
<?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="">
|
||||
<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"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<fetchedProperty name="allPrivateMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index && toUser == nil AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" 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="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" 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"/>
|
||||
<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="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<fetchedProperty name="tapbacks" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<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="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="adminMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
</entity>
|
||||
</model>
|
||||
54
Meshtastic/Persistence/WaypointEntityExtension.swift
Normal file
54
Meshtastic/Persistence/WaypointEntityExtension.swift
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// WaypointEntityExtension.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright (c) Garth Vander Houwen 1/13/23.
|
||||
//
|
||||
import CoreData
|
||||
import CoreLocation
|
||||
import MapKit
|
||||
import SwiftUI
|
||||
|
||||
extension WaypointEntity {
|
||||
|
||||
var latitude: Double? {
|
||||
|
||||
let d = Double(latitudeI)
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return d / 1e7
|
||||
}
|
||||
|
||||
var longitude: Double? {
|
||||
|
||||
let d = Double(longitudeI)
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return d / 1e7
|
||||
}
|
||||
|
||||
var waypointCoordinate: CLLocationCoordinate2D? {
|
||||
if latitudeI != 0 && longitudeI != 0 {
|
||||
let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
|
||||
return coord
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var annotaton: MKPointAnnotation {
|
||||
let pointAnn = MKPointAnnotation()
|
||||
if waypointCoordinate != nil {
|
||||
pointAnn.coordinate = waypointCoordinate!
|
||||
}
|
||||
return pointAnn
|
||||
}
|
||||
}
|
||||
|
||||
extension WaypointEntity: MKAnnotation {
|
||||
public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationHelper.DefaultLocation }
|
||||
public var title: String? { self.title ?? "" }
|
||||
public var subtitle: String? { self.description }
|
||||
}
|
||||
|
|
@ -1156,7 +1156,7 @@ struct Waypoint {
|
|||
|
||||
///
|
||||
/// Designator icon for the waypoint in the form of a unicode emoji
|
||||
var emoji: UInt32 = 0
|
||||
var icon: UInt32 = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
|
|
@ -2782,7 +2782,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
5: .same(proto: "locked"),
|
||||
6: .same(proto: "name"),
|
||||
7: .same(proto: "description"),
|
||||
8: .same(proto: "emoji"),
|
||||
8: .same(proto: "icon"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2798,7 +2798,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
case 5: try { try decoder.decodeSingularBoolField(value: &self.locked) }()
|
||||
case 6: try { try decoder.decodeSingularStringField(value: &self.name) }()
|
||||
case 7: try { try decoder.decodeSingularStringField(value: &self.description_p) }()
|
||||
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }()
|
||||
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.icon) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2826,8 +2826,8 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if !self.description_p.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 7)
|
||||
}
|
||||
if self.emoji != 0 {
|
||||
try visitor.visitSingularFixed32Field(value: self.emoji, fieldNumber: 8)
|
||||
if self.icon != 0 {
|
||||
try visitor.visitSingularFixed32Field(value: self.icon, fieldNumber: 8)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
|
@ -2840,7 +2840,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if lhs.locked != rhs.locked {return false}
|
||||
if lhs.name != rhs.name {return false}
|
||||
if lhs.description_p != rhs.description_p {return false}
|
||||
if lhs.emoji != rhs.emoji {return false}
|
||||
if lhs.icon != rhs.icon {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// LocalMBTileOverlay.swift
|
||||
// MeshtasticApple
|
||||
//
|
||||
// Created by Joshua Pirihi on 16/01/22.
|
||||
// Copyright(c) Joshua Pirihi 16/01/22.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
|
@ -41,9 +41,7 @@ enum MapTileError: Error {
|
|||
class LocalMBTileOverlay: MKTileOverlay {
|
||||
|
||||
var path: String!
|
||||
|
||||
var mb: Connection!
|
||||
|
||||
private var _boundingMapRect: MKMapRect!
|
||||
override var boundingMapRect: MKMapRect {
|
||||
get {
|
||||
|
|
@ -55,7 +53,6 @@ class LocalMBTileOverlay: MKTileOverlay {
|
|||
|
||||
super.init(urlTemplate: nil)
|
||||
self.path = path
|
||||
|
||||
do {
|
||||
self.mb = try Connection(self.path, readonly: true)
|
||||
let metadata = Table("metadata")
|
||||
|
|
@ -87,43 +84,29 @@ class LocalMBTileOverlay: MKTileOverlay {
|
|||
]
|
||||
self._boundingMapRect = MKMapRect(coordinates: coords)
|
||||
|
||||
|
||||
} catch {
|
||||
print("💥 Map tile error: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) {
|
||||
|
||||
//}
|
||||
|
||||
//override func loadTile(at path: MKTileOverlayPath) async throws -> Data {
|
||||
|
||||
let tileX = Int64(path.x)
|
||||
let tileY = Int64(path.y)
|
||||
let tileZ = Int64(path.z)
|
||||
|
||||
let tileZ = Int64(path.z)
|
||||
let tileData = Expression<SQLite.Blob>("tile_data")
|
||||
let zoomLevel = Expression<Int64>("zoom_level")
|
||||
let tileColumn = Expression<Int64>("tile_column")
|
||||
let tileRow = Expression<Int64>("tile_row")
|
||||
|
||||
if let dataQuery = try? self.mb.pluck(Table("tiles").select(tileData).filter(zoomLevel == tileZ).filter(tileColumn == tileX).filter(tileRow == tileY)) {
|
||||
|
||||
let data = Data(bytes: dataQuery[tileData].bytes, count: dataQuery[tileData].bytes.count)//dataQuery![tileData].bytes
|
||||
|
||||
//return data
|
||||
result(data, nil)
|
||||
|
||||
} else {
|
||||
print("💥 No tile here: x:\(tileX) y:\(tileY) z:\(tileZ)")
|
||||
//return Data()
|
||||
let error = NSError(domain: "LocalMBTileOverlay", code: 1, userInfo: ["reason": "no_tile"])
|
||||
result(nil, error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
//
|
||||
// mapView.isScrollEnabled = self.userTrackingMode == MKUserTrackingMode.none ? self.scrollEnabled : false
|
||||
//
|
||||
// if let scrollBoundary = self.scrollBoundaries, (mapView.cameraBoundary?.region.center.latitude != scrollBoundary.center.latitude || mapView.cameraBoundary?.region.center.longitude != scrollBoundary.center.longitude || mapView.cameraBoundary?.region.span.latitudeDelta != scrollBoundary.span.latitudeDelta || mapView.cameraBoundary?.region.span.longitudeDelta != scrollBoundary.span.longitudeDelta) {
|
||||
// if let scrollBoundary = self.scrollBoundaries, (mapView.cameraBoundary?.region.center.latitude != scrollBoundary.center.latitude || mapView.cameraBoundary?.region.center.longitude != scrollBoundary.center.longitude || mapView.camera Boundary?.region.span.latitudeDelta != scrollBoundary.span.latitudeDelta || mapView.cameraBoundary?.region.span.longitudeDelta != scrollBoundary.span.longitudeDelta) {
|
||||
// mapView.cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: scrollBoundary)
|
||||
// } else if self.scrollBoundaries == nil && mapView.cameraBoundary != nil {
|
||||
// mapView.cameraBoundary = nil
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// MapViewSwitUI.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 1/9/23.
|
||||
// Copyright(c) Josh Pirihi & Garth Vander Houwen 1/16/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
|
@ -13,6 +13,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
var onMarkerTap: (_ waypointCoordinate: CLLocationCoordinate2D? ) -> Void
|
||||
let mapView = MKMapView()
|
||||
let positions: [PositionEntity]
|
||||
let waypoints: [WaypointEntity]
|
||||
let region: MKCoordinateRegion
|
||||
let mapViewType: MKMapType
|
||||
|
||||
|
|
@ -26,18 +27,24 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let dynamicRegion: Bool = true
|
||||
|
||||
func makeUIView(context: Context) -> MKMapView {
|
||||
// Parameters
|
||||
mapView.addAnnotations(positions)
|
||||
mapView.mapType = mapViewType
|
||||
mapView.setRegion(region, animated: true)
|
||||
mapView.isRotateEnabled = true
|
||||
mapView.isPitchEnabled = true
|
||||
mapView.showsBuildings = true;
|
||||
mapView.addAnnotations(positions)
|
||||
mapView.showsUserLocation = true
|
||||
mapView.setUserTrackingMode(.none, animated: false)
|
||||
// Other MKMapView Settings
|
||||
mapView.isPitchEnabled = true
|
||||
mapView.isRotateEnabled = true
|
||||
mapView.isScrollEnabled = true
|
||||
mapView.isZoomEnabled = true
|
||||
mapView.showsBuildings = true
|
||||
mapView.showsCompass = true
|
||||
mapView.showsScale = true
|
||||
mapView.isZoomEnabled = true
|
||||
mapView.isScrollEnabled = true
|
||||
mapView.showsTraffic = true
|
||||
mapView.showsUserLocation = true
|
||||
#if targetEnvironment(macCatalyst)
|
||||
mapView.showsZoomControls = true
|
||||
#endif
|
||||
mapView.delegate = context.coordinator
|
||||
return mapView
|
||||
}
|
||||
|
|
@ -149,9 +156,14 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
annotationView.markerTintColor = UIColor(.accentColor)
|
||||
annotationView.titleVisibility = .visible
|
||||
return annotationView
|
||||
case _ as WaypointEntity:
|
||||
return nil
|
||||
|
||||
case let waypointAnnotation as WaypointEntity:
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "waypoint") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "Waypoint")
|
||||
annotationView.canShowCallout = true
|
||||
annotationView.glyphText = "🪧"
|
||||
annotationView.clusteringIdentifier = "waypointGroup"
|
||||
annotationView.markerTintColor = UIColor(.green)
|
||||
annotationView.titleVisibility = .visible
|
||||
return annotationView
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +173,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let location = longPressRecognizer.location(in: self.parent.mapView)
|
||||
// Map Coordinate - CLLocationCoordinate2D
|
||||
let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
|
||||
|
||||
// Add annotation:
|
||||
let annotation = MKPointAnnotation()
|
||||
annotation.title = "Dropped Pin"
|
||||
|
|
@ -244,7 +255,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
maximumZoomLevel: Int? = nil,
|
||||
defaultTile: DefaultTile? = nil
|
||||
) {
|
||||
|
||||
self.mapName = mapName
|
||||
self.tileType = tileType
|
||||
self.canReplaceMapContent = canReplaceMapContent
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ struct WaypointFormView: View {
|
|||
Section(header: Text("Waypoint")) {
|
||||
HStack {
|
||||
Text("Location: \(String(format: "%.5f", coordinate.latitude ) + "," + String(format: "%.5f", coordinate.longitude ))")
|
||||
.textSelection(.enabled)
|
||||
.foregroundColor(Color.gray)
|
||||
.font(.caption2)
|
||||
if coordinate.latitude != LocationHelper.DefaultLocation.latitude && coordinate.longitude != LocationHelper.DefaultLocation.longitude {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ struct NodeDetail: View {
|
|||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], animation: .default)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
var body: some View {
|
||||
|
||||
let hwModelString = node.user?.hwModel ?? "UNSET"
|
||||
|
|
@ -44,16 +47,16 @@ struct NodeDetail: View {
|
|||
MapViewSwiftUI(onMarkerTap: { coord in
|
||||
presentingWaypointForm = true
|
||||
waypointCoordinate = coord
|
||||
}, positions: annotations, region: MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType,
|
||||
}, positions: annotations, waypoints: Array(waypoints), region: MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType,
|
||||
customMapOverlay: self.customMapOverlay,
|
||||
overlays: self.overlays
|
||||
)
|
||||
VStack {
|
||||
Spacer()
|
||||
Text(mostRecent.satsInView > 0 ? "Sats: \(mostRecent.satsInView)" : " ")
|
||||
.font(.caption2)
|
||||
|
||||
Picker("", selection: $mapType) {
|
||||
.font(.caption)
|
||||
.padding()
|
||||
Picker("Map Type", selection: $mapType) {
|
||||
Text("Standard").tag(MKMapType.standard)
|
||||
Text("Muted").tag(MKMapType.mutedStandard)
|
||||
Text("Hybrid").tag(MKMapType.hybrid)
|
||||
|
|
@ -61,8 +64,7 @@ struct NodeDetail: View {
|
|||
Text("Satellite").tag(MKMapType.satellite)
|
||||
Text("Sat Flyover").tag(MKMapType.satelliteFlyover)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.padding(.bottom, 30)
|
||||
.pickerStyle(.menu)
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ struct NodeMap: View {
|
|||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: false)], animation: .default)
|
||||
private var positions: FetchedResults<PositionEntity>
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], animation: .default)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
@State private var mapType: MKMapType = .standard
|
||||
@State var waypointCoordinate: CLLocationCoordinate2D?
|
||||
@State private var presentingWaypointForm = false
|
||||
|
|
@ -42,50 +45,29 @@ struct NodeMap: View {
|
|||
)
|
||||
@State private var overlays: [MapViewSwiftUI.Overlay] = []
|
||||
|
||||
//@State private var showLabels: Bool = false
|
||||
//@State private var zoomEnabled: Bool = true
|
||||
//@State private var showZoomScale: Bool = true
|
||||
//@State private var useMinZoomBoundary: Bool = false
|
||||
//@State private var minZoom: Double = 0
|
||||
//@State private var useMaxZoomBoundary: Bool = false
|
||||
//@State private var maxZoom: Double = 3000000
|
||||
//@State private var scrollEnabled: Bool = true
|
||||
//@State private var useScrollBoundaries: Bool = false
|
||||
//@State private var scrollBoundaries: MKCoordinateRegion = MKCoordinateRegion()
|
||||
//@State private var rotationEnabled: Bool = true
|
||||
//@State private var showCompassWhenRotated: Bool = true
|
||||
//@State private var showUserLocation: Bool = true
|
||||
//@State private var userTrackingMode: MKUserTrackingMode = MKUserTrackingMode.none
|
||||
//@State private var userLocation: CLLocationCoordinate2D? = LocationHelper.currentLocation
|
||||
//@State private var showAnnotations: Bool = true
|
||||
//@State private var annotations: [MKPointAnnotation] = []
|
||||
//@State private var showOverlays: Bool = true
|
||||
//@State private var showMapCenter: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
|
||||
MapViewSwiftUI(onMarkerTap: { coord in
|
||||
presentingWaypointForm = true
|
||||
waypointCoordinate = coord
|
||||
}, positions: Array(positions), region: MKCoordinateRegion(center: LocationHelper.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType,
|
||||
}, positions: Array(positions), waypoints: Array(waypoints), region: MKCoordinateRegion(center: LocationHelper.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType,
|
||||
customMapOverlay: self.customMapOverlay,
|
||||
overlays: self.overlays
|
||||
)
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
Picker("", selection: $mapType) {
|
||||
Picker("Map Type", selection: $mapType) {
|
||||
Text("Standard").tag(MKMapType.standard)
|
||||
Text("Muted").tag(MKMapType.mutedStandard)
|
||||
Text("Standard Muted").tag(MKMapType.mutedStandard)
|
||||
Text("Hybrid").tag(MKMapType.hybrid)
|
||||
Text("Hybrid Flyover").tag(MKMapType.hybridFlyover)
|
||||
Text("Satellite").tag(MKMapType.satellite)
|
||||
Text("Sat Flyover").tag(MKMapType.satelliteFlyover)
|
||||
Text("Satellite Flyover").tag(MKMapType.satelliteFlyover)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.padding(.bottom, 30)
|
||||
.pickerStyle(.menu)
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
|
||||
|
|
@ -111,11 +93,3 @@ struct NodeMap: View {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeMap_Previews: PreviewProvider {
|
||||
static let bleManager = BLEManager()
|
||||
|
||||
static var previews: some View {
|
||||
NodeMap()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue