OpenAPS.swift 27 KB

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