// // HealthModel.swift // Twear // // Created by yangbin on 2021/11/16. // import UIKit import RealmSwift import SwiftDate //MARK: - 步数 class StepModel: Object { @Persisted var number: Int = 0 @Persisted var calorie: Float = 0 @Persisted var distance: Float = 0 @Persisted var date: Date? = nil @Persisted var percent: Int = 0 convenience init(number: Int, calorie: Float, distance: Float, date: Date) { self.init() self.number = number self.distance = distance self.calorie = calorie self.percent = min(number*100/StepsGoal, 100) self.date = date } class func toStepArray(_ bytes: [UInt8]) -> [StepModel] { var stepArray: [StepModel] = [] let date = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2]), hour: 0, minute: 0, second: 0).date let stepBytes: [UInt8] = Array(bytes[4.. StepModel { let step = bytesToInt(Array(bytes[0..<4])) print((Float(step)*(0.400 * 175)/100000)) return StepModel(number: bytesToInt(Array(bytes[0..<4])), calorie: bytesToFloat(Array(bytes[4..<8])), distance: bytesToFloat(Array(bytes[8..<12])), date: DateInRegion().date) } private class func stepToCalorie(_ step: Int) -> Float { let user = UserInfo var weight = user.weight if user.weight < 15 { weight = 65 } if user.gender == 1 { return Float((weight - 10)) * stepToDistance(step) } else { return Float((weight - 10)) * stepToDistance(step) * 1.3 } } private class func stepToDistance(_ step: Int) -> Float { let user = UserInfo if user.gender == 1 { return Float(step) * 0.400 * Float(user.height) / 100000 } else { return Float(step) * 0.350 * Float(user.height) / 100000 } } func realTimeAdd() { if let date = self.date, date.isInFuture { return } if let stepArray = RealmTools.queryObjects(StepModel.self, filter: "date = %@", [self.date!.dateAtStartOf(.hour)]) as? [StepModel] { RealmTools.deleteList(stepArray) } RealmTools.add(StepModel(number: self.number, calorie: self.calorie, distance: self.distance, date: self.date!.dateAtStartOf(.hour))) } class func addArray(_ array: [StepModel]) { for step in array { if step.number > 0 { step.realTimeAdd() } } } class func getRecentSteps() -> StepModel { let step = StepModel(number: 0, calorie: 0, distance: 0, date: DateInRegion().date) guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: 1) as? [StepModel], array.count > 0 else { return step } return array.reversed().first ?? step } class func getLastWeekSteps() -> [StepModel] { var stepArray: [StepModel] = [] let date = (DateInRegion().date - 6.days).dateAtStartOf(.day) for i in 0..<7 { let startDate = date + i.days let array = getStepHistory(startTime: startDate, endTime: startDate+1.days) if let step = array.last { stepArray.append(step) } else { stepArray.append(StepModel(number: 0, calorie: 0, distance: 0, date: startDate)) } } return stepArray } class func getStepsByDay(_ date: Date) -> [StepModel] { var stepArray = getStepHistory(startTime: date.dateAtStartOf(.day), endTime: date.dateAtEndOf(.day)) guard let step = stepArray.first, let firstDate = step.date else { return [] } if firstDate.isAfterDate(firstDate.dateAtStartOf(.day)+1.hours, orEqual: true, granularity: .hour) { stepArray.insert(StepModel(number: 0, calorie: 01, distance: 0, date: firstDate.date.dateAtStartOf(.hour)-1.hours), at: 0) } if let lastStep = stepArray.last, (DateInRegion().date.isAfterDate(lastStep.date!, orEqual: false, granularity: .minute) && DateInRegion().date.isInside(date: lastStep.date!, granularity: .day)) { stepArray[stepArray.count-1] = StepModel(number: lastStep.number, calorie: lastStep.calorie, distance: lastStep.distance, date: DateInRegion().date) } return stepArray } class func getStepsByWeek(_ date: Date) -> [StepModel] { var stepArray: [StepModel] = [] let weekDays = date.compare(.isThisWeek) ? (DateInRegion()-1.days).weekday : 7 for i in 0.. [StepModel] { var stepArray: [StepModel] = [] let monthDays = date.compare(.isThisMonth) ? DateInRegion().day : date.monthDays for i in 0.. [StepModel] { var stepArray: [StepModel] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. (max: StepModel, array: [StepModel]) { var stepArray: [StepModel] = [] var maxStepArray: [StepModel] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. [StepModel] { // let p = NSPredicate(format: "date >= %@ AND date <= %@", startTime, endTime) let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") // RealmTools.queryObjects(self, filter: "date >= %@ AND date <= %@", [startTime, endTime]) if array == nil { return [] } else { return array as! [StepModel] } } class func updateStep(date: Date, step: Int) { guard let array = RealmTools.queryObjects(self, filter: "date >= %@ AND date <= %@", [date.dateAtStartOf(.hour).date, date.dateAtEndOf(.hour).date]) as? Array, array.count > 0 else { return } let model = array.last RealmTools.updateWithTranstion { (true) in model?.number = step } } } //MARK: - 心率 class HeartRateModel: Object { @Persisted var value: Int = -1 @Persisted var date: Date? = nil convenience init(value: Int, date: Date?) { self.init() self.value = value self.date = date } class func toHeartRateArray(_ bytes: [UInt8]) -> [HeartRateModel] { var hrArray: [HeartRateModel] = [] let num = bytes.count/7 for i in 0.. Bool { if self.value == hr.value { if self.date!.isInside(date: hr.date!, granularity: .minute) { return true } } return false } func realTimeAdd() { if let hrArray = RealmTools.queryObjects(HeartRateModel.self) as? [HeartRateModel], let hr = hrArray.last { if !self.isEqual(hr) { RealmTools.add(self) } } else { RealmTools.add(self) } } class func addArray(_ array: [HeartRateModel]) { if array.count == 0 { return } for (i, hr) in array.enumerated() { if hr == array.first { hr.add() } else { if !hr.isEqual(array[i-1]) { hr.add() } } } } class func getRecentData(count: Int = 31) -> [HeartRateModel] { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: count) as? [HeartRateModel], array.count > 0 else { return [] } return array.reversed() } class func getHeartRateByDay(_ date: Date) -> (hrArray: [HeartRateModel], minHr: [Int], maxHr: [Int]) { var hrArray: [HeartRateModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] for h in 0..<24 { let hourArray = getHistoryHeartRate(startTime: date.dateAtStartOf(.day)+h.hours, endTime: date.dateAtStartOf(.day)+(h+1).hours) for m in 0..<60 { var minuteArray: [HeartRateModel] = [] for hr in hourArray { if hr.date!.minute == m { minuteArray.append(hr) } } if minuteArray.count > 0 { hrArray.append(HeartRateModel(value: minuteArray.average(\.value), date: date.dateAtStartOf(.day)+h.hours+m.minutes)) minArray.append(minuteArray.min(\.value)?.value ?? -1) maxArray.append(minuteArray.max(\.value)?.value ?? -1) } } } /* for i in 0..<24 { for m in 0..<60 { rec += 1 let minuteArray = getHistoryHeartRate(startTime: date.dateAtStartOf(.day)+i.hours+m.minutes, endTime: date.dateAtStartOf(.day)+i.hours+(m+1).minutes) if minuteArray.count > 0 { hrArray.append(HeartRateModel(value: minuteArray.average(\.value), date: date.dateAtStartOf(.day)+i.hours+m.minutes)) minArray.append(minuteArray.min(\.value)?.value ?? -1) maxArray.append(minuteArray.max(\.value)?.value ?? -1) } } } */ // if hrArray.count > 1 { // minArray.insert(hrArray[0].value, at: 0) // maxArray.insert(hrArray[0].value, at: 0) // } return (hrArray, minArray, maxArray) } class func getHeartRateByWeek(_ date: Date) -> (hrArray: [HeartRateModel], minHr: [Int], maxHr: [Int]) { var hrArray: [HeartRateModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let weekDays = date.compare(.isThisWeek) ? (DateInRegion()-1.days).weekday : 7 for i in 0.. 0 { hrArray.append(HeartRateModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (hrArray, minArray, maxArray) } class func getHeartRateByMonth(_ date: Date) -> (hrArray: [HeartRateModel], minHr: [Int], maxHr: [Int]) { var hrArray: [HeartRateModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let monthDays = date.compare(.isThisMonth) ? DateInRegion().day : date.monthDays for i in 0.. 0 { hrArray.append(HeartRateModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (hrArray, minArray, maxArray) } class func getHeartRateByYear(_ date: Date) -> (hrArray: [HeartRateModel], minHr: [Int], maxHr: [Int]) { var hrArray: [HeartRateModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. 0 { hrArray.append(HeartRateModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (hrArray, minArray, maxArray) } private class func getHistoryHeartRate(startTime: Date, endTime: Date) -> [HeartRateModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } } //MARK: - 血压 class BloodPressureModel: Object { @Persisted var sbp: Int = -1 @Persisted var dbp: Int = -1 @Persisted var date: Date? = nil convenience init(dbp: Int, sbp: Int, date: Date?) { self.init() self.sbp = sbp self.dbp = dbp self.date = date } class func toBloodPressureArray(_ bytes: [UInt8]) -> [BloodPressureModel] { var bpArray: [BloodPressureModel] = [] let num = bytes.count/8 for i in 0.. Bool { if self.sbp == bp.sbp && self.dbp == bp.dbp { if self.date!.isInside(date: bp.date!, granularity: .minute) { return true } } return false } func realTimeAdd() { if let bpArray = RealmTools.queryObjects(BloodPressureModel.self) as? [BloodPressureModel], let bp = bpArray.last { if !self.isEqual(bp) { RealmTools.add(self) } } else { RealmTools.add(self) } } class func addArray(_ array: [BloodPressureModel]) { if array.count == 0 { return } for (i, bp) in array.enumerated() { if bp == array.first { bp.add() } else { if !bp.isEqual(array[i-1]) { bp.add() } } } } class func getRecentData(count: Int = 31) -> [BloodPressureModel] { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: count) as? [BloodPressureModel], array.count > 0 else { return [] } return array.reversed() } class func getBloodPressureByDay(_ date: Date) -> [BloodPressureModel] { var bpArray: [BloodPressureModel] = [] for h in 0..<24 { let hourArray = getHistoryBloodPressure(startTime: date.dateAtStartOf(.day)+h.hours, endTime: date.dateAtStartOf(.day)+(h+1).hours) for m in 0..<60 { var minuteArray: [BloodPressureModel] = [] for bp in hourArray { if bp.date!.minute == m { minuteArray.append(bp) } } if minuteArray.count > 0 { bpArray.append(BloodPressureModel(dbp: minuteArray.average(\.dbp), sbp: minuteArray.average(\.sbp), date: date.dateAtStartOf(.day)+h.hours+m.minutes)) } } } return bpArray } class func getBloodPressureByWeek(_ date: Date) -> [BloodPressureModel] { var bpArray: [BloodPressureModel] = [] let weekDays = date.compare(.isThisWeek) ? (DateInRegion()-1.days).weekday : 7 print(date.compare(.isThisWeek)) for i in 0.. 0 { bpArray.append(BloodPressureModel(dbp: array.average(\.dbp), sbp: array.average(\.sbp), date: startDate)) } } return bpArray } class func getBloodPressureByMonth(_ date: Date) -> [BloodPressureModel] { var bpArray: [BloodPressureModel] = [] let monthDays = date.compare(.isThisMonth) ? DateInRegion().day : date.monthDays for i in 0.. 0 { bpArray.append(BloodPressureModel(dbp: array.average(\.dbp), sbp: array.average(\.sbp), date: startDate)) } } return bpArray } class func getBloodPressureByYear(_ date: Date) -> [BloodPressureModel] { var bpArray: [BloodPressureModel] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. 0 { bpArray.append(BloodPressureModel(dbp: array.average(\.dbp), sbp: array.average(\.sbp), date: startDate)) } } return bpArray } private class func getHistoryBloodPressure(startTime: Date, endTime: Date) -> [BloodPressureModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } } //MARK: - 血氧 class BloodOxygenModel: Object { @Persisted var value: Int = -1 @Persisted var date: Date? = nil convenience init(value: Int, date: Date?) { self.init() self.value = value self.date = date } class func toBloodOxygenArray(_ bytes: [UInt8]) -> [BloodOxygenModel] { var boArray: [BloodOxygenModel] = [] let num = bytes.count/7 for i in 0.. Bool { if self.value == bo.value { if self.date!.isInside(date: bo.date!, granularity: .minute) { return true } } return false } func realTimeAdd() { if let boArray = RealmTools.queryObjects(BloodOxygenModel.self) as? [BloodOxygenModel], let bo = boArray.last { if !self.isEqual(bo) { RealmTools.add(self) } } else { print("第一次测量") RealmTools.add(self) } } class func addArray(_ array: [BloodOxygenModel]) { if array.count == 0 { return } for (i, bo) in array.enumerated() { if bo == array.first { bo.add() } else { if !bo.isEqual(array[i-1]) { bo.add() } } } } class func getRecentData(count: Int = 31) -> [BloodOxygenModel] { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: count) as? [BloodOxygenModel], array.count > 0 else { return [] } return array.reversed() } class func getBloodOxygenByDay(_ date: Date) -> (boArray: [BloodOxygenModel], minBo: [Int], maxBo: [Int]) { var boArray: [BloodOxygenModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] for h in 0..<24 { let hourArray = getHistoryBloodOxygen(startTime: date.dateAtStartOf(.day)+h.hours, endTime: date.dateAtStartOf(.day)+(h+1).hours) for m in 0..<60 { var minuteArray: [BloodOxygenModel] = [] for bo in hourArray { if bo.date!.minute == m { minuteArray.append(bo) } } if minuteArray.count > 0 { boArray.append(BloodOxygenModel(value: minuteArray.average(\.value), date: date.dateAtStartOf(.day)+h.hours+m.minutes)) minArray.append(minuteArray.min(\.value)?.value ?? -1) maxArray.append(minuteArray.max(\.value)?.value ?? -1) } } } // if boArray.count > 0 { // minArray.insert(boArray[0].value, at: 0) // maxArray.insert(boArray[0].value, at: 0) // } return (boArray, minArray, maxArray) } class func getBloodOxygenByWeek(_ date: Date) -> (boArray: [BloodOxygenModel], minBo: [Int], maxBo: [Int]) { var boArray: [BloodOxygenModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let weekDays = date.compare(.isThisWeek) ? (DateInRegion()-1.days).weekday : 7 for i in 0.. 0 { boArray.append(BloodOxygenModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (boArray, minArray, maxArray) } class func getBloodOxygenByMonth(_ date: Date) -> (boArray: [BloodOxygenModel], minBo: [Int], maxBo: [Int]) { var boArray: [BloodOxygenModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let monthDays = date.compare(.isThisMonth) ? DateInRegion().day : date.monthDays for i in 0.. 0 { boArray.append(BloodOxygenModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (boArray, minArray, maxArray) } class func getBloodOxygenByYear(_ date: Date) -> (boArray: [BloodOxygenModel], minBo: [Int], maxBo: [Int]) { var boArray: [BloodOxygenModel] = [] var minArray: [Int] = [] var maxArray: [Int] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. 0 { boArray.append(BloodOxygenModel(value: array.average(\.value), date: startDate)) minArray.append(array.min(\.value)?.value ?? -1) maxArray.append(array.max(\.value)?.value ?? -1) } } return (boArray, minArray, maxArray) } private class func getHistoryBloodOxygen(startTime: Date, endTime: Date) -> [BloodOxygenModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } } //MARK: - 睡眠 enum SleepType: Int { case awake = 0 case deep = 1 case light = 2 } class SleepModel: Object { @Persisted var startDate: Date? = nil @Persisted var endDate: Date? = nil @Persisted private var typeRaw: Int = 0 @Persisted var length: Int = 0 // private var sleepTimeClosure: ((_ error: Int?) -> ())? var type: SleepType? { get { return SleepType(rawValue: typeRaw) } set { typeRaw = newValue?.rawValue ?? 0 } } convenience init(type: SleepType, startDate: Date, endDate: Date) { self.init() self.type = type self.startDate = startDate self.endDate = endDate self.length = (endDate-startDate).in(.minute) ?? 0 } class func toSleepArray(_ bytes: [UInt8]) -> [SleepModel] { var sleepArray: [SleepModel] = [] let date = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2])-1, hour: 22, minute: 0, second: 0).date let sleepBytes = Array(bytes[3.. 20 { startDate = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2])-1, hour: Int(sleepBytes[i*3+1]), minute: Int(sleepBytes[i*3+2])).date } else { startDate = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2]), hour: Int(sleepBytes[i*3+1]), minute: Int(sleepBytes[i*3+2])).date } // if i < num { if Int(sleepBytes[(i+1)*3+1]) > 20 { endDate = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2])-1, hour: Int(sleepBytes[(i+1)*3+1]), minute: Int(sleepBytes[(i+1)*3+2])).date } else { endDate = DateInRegion(year: Int(bytes[0])+2000, month: Int(bytes[1]), day: Int(bytes[2]), hour: Int(sleepBytes[(i+1)*3+1]), minute: Int(sleepBytes[(i+1)*3+2])).date } if platform == ._818 || platform == ._818_hq7 { if rawInt == 1 { rawInt = 2 } else if rawInt == 2 { rawInt = 1 } endDate = endDate + 1.days startDate = startDate + 1.days } sleepArray.append(SleepModel(type: SleepType(rawValue: rawInt) ?? .awake, startDate: startDate, endDate: endDate)) // } /* if i == 0 { sleepArray.append(SleepModel(type: .awake, startDate: date, endDate: date+Int(sleepBytes[i*3+1]).hours+Int(sleepBytes[i*3+2]).minutes)) } else { var rawInt = Int(sleepBytes[(i-1)*3]) if rawInt == 1 { rawInt = 2 } else if rawInt == 2 { rawInt = 1 } sleepArray.append(SleepModel(type: SleepType(rawValue: rawInt) ?? .awake, startDate: date+Int(sleepBytes[(i-1)*3+1]).hours+Int(sleepBytes[(i-1)*3+2]).minutes, endDate: date+Int(sleepBytes[i*3+1]).hours+Int(sleepBytes[i*3+2]).minutes)) } */ } return sleepArray } private class func toSleepSummary(_ array: [SleepModel]) -> SleepSummary { if array.count > 0 { var awake: Int = 0 var deep: Int = 0 var light: Int = 0 let startDate = array.first?.startDate let endDate = array.last?.endDate for sleep in array { switch sleep.type { case .awake: awake += sleep.length case .deep: deep += sleep.length case .light: light += sleep.length default: break } } return SleepSummary(awake: awake, light: light, deep: deep, startDate: startDate!, endDate: endDate!) } else { return SleepSummary() } } func add() { if let a = RealmTools.queryObjects(SleepModel.self, filter: "startDate = %@ AND endDate = %@", [self.startDate ?? Date(), self.endDate ?? Date()]) as? [SleepModel] { RealmTools.deleteList(a) } RealmTools.add(self) } class func addArray(_ array :[SleepModel]) { for sleep in array { sleep.add() } } class func querySleepTime(_ array: [SleepModel], sleepTime: ((_ awakeTime: Int, _ lightTime: Int, _ deepTime: Int) -> ())) { //-> (awakeTime: Int, lightTime: Int, deepTime: Int) { var awake: Int = 0 var light: Int = 0 var deep: Int = 0 for sleep in array { switch sleep.type { case .awake: awake += sleep.length case .light: light += sleep.length case .deep: deep += sleep.length case .none: break } } sleepTime(awake, light, deep) } class func querySleepPercentage(_ array: [SleepModel]) -> (awake_pct: Int, light_pct: Int, deep_pct: Int, length: Int) { var awake: Int = 0 var light: Int = 0 var deep: Int = 0 for sleep in array { switch sleep.type { case .awake: awake += sleep.length case .light: light += sleep.length case .deep: deep += sleep.length case .none: break } } let length = awake + light + deep let deep_pct = deep*100/length var light_pct = 0 var awake_pct = 0 if awake == 0 { light_pct = 100 - deep_pct } else { light_pct = light*100/length awake_pct = 100 - deep_pct - light_pct } return (awake_pct, light_pct, deep_pct, (deep+light)) } class func getRecentData(count: Int = 31) -> SleepSummary { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "startDate", isAssending: false, pageSize: count) as? [SleepModel], array.count > 0 else { return SleepSummary() } let date = array.first!.startDate return toSleepSummary(getSleepByDay(date!)) } class func getSleepByDay(_ date: Date) -> [SleepModel] { return getHistorySleep(startTime: date.dateAtStartOf(.day)-2.hours, endTime: date.dateAtEndOf(.day)-2.hours) } class func getSleepByWeek(_ date: Date) -> [SleepSummary] { var sleepArray: [SleepSummary] = [] let weekDays = date.compare(.isThisWeek) ? (DateInRegion()-1.days).weekday : 7 for i in 0.. 0 { sleepArray.append(toSleepSummary(array)) } } return sleepArray } class func getSleepByMonth(_ date: Date) -> [SleepSummary] { var sleepArray: [SleepSummary] = [] let monthDays = date.compare(.isThisMonth) ? DateInRegion().day : date.monthDays for i in 0.. 0 { sleepArray.append(toSleepSummary(array)) } } return sleepArray } class func getSleepByYear(_ date: Date) -> [SleepSummary] { var sleepArray: [SleepSummary] = [] let yearMonths = date.compare(.isThisYear) ? DateInRegion().month : 12 for i in 0.. 0 { let summary = SleepSummary(awake: array.average(\.awake), light: array.average(\.light), deep: array.average(\.deep), startDate: startDate, endDate: startDate.dateAtEndOf(.month)) sleepArray.append(summary) } } return sleepArray } private class func getHistorySleep(startTime: Date, endTime: Date) -> [SleepModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "startDate >= %@ AND endDate <= %@", argumentArray:[startTime, endTime]), sortedKey: "startDate") as? Array, array.count > 0 else { return [] } // guard let array = RealmTools.queryObjects(self, filter: "startDate >= %@ AND endDate <= %@", [startTime, endTime]) as? Array, array.count > 0 else { // return [] // } return array } } class SleepSummary: NSObject { var startDate: Date? = nil var endDate: Date? = nil var awake: Int = 0 var light: Int = 0 var deep: Int = 0 var sleepLength: Int = 0 var awake_pct: Int = 0 var light_pct: Int = 0 var deep_pct: Int = 0 init(awake: Int, light: Int, deep: Int, startDate: Date, endDate: Date) { super.init() self.startDate = startDate self.endDate = endDate self.awake = awake self.deep = deep self.light = light self.sleepLength = deep + light let length = deep + light + awake let deep_pct = deep*100/length var light_pct = 0 var awake_pct = 0 if awake == 0 { light_pct = 100 - deep_pct } else { light_pct = light*100/length awake_pct = 100 - deep_pct - light_pct } self.deep_pct = deep_pct self.light_pct = light_pct self.awake_pct = awake_pct } override init() { super.init() } } //MARK: - 训练 enum TrainType: Int { case running = 0 case walking case swimming case hiking case elliptical_trainer case exercise_bike case steppers case treadmill case bicycle case mountaineering case table_tennis case badminton case yoga case rope_skipping case volleyball case football case basketball case dance } class TrainModel: Object { @Persisted var date: Date? = nil @Persisted private var typeRaw: Int = 0 @Persisted var length: Int = 0 //秒 @Persisted var calorie: Int = 0 //卡 @Persisted var mileage: Int = 0 //米 @Persisted var steps: Int = 0 var type: TrainType? { get { return TrainType(rawValue: typeRaw) } set { typeRaw = newValue?.rawValue ?? 0 } } var typeString: String? { get { switch type { case .running: return "跑步" case .walking: return "健走" case .swimming: return "游泳" case .hiking: return "徒步" case .elliptical_trainer: return "椭圆训练机" case .exercise_bike: return "健身车" case .steppers: return "踏步机" case .treadmill: return "跑步机" case .bicycle: return "自行车" case .mountaineering: return "登山" case .table_tennis: return "兵乓球" case .badminton: return "羽毛球" case .yoga: return "瑜伽" case .rope_skipping: return "跳绳" case .volleyball: return "排球" case .football: return "足球" case .basketball: return "篮球" case .dance: return "跳舞" default: return "" } } } convenience init(type: TrainType, date: Date, length: Int, calorie: Int, mileage: Int, steps: Int) { self.init() self.type = type self.date = date self.length = length self.calorie = calorie self.mileage = mileage self.steps = steps } class func toTrainModel(_ bytes: [UInt8]) -> TrainModel { let date = DateInRegion(year: Int(bytes[1])+2000, month: Int(bytes[2]), day: Int(bytes[3]), hour: Int(bytes[4]), minute: Int(bytes[5]), second: Int(bytes[6])).date let length = bytesToInt(Array(bytes[7..<11])) let mileage = bytesToInt(Array(bytes[11..<15])) let calorie = bytesToInt(Array(bytes[15..<19])) let steps = bytesToInt(Array(bytes[19..<23])) let type = TrainType(rawValue: Int(bytes[0])) ?? .walking let train = TrainModel(type: type, date: date, length: length, calorie: calorie, mileage: mileage, steps: steps) return train } func add() { if let array = RealmTools.queryObjects(TrainModel.self, filter: "date = %@", [self.date ?? Date()]) as? [TrainModel] { RealmTools.deleteList(array) } RealmTools.add(self) } private func isEqual(_ train: TrainModel) -> Bool { if self.type == train.type { if self.date!.isInside(date: train.date!, granularity: .minute) { return true } } return false } class func addArray(_ array: [TrainModel]) { if array.count == 0 { return } for (i, train) in array.enumerated() { if train == array.first { train.add() } else { if !train.isEqual(array[i-1]) { train.add() } } } } class func getRecentData() -> [TrainModel] { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: 1) as? [TrainModel], array.count > 0 else { return [] } let date = array.last!.date return getTrainByDay(date!) } class func getTrainByDay(_ date: Date) -> [TrainModel] { return getHistoryTrain(startTime: date.dateAtStartOf(.day), endTime: date.dateAtEndOf(.day)) } class func getTrainByMonth(_ date: Date) -> [TrainModel] { return getHistoryTrain(startTime: date.dateAt(.startOfMonth), endTime: date.dateAt(.endOfMonth)) } private class func getHistoryTrain(startTime: Date, endTime: Date) -> [TrainModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } } class MotionModel: Object { @Persisted var date: Date? = nil @Persisted private var typeRaw: Int = 0 @Persisted var length: Int = 0 //秒 @Persisted var calorie: Float = 0 //千卡 @Persisted var distance: Int = 0 //米 @Persisted var steps: Int = 0 @Persisted var altitude: Double = 0 //海拔 var type: TrainType? { get { return TrainType(rawValue: typeRaw) } set { typeRaw = newValue?.rawValue ?? 0 } } var typeString: String? { get { switch type { case .running: return "跑步" case .walking: return "健走" case .swimming: return "游泳" case .hiking: return "徒步" case .elliptical_trainer: return "椭圆训练机" case .exercise_bike: return "健身车" case .steppers: return "踏步机" case .treadmill: return "跑步机" case .bicycle: return "自行车" case .mountaineering: return "登山" case .table_tennis: return "兵乓球" case .badminton: return "羽毛球" case .yoga: return "瑜伽" case .rope_skipping: return "跳绳" case .volleyball: return "排球" case .football: return "足球" case .basketball: return "篮球" case .dance: return "跳舞" default: return "" } } } convenience init(type: TrainType, date: Date, length: Int, calorie: Float, distance: Int, steps: Int, altitude: Double) { self.init() self.type = type self.date = date self.length = length self.calorie = calorie self.distance = distance self.steps = steps self.altitude = altitude } func add() { if let array = RealmTools.queryObjects(MotionModel.self, filter: "date = %@", [self.date ?? Date()]) as? [MotionModel] { RealmTools.deleteList(array) } RealmTools.add(self) } private func isEqual(_ motion: MotionModel) -> Bool { if self.type == motion.type { if self.date!.isInside(date: motion.date!, granularity: .minute) { return true } } return false } class func addArray(_ array: [MotionModel]) { if array.count == 0 { return } for (i, motion) in array.enumerated() { if motion == array.first { motion.add() } else { if !motion.isEqual(array[i-1]) { motion.add() } } } } class func getRecentData() -> [MotionModel] { guard let array = RealmTools.objectsWithPredicateAndSortedForPages(object: self, sortedKey: "date", isAssending: false, pageSize: 1) as? [MotionModel], array.count > 0 else { return [] } let date = array.last!.date return getMotionByDay(date!) } class func getMotionByDay(_ date: Date) -> [MotionModel] { return getHistoryMotion(startTime: date.dateAtStartOf(.day), endTime: date.dateAtEndOf(.day)) } class func getMotionByMonth(_ date: Date) -> [MotionModel] { return getHistoryMotion(startTime: date.dateAt(.startOfMonth), endTime: date.dateAt(.endOfMonth)) } class func getMonthData(_ type: Int, date: Date) -> [MotionModel] { return getData(type, startTime: date.dateAt(.startOfMonth), endTime: date.dateAt(.endOfMonth)) } class func getData(_ type: Int, startTime: Date, endTime: Date) -> [MotionModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "typeRaw = %@ AND date >= %@ AND date <= %@", argumentArray:[type, startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } private class func getHistoryMotion(startTime: Date, endTime: Date) -> [MotionModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date >= %@ AND date <= %@", argumentArray:[startTime, endTime]), sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } }