OpenAPS.swift 25 KB

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