2023-09-03 22:14:59 -07:00
|
|
|
//
|
|
|
|
|
// NodeInfoEntityExtension.swift
|
|
|
|
|
// Meshtastic
|
|
|
|
|
//
|
|
|
|
|
// Copyright(c) Garth Vander Houwen 9/3/23.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import Foundation
|
2026-04-16 12:10:00 -07:00
|
|
|
import SwiftData
|
2023-09-03 22:14:59 -07:00
|
|
|
|
|
|
|
|
extension NodeInfoEntity {
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2024-07-11 08:42:27 -07:00
|
|
|
var latestPosition: PositionEntity? {
|
2026-04-16 12:10:00 -07:00
|
|
|
return self.positions.sorted(by: { ($0.time ?? .distantPast) < ($1.time ?? .distantPast) }).last
|
2024-07-11 08:42:27 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-17 15:31:35 -07:00
|
|
|
var latestDeviceMetrics: TelemetryEntity? {
|
2026-04-16 12:10:00 -07:00
|
|
|
return self.telemetries.filter { $0.metricsType == 0 }.sorted(by: { ($0.time ?? .distantPast) < ($1.time ?? .distantPast) }).last
|
2024-08-17 15:31:35 -07:00
|
|
|
}
|
|
|
|
|
|
2024-07-11 08:42:27 -07:00
|
|
|
var latestEnvironmentMetrics: TelemetryEntity? {
|
2026-04-16 12:10:00 -07:00
|
|
|
return self.telemetries.filter { $0.metricsType == 1 }.sorted(by: { ($0.time ?? .distantPast) < ($1.time ?? .distantPast) }).last
|
2024-07-11 08:42:27 -07:00
|
|
|
}
|
|
|
|
|
|
2025-01-24 22:53:10 -08:00
|
|
|
var latestPowerMetrics: TelemetryEntity? {
|
2026-04-16 12:10:00 -07:00
|
|
|
return self.telemetries.filter { $0.metricsType == 2 }.sorted(by: { ($0.time ?? .distantPast) < ($1.time ?? .distantPast) }).last
|
2025-01-24 22:53:10 -08:00
|
|
|
}
|
|
|
|
|
|
2023-09-03 22:14:59 -07:00
|
|
|
var hasPositions: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
return self.positions.count > 0
|
2023-09-03 22:14:59 -07:00
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2023-09-03 22:14:59 -07:00
|
|
|
var hasDeviceMetrics: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
let deviceMetrics = telemetries.filter { $0.metricsType == 0 }
|
|
|
|
|
return deviceMetrics.count > 0
|
2023-09-03 22:14:59 -07:00
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2023-09-03 22:14:59 -07:00
|
|
|
var hasEnvironmentMetrics: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
let environmentMetrics = telemetries.filter { $0.metricsType == 1 }
|
|
|
|
|
return environmentMetrics.count > 0
|
2023-09-03 22:14:59 -07:00
|
|
|
}
|
2025-02-21 18:23:03 -05:00
|
|
|
|
|
|
|
|
func hasDataForLatestEnvironmentMetrics(attributes: [String]) -> Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
guard let latest = self.latestEnvironmentMetrics else { return false }
|
2025-02-21 18:23:03 -05:00
|
|
|
for attribute in attributes {
|
2026-04-16 12:10:00 -07:00
|
|
|
let mirror = Mirror(reflecting: latest)
|
|
|
|
|
if let child = mirror.children.first(where: { $0.label == attribute }) {
|
|
|
|
|
if child.value is Optional<Any> {
|
|
|
|
|
let m = Mirror(reflecting: child.value)
|
|
|
|
|
if m.displayStyle == .optional && m.children.count > 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2025-02-21 18:23:03 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-16 12:10:00 -07:00
|
|
|
@MainActor
|
2023-09-09 21:23:14 -07:00
|
|
|
var hasDetectionSensorMetrics: Bool {
|
|
|
|
|
return user?.sensorMessageList.count ?? 0 > 0
|
|
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2025-01-24 22:53:10 -08:00
|
|
|
var hasPowerMetrics: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
let powerMetrics = telemetries.filter { $0.metricsType == 2 }
|
|
|
|
|
return powerMetrics.count > 0
|
2025-01-24 22:53:10 -08:00
|
|
|
}
|
|
|
|
|
|
2023-12-22 08:25:59 -08:00
|
|
|
var hasTraceRoutes: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
let routes = traceRoutes.filter { $0.response }
|
|
|
|
|
return routes.count > 0
|
2023-12-22 08:25:59 -08:00
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2024-02-25 21:40:25 -08:00
|
|
|
var hasPax: Bool {
|
2026-04-16 12:10:00 -07:00
|
|
|
return pax.count > 0
|
2024-02-25 21:40:25 -08:00
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2024-02-10 17:43:01 -08:00
|
|
|
var isStoreForwardRouter: Bool {
|
|
|
|
|
return storeForwardConfig?.isRouter ?? false
|
|
|
|
|
}
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2023-09-07 23:38:53 -07:00
|
|
|
var isOnline: Bool {
|
2024-08-25 09:50:01 -07:00
|
|
|
let twoHoursAgo = Calendar.current.date(byAdding: .minute, value: -120, to: Date())
|
|
|
|
|
if lastHeard?.compare(twoHoursAgo!) == .orderedDescending {
|
2023-09-07 23:38:53 -07:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
2024-08-18 11:39:41 -07:00
|
|
|
|
2024-08-18 10:00:15 -07:00
|
|
|
var canRemoteAdmin: Bool {
|
2024-08-18 17:15:55 -07:00
|
|
|
if UserDefaults.enableAdministration {
|
2024-08-18 10:00:15 -07:00
|
|
|
return true
|
|
|
|
|
} else {
|
2026-04-16 12:10:00 -07:00
|
|
|
let adminChannel = myInfo?.channels.filter { $0.name?.lowercased() == "admin" }
|
2024-08-18 10:00:15 -07:00
|
|
|
return adminChannel?.count ?? 0 > 0
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-16 08:55:23 -08:00
|
|
|
}
|
|
|
|
|
|
2026-04-16 12:10:00 -07:00
|
|
|
func createNodeInfo(num: Int64, context: ModelContext) -> NodeInfoEntity {
|
2024-05-29 16:40:07 -05:00
|
|
|
|
2026-04-16 12:10:00 -07:00
|
|
|
let newNode = NodeInfoEntity()
|
2024-01-16 08:55:23 -08:00
|
|
|
newNode.id = Int64(num)
|
|
|
|
|
newNode.num = Int64(num)
|
2026-04-16 12:10:00 -07:00
|
|
|
let newUser = UserEntity()
|
2024-01-16 08:55:23 -08:00
|
|
|
newUser.num = Int64(num)
|
2024-08-04 15:53:59 -07:00
|
|
|
let userId = num.toHex()
|
2024-01-16 08:55:23 -08:00
|
|
|
newUser.userId = "!\(userId)"
|
|
|
|
|
let last4 = String(userId.suffix(4))
|
|
|
|
|
newUser.longName = "Meshtastic \(last4)"
|
|
|
|
|
newUser.shortName = last4
|
|
|
|
|
newUser.hwModel = "UNSET"
|
|
|
|
|
newNode.user = newUser
|
2026-04-16 12:10:00 -07:00
|
|
|
context.insert(newNode)
|
|
|
|
|
context.insert(newUser)
|
2024-01-16 08:55:23 -08:00
|
|
|
return newNode
|
2023-09-03 22:14:59 -07:00
|
|
|
}
|