OpenAPSFixed.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import Combine
  2. import Foundation
  3. import JavaScriptCore
  4. @testable import Trio
  5. /// This class provides us with an implementation of trio-oref with a number of iob bugs that are fixed.
  6. /// We can use this during testing to confirm that for an input that generated an error that a corrected
  7. /// Javascript implementation would have produced the same results
  8. final class OpenAPSFixed {
  9. func sortPumpHistory(pumpHistory: JSON) throws -> JSON {
  10. let pumpHistorySwift = try JSONBridge.pumpHistory(from: pumpHistory)
  11. return try JSONBridge.to(pumpHistorySwift.sorted(by: { $0.timestamp > $1.timestamp }))
  12. }
  13. private func middlewareScript(name: String) -> Script? {
  14. if let url = Foundation.Bundle.main.url(forResource: "javascript/\(name)", withExtension: "") {
  15. do {
  16. let body = try String(contentsOf: url)
  17. return Script(name: name, body: body)
  18. } catch {
  19. debug(.openAPS, "Failed to load script \(name): \(error)")
  20. }
  21. }
  22. return nil
  23. }
  24. func determineBasalJavascript(
  25. glucose: JSON,
  26. currentTemp: JSON,
  27. iob: JSON,
  28. profile: JSON,
  29. autosens: JSON,
  30. meal: JSON,
  31. microBolusAllowed: Bool,
  32. reservoir: JSON,
  33. pumpHistory: JSON,
  34. preferences: JSON,
  35. basalProfile: JSON,
  36. trioCustomOrefVariables: JSON,
  37. clock: Date
  38. ) async throws -> OrefFunctionResult {
  39. do {
  40. let worker = JavaScriptWorker(poolSize: 1)
  41. let testBundle = Bundle(for: OpenAPSFixed.self)
  42. let result = try await withCheckedThrowingContinuation { continuation in
  43. worker.evaluateBatch(scripts: [
  44. Script(name: "prepare/log.js"),
  45. Script.fromTestingBundle(name: "determine-basal-prepare.js", bundle: testBundle),
  46. Script.fromTestingBundle(name: "basal-set-temp.js", bundle: testBundle),
  47. Script.fromTestingBundle(name: "glucose-get-last.js", bundle: testBundle),
  48. Script.fromTestingBundle(name: "determine-basal.js", bundle: testBundle)
  49. ])
  50. if let middleware = self.middlewareScript(name: OpenAPS.Middleware.determineBasal) {
  51. worker.evaluate(script: middleware)
  52. }
  53. let result = worker.call(function: "generate", with: [
  54. iob,
  55. currentTemp,
  56. glucose,
  57. profile,
  58. autosens,
  59. meal,
  60. microBolusAllowed,
  61. reservoir,
  62. clock,
  63. pumpHistory,
  64. preferences,
  65. basalProfile,
  66. trioCustomOrefVariables
  67. ])
  68. continuation.resume(returning: result)
  69. }
  70. return .success(result)
  71. } catch {
  72. return .failure(error)
  73. }
  74. }
  75. func iobHistory(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON, zeroTempDuration: JSON) async throws -> JSON {
  76. let jsWorker = JavaScriptWorker(poolSize: 1)
  77. let testBundle = Bundle(for: OpenAPSFixed.self)
  78. let pumphistory: JSON = try! sortPumpHistory(pumpHistory: pumphistory)
  79. let result = try await withCheckedThrowingContinuation { continuation in
  80. jsWorker.inCommonContext { worker in
  81. worker.evaluateBatch(scripts: [
  82. Script(name: "prepare/log.js"),
  83. Script.fromTestingBundle(name: "iob-history.js", bundle: testBundle),
  84. Script.fromTestingBundle(name: "iob-history-prepare.js", bundle: testBundle)
  85. ])
  86. let result = worker.call(function: "generate", with: [
  87. pumphistory,
  88. profile,
  89. clock,
  90. autosens,
  91. zeroTempDuration
  92. ])
  93. continuation.resume(returning: result)
  94. }
  95. }
  96. return result
  97. }
  98. func mealJavascript(
  99. pumphistory: JSON,
  100. profile: JSON,
  101. basalProfile: JSON,
  102. clock: JSON,
  103. carbs: JSON,
  104. glucose: JSON
  105. ) async -> OrefFunctionResult {
  106. let jsWorker = JavaScriptWorker(poolSize: 1)
  107. let testBundle = Bundle(for: OpenAPSFixed.self)
  108. do {
  109. let result = try await withCheckedThrowingContinuation { continuation in
  110. jsWorker.inCommonContext { worker in
  111. worker.evaluateBatch(scripts: [
  112. Script(name: "prepare/log.js"),
  113. Script.fromTestingBundle(name: "meal.js", bundle: testBundle),
  114. Script(name: "prepare/meal.js")
  115. ])
  116. let result = worker.call(function: "generate", with: [
  117. pumphistory,
  118. profile,
  119. clock,
  120. glucose,
  121. basalProfile,
  122. carbs
  123. ])
  124. continuation.resume(returning: result)
  125. }
  126. }
  127. return .success(result)
  128. } catch {
  129. return .failure(error)
  130. }
  131. }
  132. func autosenseJavascript(
  133. glucose: JSON,
  134. pumpHistory: JSON,
  135. basalprofile: JSON,
  136. profile: JSON,
  137. carbs: JSON,
  138. temptargets: JSON,
  139. clock: JSON
  140. ) async -> OrefFunctionResult {
  141. do {
  142. let result = try await withCheckedThrowingContinuation { continuation in
  143. let jsWorker = JavaScriptWorker(poolSize: 1)
  144. let testBundle = Bundle(for: OpenAPSFixed.self)
  145. jsWorker.inCommonContext { worker in
  146. worker.evaluateBatch(scripts: [
  147. Script(name: "prepare/log.js"),
  148. Script.fromTestingBundle(name: "autosens.js", bundle: testBundle),
  149. Script.fromTestingBundle(name: "autosens-prepare.js", bundle: testBundle)
  150. ])
  151. let result = worker.call(function: "generate", with: [
  152. glucose,
  153. pumpHistory,
  154. basalprofile,
  155. profile,
  156. carbs,
  157. temptargets,
  158. clock
  159. ])
  160. continuation.resume(returning: result)
  161. }
  162. }
  163. return .success(result)
  164. } catch {
  165. return .failure(error)
  166. }
  167. }
  168. func iobJavascript(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON) async -> OrefFunctionResult {
  169. do {
  170. let testBundle = Bundle(for: OpenAPSFixed.self)
  171. let pumphistory: JSON = try! sortPumpHistory(pumpHistory: pumphistory)
  172. let result = try await withCheckedThrowingContinuation { continuation in
  173. let jsWorker = JavaScriptWorker(poolSize: 1)
  174. jsWorker.inCommonContext { worker in
  175. worker.evaluateBatch(scripts: [
  176. Script(name: "prepare/log.js"),
  177. Script.fromTestingBundle(name: "iob.js", bundle: testBundle),
  178. Script(name: "prepare/iob.js")
  179. ])
  180. let result = worker.call(function: "generate", with: [
  181. pumphistory,
  182. profile,
  183. clock,
  184. autosens
  185. ])
  186. continuation.resume(returning: result)
  187. }
  188. }
  189. return .success(result)
  190. } catch {
  191. return .failure(error)
  192. }
  193. }
  194. }
  195. extension Script {
  196. static func fromTestingBundle(name: String, bundle: Bundle) -> Script {
  197. let body: String
  198. if let url = bundle.url(forResource: "\(name)", withExtension: "") {
  199. do {
  200. body = try String(contentsOf: url)
  201. } catch {
  202. print("Error loading script: \(error.localizedDescription)")
  203. body = "Error loading script"
  204. }
  205. } else {
  206. print("Resource not found: javascript/\(name)")
  207. testPrintAllJSFiles(testBundle: bundle)
  208. body = "Resource not found"
  209. }
  210. return Script(name: name, body: body)
  211. }
  212. static func testPrintAllJSFiles(testBundle: Bundle) {
  213. // Get all .js files in the bundle
  214. if let jsURLs = testBundle.urls(forResourcesWithExtension: "js", subdirectory: nil) {
  215. print("JavaScript files in test bundle:")
  216. for jsURL in jsURLs {
  217. print("- \(jsURL.lastPathComponent)")
  218. print(" Full path: \(jsURL.path)")
  219. }
  220. print("Total JS files found: \(jsURLs.count)")
  221. } else {
  222. print("No JavaScript files found in test bundle")
  223. }
  224. }
  225. }