2022-05-27 19:18:33 -07:00
//
// M e s h P a c k e t s . s w i f t
// M e s h t a s t i c A p p l e
//
// C r e a t e d b y G a r t h V a n d e r H o u w e n o n 5 / 2 7 / 2 2 .
//
import Foundation
import CoreData
2022-06-01 23:23:02 -07:00
func myInfoPacket ( myInfo : MyNodeInfo , meshLogging : Bool , context : NSManagedObjectContext ) -> MyInfoEntity ? {
let fetchMyInfoRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " MyInfoEntity " )
fetchMyInfoRequest . predicate = NSPredicate ( format : " myNodeNum == %lld " , Int64 ( myInfo . myNodeNum ) )
do {
let fetchedMyInfo = try context . fetch ( fetchMyInfoRequest ) as ! [ MyInfoEntity ]
// N o t F o u n d I n s e r t
if fetchedMyInfo . isEmpty {
let myInfoEntity = MyInfoEntity ( context : context )
myInfoEntity . myNodeNum = Int64 ( myInfo . myNodeNum )
myInfoEntity . hasGps = myInfo . hasGps_p
myInfoEntity . hasWifi = myInfo . hasWifi_p
myInfoEntity . bitrate = myInfo . bitrate
// S w i f t d o e s s t r i n g s w e i r d , t h i s d o e s w o r k t o g e t t h e v e r s i o n w i t h o u t t h e g i t h u b h a s h
let lastDotIndex = myInfo . firmwareVersion . lastIndex ( of : " . " )
var version = myInfo . firmwareVersion [ . . . ( lastDotIndex ? ? String . Index ( utf16Offset : 6 , in : myInfo . firmwareVersion ) ) ]
version = version . dropLast ( )
myInfoEntity . firmwareVersion = String ( version )
myInfoEntity . messageTimeoutMsec = Int32 ( bitPattern : myInfo . messageTimeoutMsec )
myInfoEntity . minAppVersion = Int32 ( bitPattern : myInfo . minAppVersion )
myInfoEntity . maxChannels = Int32 ( bitPattern : myInfo . maxChannels )
do {
try context . save ( )
if meshLogging { MeshLogger . log ( " 💾 Saved a new myInfo for node number: \( String ( myInfo . myNodeNum ) ) " ) }
return myInfoEntity
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Inserting New Core Data MyInfoEntity: \( nsError ) " )
}
} else {
fetchedMyInfo [ 0 ] . myNodeNum = Int64 ( myInfo . myNodeNum )
fetchedMyInfo [ 0 ] . hasGps = myInfo . hasGps_p
fetchedMyInfo [ 0 ] . bitrate = myInfo . bitrate
let lastDotIndex = myInfo . firmwareVersion . lastIndex ( of : " . " ) // . l a s t I n d e x ( o f : " . " , o f f s e t B y : - 1 )
var version = myInfo . firmwareVersion [ . . . ( lastDotIndex ? ? String . Index ( utf16Offset : 6 , in : myInfo . firmwareVersion ) ) ]
version = version . dropLast ( )
fetchedMyInfo [ 0 ] . firmwareVersion = String ( version )
fetchedMyInfo [ 0 ] . messageTimeoutMsec = Int32 ( bitPattern : myInfo . messageTimeoutMsec )
fetchedMyInfo [ 0 ] . minAppVersion = Int32 ( bitPattern : myInfo . minAppVersion )
fetchedMyInfo [ 0 ] . maxChannels = Int32 ( bitPattern : myInfo . maxChannels )
do {
try context . save ( )
if meshLogging { MeshLogger . log ( " 💾 Updated myInfo for node number: \( String ( myInfo . myNodeNum ) ) " ) }
return fetchedMyInfo [ 0 ]
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Updating Core Data MyInfoEntity: \( nsError ) " )
}
}
} catch {
print ( " 💥 Fetch MyInfo Error " )
}
return nil
}
func nodeInfoPacket ( nodeInfo : NodeInfo , meshLogging : Bool , context : NSManagedObjectContext ) -> NodeInfoEntity ? {
let fetchNodeInfoRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " NodeInfoEntity " )
fetchNodeInfoRequest . predicate = NSPredicate ( format : " num == %lld " , Int64 ( nodeInfo . num ) )
do {
let fetchedNode = try context . fetch ( fetchNodeInfoRequest ) as ! [ NodeInfoEntity ]
// N o t F o u n d I n s e r t
if fetchedNode . isEmpty && nodeInfo . hasUser {
let newNode = NodeInfoEntity ( context : context )
newNode . id = Int64 ( nodeInfo . num )
newNode . num = Int64 ( nodeInfo . num )
if nodeInfo . hasDeviceMetrics {
let telemetry = TelemetryEntity ( context : context )
telemetry . batteryLevel = Int32 ( nodeInfo . deviceMetrics . batteryLevel )
telemetry . voltage = nodeInfo . deviceMetrics . voltage
telemetry . channelUtilization = nodeInfo . deviceMetrics . channelUtilization
telemetry . airUtilTx = nodeInfo . deviceMetrics . airUtilTx
var newTelemetries = [ TelemetryEntity ] ( )
newTelemetries . append ( telemetry )
newNode . telemetries ? = NSOrderedSet ( array : newTelemetries )
}
newNode . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( nodeInfo . lastHeard ) ) )
newNode . snr = nodeInfo . snr
if nodeInfo . hasUser {
let newUser = UserEntity ( context : context )
newUser . userId = nodeInfo . user . id
newUser . num = Int64 ( nodeInfo . num )
newUser . longName = nodeInfo . user . longName
newUser . shortName = nodeInfo . user . shortName
newUser . macaddr = nodeInfo . user . macaddr
newUser . hwModel = String ( describing : nodeInfo . user . hwModel ) . uppercased ( )
newNode . user = newUser
}
let position = PositionEntity ( context : context )
position . latitudeI = nodeInfo . position . latitudeI
position . longitudeI = nodeInfo . position . longitudeI
position . altitude = nodeInfo . position . altitude
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( nodeInfo . position . time ) ) )
var newPostions = [ PositionEntity ] ( )
newPostions . append ( position )
newNode . positions ? = NSOrderedSet ( array : newPostions )
// L o o k f o r a M y I n f o
let fetchMyInfoRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " MyInfoEntity " )
fetchMyInfoRequest . predicate = NSPredicate ( format : " myNodeNum == %lld " , Int64 ( nodeInfo . num ) )
do {
let fetchedMyInfo = try context . fetch ( fetchMyInfoRequest ) as ! [ MyInfoEntity ]
if fetchedMyInfo . count > 0 {
newNode . myInfo = fetchedMyInfo [ 0 ]
}
do {
try context . save ( )
if nodeInfo . hasUser {
if meshLogging { MeshLogger . log ( " 💾 BLE FROMRADIO received and nodeInfo inserted for \( nodeInfo . user . longName ) " ) }
} else {
if meshLogging { MeshLogger . log ( " 💾 BLE FROMRADIO received and nodeInfo inserted for \( nodeInfo . num ) " ) }
}
return newNode
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving Core Data NodeInfoEntity: \( nsError ) " )
}
} catch {
print ( " 💥 Fetch MyInfo Error " )
}
} else if nodeInfo . hasUser && nodeInfo . num > 0 {
fetchedNode [ 0 ] . id = Int64 ( nodeInfo . num )
fetchedNode [ 0 ] . num = Int64 ( nodeInfo . num )
fetchedNode [ 0 ] . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( nodeInfo . lastHeard ) ) )
fetchedNode [ 0 ] . snr = nodeInfo . snr
if nodeInfo . hasUser {
fetchedNode [ 0 ] . user ! . userId = nodeInfo . user . id
fetchedNode [ 0 ] . user ! . num = Int64 ( nodeInfo . num )
fetchedNode [ 0 ] . user ! . longName = nodeInfo . user . longName
fetchedNode [ 0 ] . user ! . shortName = nodeInfo . user . shortName
fetchedNode [ 0 ] . user ! . macaddr = nodeInfo . user . macaddr
fetchedNode [ 0 ] . user ! . hwModel = String ( describing : nodeInfo . user . hwModel ) . uppercased ( )
}
if nodeInfo . hasDeviceMetrics {
let newTelemetry = TelemetryEntity ( context : context )
newTelemetry . batteryLevel = Int32 ( nodeInfo . deviceMetrics . batteryLevel )
newTelemetry . voltage = nodeInfo . deviceMetrics . voltage
newTelemetry . channelUtilization = nodeInfo . deviceMetrics . channelUtilization
newTelemetry . airUtilTx = nodeInfo . deviceMetrics . airUtilTx
let mutableTelemetries = fetchedNode [ 0 ] . telemetries ! . mutableCopy ( ) as ! NSMutableOrderedSet
fetchedNode [ 0 ] . telemetries = mutableTelemetries . copy ( ) as ? NSOrderedSet
}
if nodeInfo . hasPosition {
let position = PositionEntity ( context : context )
position . latitudeI = nodeInfo . position . latitudeI
position . longitudeI = nodeInfo . position . longitudeI
position . altitude = nodeInfo . position . altitude
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( nodeInfo . position . time ) ) )
let mutablePositions = fetchedNode [ 0 ] . positions ! . mutableCopy ( ) as ! NSMutableOrderedSet
fetchedNode [ 0 ] . positions = mutablePositions . copy ( ) as ? NSOrderedSet
}
// L o o k f o r a M y I n f o
let fetchMyInfoRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " MyInfoEntity " )
fetchMyInfoRequest . predicate = NSPredicate ( format : " myNodeNum == %lld " , Int64 ( nodeInfo . num ) )
do {
let fetchedMyInfo = try context . fetch ( fetchMyInfoRequest ) as ! [ MyInfoEntity ]
if fetchedMyInfo . count > 0 {
fetchedNode [ 0 ] . myInfo = fetchedMyInfo [ 0 ]
}
do {
try context . save ( )
if nodeInfo . hasUser {
if meshLogging { MeshLogger . log ( " 💾 BLE FROMRADIO received and nodeInfo inserted for \( nodeInfo . user . longName ) " ) }
} else {
if meshLogging { MeshLogger . log ( " 💾 BLE FROMRADIO received and nodeInfo inserted for \( nodeInfo . num ) " ) }
}
return fetchedNode [ 0 ]
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving Core Data NodeInfoEntity: \( nsError ) " )
}
} catch {
print ( " 💥 Fetch MyInfo Error " )
}
}
} catch {
print ( " 💥 Fetch NodeInfoEntity Error " )
}
return nil
}
func nodeInfoAppPacket ( packet : MeshPacket , meshLogging : Bool , context : NSManagedObjectContext ) {
2022-05-27 19:18:33 -07:00
let fetchNodeInfoAppRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " NodeInfoEntity " )
fetchNodeInfoAppRequest . predicate = NSPredicate ( format : " num == %lld " , Int64 ( packet . from ) )
do {
let fetchedNode = try context . fetch ( fetchNodeInfoAppRequest ) as ! [ NodeInfoEntity ]
if fetchedNode . count = = 1 {
fetchedNode [ 0 ] . id = Int64 ( packet . from )
fetchedNode [ 0 ] . num = Int64 ( packet . from )
fetchedNode [ 0 ] . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( packet . rxTime ) ) )
fetchedNode [ 0 ] . snr = packet . rxSnr
2022-06-03 18:13:31 -07:00
if let nodeInfoMessage = try ? NodeInfo ( serializedData : packet . decoded . payload ) {
if nodeInfoMessage . hasDeviceMetrics {
let telemetry = TelemetryEntity ( context : context )
telemetry . batteryLevel = Int32 ( nodeInfoMessage . deviceMetrics . batteryLevel )
telemetry . voltage = nodeInfoMessage . deviceMetrics . voltage
telemetry . channelUtilization = nodeInfoMessage . deviceMetrics . channelUtilization
telemetry . airUtilTx = nodeInfoMessage . deviceMetrics . airUtilTx
var newTelemetries = [ TelemetryEntity ] ( )
newTelemetries . append ( telemetry )
fetchedNode [ 0 ] . telemetries ? = NSOrderedSet ( array : newTelemetries )
}
}
2022-05-27 19:18:33 -07:00
} else {
return
}
do {
try context . save ( )
if meshLogging { MeshLogger . log ( " 💾 Updated NodeInfo SNR \( packet . rxSnr ) and Time from Node Info App Packet For: \( fetchedNode [ 0 ] . num ) " ) }
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving NodeInfoEntity from NODEINFO_APP \( nsError ) " )
}
} catch {
print ( " 💥 Error Fetching NodeInfoEntity for NODEINFO_APP " )
}
}
func positionPacket ( packet : MeshPacket , meshLogging : Bool , context : NSManagedObjectContext ) {
let fetchNodePositionRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " NodeInfoEntity " )
fetchNodePositionRequest . predicate = NSPredicate ( format : " num == %lld " , Int64 ( packet . from ) )
do {
let fetchedNode = try context . fetch ( fetchNodePositionRequest ) as ! [ NodeInfoEntity ]
if fetchedNode . count = = 1 {
fetchedNode [ 0 ] . id = Int64 ( packet . from )
fetchedNode [ 0 ] . num = Int64 ( packet . from )
fetchedNode [ 0 ] . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( packet . rxTime ) ) )
fetchedNode [ 0 ] . snr = packet . rxSnr
if let positionMessage = try ? Position ( serializedData : packet . decoded . payload ) {
let position = PositionEntity ( context : context )
position . latitudeI = positionMessage . latitudeI
position . longitudeI = positionMessage . longitudeI
position . altitude = positionMessage . altitude
position . time = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( positionMessage . time ) ) )
let mutablePositions = fetchedNode [ 0 ] . positions ! . mutableCopy ( ) as ! NSMutableOrderedSet
mutablePositions . add ( position )
fetchedNode [ 0 ] . positions = mutablePositions . copy ( ) as ? NSOrderedSet
}
} else {
return
}
do {
try context . save ( )
if meshLogging {
2022-05-27 22:14:57 -07:00
MeshLogger . log ( " 💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \( fetchedNode [ 0 ] . num ) " )
2022-05-27 19:18:33 -07:00
}
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving NodeInfoEntity from POSITION_APP \( nsError ) " )
}
} catch {
print ( " 💥 Error Fetching NodeInfoEntity for POSITION_APP " )
}
}
func routingPacket ( packet : MeshPacket , meshLogging : Bool , context : NSManagedObjectContext ) {
if let routingMessage = try ? Routing ( serializedData : packet . decoded . payload ) {
let error = routingMessage . errorReason
var errorExplanation = " Unknown Routing Error "
switch error {
case Routing . Error . none :
errorExplanation = " This message is not a failure "
case Routing . Error . noRoute :
errorExplanation = " Our node doesn't have a route to the requested destination anymore. "
case Routing . Error . gotNak :
errorExplanation = " We received a nak while trying to forward on your behalf "
case Routing . Error . timeout :
errorExplanation = " Timeout "
case Routing . Error . noInterface :
errorExplanation = " No suitable interface could be found for delivering this packet "
case Routing . Error . maxRetransmit :
errorExplanation = " We reached the max retransmission count (typically for naive flood routing) "
case Routing . Error . noChannel :
errorExplanation = " No suitable channel was found for sending this packet (i.e. was requested channel index disabled?) "
case Routing . Error . tooLarge :
errorExplanation = " The packet was too big for sending (exceeds interface MTU after encoding) "
case Routing . Error . noResponse :
errorExplanation = " The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions) "
case Routing . Error . badRequest :
errorExplanation = " The application layer service on the remote node received your request, but considered your request somehow invalid "
case Routing . Error . notAuthorized :
errorExplanation = " The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) "
fallthrough
2022-06-03 18:13:31 -07:00
default : ( )
2022-05-27 19:18:33 -07:00
}
if meshLogging { MeshLogger . log ( " 🕸️ ROUTING PACKET received for RequestID: \( packet . decoded . requestID ) Error: \( errorExplanation ) " ) }
if routingMessage . errorReason = = Routing . Error . none {
let fetchMessageRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " MessageEntity " )
fetchMessageRequest . predicate = NSPredicate ( format : " messageId == %lld " , Int64 ( packet . decoded . requestID ) )
do {
let fetchedMessage = try context . fetch ( fetchMessageRequest ) [ 0 ] as ? MessageEntity
if fetchedMessage != nil {
fetchedMessage ! . receivedACK = true
fetchedMessage ! . ackSNR = packet . rxSnr
fetchedMessage ! . ackTimestamp = Int32 ( packet . rxTime )
fetchedMessage ! . objectWillChange . send ( )
2022-06-04 07:41:52 -07:00
fetchedMessage ! . fromUser ? . objectWillChange . send ( )
fetchedMessage ! . toUser ? . objectWillChange . send ( )
2022-05-27 19:18:33 -07:00
}
try context . save ( )
if meshLogging {
MeshLogger . log ( " 💾 ACK Received and saved for MessageID \( packet . decoded . requestID ) " )
}
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving ACK for message MessageID \( packet . id ) Error: \( nsError ) " )
}
}
}
}
func telemetryPacket ( packet : MeshPacket , meshLogging : Bool , context : NSManagedObjectContext ) {
if let telemetryMessage = try ? Telemetry ( serializedData : packet . decoded . payload ) {
let telemetry = TelemetryEntity ( context : context )
2022-06-01 23:23:02 -07:00
let fetchNodeTelemetryRequest : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " NodeInfoEntity " )
fetchNodeTelemetryRequest . predicate = NSPredicate ( format : " num == %lld " , Int64 ( packet . from ) )
do {
let fetchedNode = try context . fetch ( fetchNodeTelemetryRequest ) as ! [ NodeInfoEntity ]
if fetchedNode . count = = 1 {
// D e v i c e M e t r i c s
telemetry . airUtilTx = telemetryMessage . deviceMetrics . airUtilTx
telemetry . channelUtilization = telemetryMessage . deviceMetrics . channelUtilization
telemetry . batteryLevel = Int32 ( telemetryMessage . deviceMetrics . batteryLevel )
telemetry . voltage = telemetryMessage . deviceMetrics . voltage
// E n v i r o n m e n t M e t r i c s
telemetry . barometricPressure = telemetryMessage . environmentMetrics . barometricPressure
telemetry . current = telemetryMessage . environmentMetrics . current
telemetry . gasResistance = telemetryMessage . environmentMetrics . gasResistance
telemetry . relativeHumidity = telemetryMessage . environmentMetrics . relativeHumidity
telemetry . temperature = telemetryMessage . environmentMetrics . temperature
let mutableTelemetries = fetchedNode [ 0 ] . telemetries ! . mutableCopy ( ) as ! NSMutableOrderedSet
mutableTelemetries . add ( telemetry )
fetchedNode [ 0 ] . lastHeard = Date ( timeIntervalSince1970 : TimeInterval ( Int64 ( telemetryMessage . time ) ) )
fetchedNode [ 0 ] . telemetries = mutableTelemetries . copy ( ) as ? NSOrderedSet
fetchedNode [ 0 ] . objectWillChange . send ( )
}
try context . save ( )
if meshLogging {
MeshLogger . log ( " 💾 Telemetry Saved for Node: \( packet . from ) " )
}
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Error Saving Telemetry for Node \( packet . from ) Error: \( nsError ) " )
}
2022-05-27 19:18:33 -07:00
} else {
}
}
func textMessageAppPacket ( packet : MeshPacket , connectedNode : Int64 , meshLogging : Bool , context : NSManagedObjectContext ) {
let broadcastNodeNum : UInt32 = 4294967295
if let messageText = String ( bytes : packet . decoded . payload , encoding : . utf8 ) {
2022-05-27 22:14:57 -07:00
if meshLogging { MeshLogger . log ( " 💬 Message received for text message app \( messageText ) " ) }
2022-05-27 19:18:33 -07:00
let messageUsers : NSFetchRequest < NSFetchRequestResult > = NSFetchRequest . init ( entityName : " UserEntity " )
messageUsers . predicate = NSPredicate ( format : " num IN %@ " , [ packet . to , packet . from ] )
do {
let fetchedUsers = try context . fetch ( messageUsers ) as ! [ UserEntity ]
let newMessage = MessageEntity ( context : context )
newMessage . messageId = Int64 ( packet . id )
newMessage . messageTimestamp = Int32 ( bitPattern : packet . rxTime )
newMessage . receivedACK = false
newMessage . direction = " IN "
newMessage . isEmoji = packet . decoded . emoji = = 1
if packet . decoded . replyID > 0 {
newMessage . replyID = Int64 ( packet . decoded . replyID )
}
if packet . to = = broadcastNodeNum && fetchedUsers . count = = 1 {
// S a v e t h e b r o a d c a s t u s e r i f i t d o e s n o t e x i s t
let bcu : UserEntity = UserEntity ( context : context )
bcu . shortName = " ALL "
bcu . longName = " All - Broadcast "
bcu . hwModel = " UNSET "
bcu . num = Int64 ( broadcastNodeNum )
bcu . userId = " BROADCASTNODE "
newMessage . toUser = bcu
} else {
newMessage . toUser = fetchedUsers . first ( where : { $0 . num = = packet . to } )
}
newMessage . fromUser = fetchedUsers . first ( where : { $0 . num = = packet . from } )
newMessage . messagePayload = messageText
2022-06-06 22:24:35 -07:00
newMessage . fromUser ? . objectWillChange . send ( )
newMessage . toUser ? . objectWillChange . send ( )
2022-05-27 19:18:33 -07:00
do {
try context . save ( )
print ( " 💾 Saved a new message for \( packet . id ) " )
if meshLogging { MeshLogger . log ( " 💾 Saved a new message for \( newMessage . messageId ) " ) }
if newMessage . toUser != nil && newMessage . toUser ! . num = = broadcastNodeNum || connectedNode = = newMessage . toUser ! . num {
// C r e a t e a n i O S N o t i f i c a t i o n f o r t h e r e c e i v e d m e s s a g e a n d s c h e d u l e i t i m m e d i a t e l y
let manager = LocalNotificationManager ( )
manager . notifications = [
Notification (
id : ( " notification.id. \( newMessage . messageId ) " ) ,
title : " \( newMessage . fromUser ? . longName ? ? " Unknown " ) " ,
subtitle : " AKA \( newMessage . fromUser ? . shortName ? ? " ??? " ) " ,
content : messageText )
]
manager . schedule ( )
if meshLogging { MeshLogger . log ( " 💬 iOS Notification Scheduled for text message from \( newMessage . fromUser ? . longName ? ? " Unknown " ) \( messageText ) " ) }
}
} catch {
context . rollback ( )
let nsError = error as NSError
print ( " 💥 Failed to save new MessageEntity \( nsError ) " )
}
} catch {
print ( " 💥 Fetch Message To and From Users Error " )
}
}
}