OpenAPS.swift 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. import Combine
  2. import CoreData
  3. import Foundation
  4. import JavaScriptCore
  5. final class OpenAPS {
  6. private let jsWorker = JavaScriptWorker()
  7. private let processQueue = DispatchQueue(label: "OpenAPS.processQueue", qos: .utility)
  8. private let storage: FileStorage
  9. let coredataContext = CoreDataStack.shared.persistentContainer.viewContext // newBackgroundContext()
  10. let context = CoreDataStack.shared.persistentContainer.newBackgroundContext()
  11. init(storage: FileStorage) {
  12. self.storage = storage
  13. }
  14. func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> Future<Determination?, Never> {
  15. Future { promise in
  16. self.processQueue.async {
  17. debug(.openAPS, "Start determineBasal")
  18. // clock
  19. self.storage.save(clock, as: Monitor.clock)
  20. // temp_basal
  21. let tempBasal = currentTemp.rawJSON
  22. self.storage.save(tempBasal, as: Monitor.tempBasal)
  23. // meal
  24. let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
  25. let carbs = self.loadFileFromStorage(name: Monitor.carbHistory)
  26. let glucose = self.loadFileFromStorage(name: Monitor.glucose)
  27. let profile = self.loadFileFromStorage(name: Settings.profile)
  28. let basalProfile = self.loadFileFromStorage(name: Settings.basalProfile)
  29. let meal = self.meal(
  30. pumphistory: pumpHistory,
  31. profile: profile,
  32. basalProfile: basalProfile,
  33. clock: clock,
  34. carbs: carbs,
  35. glucose: glucose
  36. )
  37. self.storage.save(meal, as: Monitor.meal)
  38. // iob
  39. let autosens = self.loadFileFromStorage(name: Settings.autosense)
  40. let iob = self.iob(
  41. pumphistory: pumpHistory,
  42. profile: profile,
  43. clock: clock,
  44. autosens: autosens.isEmpty ? .null : autosens
  45. )
  46. self.storage.save(iob, as: Monitor.iob)
  47. // determine-basal
  48. let reservoir = self.loadFileFromStorage(name: Monitor.reservoir)
  49. let preferences = self.loadFileFromStorage(name: Settings.preferences)
  50. // oref2
  51. let oref2_variables = self.oref2()
  52. let orefDetermination = self.determineBasal(
  53. glucose: glucose,
  54. currentTemp: tempBasal,
  55. iob: iob,
  56. profile: profile,
  57. autosens: autosens.isEmpty ? .null : autosens,
  58. meal: meal,
  59. microBolusAllowed: true,
  60. reservoir: reservoir,
  61. pumpHistory: pumpHistory,
  62. preferences: preferences,
  63. basalProfile: basalProfile,
  64. oref2_variables: oref2_variables
  65. )
  66. debug(.openAPS, "SUGGESTED: \(orefDetermination)")
  67. if var determination = Determination(from: orefDetermination) {
  68. determination.timestamp = determination.deliverAt ?? clock
  69. self.storage.save(determination, as: Enact.suggested)
  70. // save to core data asynchronously
  71. self.coredataContext.perform {
  72. let new = OrefDetermination(context: self.coredataContext)
  73. new.id = UUID()
  74. new.totalDailyDose = determination.tdd as? NSDecimalNumber
  75. new.insulinSensitivity = determination.isf as? NSDecimalNumber
  76. new.currentTarget = determination.current_target as? NSDecimalNumber
  77. new.eventualBG = determination.eventualBG as? NSDecimalNumber
  78. new.deliverAt = determination.deliverAt
  79. new.enacted = false
  80. new.insulinForManualBolus = determination.insulinForManualBolus as? NSDecimalNumber
  81. new.carbRatio = determination.carbRatio as? NSDecimalNumber
  82. new.glucose = determination.bg as? NSDecimalNumber
  83. new.reservoir = determination.reservoir as? NSDecimalNumber
  84. new.insulinReq = determination.insulinReq as? NSDecimalNumber
  85. new.temp = determination.temp?.rawValue ?? "absolute"
  86. new.rate = determination.rate as? NSDecimalNumber
  87. new.reason = determination.reason
  88. new.duration = Int16(determination.duration ?? 0)
  89. new.iob = determination.iob as? NSDecimalNumber
  90. new.treshold = determination.treshold as? NSDecimalNumber
  91. // new.predBGs: [
  92. // "zt": immutableDetermination.predictions?.zt ?? [],
  93. // "cob": immutableDetermination.predictions?.cob ?? [],
  94. // "iob": immutableDetermination.predictions?.iob ?? [],
  95. // "uam": immutableDetermination.predictions?.uam ?? []
  96. // ]
  97. new.minDelta = determination.minDelta as? NSDecimalNumber
  98. new.sensitivityRatio = determination.sensitivityRatio as? NSDecimalNumber
  99. new.expectedDelta = determination.expectedDelta as? NSDecimalNumber
  100. new.cob = Int16(Int(determination.cob ?? 0))
  101. new.manualBolusErrorString = determination.manualBolusErrorString as? NSDecimalNumber
  102. new.timestampEnacted = nil
  103. new.tempBasal = determination.insulin?.temp_basal as? NSDecimalNumber
  104. new.scheduledBasal = determination.insulin?.scheduled_basal as? NSDecimalNumber
  105. new.bolus = determination.insulin?.bolus as? NSDecimalNumber
  106. new.smbToDeliver = determination.units as? NSDecimalNumber
  107. new.carbsRequired = Int16(Int(determination.carbsReq ?? 0))
  108. do {
  109. try self.coredataContext.save()
  110. debugPrint(
  111. "OpenAPS: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved determination"
  112. )
  113. } catch {
  114. debugPrint(
  115. "OpenAPS: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while saving determination"
  116. )
  117. }
  118. }
  119. // MARK: Save to CoreData also. To do: Remove JSON saving
  120. if determination.tdd ?? 0 > 0 {
  121. self.coredataContext.perform {
  122. let saveToTDD = TDD(context: self.coredataContext)
  123. saveToTDD.timestamp = determination.timestamp ?? Date()
  124. saveToTDD.tdd = (determination.tdd ?? 0) as NSDecimalNumber?
  125. try? self.coredataContext.save()
  126. let saveTarget = Target(context: self.coredataContext)
  127. saveTarget.current = (determination.current_target ?? 100) as NSDecimalNumber?
  128. try? self.coredataContext.save()
  129. }
  130. // self.coredataContext.perform {
  131. // let saveToInsulin = InsulinDistribution(context: self.coredataContext)
  132. //
  133. // saveToInsulin.bolus = (determination.insulin?.bolus ?? 0) as NSDecimalNumber?
  134. // saveToInsulin.scheduledBasal = (determination.insulin?.scheduled_basal ?? 0) as NSDecimalNumber?
  135. // saveToInsulin.tempBasal = (determination.insulin?.temp_basal ?? 0) as NSDecimalNumber?
  136. // saveToInsulin.date = Date()
  137. //
  138. // try? self.coredataContext.save()
  139. // }
  140. }
  141. promise(.success(determination))
  142. } else {
  143. promise(.success(nil))
  144. }
  145. }
  146. }
  147. }
  148. func oref2() -> RawJSON {
  149. coredataContext.performAndWait {
  150. let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
  151. var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
  152. let wp = preferences?.weightPercentage ?? 1
  153. let smbMinutes = (preferences?.maxSMBBasalMinutes ?? 30) as NSDecimalNumber
  154. let uamMinutes = (preferences?.maxUAMSMBBasalMinutes ?? 30) as NSDecimalNumber
  155. let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
  156. let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
  157. var uniqueEvents = [TDD]()
  158. let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
  159. requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
  160. let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
  161. requestTDD.sortDescriptors = [sortTDD]
  162. try? uniqueEvents = coredataContext.fetch(requestTDD)
  163. var sliderArray = [TempTargetsSlider]()
  164. let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest<TempTargetsSlider>
  165. let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
  166. requestIsEnbled.sortDescriptors = [sortIsEnabled]
  167. // requestIsEnbled.fetchLimit = 1
  168. try? sliderArray = coredataContext.fetch(requestIsEnbled)
  169. var overrideArray = [Override]()
  170. let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
  171. let sortOverride = NSSortDescriptor(key: "date", ascending: false)
  172. requestOverrides.sortDescriptors = [sortOverride]
  173. // requestOverrides.fetchLimit = 1
  174. try? overrideArray = coredataContext.fetch(requestOverrides)
  175. var tempTargetsArray = [TempTargets]()
  176. let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest<TempTargets>
  177. let sortTT = NSSortDescriptor(key: "date", ascending: false)
  178. requestTempTargets.sortDescriptors = [sortTT]
  179. requestTempTargets.fetchLimit = 1
  180. try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
  181. let total = uniqueEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
  182. var indeces = uniqueEvents.count
  183. // Only fetch once. Use same (previous) fetch
  184. let twoHoursArray = uniqueEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
  185. var nrOfIndeces = twoHoursArray.count
  186. let totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
  187. var temptargetActive = tempTargetsArray.first?.active ?? false
  188. let isPercentageEnabled = sliderArray.first?.enabled ?? false
  189. var useOverride = overrideArray.first?.enabled ?? false
  190. var overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
  191. var unlimited = overrideArray.first?.indefinite ?? true
  192. var disableSMBs = overrideArray.first?.smbIsOff ?? false
  193. let currentTDD = (uniqueEvents.last?.tdd ?? 0) as Decimal
  194. if indeces == 0 {
  195. indeces = 1
  196. }
  197. if nrOfIndeces == 0 {
  198. nrOfIndeces = 1
  199. }
  200. let average2hours = totalAmount / Decimal(nrOfIndeces)
  201. let average14 = total / Decimal(indeces)
  202. let weight = wp
  203. let weighted_average = weight * average2hours + (1 - weight) * average14
  204. var duration: Decimal = 0
  205. var newDuration: Decimal = 0
  206. var overrideTarget: Decimal = 0
  207. if useOverride {
  208. duration = (overrideArray.first?.duration ?? 0) as Decimal
  209. overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
  210. let advancedSettings = overrideArray.first?.advancedSettings ?? false
  211. let addedMinutes = Int(duration)
  212. let date = overrideArray.first?.date ?? Date()
  213. if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
  214. !unlimited
  215. {
  216. useOverride = false
  217. let saveToCoreData = Override(context: self.coredataContext)
  218. saveToCoreData.enabled = false
  219. saveToCoreData.date = Date()
  220. saveToCoreData.duration = 0
  221. saveToCoreData.indefinite = false
  222. saveToCoreData.percentage = 100
  223. try? self.coredataContext.save()
  224. }
  225. }
  226. if !useOverride {
  227. unlimited = true
  228. overridePercentage = 100
  229. duration = 0
  230. overrideTarget = 0
  231. disableSMBs = false
  232. }
  233. if temptargetActive {
  234. var duration_ = 0
  235. var hbt = Double(hbt_)
  236. var dd = 0.0
  237. if temptargetActive {
  238. duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
  239. hbt = tempTargetsArray.first?.hbt ?? Double(hbt_)
  240. let startDate = tempTargetsArray.first?.startDate ?? Date()
  241. let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
  242. dd = durationPlusStart.timeIntervalSinceNow.minutes
  243. if dd > 0.1 {
  244. hbt_ = Decimal(hbt)
  245. temptargetActive = true
  246. } else {
  247. temptargetActive = false
  248. }
  249. }
  250. }
  251. if currentTDD > 0 {
  252. let averages = Oref2_variables(
  253. average_total_data: average14,
  254. weightedAverage: weighted_average,
  255. past2hoursAverage: average2hours,
  256. date: Date(),
  257. isEnabled: temptargetActive,
  258. presetActive: isPercentageEnabled,
  259. overridePercentage: overridePercentage,
  260. useOverride: useOverride,
  261. duration: duration,
  262. unlimited: unlimited,
  263. hbt: hbt_,
  264. overrideTarget: overrideTarget,
  265. smbIsOff: disableSMBs,
  266. advancedSettings: overrideArray.first?.advancedSettings ?? false,
  267. isfAndCr: overrideArray.first?.isfAndCr ?? false,
  268. isf: overrideArray.first?.isf ?? false,
  269. cr: overrideArray.first?.cr ?? false,
  270. smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
  271. start: (overrideArray.first?.start ?? 0) as Decimal,
  272. end: (overrideArray.first?.end ?? 0) as Decimal,
  273. smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
  274. uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
  275. )
  276. storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
  277. return self.loadFileFromStorage(name: Monitor.oref2_variables)
  278. } else {
  279. let averages = Oref2_variables(
  280. average_total_data: 0,
  281. weightedAverage: 1,
  282. past2hoursAverage: 0,
  283. date: Date(),
  284. isEnabled: temptargetActive,
  285. presetActive: isPercentageEnabled,
  286. overridePercentage: overridePercentage,
  287. useOverride: useOverride,
  288. duration: duration,
  289. unlimited: unlimited,
  290. hbt: hbt_,
  291. overrideTarget: overrideTarget,
  292. smbIsOff: disableSMBs,
  293. advancedSettings: overrideArray.first?.advancedSettings ?? false,
  294. isfAndCr: overrideArray.first?.isfAndCr ?? false,
  295. isf: overrideArray.first?.isf ?? false,
  296. cr: overrideArray.first?.cr ?? false,
  297. smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
  298. start: (overrideArray.first?.start ?? 0) as Decimal,
  299. end: (overrideArray.first?.end ?? 0) as Decimal,
  300. smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
  301. uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
  302. )
  303. storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
  304. return self.loadFileFromStorage(name: Monitor.oref2_variables)
  305. }
  306. }
  307. }
  308. func autosense() -> Future<Autosens?, Never> {
  309. Future { promise in
  310. self.processQueue.async {
  311. debug(.openAPS, "Start autosens")
  312. let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
  313. let carbs = self.loadFileFromStorage(name: Monitor.carbHistory)
  314. let glucose = self.loadFileFromStorage(name: Monitor.glucose)
  315. let profile = self.loadFileFromStorage(name: Settings.profile)
  316. let basalProfile = self.loadFileFromStorage(name: Settings.basalProfile)
  317. let tempTargets = self.loadFileFromStorage(name: Settings.tempTargets)
  318. let autosensResult = self.autosense(
  319. glucose: glucose,
  320. pumpHistory: pumpHistory,
  321. basalprofile: basalProfile,
  322. profile: profile,
  323. carbs: carbs,
  324. temptargets: tempTargets
  325. )
  326. debug(.openAPS, "AUTOSENS: \(autosensResult)")
  327. if var autosens = Autosens(from: autosensResult) {
  328. autosens.timestamp = Date()
  329. self.storage.save(autosens, as: Settings.autosense)
  330. promise(.success(autosens))
  331. } else {
  332. promise(.success(nil))
  333. }
  334. }
  335. }
  336. }
  337. func autotune(categorizeUamAsBasal: Bool = false, tuneInsulinCurve: Bool = false) -> Future<Autotune?, Never> {
  338. Future { promise in
  339. self.processQueue.async {
  340. debug(.openAPS, "Start autotune")
  341. let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
  342. let glucose = self.loadFileFromStorage(name: Monitor.glucose)
  343. let profile = self.loadFileFromStorage(name: Settings.profile)
  344. let pumpProfile = self.loadFileFromStorage(name: Settings.pumpProfile)
  345. let carbs = self.loadFileFromStorage(name: Monitor.carbHistory)
  346. let autotunePreppedGlucose = self.autotunePrepare(
  347. pumphistory: pumpHistory,
  348. profile: profile,
  349. glucose: glucose,
  350. pumpprofile: pumpProfile,
  351. carbs: carbs,
  352. categorizeUamAsBasal: categorizeUamAsBasal,
  353. tuneInsulinCurve: tuneInsulinCurve
  354. )
  355. debug(.openAPS, "AUTOTUNE PREP: \(autotunePreppedGlucose)")
  356. let previousAutotune = self.storage.retrieve(Settings.autotune, as: RawJSON.self)
  357. let autotuneResult = self.autotuneRun(
  358. autotunePreparedData: autotunePreppedGlucose,
  359. previousAutotuneResult: previousAutotune ?? profile,
  360. pumpProfile: pumpProfile
  361. )
  362. debug(.openAPS, "AUTOTUNE RESULT: \(autotuneResult)")
  363. if let autotune = Autotune(from: autotuneResult) {
  364. self.storage.save(autotuneResult, as: Settings.autotune)
  365. promise(.success(autotune))
  366. } else {
  367. promise(.success(nil))
  368. }
  369. }
  370. }
  371. }
  372. func makeProfiles(useAutotune: Bool) -> Future<Autotune?, Never> {
  373. Future { promise in
  374. debug(.openAPS, "Start makeProfiles")
  375. self.processQueue.async {
  376. var preferences = self.loadFileFromStorage(name: Settings.preferences)
  377. if preferences.isEmpty {
  378. preferences = Preferences().rawJSON
  379. }
  380. let pumpSettings = self.loadFileFromStorage(name: Settings.settings)
  381. let bgTargets = self.loadFileFromStorage(name: Settings.bgTargets)
  382. let basalProfile = self.loadFileFromStorage(name: Settings.basalProfile)
  383. let isf = self.loadFileFromStorage(name: Settings.insulinSensitivities)
  384. let cr = self.loadFileFromStorage(name: Settings.carbRatios)
  385. let tempTargets = self.loadFileFromStorage(name: Settings.tempTargets)
  386. let model = self.loadFileFromStorage(name: Settings.model)
  387. let autotune = useAutotune ? self.loadFileFromStorage(name: Settings.autotune) : .empty
  388. let freeaps = self.loadFileFromStorage(name: FreeAPS.settings)
  389. let pumpProfile = self.makeProfile(
  390. preferences: preferences,
  391. pumpSettings: pumpSettings,
  392. bgTargets: bgTargets,
  393. basalProfile: basalProfile,
  394. isf: isf,
  395. carbRatio: cr,
  396. tempTargets: tempTargets,
  397. model: model,
  398. autotune: RawJSON.null,
  399. freeaps: freeaps
  400. )
  401. let profile = self.makeProfile(
  402. preferences: preferences,
  403. pumpSettings: pumpSettings,
  404. bgTargets: bgTargets,
  405. basalProfile: basalProfile,
  406. isf: isf,
  407. carbRatio: cr,
  408. tempTargets: tempTargets,
  409. model: model,
  410. autotune: autotune.isEmpty ? .null : autotune,
  411. freeaps: freeaps
  412. )
  413. self.storage.save(pumpProfile, as: Settings.pumpProfile)
  414. self.storage.save(profile, as: Settings.profile)
  415. if let tunedProfile = Autotune(from: profile) {
  416. promise(.success(tunedProfile))
  417. return
  418. }
  419. promise(.success(nil))
  420. }
  421. }
  422. }
  423. // MARK: - Private
  424. private func iob(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON) -> RawJSON {
  425. dispatchPrecondition(condition: .onQueue(processQueue))
  426. return jsWorker.inCommonContext { worker in
  427. worker.evaluate(script: Script(name: Prepare.log))
  428. worker.evaluate(script: Script(name: Bundle.iob))
  429. worker.evaluate(script: Script(name: Prepare.iob))
  430. return worker.call(function: Function.generate, with: [
  431. pumphistory,
  432. profile,
  433. clock,
  434. autosens
  435. ])
  436. }
  437. }
  438. private func meal(pumphistory: JSON, profile: JSON, basalProfile: JSON, clock: JSON, carbs: JSON, glucose: JSON) -> RawJSON {
  439. dispatchPrecondition(condition: .onQueue(processQueue))
  440. return jsWorker.inCommonContext { worker in
  441. worker.evaluate(script: Script(name: Prepare.log))
  442. worker.evaluate(script: Script(name: Bundle.meal))
  443. worker.evaluate(script: Script(name: Prepare.meal))
  444. return worker.call(function: Function.generate, with: [
  445. pumphistory,
  446. profile,
  447. clock,
  448. glucose,
  449. basalProfile,
  450. carbs
  451. ])
  452. }
  453. }
  454. private func autotunePrepare(
  455. pumphistory: JSON,
  456. profile: JSON,
  457. glucose: JSON,
  458. pumpprofile: JSON,
  459. carbs: JSON,
  460. categorizeUamAsBasal: Bool,
  461. tuneInsulinCurve: Bool
  462. ) -> RawJSON {
  463. dispatchPrecondition(condition: .onQueue(processQueue))
  464. return jsWorker.inCommonContext { worker in
  465. worker.evaluate(script: Script(name: Prepare.log))
  466. worker.evaluate(script: Script(name: Bundle.autotunePrep))
  467. worker.evaluate(script: Script(name: Prepare.autotunePrep))
  468. return worker.call(function: Function.generate, with: [
  469. pumphistory,
  470. profile,
  471. glucose,
  472. pumpprofile,
  473. carbs,
  474. categorizeUamAsBasal,
  475. tuneInsulinCurve
  476. ])
  477. }
  478. }
  479. private func autotuneRun(
  480. autotunePreparedData: JSON,
  481. previousAutotuneResult: JSON,
  482. pumpProfile: JSON
  483. ) -> RawJSON {
  484. dispatchPrecondition(condition: .onQueue(processQueue))
  485. return jsWorker.inCommonContext { worker in
  486. worker.evaluate(script: Script(name: Prepare.log))
  487. worker.evaluate(script: Script(name: Bundle.autotuneCore))
  488. worker.evaluate(script: Script(name: Prepare.autotuneCore))
  489. return worker.call(function: Function.generate, with: [
  490. autotunePreparedData,
  491. previousAutotuneResult,
  492. pumpProfile
  493. ])
  494. }
  495. }
  496. private func determineBasal(
  497. glucose: JSON,
  498. currentTemp: JSON,
  499. iob: JSON,
  500. profile: JSON,
  501. autosens: JSON,
  502. meal: JSON,
  503. microBolusAllowed: Bool,
  504. reservoir: JSON,
  505. pumpHistory: JSON,
  506. preferences: JSON,
  507. basalProfile: JSON,
  508. oref2_variables: JSON
  509. ) -> RawJSON {
  510. dispatchPrecondition(condition: .onQueue(processQueue))
  511. return jsWorker.inCommonContext { worker in
  512. worker.evaluate(script: Script(name: Prepare.log))
  513. worker.evaluate(script: Script(name: Prepare.determineBasal))
  514. worker.evaluate(script: Script(name: Bundle.basalSetTemp))
  515. worker.evaluate(script: Script(name: Bundle.getLastGlucose))
  516. worker.evaluate(script: Script(name: Bundle.determineBasal))
  517. if let middleware = self.middlewareScript(name: OpenAPS.Middleware.determineBasal) {
  518. worker.evaluate(script: middleware)
  519. }
  520. return worker.call(
  521. function: Function.generate,
  522. with: [
  523. iob,
  524. currentTemp,
  525. glucose,
  526. profile,
  527. autosens,
  528. meal,
  529. microBolusAllowed,
  530. reservoir,
  531. Date(),
  532. pumpHistory,
  533. preferences,
  534. basalProfile,
  535. oref2_variables
  536. ]
  537. )
  538. }
  539. }
  540. private func autosense(
  541. glucose: JSON,
  542. pumpHistory: JSON,
  543. basalprofile: JSON,
  544. profile: JSON,
  545. carbs: JSON,
  546. temptargets: JSON
  547. ) -> RawJSON {
  548. dispatchPrecondition(condition: .onQueue(processQueue))
  549. return jsWorker.inCommonContext { worker in
  550. worker.evaluate(script: Script(name: Prepare.log))
  551. worker.evaluate(script: Script(name: Bundle.autosens))
  552. worker.evaluate(script: Script(name: Prepare.autosens))
  553. return worker.call(
  554. function: Function.generate,
  555. with: [
  556. glucose,
  557. pumpHistory,
  558. basalprofile,
  559. profile,
  560. carbs,
  561. temptargets
  562. ]
  563. )
  564. }
  565. }
  566. private func exportDefaultPreferences() -> RawJSON {
  567. dispatchPrecondition(condition: .onQueue(processQueue))
  568. return jsWorker.inCommonContext { worker in
  569. worker.evaluate(script: Script(name: Prepare.log))
  570. worker.evaluate(script: Script(name: Bundle.profile))
  571. worker.evaluate(script: Script(name: Prepare.profile))
  572. return worker.call(function: Function.exportDefaults, with: [])
  573. }
  574. }
  575. private func makeProfile(
  576. preferences: JSON,
  577. pumpSettings: JSON,
  578. bgTargets: JSON,
  579. basalProfile: JSON,
  580. isf: JSON,
  581. carbRatio: JSON,
  582. tempTargets: JSON,
  583. model: JSON,
  584. autotune: JSON,
  585. freeaps: JSON
  586. ) -> RawJSON {
  587. dispatchPrecondition(condition: .onQueue(processQueue))
  588. return jsWorker.inCommonContext { worker in
  589. worker.evaluate(script: Script(name: Prepare.log))
  590. worker.evaluate(script: Script(name: Bundle.profile))
  591. worker.evaluate(script: Script(name: Prepare.profile))
  592. return worker.call(
  593. function: Function.generate,
  594. with: [
  595. pumpSettings,
  596. bgTargets,
  597. isf,
  598. basalProfile,
  599. preferences,
  600. carbRatio,
  601. tempTargets,
  602. model,
  603. autotune,
  604. freeaps
  605. ]
  606. )
  607. }
  608. }
  609. private func loadJSON(name: String) -> String {
  610. try! String(contentsOf: Foundation.Bundle.main.url(forResource: "json/\(name)", withExtension: "json")!)
  611. }
  612. private func loadFileFromStorage(name: String) -> RawJSON {
  613. storage.retrieveRaw(name) ?? OpenAPS.defaults(for: name)
  614. }
  615. private func middlewareScript(name: String) -> Script? {
  616. if let body = storage.retrieveRaw(name) {
  617. return Script(name: "Middleware", body: body)
  618. }
  619. if let url = Foundation.Bundle.main.url(forResource: "javascript/\(name)", withExtension: "") {
  620. return Script(name: "Middleware", body: try! String(contentsOf: url))
  621. }
  622. return nil
  623. }
  624. static func defaults(for file: String) -> RawJSON {
  625. let prefix = file.hasSuffix(".json") ? "json/defaults" : "javascript"
  626. guard let url = Foundation.Bundle.main.url(forResource: "\(prefix)/\(file)", withExtension: "") else {
  627. return ""
  628. }
  629. return (try? String(contentsOf: url)) ?? ""
  630. }
  631. }