Merge pull request #58 from meshtastic/feature/message_ack

Feature/message ack
This commit is contained in:
Garth Vander Houwen 2022-02-24 08:29:38 -10:00 committed by GitHub
commit 285e314af9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 101 additions and 85 deletions

View file

@ -738,7 +738,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;
@ -769,7 +769,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;

View file

@ -742,7 +742,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("💾 Saved a new message for \(decodedInfo.packet.id)")
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new message for \(newMessage.messageId)") }
if newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
if newMessage.toUser != nil && newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
@ -891,17 +891,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// MARK: Incoming ROUTING_APP Packet
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
let currentNodeNum = self.connectedPeripheral.num
if let routingMessage = try? Routing(serializedData: decodedInfo.packet.decoded.payload) {
print(decodedInfo.packet.decoded.requestID)
print(routingMessage)
//let mes = routingMessage.
let error = routingMessage.errorReason
//routingMessage.routeRequest
}
if decodedInfo.packet.priority == MeshPacket.Priority.ack {
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
@ -909,15 +898,26 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
do {
let fetchedMessage = try context?.fetch(fetchMessageRequest) as! [MessageEntity]
let fetchedMessage = try context?.fetch(fetchMessageRequest)[0] as? MessageEntity
if fetchedMessage.count > 0 {
if fetchedMessage != nil {
fetchedMessage!.receivedACK = true
fetchedMessage!.ackTimestamp = Int32(Date().timeIntervalSince1970)
}
try context!.save()
if meshLoggingEnabled {
MeshLogger.log("💾 ACK Received and saved for MessageID \(decodedInfo.packet.id)")
}
print("💾 ACK Received and saved for MessageID \(decodedInfo.packet.id)")
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Saving ACK for message MessageID \(decodedInfo.packet.id) Error: \(nsError)")
}
}
@ -1100,7 +1100,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let fromNodeNum = connectedPeripheral.num
if fromNodeNum <= 0 {
if fromNodeNum <= 0 || (LocationHelper.currentLocation.latitude == LocationHelper.DefaultLocation.latitude && LocationHelper.currentLocation.longitude == LocationHelper.DefaultLocation.longitude) {
return false
}
@ -1188,6 +1188,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Send a position out to the mesh if "share location with the mesh" is enabled in settings
if userSettings!.provideLocation {
let success = sendPosition(destNum: connectedPeripheral.num, wantResponse: false)
if !success {

View file

@ -4,8 +4,8 @@ class LocationHelper: NSObject, ObservableObject {
static let shared = LocationHelper()
// Mount Rainier
static let DefaultLocation = CLLocationCoordinate2D(latitude: 46.879967, longitude: -121.726906)
// Apple Park
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)

View file

@ -40,7 +40,7 @@
<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>

View file

@ -1,6 +1,7 @@
<?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="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="direction" attributeType="String"/>
<attribute name="isTapback" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
@ -76,7 +77,7 @@
</fetchedProperty>
</entity>
<elements>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="185"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="200"/>
<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"/>

View file

@ -19,7 +19,7 @@ struct CircleText: View {
Circle()
.fill(color)
.frame(width: circleSize, height: circleSize)
Text(text).textCase(.uppercase).font(font).foregroundColor(.white)
Text(text).textCase(.uppercase).font(font).foregroundColor(.white).fixedSize()
.frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).offset(x: 0, y: 0)
}
}

View file

@ -25,8 +25,11 @@ struct Contacts: View {
List(users) { (user: UserEntity) in
let allMessages = user.value(forKey: "allMessages") as! [MessageEntity]
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
NavigationLink(destination: UserMessageList(user: user)) {
if user.num != connectedNodeNum {
NavigationLink(destination: UserMessageList(user: user)) {
if allMessages.count > 0 {
@ -120,6 +123,7 @@ struct Contacts: View {
}.padding()
}
}
}
}
.navigationTitle("Contacts")
.navigationBarTitleDisplayMode(.inline)

View file

@ -218,15 +218,9 @@ struct UserMessageList: View {
VStack {
let time = Int32(message.messageTimestamp)
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
if time != 0 {
Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray)
} else {
Text("Unknown").font(.caption2).foregroundColor(.gray)
}
let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp))
Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray)
}
VStack {
@ -234,6 +228,13 @@ struct UserMessageList: View {
Text("Received ACK: \(message.receivedACK ? "✔️" : "")")
}
if message.receivedACK {
VStack {
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
Text("ACK \(ackDate, style: .date) \(ackDate, style: .time)").font(.caption2).foregroundColor(.gray)
}
}
}
Divider()
Button(role: .destructive, action: {
@ -285,13 +286,6 @@ struct UserMessageList: View {
let time = Int32(message.messageTimestamp)
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
let showUntil = Date().addingTimeInterval(3600)
if time != 0 {
// Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray)
// Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray)
} else {
// Text("Unknown").font(.caption2).foregroundColor(.gray)
}
if messageDate <= showUntil && message.receivedACK {
@ -352,7 +346,7 @@ struct UserMessageList: View {
let index = count - 1
if index > 2 {
if index > 3 {
scrollView.scrollTo(index, anchor: .bottom)
}
@ -394,11 +388,25 @@ struct UserMessageList: View {
sendPositionWithMessage = true
if user.num == bleManager.broadcastNodeNum {
typingMessage = "📍 " + userLongName + " Has shared their position with the mesh."
if userSettings.meshtasticUsername.count > 0 {
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with the mesh from node " + userLongName
} else {
typingMessage = "📍 " + userLongName + " has shared their position with the mesh."
}
} else {
typingMessage = "📍 " + userLongName + " Has shared their position with you."
if userSettings.meshtasticUsername.count > 0 {
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName
} else {
typingMessage = "📍 " + userLongName + " has shared their position with you."
}
}
} label: {
Image(systemName: "mappin.and.ellipse")

View file

@ -43,7 +43,7 @@ struct NodeDetail: View {
MapAnnotation(
coordinate: location.coordinate,
content: {
CircleText(text: node.user!.shortName ?? "???", color: .accentColor, circleSize: 33, fontSize: 16)
CircleText(text: node.user!.shortName ?? "???", color: .accentColor, circleSize: 32, fontSize: 14)
}
)
}
@ -271,7 +271,7 @@ struct NodeDetail: View {
.padding(1)
}
}
.navigationTitle(node != nil ? String(node.user!.longName ?? "Unknown") : "Unknown")
.navigationTitle((node != nil && node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:

View file

@ -87,28 +87,28 @@ struct NodeList: View {
}
.padding([.leading, .top, .bottom])
}
.swipeActions {
Button {
context.delete(node)
do {
try context.save()
print("Successfully Deleted NodeInfoEntiy: \(node.num)")
} catch {
print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)")
}
} label: {
Label("Delete from app", systemImage: "trash")
}
.tint(.red)
}
// .swipeActions {
//
// Button {
//
// context.delete(node)
//
// do {
//
// try context.save()
// print("Successfully Deleted NodeInfoEntiy: \(node.num)")
//
// } catch {
//
// print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)")
// }
//
// } label: {
//
// Label("Delete from app", systemImage: "trash")
// }
// .tint(.red)
// }
}
}
}

View file

@ -54,11 +54,11 @@ enum MeshMapType: String, CaseIterable, Identifiable {
}
class UserSettings: ObservableObject {
// @Published var meshtasticUsername: String {
// didSet {
// UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername")
// }
// }
@Published var meshtasticUsername: String {
didSet {
UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername")
}
}
@Published var preferredPeripheralName: String {
didSet {
UserDefaults.standard.set(preferredPeripheralName, forKey: "preferredPeripheralName")
@ -99,7 +99,7 @@ class UserSettings: ObservableObject {
init() {
// self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
self.preferredPeripheralName = UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? ""
self.preferredPeripheralId = UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? ""
self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false
@ -131,12 +131,14 @@ struct AppSettings: View {
List {
Section(header: Text("USER DETAILS")) {
// HStack {
// Label("Name", systemImage: "person.crop.rectangle.fill")
// TextField("Username", text: $userSettings.meshtasticUsername)
// .foregroundColor(.gray)
// }
// .listRowSeparator(.visible)
HStack {
Label("Name", systemImage: "person.crop.rectangle.fill")
TextField("Username", text: $userSettings.meshtasticUsername)
.foregroundColor(.gray)
}
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
.listRowSeparator(.visible)
Toggle(isOn: $userSettings.provideLocation) {
Label("Provide location to mesh", systemImage: "location.circle.fill")
@ -173,13 +175,13 @@ struct AppSettings: View {
.pickerStyle(DefaultPickerStyle())
// TextField("Custom Tile Server", text: $userSettings.meshMapCustomTileServer)
}
Section(header: Text("DEBUG")) {
// Toggle(isOn: $userSettings.meshActivityLog) {
Section(header: Text("DEBUG OPTIONS")) {
Toggle(isOn: $userSettings.meshActivityLog) {
// Label("Log all Mesh activity", systemImage: "network")
// }
// .toggleStyle(SwitchToggleStyle(tint: .accentColor))
if true {// userSettings.meshActivityLog {
Label("Log all Mesh activity", systemImage: "network")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if userSettings.meshActivityLog {
NavigationLink(destination: MeshLog()) {
Text("View Mesh Log")
}