mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #49 from meshtastic/feature/data_migration
Feature/data migration
This commit is contained in:
commit
a7d436d0b4
12 changed files with 240 additions and 25 deletions
|
|
@ -85,6 +85,7 @@
|
|||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
|
||||
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = "<group>"; };
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = "<group>"; };
|
||||
DD47E3D826F3093800029299 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -737,7 +738,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -754,7 +755,7 @@
|
|||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -768,7 +769,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 3;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -784,7 +785,7 @@
|
|||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -949,9 +950,10 @@
|
|||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */,
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */;
|
||||
currentVersion = DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = MeshtasticClient/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
peripheralName = name
|
||||
}
|
||||
|
||||
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, subscribed: false, peripheral: peripheral)
|
||||
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, channelUtilization: nil, airTime: nil, subscribed: false, peripheral: peripheral)
|
||||
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
|
||||
|
||||
if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected {
|
||||
|
|
@ -409,7 +409,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
myInfo.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
|
||||
myInfo.hasGps = decodedInfo.myInfo.hasGps_p
|
||||
myInfo.channelUtilization = decodedInfo.myInfo.channelUtilization
|
||||
myInfo.numBands = Int32(bitPattern: decodedInfo.myInfo.numBands)
|
||||
|
||||
// Swift does strings weird, this does work
|
||||
let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1)
|
||||
|
|
@ -424,6 +423,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
self.connectedPeripheral.num = myInfo.myNodeNum
|
||||
self.connectedPeripheral.firmwareVersion = myInfo.firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.name = myInfo.bleName ?? "Unknown"
|
||||
self.connectedPeripheral.channelUtilization = myInfo.channelUtilization
|
||||
self.connectedPeripheral.airTime = myInfo.airUtilTx
|
||||
|
||||
let fetchBCUserRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
|
||||
fetchBCUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.myInfo.myNodeNum))
|
||||
|
|
@ -452,7 +453,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
fetchedMyInfo[0].myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
|
||||
fetchedMyInfo[0].hasGps = decodedInfo.myInfo.hasGps_p
|
||||
fetchedMyInfo[0].channelUtilization = decodedInfo.myInfo.channelUtilization
|
||||
fetchedMyInfo[0].numBands = Int32(bitPattern: decodedInfo.myInfo.numBands)
|
||||
let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1)
|
||||
var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: decodedInfo.myInfo.firmwareVersion))]
|
||||
version = version.dropLast()
|
||||
|
|
@ -461,9 +461,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
|
||||
fetchedMyInfo[0].minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
|
||||
fetchedMyInfo[0].maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
|
||||
connectedPeripheral.num = fetchedMyInfo[0].myNodeNum
|
||||
connectedPeripheral.firmwareVersion = fetchedMyInfo[0].firmwareVersion ?? "Unknown"
|
||||
connectedPeripheral.name = fetchedMyInfo[0].bleName ?? "Unknown"
|
||||
self.connectedPeripheral.num = fetchedMyInfo[0].myNodeNum
|
||||
self.connectedPeripheral.firmwareVersion = fetchedMyInfo[0].firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.name = fetchedMyInfo[0].bleName ?? "Unknown"
|
||||
self.connectedPeripheral.channelUtilization = fetchedMyInfo[0].channelUtilization
|
||||
self.connectedPeripheral.airTime = fetchedMyInfo[0].airUtilTx
|
||||
|
||||
}
|
||||
do {
|
||||
|
|
@ -781,7 +783,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))")}
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(fetchedNode[0].num)")}
|
||||
print("💾 Updated NodeInfo SNR and Time from Packet For: \(fetchedNode[0].num)")
|
||||
|
||||
} catch {
|
||||
|
|
@ -1075,4 +1077,97 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
return success
|
||||
}
|
||||
|
||||
// Send Message
|
||||
public func sendPosition(destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var success = false
|
||||
|
||||
let fromNodeNum = connectedPeripheral.num
|
||||
|
||||
if fromNodeNum <= 0 {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
let fetchNode: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNode.predicate = NSPredicate(format: "num == %lld", fromNodeNum)
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity]
|
||||
|
||||
if fetchedNode.count == 1 {
|
||||
var positionPacket = Position()
|
||||
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
|
||||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
|
||||
positionPacket.time = UInt32(Date().timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
|
||||
let mostRecentPosition = fetchedNode[0].positions?.lastObject as! PositionEntity
|
||||
positionPacket.batteryLevel = mostRecentPosition.batteryLevel
|
||||
|
||||
var meshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(broadcastNodeNum)
|
||||
meshPacket.from = UInt32(connectedPeripheral.num)
|
||||
meshPacket.wantAck = wantResponse
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! positionPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.positionApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let position = PositionEntity(context: context!)
|
||||
position.latitudeI = positionPacket.latitudeI
|
||||
position.longitudeI = positionPacket.longitudeI
|
||||
position.altitude = positionPacket.altitude
|
||||
position.batteryLevel = positionPacket.batteryLevel
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionPacket.time)))
|
||||
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet for node: \(fromNodeNum)") }
|
||||
print("📍 Sent a Position Packet for node: \(fromNodeNum)")
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
print("💾 Saved a new position for the connected node: \(fromNodeNum)")
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new position for the connected node: \(fromNodeNum)") }
|
||||
success = true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("🚫 Unresolved Core Data error in Send Position Function \(nsError)")
|
||||
if meshLoggingEnabled { MeshLogger.log("🚫 Unresolved Core Data error in Send Position Function \(nsError)") }
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
|
||||
// Mount Rainier
|
||||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 46.879967, longitude: -121.726906)
|
||||
|
||||
static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)
|
||||
|
||||
static var currentLocation: CLLocationCoordinate2D {
|
||||
|
||||
|
|
@ -15,6 +17,14 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
return location.coordinate
|
||||
}
|
||||
|
||||
static var currentAltitude: CLLocationDistance {
|
||||
|
||||
guard let altitude = shared.locationManager.location?.altitude else {
|
||||
return DefaultAltitude
|
||||
}
|
||||
return altitude
|
||||
}
|
||||
|
||||
private let locationManager = CLLocationManager()
|
||||
|
||||
private override init() {
|
||||
|
|
|
|||
|
|
@ -40,13 +40,15 @@
|
|||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>We use your location to center maps of the mesh</string>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>Privacy – Bluetooth Always Usage Description</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>CoreDataSample.xcdatamodel</string>
|
||||
<string>MeshtasticDataModel v2.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="19574" systemVersion="21D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="direction" attributeType="String"/>
|
||||
<attribute name="isTapback" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
|
|
@ -37,7 +38,6 @@
|
|||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<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"/>
|
||||
|
|
@ -56,6 +56,7 @@
|
|||
<attribute name="batteryLevel" 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="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
|
|
@ -76,9 +77,9 @@
|
|||
</entity>
|
||||
<elements>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="185"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="179"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="194"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="134"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="134"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="200"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="direction" attributeType="String"/>
|
||||
<attribute name="isTapback" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" 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 isTapback == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" 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"/>
|
||||
<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="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<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="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"/>
|
||||
<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="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" 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="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="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="team" optional="YES" 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="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isTapback == false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="185"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="179"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="134"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="200"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -9,10 +9,12 @@ struct Peripheral: Identifiable {
|
|||
var longName: String
|
||||
var firmwareVersion: String
|
||||
var rssi: Int
|
||||
var channelUtilization: Float?
|
||||
var airTime: Float?
|
||||
var subscribed: Bool
|
||||
var peripheral: CBPeripheral
|
||||
|
||||
init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, subscribed: Bool, peripheral: CBPeripheral) {
|
||||
init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, channelUtilization: Float?, airTime: Float?, subscribed: Bool, peripheral: CBPeripheral) {
|
||||
self.id = id
|
||||
self.num = num
|
||||
self.name = name
|
||||
|
|
@ -20,6 +22,8 @@ struct Peripheral: Identifiable {
|
|||
self.longName = longName
|
||||
self.firmwareVersion = firmwareVersion
|
||||
self.rssi = rssi
|
||||
self.channelUtilization = channelUtilization
|
||||
self.airTime = airTime
|
||||
self.subscribed = subscribed
|
||||
self.peripheral = peripheral
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ struct Connect: View {
|
|||
let firmwareVersion = bleManager.lastConnnectionVersion
|
||||
let minimumVersion = "1.2.52"
|
||||
let supportedVersion = firmwareVersion == "0.0.0" || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame
|
||||
|
||||
|
||||
NavigationView {
|
||||
|
||||
|
|
@ -75,6 +74,10 @@ struct Connect: View {
|
|||
if bleManager.connectedPeripheral != nil {
|
||||
Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion)
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Channel Utilization: ").font(.caption)+Text(String(bleManager.connectedPeripheral.channelUtilization ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Air Time: ").font(.caption)+Text(String(bleManager.connectedPeripheral.airTime ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
}
|
||||
if bleManager.connectedPeripheral.subscribed {
|
||||
Text("Properly Subscribed").font(.caption)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct CircleText: View {
|
|||
var text: String
|
||||
var color: Color
|
||||
var circleSize: CGFloat? = 50
|
||||
var fontSize: CGFloat? = 24
|
||||
var fontSize: CGFloat? = 22
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ struct Contacts: View {
|
|||
|
||||
List(users) { (user: UserEntity) in
|
||||
|
||||
//let currentUserNum = self.bleManager.connectedPeripheral != nil ? self.bleManager.connectedPeripheral.num : 0
|
||||
|
||||
let allMessages = user.value(forKey: "allMessages") as! [MessageEntity]
|
||||
|
||||
NavigationLink(destination: UserMessageList(user: user)) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct UserMessageList: View {
|
|||
@State var showDeleteMessageAlert = false
|
||||
@State private var deleteMessageId: Int64 = 0
|
||||
@State private var replyMessageId: Int64 = 0
|
||||
@State private var sendPositionWithMessage: Bool = false
|
||||
|
||||
@State var messageCount = 0
|
||||
|
||||
|
|
@ -385,6 +386,15 @@ struct UserMessageList: View {
|
|||
.font(.subheadline)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
||||
sendPositionWithMessage = true
|
||||
typingMessage = "📍 " + userLongName + " Has shared their position with the mesh."
|
||||
|
||||
} label: {
|
||||
Image(systemName: "mappin")
|
||||
}
|
||||
|
||||
ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
|
||||
.frame(width: 130)
|
||||
|
|
@ -409,6 +419,11 @@ struct UserMessageList: View {
|
|||
typingMessage = ""
|
||||
focusedField = nil
|
||||
replyMessageId = 0
|
||||
if sendPositionWithMessage {
|
||||
if bleManager.sendPosition(destNum: user.num, wantResponse: false) {
|
||||
print("Position Sent")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue