Graphs.swift 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927
  1. // LoopFollow
  2. // Graphs.swift
  3. // Created by Jon Fawcett on 2020-06-17.
  4. import Charts
  5. import Foundation
  6. import UIKit
  7. import Charts
  8. enum GraphDataIndex: Int {
  9. case bg = 0
  10. case prediction = 1
  11. case basal = 2
  12. case bolus = 3
  13. case carbs = 4
  14. case basalScheduled = 5
  15. case override = 6
  16. case bgCheck = 7
  17. case suspend = 8
  18. case resumePump = 9
  19. case sensorStart = 10
  20. case note = 11
  21. case ztPrediction = 12
  22. case iobPrediction = 13
  23. case cobPrediction = 14
  24. case uamPrediction = 15
  25. case smb = 16
  26. case tempTarget = 17
  27. }
  28. extension GraphDataIndex {
  29. var description: String {
  30. switch self {
  31. case .bg: return "BG"
  32. case .prediction: return "Prediction"
  33. case .basal: return "Basal"
  34. case .bolus: return "Bolus"
  35. case .carbs: return "Carbs"
  36. case .basalScheduled: return "Basal Scheduled"
  37. case .override: return "Override"
  38. case .bgCheck: return "BG Check"
  39. case .suspend: return "Suspend"
  40. case .resumePump: return "Resume Pump"
  41. case .sensorStart: return "Sensor Start"
  42. case .note: return "Note"
  43. case .ztPrediction: return "ZT Prediction"
  44. case .iobPrediction: return "IOB Prediction"
  45. case .cobPrediction: return "COB Prediction"
  46. case .uamPrediction: return "UAM Prediction"
  47. case .smb: return "SMB"
  48. case .tempTarget: return "Temp Target"
  49. }
  50. }
  51. }
  52. class CompositeRenderer: LineChartRenderer {
  53. let tempTargetRenderer: TempTargetRenderer
  54. let triangleRenderer: TriangleRenderer
  55. init(dataProvider: LineChartDataProvider?, animator: Animator?, viewPortHandler: ViewPortHandler?, tempTargetDataSetIndex: Int, smbDataSetIndex: Int) {
  56. tempTargetRenderer = TempTargetRenderer(
  57. dataProvider: dataProvider,
  58. animator: animator,
  59. viewPortHandler: viewPortHandler,
  60. tempTargetDataSetIndex: tempTargetDataSetIndex
  61. )
  62. triangleRenderer = TriangleRenderer(
  63. dataProvider: dataProvider,
  64. animator: animator,
  65. viewPortHandler: viewPortHandler,
  66. smbDataSetIndex: smbDataSetIndex
  67. )
  68. super.init(dataProvider: dataProvider!, animator: animator!, viewPortHandler: viewPortHandler!)
  69. }
  70. override func drawExtras(context: CGContext) {
  71. super.drawExtras(context: context)
  72. tempTargetRenderer.drawExtras(context: context)
  73. triangleRenderer.drawExtras(context: context)
  74. }
  75. }
  76. class TriangleRenderer: LineChartRenderer {
  77. let smbDataSetIndex: Int
  78. init(dataProvider: LineChartDataProvider?, animator: Animator?, viewPortHandler: ViewPortHandler?, smbDataSetIndex: Int) {
  79. self.smbDataSetIndex = smbDataSetIndex
  80. super.init(dataProvider: dataProvider!, animator: animator!, viewPortHandler: viewPortHandler!)
  81. }
  82. override func drawExtras(context: CGContext) {
  83. super.drawExtras(context: context)
  84. guard let dataProvider = dataProvider else { return }
  85. if dataProvider.lineData?.dataSets.count ?? 0 > smbDataSetIndex, let lineDataSet = dataProvider.lineData?.dataSets[smbDataSetIndex] as? LineChartDataSet {
  86. let trans = dataProvider.getTransformer(forAxis: lineDataSet.axisDependency)
  87. let phaseY = animator.phaseY
  88. for j in 0 ..< lineDataSet.entryCount {
  89. guard let e = lineDataSet.entryForIndex(j) else { continue }
  90. let pt = trans.pixelForValues(x: e.x, y: e.y * phaseY)
  91. context.saveGState()
  92. context.beginPath()
  93. context.move(to: CGPoint(x: pt.x, y: pt.y + 9))
  94. context.addLine(to: CGPoint(x: pt.x - 5, y: pt.y - 1))
  95. context.addLine(to: CGPoint(x: pt.x + 5, y: pt.y - 1))
  96. context.closePath()
  97. context.setFillColor(lineDataSet.circleColors.first!.cgColor)
  98. context.fillPath()
  99. context.restoreGState()
  100. }
  101. }
  102. }
  103. }
  104. class TempTargetChartDataEntry: ChartDataEntry {
  105. var xStart: Double = 0.0
  106. var xEnd: Double = 0.0
  107. var yTop: Double = 0.0
  108. var yBottom: Double = 0.0
  109. required init() {
  110. super.init()
  111. }
  112. init(xStart: Double, xEnd: Double, yTop: Double, yBottom: Double, data: Any?) {
  113. self.xStart = xStart
  114. self.xEnd = xEnd
  115. self.yTop = yTop
  116. self.yBottom = yBottom
  117. super.init(x: xStart, y: yTop)
  118. self.data = data
  119. }
  120. override func copy(with _: NSZone? = nil) -> Any {
  121. let copy = TempTargetChartDataEntry(
  122. xStart: xStart,
  123. xEnd: xEnd,
  124. yTop: yTop,
  125. yBottom: yBottom,
  126. data: data
  127. )
  128. return copy
  129. }
  130. }
  131. class TempTargetRenderer: LineChartRenderer {
  132. let tempTargetDataSetIndex: Int
  133. init(dataProvider: LineChartDataProvider?, animator: Animator?, viewPortHandler: ViewPortHandler?, tempTargetDataSetIndex: Int) {
  134. self.tempTargetDataSetIndex = tempTargetDataSetIndex
  135. super.init(dataProvider: dataProvider!, animator: animator!, viewPortHandler: viewPortHandler!)
  136. }
  137. override func drawExtras(context: CGContext) {
  138. super.drawExtras(context: context)
  139. guard let dataProvider = dataProvider else { return }
  140. if dataProvider.lineData?.dataSets.count ?? 0 > tempTargetDataSetIndex,
  141. let lineDataSet = dataProvider.lineData?.dataSets[tempTargetDataSetIndex] as? LineChartDataSet
  142. {
  143. let trans = dataProvider.getTransformer(forAxis: lineDataSet.axisDependency)
  144. let phaseY = animator.phaseY
  145. for i in 0 ..< lineDataSet.entryCount {
  146. guard let entry = lineDataSet.entryForIndex(i) as? TempTargetChartDataEntry else { continue }
  147. let xStart = entry.xStart
  148. let xEnd = entry.xEnd
  149. let yTop = entry.yTop * phaseY
  150. let yBottom = entry.yBottom * phaseY
  151. let leftTop = trans.pixelForValues(x: xStart, y: yTop)
  152. let rightBottom = trans.pixelForValues(x: xEnd, y: yBottom)
  153. var rect = CGRect(x: leftTop.x, y: leftTop.y, width: rightBottom.x - leftTop.x, height: rightBottom.y - leftTop.y)
  154. if rect.width < 0 {
  155. rect.origin.x += rect.width
  156. rect.size.width = abs(rect.width)
  157. }
  158. if rect.height < 0 {
  159. rect.origin.y += rect.height
  160. rect.size.height = abs(rect.height)
  161. }
  162. context.saveGState()
  163. context.setFillColor(NSUIColor.systemPurple.withAlphaComponent(0.5).cgColor)
  164. context.fill(rect)
  165. context.restoreGState()
  166. }
  167. }
  168. }
  169. }
  170. let ScaleXMax: Float = 150.0
  171. extension MainViewController {
  172. func updateChartRenderers() {
  173. let tempTargetDataIndex = GraphDataIndex.tempTarget.rawValue
  174. let smbDataIndex = GraphDataIndex.smb.rawValue
  175. let compositeRenderer = CompositeRenderer(
  176. dataProvider: BGChart,
  177. animator: BGChart.chartAnimator,
  178. viewPortHandler: BGChart.viewPortHandler,
  179. tempTargetDataSetIndex: tempTargetDataIndex,
  180. smbDataSetIndex: smbDataIndex
  181. )
  182. BGChart.renderer = compositeRenderer
  183. BGChart.data?.notifyDataChanged()
  184. BGChart.notifyDataSetChanged()
  185. }
  186. func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight _: Highlight) {
  187. if chartView == BGChartFull {
  188. BGChart.moveViewToX(entry.x)
  189. }
  190. if entry.data as? String == "hide" {
  191. BGChart.highlightValue(nil, callDelegate: false)
  192. }
  193. }
  194. func chartScaled(_: ChartViewBase, scaleX _: CGFloat, scaleY _: CGFloat) {
  195. // dont store huge values
  196. var scale = Float(BGChart.scaleX)
  197. if scale > ScaleXMax {
  198. scale = ScaleXMax
  199. }
  200. UserDefaultsRepository.chartScaleX.value = Float(scale)
  201. }
  202. func createGraph() {
  203. // Create the BG Graph Data
  204. let bgChartEntry = [ChartDataEntry]()
  205. let maxBG: Float = UserDefaultsRepository.minBGScale.value
  206. // Setup BG line details
  207. let lineBG = LineChartDataSet(entries: bgChartEntry, label: "")
  208. lineBG.circleRadius = CGFloat(globalVariables.dotBG)
  209. lineBG.circleColors = [NSUIColor.systemGreen]
  210. lineBG.drawCircleHoleEnabled = false
  211. lineBG.axisDependency = YAxis.AxisDependency.right
  212. lineBG.highlightEnabled = true
  213. lineBG.drawValuesEnabled = false
  214. if UserDefaultsRepository.showLines.value {
  215. lineBG.lineWidth = 2
  216. } else {
  217. lineBG.lineWidth = 0
  218. }
  219. if UserDefaultsRepository.showDots.value {
  220. lineBG.drawCirclesEnabled = true
  221. } else {
  222. lineBG.drawCirclesEnabled = false
  223. }
  224. lineBG.setDrawHighlightIndicators(false)
  225. lineBG.valueFont.withSize(50)
  226. // Setup Prediction line details
  227. let predictionChartEntry = [ChartDataEntry]()
  228. let linePrediction = LineChartDataSet(entries: predictionChartEntry, label: "")
  229. linePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  230. linePrediction.circleColors = [NSUIColor.systemPurple]
  231. linePrediction.colors = [NSUIColor.systemPurple]
  232. linePrediction.drawCircleHoleEnabled = false
  233. linePrediction.axisDependency = YAxis.AxisDependency.right
  234. linePrediction.highlightEnabled = true
  235. linePrediction.drawValuesEnabled = false
  236. if UserDefaultsRepository.showLines.value {
  237. linePrediction.lineWidth = 2
  238. } else {
  239. linePrediction.lineWidth = 0
  240. }
  241. if UserDefaultsRepository.showDots.value {
  242. linePrediction.drawCirclesEnabled = true
  243. } else {
  244. linePrediction.drawCirclesEnabled = false
  245. }
  246. linePrediction.setDrawHighlightIndicators(false)
  247. linePrediction.valueFont.withSize(50)
  248. // create Basal graph data
  249. let chartEntry = [ChartDataEntry]()
  250. let maxBasal = UserDefaultsRepository.minBasalScale.value
  251. let lineBasal = LineChartDataSet(entries: chartEntry, label: "")
  252. lineBasal.setDrawHighlightIndicators(false)
  253. lineBasal.setColor(NSUIColor.systemBlue, alpha: 0.5)
  254. lineBasal.lineWidth = 0
  255. lineBasal.drawFilledEnabled = true
  256. lineBasal.fillColor = NSUIColor.systemBlue
  257. lineBasal.fillAlpha = 0.5
  258. lineBasal.drawCirclesEnabled = false
  259. lineBasal.axisDependency = YAxis.AxisDependency.left
  260. lineBasal.highlightEnabled = true
  261. lineBasal.drawValuesEnabled = false
  262. lineBasal.fillFormatter = basalFillFormatter()
  263. // Boluses
  264. let chartEntryBolus = [ChartDataEntry]()
  265. let lineBolus = LineChartDataSet(entries: chartEntryBolus, label: "")
  266. lineBolus.circleRadius = CGFloat(globalVariables.dotBolus)
  267. lineBolus.circleColors = [NSUIColor.systemBlue.withAlphaComponent(0.75)]
  268. lineBolus.drawCircleHoleEnabled = false
  269. lineBolus.setDrawHighlightIndicators(false)
  270. lineBolus.setColor(NSUIColor.systemBlue, alpha: 1.0)
  271. lineBolus.lineWidth = 0
  272. lineBolus.axisDependency = YAxis.AxisDependency.right
  273. lineBolus.valueFormatter = ChartYDataValueFormatter()
  274. lineBolus.valueTextColor = NSUIColor.label
  275. lineBolus.fillColor = NSUIColor.systemBlue
  276. lineBolus.fillAlpha = 0.6
  277. lineBolus.drawCirclesEnabled = true
  278. lineBolus.drawFilledEnabled = false
  279. if UserDefaultsRepository.showValues.value {
  280. lineBolus.drawValuesEnabled = true
  281. lineBolus.highlightEnabled = false
  282. } else {
  283. lineBolus.drawValuesEnabled = false
  284. lineBolus.highlightEnabled = true
  285. }
  286. // Carbs
  287. let chartEntryCarbs = [ChartDataEntry]()
  288. let lineCarbs = LineChartDataSet(entries: chartEntryCarbs, label: "")
  289. lineCarbs.circleRadius = CGFloat(globalVariables.dotCarb)
  290. lineCarbs.circleColors = [NSUIColor.systemOrange.withAlphaComponent(0.75)]
  291. lineCarbs.drawCircleHoleEnabled = false
  292. lineCarbs.setDrawHighlightIndicators(false)
  293. lineCarbs.setColor(NSUIColor.systemBlue, alpha: 1.0)
  294. lineCarbs.lineWidth = 0
  295. lineCarbs.axisDependency = YAxis.AxisDependency.right
  296. lineCarbs.valueFormatter = ChartYDataValueFormatter()
  297. lineCarbs.valueTextColor = NSUIColor.label
  298. lineCarbs.fillColor = NSUIColor.systemOrange
  299. lineCarbs.fillAlpha = 0.6
  300. lineCarbs.drawCirclesEnabled = true
  301. lineCarbs.drawFilledEnabled = false
  302. if UserDefaultsRepository.showValues.value {
  303. lineCarbs.drawValuesEnabled = true
  304. lineCarbs.highlightEnabled = false
  305. } else {
  306. lineCarbs.drawValuesEnabled = false
  307. lineCarbs.highlightEnabled = true
  308. }
  309. // create Scheduled Basal graph data
  310. let chartBasalScheduledEntry = [ChartDataEntry]()
  311. let lineBasalScheduled = LineChartDataSet(entries: chartBasalScheduledEntry, label: "")
  312. lineBasalScheduled.setDrawHighlightIndicators(false)
  313. lineBasalScheduled.setColor(NSUIColor.systemBlue, alpha: 0.8)
  314. lineBasalScheduled.lineWidth = 2
  315. lineBasalScheduled.drawFilledEnabled = false
  316. lineBasalScheduled.drawCirclesEnabled = false
  317. lineBasalScheduled.axisDependency = YAxis.AxisDependency.left
  318. lineBasalScheduled.highlightEnabled = false
  319. lineBasalScheduled.drawValuesEnabled = false
  320. lineBasalScheduled.lineDashLengths = [10.0, 5.0]
  321. // create Override graph data
  322. let chartOverrideEntry = [ChartDataEntry]()
  323. let lineOverride = LineChartDataSet(entries: chartOverrideEntry, label: "")
  324. lineOverride.setDrawHighlightIndicators(false)
  325. lineOverride.lineWidth = 0
  326. lineOverride.drawFilledEnabled = true
  327. lineOverride.fillFormatter = OverrideFillFormatter()
  328. lineOverride.fillColor = NSUIColor.systemGreen
  329. lineOverride.fillAlpha = 0.6
  330. lineOverride.drawCirclesEnabled = false
  331. lineOverride.axisDependency = YAxis.AxisDependency.right
  332. lineOverride.highlightEnabled = true
  333. lineOverride.drawValuesEnabled = false
  334. // BG Check
  335. let chartEntryBGCheck = [ChartDataEntry]()
  336. let lineBGCheck = LineChartDataSet(entries: chartEntryBGCheck, label: "")
  337. lineBGCheck.circleRadius = CGFloat(globalVariables.dotOther)
  338. lineBGCheck.circleColors = [NSUIColor.systemRed.withAlphaComponent(0.75)]
  339. lineBGCheck.drawCircleHoleEnabled = false
  340. lineBGCheck.setDrawHighlightIndicators(false)
  341. lineBGCheck.setColor(NSUIColor.systemRed, alpha: 1.0)
  342. lineBGCheck.drawCirclesEnabled = true
  343. lineBGCheck.lineWidth = 0
  344. lineBGCheck.highlightEnabled = true
  345. lineBGCheck.axisDependency = YAxis.AxisDependency.right
  346. lineBGCheck.valueFormatter = ChartYDataValueFormatter()
  347. lineBGCheck.drawValuesEnabled = false
  348. // Suspend Pump
  349. let chartEntrySuspend = [ChartDataEntry]()
  350. let lineSuspend = LineChartDataSet(entries: chartEntrySuspend, label: "")
  351. lineSuspend.circleRadius = CGFloat(globalVariables.dotOther)
  352. lineSuspend.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  353. lineSuspend.drawCircleHoleEnabled = false
  354. lineSuspend.setDrawHighlightIndicators(false)
  355. lineSuspend.setColor(NSUIColor.systemGray2, alpha: 1.0)
  356. lineSuspend.drawCirclesEnabled = true
  357. lineSuspend.lineWidth = 0
  358. lineSuspend.highlightEnabled = true
  359. lineSuspend.axisDependency = YAxis.AxisDependency.right
  360. lineSuspend.valueFormatter = ChartYDataValueFormatter()
  361. lineSuspend.drawValuesEnabled = false
  362. // Resume Pump
  363. let chartEntryResume = [ChartDataEntry]()
  364. let lineResume = LineChartDataSet(entries: chartEntryResume, label: "")
  365. lineResume.circleRadius = CGFloat(globalVariables.dotOther)
  366. lineResume.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  367. lineResume.drawCircleHoleEnabled = false
  368. lineResume.setDrawHighlightIndicators(false)
  369. lineResume.setColor(NSUIColor.systemGray4, alpha: 1.0)
  370. lineResume.drawCirclesEnabled = true
  371. lineResume.lineWidth = 0
  372. lineResume.highlightEnabled = true
  373. lineResume.axisDependency = YAxis.AxisDependency.right
  374. lineResume.valueFormatter = ChartYDataValueFormatter()
  375. lineResume.drawValuesEnabled = false
  376. // Sensor Start
  377. let chartEntrySensor = [ChartDataEntry]()
  378. let lineSensor = LineChartDataSet(entries: chartEntrySensor, label: "")
  379. lineSensor.circleRadius = CGFloat(globalVariables.dotOther)
  380. lineSensor.circleColors = [NSUIColor.systemIndigo.withAlphaComponent(0.75)]
  381. lineSensor.drawCircleHoleEnabled = false
  382. lineSensor.setDrawHighlightIndicators(false)
  383. lineSensor.setColor(NSUIColor.systemGray3, alpha: 1.0)
  384. lineSensor.drawCirclesEnabled = true
  385. lineSensor.lineWidth = 0
  386. lineSensor.highlightEnabled = true
  387. lineSensor.axisDependency = YAxis.AxisDependency.right
  388. lineSensor.valueFormatter = ChartYDataValueFormatter()
  389. lineSensor.drawValuesEnabled = false
  390. // Notes
  391. let chartEntryNote = [ChartDataEntry]()
  392. let lineNote = LineChartDataSet(entries: chartEntryNote, label: "")
  393. lineNote.circleRadius = CGFloat(globalVariables.dotOther)
  394. lineNote.circleColors = [NSUIColor.systemGray.withAlphaComponent(0.75)]
  395. lineNote.drawCircleHoleEnabled = false
  396. lineNote.setDrawHighlightIndicators(false)
  397. lineNote.setColor(NSUIColor.systemGray3, alpha: 1.0)
  398. lineNote.drawCirclesEnabled = true
  399. lineNote.lineWidth = 0
  400. lineNote.highlightEnabled = true
  401. lineNote.axisDependency = YAxis.AxisDependency.right
  402. lineNote.valueFormatter = ChartYDataValueFormatter()
  403. lineNote.drawValuesEnabled = false
  404. // Setup COB Prediction line details
  405. let COBpredictionChartEntry = [ChartDataEntry]()
  406. let COBlinePrediction = LineChartDataSet(entries: COBpredictionChartEntry, label: "")
  407. COBlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  408. COBlinePrediction.circleColors = [NSUIColor.systemPurple]
  409. COBlinePrediction.colors = [NSUIColor.systemPurple]
  410. COBlinePrediction.drawCircleHoleEnabled = false
  411. COBlinePrediction.axisDependency = YAxis.AxisDependency.right
  412. COBlinePrediction.highlightEnabled = true
  413. COBlinePrediction.drawValuesEnabled = false
  414. if UserDefaultsRepository.showLines.value {
  415. COBlinePrediction.lineWidth = 2
  416. } else {
  417. COBlinePrediction.lineWidth = 0
  418. }
  419. if UserDefaultsRepository.showDots.value {
  420. COBlinePrediction.drawCirclesEnabled = true
  421. } else {
  422. COBlinePrediction.drawCirclesEnabled = false
  423. }
  424. COBlinePrediction.setDrawHighlightIndicators(false)
  425. COBlinePrediction.valueFont.withSize(50)
  426. // Setup IOB Prediction line details
  427. let IOBpredictionChartEntry = [ChartDataEntry]()
  428. let IOBlinePrediction = LineChartDataSet(entries: IOBpredictionChartEntry, label: "")
  429. IOBlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  430. IOBlinePrediction.circleColors = [NSUIColor.systemPurple]
  431. IOBlinePrediction.colors = [NSUIColor.systemPurple]
  432. IOBlinePrediction.drawCircleHoleEnabled = false
  433. IOBlinePrediction.axisDependency = YAxis.AxisDependency.right
  434. IOBlinePrediction.highlightEnabled = true
  435. IOBlinePrediction.drawValuesEnabled = false
  436. if UserDefaultsRepository.showLines.value {
  437. IOBlinePrediction.lineWidth = 2
  438. } else {
  439. IOBlinePrediction.lineWidth = 0
  440. }
  441. if UserDefaultsRepository.showDots.value {
  442. IOBlinePrediction.drawCirclesEnabled = true
  443. } else {
  444. IOBlinePrediction.drawCirclesEnabled = false
  445. }
  446. IOBlinePrediction.setDrawHighlightIndicators(false)
  447. IOBlinePrediction.valueFont.withSize(50)
  448. // Setup UAM Prediction line details
  449. let UAMpredictionChartEntry = [ChartDataEntry]()
  450. let UAMlinePrediction = LineChartDataSet(entries: UAMpredictionChartEntry, label: "")
  451. UAMlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  452. UAMlinePrediction.circleColors = [NSUIColor.systemPurple]
  453. UAMlinePrediction.colors = [NSUIColor.systemPurple]
  454. UAMlinePrediction.drawCircleHoleEnabled = false
  455. UAMlinePrediction.axisDependency = YAxis.AxisDependency.right
  456. UAMlinePrediction.highlightEnabled = true
  457. UAMlinePrediction.drawValuesEnabled = false
  458. if UserDefaultsRepository.showLines.value {
  459. UAMlinePrediction.lineWidth = 2
  460. } else {
  461. UAMlinePrediction.lineWidth = 0
  462. }
  463. if UserDefaultsRepository.showDots.value {
  464. UAMlinePrediction.drawCirclesEnabled = true
  465. } else {
  466. UAMlinePrediction.drawCirclesEnabled = false
  467. }
  468. linePrediction.setDrawHighlightIndicators(false)
  469. linePrediction.valueFont.withSize(50)
  470. // Setup ZT Prediction line details
  471. let ZTpredictionChartEntry = [ChartDataEntry]()
  472. let ZTlinePrediction = LineChartDataSet(entries: ZTpredictionChartEntry, label: "")
  473. ZTlinePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  474. ZTlinePrediction.circleColors = [NSUIColor.systemPurple]
  475. ZTlinePrediction.colors = [NSUIColor.systemPurple]
  476. ZTlinePrediction.drawCircleHoleEnabled = false
  477. ZTlinePrediction.axisDependency = YAxis.AxisDependency.right
  478. ZTlinePrediction.highlightEnabled = true
  479. ZTlinePrediction.drawValuesEnabled = false
  480. if UserDefaultsRepository.showLines.value {
  481. ZTlinePrediction.lineWidth = 2
  482. } else {
  483. ZTlinePrediction.lineWidth = 0
  484. }
  485. if UserDefaultsRepository.showDots.value {
  486. ZTlinePrediction.drawCirclesEnabled = true
  487. } else {
  488. ZTlinePrediction.drawCirclesEnabled = false
  489. }
  490. ZTlinePrediction.setDrawHighlightIndicators(false)
  491. ZTlinePrediction.valueFont.withSize(50)
  492. // SMB
  493. let chartEntrySmb = [ChartDataEntry]()
  494. let lineSmb = LineChartDataSet(entries: chartEntrySmb, label: "")
  495. lineSmb.circleRadius = CGFloat(globalVariables.dotBolus)
  496. lineSmb.circleColors = [NSUIColor.systemBlue.withAlphaComponent(1.0)]
  497. lineSmb.drawCircleHoleEnabled = false
  498. lineSmb.setDrawHighlightIndicators(false)
  499. lineSmb.setColor(NSUIColor.red, alpha: 1.0)
  500. lineSmb.lineWidth = 0
  501. lineSmb.axisDependency = YAxis.AxisDependency.right
  502. lineSmb.valueFormatter = ChartYDataValueFormatter()
  503. lineSmb.valueTextColor = NSUIColor.label
  504. lineSmb.drawCirclesEnabled = false
  505. lineSmb.drawFilledEnabled = false
  506. if UserDefaultsRepository.showValues.value {
  507. lineSmb.drawValuesEnabled = true
  508. lineSmb.highlightEnabled = false
  509. } else {
  510. lineSmb.drawValuesEnabled = false
  511. lineSmb.highlightEnabled = true
  512. }
  513. // TempTarget graph data
  514. let chartTempTargetEntry = [ChartDataEntry]()
  515. let lineTempTarget = LineChartDataSet(entries: chartTempTargetEntry, label: "")
  516. lineTempTarget.setDrawHighlightIndicators(false)
  517. lineTempTarget.lineWidth = 0
  518. lineTempTarget.drawFilledEnabled = false
  519. lineTempTarget.fillColor = NSUIColor.systemPurple
  520. lineTempTarget.fillAlpha = 0.6
  521. lineTempTarget.drawCirclesEnabled = false
  522. lineTempTarget.axisDependency = YAxis.AxisDependency.right
  523. lineTempTarget.highlightEnabled = true
  524. lineTempTarget.drawValuesEnabled = false
  525. // Setup the chart data of all lines
  526. let data = LineChartData()
  527. data.append(lineBG) // Dataset 0
  528. data.append(linePrediction) // Dataset 1
  529. data.append(lineBasal) // Dataset 2
  530. data.append(lineBolus) // Dataset 3
  531. data.append(lineCarbs) // Dataset 4
  532. data.append(lineBasalScheduled) // Dataset 5
  533. data.append(lineOverride) // Dataset 6
  534. data.append(lineBGCheck) // Dataset 7
  535. data.append(lineSuspend) // Dataset 8
  536. data.append(lineResume) // Dataset 9
  537. data.append(lineSensor) // Dataset 10
  538. data.append(lineNote) // Dataset 11
  539. data.append(ZTlinePrediction) // Dataset 12
  540. data.append(IOBlinePrediction) // Dataset 13
  541. data.append(COBlinePrediction) // Dataset 14
  542. data.append(UAMlinePrediction) // Dataset 15
  543. data.append(lineSmb) // Dataset 16
  544. data.append(lineTempTarget)
  545. data.setValueFont(UIFont.systemFont(ofSize: 12))
  546. // Add marker popups for bolus and carbs
  547. let marker = PillMarker(color: .secondarySystemBackground, font: UIFont.boldSystemFont(ofSize: 14), textColor: .label)
  548. BGChart.marker = marker
  549. // Clear limit lines so they don't add multiples when changing the settings
  550. BGChart.rightAxis.removeAllLimitLines()
  551. // Add lower red line based on low alert value
  552. let ll = ChartLimitLine()
  553. ll.limit = Double(UserDefaultsRepository.lowLine.value)
  554. ll.lineColor = NSUIColor.systemRed.withAlphaComponent(0.5)
  555. BGChart.rightAxis.addLimitLine(ll)
  556. // Add upper yellow line based on low alert value
  557. let ul = ChartLimitLine()
  558. ul.limit = Double(UserDefaultsRepository.highLine.value)
  559. ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
  560. BGChart.rightAxis.addLimitLine(ul)
  561. // Add vertical lines as configured
  562. createVerticalLines()
  563. startGraphNowTimer()
  564. // Setup the main graph overall details
  565. BGChart.xAxis.valueFormatter = ChartXValueFormatter()
  566. BGChart.xAxis.granularity = 1800
  567. BGChart.xAxis.labelTextColor = NSUIColor.label
  568. BGChart.xAxis.labelPosition = XAxis.LabelPosition.bottom
  569. BGChart.xAxis.drawGridLinesEnabled = false
  570. BGChart.leftAxis.enabled = true
  571. BGChart.leftAxis.labelPosition = YAxis.LabelPosition.insideChart
  572. BGChart.leftAxis.axisMaximum = maxBasal
  573. BGChart.leftAxis.axisMinimum = 0
  574. BGChart.leftAxis.drawGridLinesEnabled = false
  575. BGChart.leftAxis.granularityEnabled = true
  576. BGChart.leftAxis.granularity = 0.5
  577. BGChart.rightAxis.labelTextColor = NSUIColor.label
  578. BGChart.rightAxis.labelPosition = YAxis.LabelPosition.insideChart
  579. BGChart.rightAxis.axisMinimum = 0.0
  580. BGChart.rightAxis.axisMaximum = Double(maxBG)
  581. BGChart.rightAxis.gridLineDashLengths = [5.0, 5.0]
  582. BGChart.rightAxis.drawGridLinesEnabled = false
  583. BGChart.rightAxis.valueFormatter = ChartYMMOLValueFormatter()
  584. BGChart.rightAxis.granularityEnabled = true
  585. BGChart.rightAxis.granularity = 50
  586. BGChart.maxHighlightDistance = 15.0
  587. BGChart.legend.enabled = false
  588. BGChart.scaleYEnabled = false
  589. BGChart.drawGridBackgroundEnabled = true
  590. BGChart.gridBackgroundColor = NSUIColor.secondarySystemBackground
  591. BGChart.highlightValue(nil, callDelegate: false)
  592. BGChart.data = data
  593. BGChart.setExtraOffsets(left: 5, top: 10, right: 5, bottom: 10)
  594. }
  595. func createVerticalLines() {
  596. BGChart.xAxis.removeAllLimitLines()
  597. BGChartFull.xAxis.removeAllLimitLines()
  598. createNowAndDIALines()
  599. createMidnightLines()
  600. }
  601. func createNowAndDIALines() {
  602. let ul = ChartLimitLine()
  603. ul.limit = Double(dateTimeUtils.getNowTimeIntervalUTC())
  604. ul.lineColor = NSUIColor.systemGray.withAlphaComponent(0.5)
  605. ul.lineWidth = 1
  606. BGChart.xAxis.addLimitLine(ul)
  607. if UserDefaultsRepository.show30MinLine.value {
  608. let ul2 = ChartLimitLine()
  609. ul2.limit = Double(dateTimeUtils.getNowTimeIntervalUTC().advanced(by: -30 * 60))
  610. ul2.lineColor = NSUIColor.systemBlue.withAlphaComponent(0.5)
  611. ul2.lineWidth = 1
  612. BGChart.xAxis.addLimitLine(ul2)
  613. }
  614. if UserDefaultsRepository.showDIALines.value {
  615. for i in 1 ..< 7 {
  616. let ul = ChartLimitLine()
  617. ul.limit = Double(dateTimeUtils.getNowTimeIntervalUTC() - Double(i * 60 * 60))
  618. ul.lineColor = NSUIColor.systemGray.withAlphaComponent(0.3)
  619. let dash = 10.0 - Double(i)
  620. let space = 5.0 + Double(i)
  621. ul.lineDashLengths = [CGFloat(dash), CGFloat(space)]
  622. ul.lineWidth = 1
  623. BGChart.xAxis.addLimitLine(ul)
  624. }
  625. }
  626. if UserDefaultsRepository.show90MinLine.value {
  627. let ul3 = ChartLimitLine()
  628. ul3.limit = Double(dateTimeUtils.getNowTimeIntervalUTC().advanced(by: -90 * 60))
  629. ul3.lineColor = NSUIColor.systemOrange.withAlphaComponent(0.5)
  630. ul3.lineWidth = 1
  631. BGChart.xAxis.addLimitLine(ul3)
  632. }
  633. }
  634. func createMidnightLines() {
  635. // Draw a line at midnight: useful when showing multiple days of data
  636. if UserDefaultsRepository.showMidnightLines.value {
  637. var midnightTimeInterval = dateTimeUtils.getTimeIntervalMidnightToday()
  638. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  639. let graphStart = dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours)
  640. while midnightTimeInterval > graphStart {
  641. // Large chart
  642. let ul = ChartLimitLine()
  643. ul.limit = Double(midnightTimeInterval)
  644. ul.lineColor = NSUIColor.systemTeal.withAlphaComponent(0.5)
  645. ul.lineDashLengths = [CGFloat(2), CGFloat(5)]
  646. ul.lineWidth = 1
  647. BGChart.xAxis.addLimitLine(ul)
  648. // Small chart
  649. let sl = ChartLimitLine()
  650. sl.limit = Double(midnightTimeInterval)
  651. sl.lineColor = NSUIColor.systemTeal
  652. sl.lineDashLengths = [CGFloat(2), CGFloat(2)]
  653. sl.lineWidth = 1
  654. BGChartFull.xAxis.addLimitLine(sl)
  655. midnightTimeInterval = midnightTimeInterval.advanced(by: -24 * 60 * 60)
  656. }
  657. }
  658. }
  659. func updateBGGraphSettings() {
  660. let dataIndex = 0
  661. let dataIndexPrediction = 1
  662. let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  663. let linePrediction = BGChart.lineData!.dataSets[dataIndexPrediction] as! LineChartDataSet
  664. if UserDefaultsRepository.showLines.value {
  665. lineBG.lineWidth = 2
  666. linePrediction.lineWidth = 2
  667. } else {
  668. lineBG.lineWidth = 0
  669. linePrediction.lineWidth = 0
  670. }
  671. if UserDefaultsRepository.showDots.value {
  672. lineBG.drawCirclesEnabled = true
  673. linePrediction.drawCirclesEnabled = true
  674. } else {
  675. lineBG.drawCirclesEnabled = false
  676. linePrediction.drawCirclesEnabled = false
  677. }
  678. BGChart.rightAxis.axisMinimum = 0
  679. // Clear limit lines so they don't add multiples when changing the settings
  680. BGChart.rightAxis.removeAllLimitLines()
  681. // Add lower red line based on low alert value
  682. let ll = ChartLimitLine()
  683. ll.limit = Double(UserDefaultsRepository.lowLine.value)
  684. ll.lineColor = NSUIColor.systemRed.withAlphaComponent(0.5)
  685. BGChart.rightAxis.addLimitLine(ll)
  686. // Add upper yellow line based on low alert value
  687. let ul = ChartLimitLine()
  688. ul.limit = Double(UserDefaultsRepository.highLine.value)
  689. ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
  690. BGChart.rightAxis.addLimitLine(ul)
  691. // Re-create vertical markers in case their settings changed
  692. createVerticalLines()
  693. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  694. BGChart.data?.notifyDataChanged()
  695. BGChart.notifyDataSetChanged()
  696. }
  697. func updateBGGraph() {
  698. let dataIndex = 0
  699. let entries = bgData
  700. guard !entries.isEmpty else {
  701. return
  702. }
  703. let mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  704. let smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  705. mainChart.removeAll(keepingCapacity: false)
  706. smallChart.removeAll(keepingCapacity: false)
  707. let maxBGOffset: Float = 50
  708. var colors = [NSUIColor]()
  709. topBG = UserDefaultsRepository.minBGScale.value
  710. for i in 0 ..< entries.count {
  711. if Float(entries[i].sgv) > topBG - maxBGOffset {
  712. topBG = Float(entries[i].sgv) + maxBGOffset
  713. }
  714. let value = ChartDataEntry(x: Double(entries[i].date), y: Double(entries[i].sgv), data: formatPillText(line1: Localizer.toDisplayUnits(String(entries[i].sgv)), time: entries[i].date))
  715. mainChart.append(value)
  716. smallChart.append(value)
  717. if Double(entries[i].sgv) >= Double(UserDefaultsRepository.highLine.value) {
  718. colors.append(NSUIColor.systemYellow)
  719. } else if Double(entries[i].sgv) <= Double(UserDefaultsRepository.lowLine.value) {
  720. colors.append(NSUIColor.systemRed)
  721. } else {
  722. colors.append(NSUIColor.systemGreen)
  723. }
  724. }
  725. // Set Colors
  726. let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  727. let lineBGSmall = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  728. lineBG.colors.removeAll()
  729. lineBG.circleColors.removeAll()
  730. lineBGSmall.colors.removeAll()
  731. lineBGSmall.circleColors.removeAll()
  732. if colors.count > 0 {
  733. for i in 0 ..< colors.count {
  734. mainChart.addColor(colors[i])
  735. mainChart.circleColors.append(colors[i])
  736. smallChart.addColor(colors[i])
  737. smallChart.circleColors.append(colors[i])
  738. }
  739. }
  740. BGChart.rightAxis.axisMaximum = Double(calculateMaxBgGraphValue())
  741. BGChart.setVisibleXRangeMinimum(600)
  742. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  743. BGChart.data?.notifyDataChanged()
  744. BGChart.notifyDataSetChanged()
  745. BGChartFull.rightAxis.axisMaximum = Double(calculateMaxBgGraphValue())
  746. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  747. BGChartFull.data?.notifyDataChanged()
  748. BGChartFull.notifyDataSetChanged()
  749. if firstGraphLoad {
  750. var scaleX = CGFloat(UserDefaultsRepository.chartScaleX.value)
  751. if scaleX > CGFloat(ScaleXMax) {
  752. scaleX = CGFloat(ScaleXMax)
  753. UserDefaultsRepository.chartScaleX.value = ScaleXMax
  754. }
  755. BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
  756. firstGraphLoad = false
  757. }
  758. // Move to current reading everytime new readings load
  759. // Check if auto-scrolling should be performed
  760. if autoScrollPauseUntil == nil || Date() > autoScrollPauseUntil! {
  761. BGChart.moveViewToAnimated(xValue: dateTimeUtils.getNowTimeIntervalUTC() - (BGChart.visibleXRange * 0.7), yValue: 0.0, axis: .right, duration: 1, easingOption: .easeInBack)
  762. }
  763. }
  764. func updatePredictionGraph(color: UIColor? = nil) {
  765. let dataIndex = 1
  766. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  767. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  768. mainChart.clear()
  769. smallChart.clear()
  770. var colors = [NSUIColor]()
  771. let maxBGOffset: Float = 20
  772. topPredictionBG = UserDefaultsRepository.minBGScale.value
  773. for i in 0 ..< predictionData.count {
  774. var predictionVal = Double(predictionData[i].sgv)
  775. if Float(predictionVal) > topPredictionBG - maxBGOffset {
  776. topPredictionBG = Float(predictionVal) + maxBGOffset
  777. }
  778. if i == 0 {
  779. if UserDefaultsRepository.showDots.value {
  780. colors.append((color ?? NSUIColor.systemPurple).withAlphaComponent(0.0))
  781. } else {
  782. colors.append((color ?? NSUIColor.systemPurple).withAlphaComponent(1.0))
  783. }
  784. } else if predictionVal > 400 {
  785. colors.append(color ?? NSUIColor.systemYellow)
  786. } else if predictionVal < 0 {
  787. colors.append(color ?? NSUIColor.systemRed)
  788. } else {
  789. colors.append(color ?? NSUIColor.systemPurple)
  790. }
  791. let value = ChartDataEntry(x: predictionData[i].date, y: predictionVal, data: formatPillText(line1: Localizer.toDisplayUnits(String(predictionData[i].sgv)), time: predictionData[i].date))
  792. mainChart.addEntry(value)
  793. smallChart.addEntry(value)
  794. }
  795. smallChart.circleColors.removeAll()
  796. smallChart.colors.removeAll()
  797. mainChart.colors.removeAll()
  798. mainChart.circleColors.removeAll()
  799. if colors.count > 0 {
  800. for i in 0 ..< colors.count {
  801. mainChart.addColor(colors[i])
  802. mainChart.circleColors.append(colors[i])
  803. smallChart.addColor(colors[i])
  804. smallChart.circleColors.append(colors[i])
  805. }
  806. }
  807. BGChart.rightAxis.axisMaximum = Double(calculateMaxBgGraphValue())
  808. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  809. BGChart.data?.notifyDataChanged()
  810. BGChart.notifyDataSetChanged()
  811. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  812. BGChartFull.data?.notifyDataChanged()
  813. BGChartFull.notifyDataSetChanged()
  814. }
  815. func updateBasalGraph() {
  816. var dataIndex = 2
  817. BGChart.lineData?.dataSets[dataIndex].clear()
  818. BGChartFull.lineData?.dataSets[dataIndex].clear()
  819. var maxBasal = UserDefaultsRepository.minBasalScale.value
  820. var maxBasalSmall = 0.0
  821. for i in 0 ..< basalData.count {
  822. let value = ChartDataEntry(x: Double(basalData[i].date), y: Double(basalData[i].basalRate), data: formatPillText(line1: String(basalData[i].basalRate), time: basalData[i].date))
  823. BGChart.data?.dataSets[dataIndex].addEntry(value)
  824. if UserDefaultsRepository.smallGraphTreatments.value {
  825. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  826. }
  827. if basalData[i].basalRate > maxBasal {
  828. maxBasal = basalData[i].basalRate
  829. }
  830. if basalData[i].basalRate > maxBasalSmall {
  831. maxBasalSmall = basalData[i].basalRate
  832. }
  833. }
  834. BGChart.leftAxis.axisMaximum = maxBasal
  835. BGChartFull.leftAxis.axisMaximum = maxBasalSmall
  836. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  837. BGChart.data?.notifyDataChanged()
  838. BGChart.notifyDataSetChanged()
  839. if UserDefaultsRepository.smallGraphTreatments.value {
  840. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  841. BGChartFull.data?.notifyDataChanged()
  842. BGChartFull.notifyDataSetChanged()
  843. }
  844. }
  845. func updateBasalScheduledGraph() {
  846. var dataIndex = 5
  847. BGChart.lineData?.dataSets[dataIndex].clear()
  848. BGChartFull.lineData?.dataSets[dataIndex].clear()
  849. for i in 0 ..< basalScheduleData.count {
  850. let value = ChartDataEntry(x: Double(basalScheduleData[i].date), y: Double(basalScheduleData[i].basalRate))
  851. BGChart.data?.dataSets[dataIndex].addEntry(value)
  852. if UserDefaultsRepository.smallGraphTreatments.value {
  853. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  854. }
  855. }
  856. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  857. BGChart.data?.notifyDataChanged()
  858. BGChart.notifyDataSetChanged()
  859. if UserDefaultsRepository.smallGraphTreatments.value {
  860. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  861. BGChartFull.data?.notifyDataChanged()
  862. BGChartFull.notifyDataSetChanged()
  863. }
  864. }
  865. func updateBolusGraph() {
  866. var dataIndex = 3
  867. var yTop: Double = 370
  868. var yBottom: Double = 345
  869. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  870. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  871. mainChart.clear()
  872. smallChart.clear()
  873. var colors = [NSUIColor]()
  874. for i in 0 ..< bolusData.count {
  875. let formatter = NumberFormatter()
  876. formatter.minimumFractionDigits = 0
  877. formatter.maximumFractionDigits = 2
  878. formatter.minimumIntegerDigits = 0
  879. // Check overlapping carbs to shift left if needed
  880. let bolusShift = findNextBolusTime(timeWithin: 240, needle: bolusData[i].date, haystack: bolusData, startingIndex: i)
  881. var dateTimeStamp = bolusData[i].date
  882. colors.append(NSUIColor.systemBlue.withAlphaComponent(1.0))
  883. if bolusShift {
  884. // Move it half the distance between BG readings
  885. dateTimeStamp = dateTimeStamp - 150
  886. }
  887. // skip if outside of visible area
  888. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  889. if dateTimeStamp < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  890. let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(bolusData[i].sgv), data: formatter.string(from: NSNumber(value: bolusData[i].value)))
  891. mainChart.addEntry(dot)
  892. if UserDefaultsRepository.smallGraphTreatments.value {
  893. smallChart.addEntry(dot)
  894. }
  895. }
  896. // Set Colors
  897. let lineBolus = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  898. let lineBolusSmall = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  899. lineBolus.colors.removeAll()
  900. lineBolus.circleColors.removeAll()
  901. lineBolusSmall.colors.removeAll()
  902. lineBolusSmall.circleColors.removeAll()
  903. if colors.count > 0 {
  904. for i in 0 ..< colors.count {
  905. mainChart.addColor(colors[i])
  906. mainChart.circleColors.append(colors[i])
  907. smallChart.addColor(colors[i])
  908. smallChart.circleColors.append(colors[i])
  909. }
  910. }
  911. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  912. BGChart.data?.notifyDataChanged()
  913. BGChart.notifyDataSetChanged()
  914. if UserDefaultsRepository.smallGraphTreatments.value {
  915. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  916. BGChartFull.data?.notifyDataChanged()
  917. BGChartFull.notifyDataSetChanged()
  918. }
  919. }
  920. func updateSmbGraph() {
  921. var dataIndex = 16
  922. var yTop: Double = 370
  923. var yBottom: Double = 345
  924. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  925. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  926. mainChart.clear()
  927. smallChart.clear()
  928. let lightBlue = NSUIColor(red: 135 / 255, green: 206 / 255, blue: 235 / 255, alpha: 1.0) // Light Sky Blue
  929. var colors = [NSUIColor]()
  930. for i in 0 ..< smbData.count {
  931. let formatter = NumberFormatter()
  932. formatter.minimumFractionDigits = 0
  933. formatter.maximumFractionDigits = 2
  934. formatter.minimumIntegerDigits = 0
  935. let bolusShift = findNextBolusTime(timeWithin: 240, needle: smbData[i].date, haystack: smbData, startingIndex: i)
  936. var dateTimeStamp = smbData[i].date
  937. let nowTime = dateTimeUtils.getNowTimeIntervalUTC()
  938. let diffTimeHours = (nowTime - dateTimeStamp) / 60 / 60
  939. if diffTimeHours <= 1 {
  940. colors.append(lightBlue.withAlphaComponent(1.0))
  941. } else if diffTimeHours > 6 {
  942. colors.append(lightBlue.withAlphaComponent(0.25))
  943. } else {
  944. let thisAlpha = 1.0 - (0.15 * diffTimeHours)
  945. colors.append(lightBlue.withAlphaComponent(CGFloat(thisAlpha)))
  946. }
  947. if bolusShift {
  948. dateTimeStamp = dateTimeStamp - 150
  949. }
  950. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  951. if dateTimeStamp < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  952. let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(smbData[i].sgv), data: formatter.string(from: NSNumber(value: smbData[i].value)))
  953. mainChart.addEntry(dot)
  954. if UserDefaultsRepository.smallGraphTreatments.value {
  955. smallChart.addEntry(dot)
  956. }
  957. }
  958. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  959. BGChart.data?.notifyDataChanged()
  960. BGChart.notifyDataSetChanged()
  961. if UserDefaultsRepository.smallGraphTreatments.value {
  962. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  963. BGChartFull.data?.notifyDataChanged()
  964. BGChartFull.notifyDataSetChanged()
  965. }
  966. }
  967. func updateCarbGraph() {
  968. var dataIndex = 4
  969. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  970. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  971. mainChart.clear()
  972. smallChart.clear()
  973. var colors = [NSUIColor]()
  974. for i in 0 ..< carbData.count {
  975. let formatter = NumberFormatter()
  976. formatter.minimumFractionDigits = 0
  977. formatter.maximumFractionDigits = 2
  978. formatter.minimumIntegerDigits = 1
  979. var valueString: String = formatter.string(from: NSNumber(value: carbData[i].value))!
  980. var hours = 3
  981. if carbData[i].absorptionTime > 0, UserDefaultsRepository.showAbsorption.value {
  982. hours = carbData[i].absorptionTime / 60
  983. valueString += " " + String(hours) + "h"
  984. }
  985. // Check overlapping carbs to shift left if needed
  986. let carbShift = findNextCarbTime(timeWithin: 250, needle: carbData[i].date, haystack: carbData, startingIndex: i)
  987. var dateTimeStamp = carbData[i].date
  988. colors.append(NSUIColor.systemOrange.withAlphaComponent(1.0))
  989. // skip if outside of visible area
  990. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  991. if dateTimeStamp < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  992. if carbShift {
  993. dateTimeStamp = dateTimeStamp - 250
  994. }
  995. let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(carbData[i].sgv), data: valueString)
  996. BGChart.data?.dataSets[dataIndex].addEntry(dot)
  997. if UserDefaultsRepository.smallGraphTreatments.value {
  998. BGChartFull.data?.dataSets[dataIndex].addEntry(dot)
  999. }
  1000. }
  1001. // Set Colors
  1002. let lineCarbs = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1003. let lineCarbsSmall = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1004. lineCarbs.colors.removeAll()
  1005. lineCarbs.circleColors.removeAll()
  1006. lineCarbsSmall.colors.removeAll()
  1007. lineCarbsSmall.circleColors.removeAll()
  1008. if colors.count > 0 {
  1009. for i in 0 ..< colors.count {
  1010. mainChart.addColor(colors[i])
  1011. mainChart.circleColors.append(colors[i])
  1012. smallChart.addColor(colors[i])
  1013. smallChart.circleColors.append(colors[i])
  1014. }
  1015. }
  1016. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1017. BGChart.data?.notifyDataChanged()
  1018. BGChart.notifyDataSetChanged()
  1019. if UserDefaultsRepository.smallGraphTreatments.value {
  1020. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1021. BGChartFull.data?.notifyDataChanged()
  1022. BGChartFull.notifyDataSetChanged()
  1023. }
  1024. }
  1025. func updateBGCheckGraph() {
  1026. var dataIndex = 7
  1027. BGChart.lineData?.dataSets[dataIndex].clear()
  1028. BGChartFull.lineData?.dataSets[dataIndex].clear()
  1029. for i in 0 ..< bgCheckData.count {
  1030. let formatter = NumberFormatter()
  1031. formatter.minimumFractionDigits = 0
  1032. formatter.maximumFractionDigits = 2
  1033. formatter.minimumIntegerDigits = 1
  1034. // skip if outside of visible area
  1035. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  1036. if bgCheckData[i].date < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  1037. let value = ChartDataEntry(x: Double(bgCheckData[i].date), y: Double(bgCheckData[i].sgv), data: formatPillText(line1: Localizer.toDisplayUnits(String(bgCheckData[i].sgv)), time: bgCheckData[i].date))
  1038. BGChart.data?.dataSets[dataIndex].addEntry(value)
  1039. if UserDefaultsRepository.smallGraphTreatments.value {
  1040. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  1041. }
  1042. }
  1043. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1044. BGChart.data?.notifyDataChanged()
  1045. BGChart.notifyDataSetChanged()
  1046. if UserDefaultsRepository.smallGraphTreatments.value {
  1047. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1048. BGChartFull.data?.notifyDataChanged()
  1049. BGChartFull.notifyDataSetChanged()
  1050. }
  1051. }
  1052. func updateSuspendGraph() {
  1053. var dataIndex = 8
  1054. BGChart.lineData?.dataSets[dataIndex].clear()
  1055. BGChartFull.lineData?.dataSets[dataIndex].clear()
  1056. let thisData = suspendGraphData
  1057. for i in 0 ..< thisData.count {
  1058. // skip if outside of visible area
  1059. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  1060. if thisData[i].date < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  1061. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Suspend Pump", time: thisData[i].date))
  1062. BGChart.data?.dataSets[dataIndex].addEntry(value)
  1063. if UserDefaultsRepository.smallGraphTreatments.value {
  1064. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  1065. }
  1066. }
  1067. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1068. BGChart.data?.notifyDataChanged()
  1069. BGChart.notifyDataSetChanged()
  1070. if UserDefaultsRepository.smallGraphTreatments.value {
  1071. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1072. BGChartFull.data?.notifyDataChanged()
  1073. BGChartFull.notifyDataSetChanged()
  1074. }
  1075. }
  1076. func updateResumeGraph() {
  1077. var dataIndex = 9
  1078. BGChart.lineData?.dataSets[dataIndex].clear()
  1079. BGChartFull.lineData?.dataSets[dataIndex].clear()
  1080. let thisData = resumeGraphData
  1081. for i in 0 ..< thisData.count {
  1082. // skip if outside of visible area
  1083. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  1084. if thisData[i].date < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  1085. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Resume Pump", time: thisData[i].date))
  1086. BGChart.data?.dataSets[dataIndex].addEntry(value)
  1087. if UserDefaultsRepository.smallGraphTreatments.value {
  1088. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  1089. }
  1090. }
  1091. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1092. BGChart.data?.notifyDataChanged()
  1093. BGChart.notifyDataSetChanged()
  1094. if UserDefaultsRepository.smallGraphTreatments.value {
  1095. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1096. BGChartFull.data?.notifyDataChanged()
  1097. BGChartFull.notifyDataSetChanged()
  1098. }
  1099. }
  1100. func updateSensorStart() {
  1101. var dataIndex = 10
  1102. BGChart.lineData?.dataSets[dataIndex].clear()
  1103. BGChartFull.lineData?.dataSets[dataIndex].clear()
  1104. let thisData = sensorStartGraphData
  1105. for i in 0 ..< thisData.count {
  1106. // skip if outside of visible area
  1107. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  1108. if thisData[i].date < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  1109. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Start Sensor", time: thisData[i].date))
  1110. BGChart.data?.dataSets[dataIndex].addEntry(value)
  1111. if UserDefaultsRepository.smallGraphTreatments.value {
  1112. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  1113. }
  1114. }
  1115. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1116. BGChart.data?.notifyDataChanged()
  1117. BGChart.notifyDataSetChanged()
  1118. if UserDefaultsRepository.smallGraphTreatments.value {
  1119. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1120. BGChartFull.data?.notifyDataChanged()
  1121. BGChartFull.notifyDataSetChanged()
  1122. }
  1123. }
  1124. func updateNotes() {
  1125. var dataIndex = 11
  1126. BGChart.lineData?.dataSets[dataIndex].clear()
  1127. BGChartFull.lineData?.dataSets[dataIndex].clear()
  1128. let thisData = noteGraphData
  1129. for i in 0 ..< thisData.count {
  1130. // skip if outside of visible area
  1131. let graphHours = 24 * UserDefaultsRepository.downloadDays.value
  1132. if thisData[i].date < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue }
  1133. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: thisData[i].note, time: thisData[i].date))
  1134. BGChart.data?.dataSets[dataIndex].addEntry(value)
  1135. if UserDefaultsRepository.smallGraphTreatments.value {
  1136. BGChartFull.data?.dataSets[dataIndex].addEntry(value)
  1137. }
  1138. }
  1139. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1140. BGChart.data?.notifyDataChanged()
  1141. BGChart.notifyDataSetChanged()
  1142. if UserDefaultsRepository.smallGraphTreatments.value {
  1143. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1144. BGChartFull.data?.notifyDataChanged()
  1145. BGChartFull.notifyDataSetChanged()
  1146. }
  1147. }
  1148. func createSmallBGGraph() {
  1149. let entries = bgData
  1150. var bgChartEntry = [ChartDataEntry]()
  1151. var colors = [NSUIColor]()
  1152. var maxBG: Float = UserDefaultsRepository.minBGScale.value
  1153. let lineBG = LineChartDataSet(entries: bgChartEntry, label: "")
  1154. lineBG.drawCirclesEnabled = false
  1155. // line2.setDrawHighlightIndicators(false)
  1156. lineBG.highlightEnabled = true
  1157. lineBG.drawHorizontalHighlightIndicatorEnabled = false
  1158. lineBG.drawVerticalHighlightIndicatorEnabled = false
  1159. lineBG.highlightColor = NSUIColor.label
  1160. lineBG.drawValuesEnabled = false
  1161. lineBG.lineWidth = 1.5
  1162. lineBG.axisDependency = YAxis.AxisDependency.right
  1163. // Setup Prediction line details
  1164. var predictionChartEntry = [ChartDataEntry]()
  1165. let linePrediction = LineChartDataSet(entries: predictionChartEntry, label: "")
  1166. linePrediction.drawCirclesEnabled = false
  1167. // line2.setDrawHighlightIndicators(false)
  1168. linePrediction.setColor(NSUIColor.systemPurple)
  1169. linePrediction.highlightEnabled = true
  1170. linePrediction.drawHorizontalHighlightIndicatorEnabled = false
  1171. linePrediction.drawVerticalHighlightIndicatorEnabled = false
  1172. linePrediction.highlightColor = NSUIColor.label
  1173. linePrediction.drawValuesEnabled = false
  1174. linePrediction.lineWidth = 1.5
  1175. linePrediction.axisDependency = YAxis.AxisDependency.right
  1176. // create Basal graph data
  1177. var chartEntry = [ChartDataEntry]()
  1178. var maxBasal = UserDefaultsRepository.minBasalScale.value
  1179. let lineBasal = LineChartDataSet(entries: chartEntry, label: "")
  1180. lineBasal.setDrawHighlightIndicators(false)
  1181. lineBasal.setColor(NSUIColor.systemBlue, alpha: 0.5)
  1182. lineBasal.lineWidth = 0
  1183. lineBasal.drawFilledEnabled = true
  1184. lineBasal.fillColor = NSUIColor.systemBlue
  1185. lineBasal.fillAlpha = 0.35
  1186. lineBasal.drawCirclesEnabled = false
  1187. lineBasal.axisDependency = YAxis.AxisDependency.left
  1188. lineBasal.highlightEnabled = false
  1189. lineBasal.drawValuesEnabled = false
  1190. lineBasal.fillFormatter = basalFillFormatter()
  1191. // Boluses
  1192. var chartEntryBolus = [ChartDataEntry]()
  1193. let lineBolus = LineChartDataSet(entries: chartEntryBolus, label: "")
  1194. lineBolus.circleRadius = 2
  1195. lineBolus.circleColors = [NSUIColor.systemBlue.withAlphaComponent(0.75)]
  1196. lineBolus.drawCircleHoleEnabled = false
  1197. lineBolus.setDrawHighlightIndicators(false)
  1198. lineBolus.setColor(NSUIColor.systemBlue, alpha: 1.0)
  1199. lineBolus.lineWidth = 0
  1200. lineBolus.axisDependency = YAxis.AxisDependency.right
  1201. lineBolus.valueFormatter = ChartYDataValueFormatter()
  1202. lineBolus.valueTextColor = NSUIColor.label
  1203. lineBolus.fillColor = NSUIColor.systemBlue
  1204. lineBolus.fillAlpha = 0.6
  1205. lineBolus.drawCirclesEnabled = true
  1206. lineBolus.drawFilledEnabled = false
  1207. lineBolus.drawValuesEnabled = false
  1208. lineBolus.highlightEnabled = false
  1209. // Carbs
  1210. var chartEntryCarbs = [ChartDataEntry]()
  1211. let lineCarbs = LineChartDataSet(entries: chartEntryCarbs, label: "")
  1212. lineCarbs.circleRadius = 2
  1213. lineCarbs.circleColors = [NSUIColor.systemOrange.withAlphaComponent(0.75)]
  1214. lineCarbs.drawCircleHoleEnabled = false
  1215. lineCarbs.setDrawHighlightIndicators(false)
  1216. lineCarbs.setColor(NSUIColor.systemBlue, alpha: 1.0)
  1217. lineCarbs.lineWidth = 0
  1218. lineCarbs.axisDependency = YAxis.AxisDependency.right
  1219. lineCarbs.valueFormatter = ChartYDataValueFormatter()
  1220. lineCarbs.valueTextColor = NSUIColor.label
  1221. lineCarbs.fillColor = NSUIColor.systemOrange
  1222. lineCarbs.fillAlpha = 0.6
  1223. lineCarbs.drawCirclesEnabled = true
  1224. lineCarbs.drawFilledEnabled = false
  1225. lineCarbs.drawValuesEnabled = false
  1226. lineCarbs.highlightEnabled = false
  1227. // create Scheduled Basal graph data
  1228. var chartBasalScheduledEntry = [ChartDataEntry]()
  1229. let lineBasalScheduled = LineChartDataSet(entries: chartBasalScheduledEntry, label: "")
  1230. lineBasalScheduled.setDrawHighlightIndicators(false)
  1231. lineBasalScheduled.setColor(NSUIColor.systemBlue, alpha: 0.8)
  1232. lineBasalScheduled.lineWidth = 0.5
  1233. lineBasalScheduled.drawFilledEnabled = false
  1234. lineBasalScheduled.drawCirclesEnabled = false
  1235. lineBasalScheduled.axisDependency = YAxis.AxisDependency.left
  1236. lineBasalScheduled.highlightEnabled = false
  1237. lineBasalScheduled.drawValuesEnabled = false
  1238. lineBasalScheduled.lineDashLengths = [2, 1]
  1239. // create Override graph data
  1240. var chartOverrideEntry = [ChartDataEntry]()
  1241. let lineOverride = LineChartDataSet(entries: chartOverrideEntry, label: "")
  1242. lineOverride.setDrawHighlightIndicators(false)
  1243. lineOverride.lineWidth = 0
  1244. lineOverride.drawFilledEnabled = true
  1245. lineOverride.fillFormatter = OverrideFillFormatter()
  1246. lineOverride.fillColor = NSUIColor.systemGreen
  1247. lineOverride.fillAlpha = 0.6
  1248. lineOverride.drawCirclesEnabled = false
  1249. lineOverride.axisDependency = YAxis.AxisDependency.right
  1250. lineOverride.highlightEnabled = true
  1251. lineOverride.drawValuesEnabled = false
  1252. // BG Check
  1253. var chartEntryBGCheck = [ChartDataEntry]()
  1254. let lineBGCheck = LineChartDataSet(entries: chartEntryBGCheck, label: "")
  1255. lineBGCheck.circleRadius = 2
  1256. lineBGCheck.circleColors = [NSUIColor.systemRed.withAlphaComponent(0.75)]
  1257. lineBGCheck.drawCircleHoleEnabled = false
  1258. lineBGCheck.setDrawHighlightIndicators(false)
  1259. lineBGCheck.setColor(NSUIColor.systemRed, alpha: 1.0)
  1260. lineBGCheck.drawCirclesEnabled = true
  1261. lineBGCheck.lineWidth = 0
  1262. lineBGCheck.highlightEnabled = false
  1263. lineBGCheck.axisDependency = YAxis.AxisDependency.right
  1264. lineBGCheck.valueFormatter = ChartYDataValueFormatter()
  1265. lineBGCheck.drawValuesEnabled = false
  1266. // Suspend Pump
  1267. var chartEntrySuspend = [ChartDataEntry]()
  1268. let lineSuspend = LineChartDataSet(entries: chartEntrySuspend, label: "")
  1269. lineSuspend.circleRadius = 2
  1270. lineSuspend.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  1271. lineSuspend.drawCircleHoleEnabled = false
  1272. lineSuspend.setDrawHighlightIndicators(false)
  1273. lineSuspend.setColor(NSUIColor.systemGray2, alpha: 1.0)
  1274. lineSuspend.drawCirclesEnabled = true
  1275. lineSuspend.lineWidth = 0
  1276. lineSuspend.highlightEnabled = false
  1277. lineSuspend.axisDependency = YAxis.AxisDependency.right
  1278. lineSuspend.valueFormatter = ChartYDataValueFormatter()
  1279. lineSuspend.drawValuesEnabled = false
  1280. // Resume Pump
  1281. var chartEntryResume = [ChartDataEntry]()
  1282. let lineResume = LineChartDataSet(entries: chartEntryResume, label: "")
  1283. lineResume.circleRadius = 2
  1284. lineResume.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  1285. lineResume.drawCircleHoleEnabled = false
  1286. lineResume.setDrawHighlightIndicators(false)
  1287. lineResume.setColor(NSUIColor.systemGray4, alpha: 1.0)
  1288. lineResume.drawCirclesEnabled = true
  1289. lineResume.lineWidth = 0
  1290. lineResume.highlightEnabled = false
  1291. lineResume.axisDependency = YAxis.AxisDependency.right
  1292. lineResume.valueFormatter = ChartYDataValueFormatter()
  1293. lineResume.drawValuesEnabled = false
  1294. // Sensor Start
  1295. var chartEntrySensor = [ChartDataEntry]()
  1296. let lineSensor = LineChartDataSet(entries: chartEntrySensor, label: "")
  1297. lineSensor.circleRadius = 2
  1298. lineSensor.circleColors = [NSUIColor.systemIndigo.withAlphaComponent(0.75)]
  1299. lineSensor.drawCircleHoleEnabled = false
  1300. lineSensor.setDrawHighlightIndicators(false)
  1301. lineSensor.setColor(NSUIColor.systemGray3, alpha: 1.0)
  1302. lineSensor.drawCirclesEnabled = true
  1303. lineSensor.lineWidth = 0
  1304. lineSensor.highlightEnabled = false
  1305. lineSensor.axisDependency = YAxis.AxisDependency.right
  1306. lineSensor.valueFormatter = ChartYDataValueFormatter()
  1307. lineSensor.drawValuesEnabled = false
  1308. // Notes
  1309. var chartEntryNote = [ChartDataEntry]()
  1310. let lineNote = LineChartDataSet(entries: chartEntryNote, label: "")
  1311. lineNote.circleRadius = 2
  1312. lineNote.circleColors = [NSUIColor.systemGray.withAlphaComponent(0.75)]
  1313. lineNote.drawCircleHoleEnabled = false
  1314. lineNote.setDrawHighlightIndicators(false)
  1315. lineNote.setColor(NSUIColor.systemGray3, alpha: 1.0)
  1316. lineNote.drawCirclesEnabled = true
  1317. lineNote.lineWidth = 0
  1318. lineNote.highlightEnabled = false
  1319. lineNote.axisDependency = YAxis.AxisDependency.right
  1320. lineNote.valueFormatter = ChartYDataValueFormatter()
  1321. lineNote.drawValuesEnabled = false
  1322. // Setup COB Prediction line details
  1323. var COBpredictionChartEntry = [ChartDataEntry]()
  1324. let COBlinePrediction = LineChartDataSet(entries: COBpredictionChartEntry, label: "")
  1325. COBlinePrediction.drawCirclesEnabled = false
  1326. COBlinePrediction.setColor(NSUIColor.systemPurple)
  1327. COBlinePrediction.highlightEnabled = true
  1328. COBlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
  1329. COBlinePrediction.drawVerticalHighlightIndicatorEnabled = false
  1330. COBlinePrediction.highlightColor = NSUIColor.label
  1331. COBlinePrediction.drawValuesEnabled = false
  1332. COBlinePrediction.lineWidth = 1.5
  1333. COBlinePrediction.axisDependency = YAxis.AxisDependency.right
  1334. // Setup IOB Prediction line details
  1335. var IOBpredictionChartEntry = [ChartDataEntry]()
  1336. let IOBlinePrediction = LineChartDataSet(entries: IOBpredictionChartEntry, label: "")
  1337. IOBlinePrediction.drawCirclesEnabled = false
  1338. IOBlinePrediction.setColor(NSUIColor.systemPurple)
  1339. IOBlinePrediction.highlightEnabled = true
  1340. IOBlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
  1341. IOBlinePrediction.drawVerticalHighlightIndicatorEnabled = false
  1342. IOBlinePrediction.highlightColor = NSUIColor.label
  1343. IOBlinePrediction.drawValuesEnabled = false
  1344. IOBlinePrediction.lineWidth = 1.5
  1345. IOBlinePrediction.axisDependency = YAxis.AxisDependency.right
  1346. // Setup UAM Prediction line details
  1347. var UAMpredictionChartEntry = [ChartDataEntry]()
  1348. let UAMlinePrediction = LineChartDataSet(entries: UAMpredictionChartEntry, label: "")
  1349. UAMlinePrediction.drawCirclesEnabled = false
  1350. UAMlinePrediction.setColor(NSUIColor.systemPurple)
  1351. UAMlinePrediction.highlightEnabled = true
  1352. UAMlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
  1353. UAMlinePrediction.drawVerticalHighlightIndicatorEnabled = false
  1354. UAMlinePrediction.highlightColor = NSUIColor.label
  1355. UAMlinePrediction.drawValuesEnabled = false
  1356. UAMlinePrediction.lineWidth = 1.5
  1357. UAMlinePrediction.axisDependency = YAxis.AxisDependency.right
  1358. // Setup ZT Prediction line details
  1359. var ZTpredictionChartEntry = [ChartDataEntry]()
  1360. let ZTlinePrediction = LineChartDataSet(entries: ZTpredictionChartEntry, label: "")
  1361. ZTlinePrediction.drawCirclesEnabled = false
  1362. ZTlinePrediction.setColor(NSUIColor.systemPurple)
  1363. ZTlinePrediction.highlightEnabled = true
  1364. ZTlinePrediction.drawHorizontalHighlightIndicatorEnabled = false
  1365. ZTlinePrediction.drawVerticalHighlightIndicatorEnabled = false
  1366. ZTlinePrediction.highlightColor = NSUIColor.label
  1367. ZTlinePrediction.drawValuesEnabled = false
  1368. ZTlinePrediction.lineWidth = 1.5
  1369. ZTlinePrediction.axisDependency = YAxis.AxisDependency.right
  1370. // SMB
  1371. var chartEntrySmb = [ChartDataEntry]()
  1372. let lineSmb = LineChartDataSet(entries: chartEntrySmb, label: "")
  1373. lineSmb.circleRadius = 2
  1374. lineSmb.circleColors = [NSUIColor.systemBlue.withAlphaComponent(0.75)]
  1375. lineSmb.drawCircleHoleEnabled = false
  1376. lineSmb.setDrawHighlightIndicators(false)
  1377. lineSmb.setColor(NSUIColor.systemBlue, alpha: 1.0)
  1378. lineSmb.lineWidth = 0
  1379. lineSmb.axisDependency = YAxis.AxisDependency.right
  1380. lineSmb.valueFormatter = ChartYDataValueFormatter()
  1381. lineSmb.valueTextColor = NSUIColor.label
  1382. lineSmb.fillColor = NSUIColor.systemBlue
  1383. lineSmb.fillAlpha = 0.6
  1384. lineSmb.drawCirclesEnabled = true
  1385. lineSmb.drawFilledEnabled = false
  1386. lineSmb.drawValuesEnabled = false
  1387. lineSmb.highlightEnabled = false
  1388. // Temp Target graph data
  1389. let chartTempTargetEntry = [ChartDataEntry]()
  1390. let lineTempTarget = LineChartDataSet(entries: chartTempTargetEntry, label: "")
  1391. lineTempTarget.setDrawHighlightIndicators(false)
  1392. lineTempTarget.lineWidth = 0
  1393. lineTempTarget.drawFilledEnabled = false
  1394. lineTempTarget.fillColor = NSUIColor.systemPurple
  1395. lineTempTarget.fillAlpha = 0.6
  1396. lineTempTarget.drawCirclesEnabled = false
  1397. lineTempTarget.axisDependency = YAxis.AxisDependency.right
  1398. lineTempTarget.highlightEnabled = true
  1399. lineTempTarget.drawValuesEnabled = false
  1400. // Setup the chart data of all lines
  1401. let data = LineChartData()
  1402. data.append(lineBG) // Dataset 0
  1403. data.append(linePrediction) // Dataset 1
  1404. data.append(lineBasal) // Dataset 2
  1405. data.append(lineBolus) // Dataset 3
  1406. data.append(lineCarbs) // Dataset 4
  1407. data.append(lineBasalScheduled) // Dataset 5
  1408. data.append(lineOverride) // Dataset 6
  1409. data.append(lineBGCheck) // Dataset 7
  1410. data.append(lineSuspend) // Dataset 8
  1411. data.append(lineResume) // Dataset 9
  1412. data.append(lineSensor) // Dataset 10
  1413. data.append(lineNote) // Dataset 11
  1414. data.append(ZTlinePrediction) // Dataset 12
  1415. data.append(IOBlinePrediction) // Dataset 13
  1416. data.append(COBlinePrediction) // Dataset 14
  1417. data.append(UAMlinePrediction) // Dataset 15
  1418. data.append(lineSmb) // Dataset 16
  1419. data.append(lineTempTarget)
  1420. BGChartFull.highlightPerDragEnabled = true
  1421. BGChartFull.leftAxis.enabled = false
  1422. BGChartFull.leftAxis.axisMaximum = maxBasal
  1423. BGChartFull.leftAxis.axisMinimum = 0
  1424. BGChartFull.rightAxis.enabled = false
  1425. BGChartFull.rightAxis.axisMinimum = 0.0
  1426. BGChartFull.rightAxis.axisMaximum = Double(maxBG)
  1427. BGChartFull.xAxis.drawLabelsEnabled = false
  1428. BGChartFull.xAxis.drawGridLinesEnabled = false
  1429. BGChartFull.xAxis.drawAxisLineEnabled = false
  1430. BGChartFull.legend.enabled = false
  1431. BGChartFull.scaleYEnabled = false
  1432. BGChartFull.scaleXEnabled = false
  1433. BGChartFull.drawGridBackgroundEnabled = false
  1434. BGChartFull.data = data
  1435. }
  1436. func updateOverrideGraph() {
  1437. var dataIndex = 6
  1438. var yTop = Double(calculateMaxBgGraphValue() - 5)
  1439. var yBottom = Double(calculateMaxBgGraphValue() - 25)
  1440. var chart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1441. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1442. chart.clear()
  1443. smallChart.clear()
  1444. let thisData = overrideGraphData
  1445. var colors = [NSUIColor]()
  1446. for i in 0 ..< thisData.count {
  1447. let thisItem = thisData[i]
  1448. let multiplier = thisItem.insulNeedsScaleFactor as! Double * 100.0
  1449. var labelText = thisItem.reason + "\r\n"
  1450. labelText += String(Int(thisItem.insulNeedsScaleFactor * 100)) + "% "
  1451. if thisItem.correctionRange.count == 2 {
  1452. labelText += String(thisItem.correctionRange[0]) + "-" + String(thisItem.correctionRange[1])
  1453. }
  1454. if thisItem.enteredBy.count > 0 {
  1455. labelText += "\r\nEntered By: " + thisItem.enteredBy
  1456. }
  1457. // Start Dot
  1458. // Shift dots 30 seconds to create an empty 0 space between consecutive temps
  1459. let preStartDot = ChartDataEntry(x: Double(thisItem.date), y: yBottom, data: labelText)
  1460. BGChart.data?.dataSets[dataIndex].addEntry(preStartDot)
  1461. if UserDefaultsRepository.smallGraphTreatments.value {
  1462. BGChartFull.data?.dataSets[dataIndex].addEntry(preStartDot)
  1463. }
  1464. let startDot = ChartDataEntry(x: Double(thisItem.date + 1), y: yTop, data: labelText)
  1465. BGChart.data?.dataSets[dataIndex].addEntry(startDot)
  1466. if UserDefaultsRepository.smallGraphTreatments.value {
  1467. BGChartFull.data?.dataSets[dataIndex].addEntry(startDot)
  1468. }
  1469. // End Dot
  1470. let endDot = ChartDataEntry(x: Double(thisItem.endDate - 2), y: yTop, data: labelText)
  1471. BGChart.data?.dataSets[dataIndex].addEntry(endDot)
  1472. if UserDefaultsRepository.smallGraphTreatments.value {
  1473. BGChartFull.data?.dataSets[dataIndex].addEntry(endDot)
  1474. }
  1475. // Post end dot
  1476. let postEndDot = ChartDataEntry(x: Double(thisItem.endDate - 1), y: yBottom, data: labelText)
  1477. BGChart.data?.dataSets[dataIndex].addEntry(postEndDot)
  1478. if UserDefaultsRepository.smallGraphTreatments.value {
  1479. BGChartFull.data?.dataSets[dataIndex].addEntry(postEndDot)
  1480. }
  1481. }
  1482. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1483. BGChart.data?.notifyDataChanged()
  1484. BGChart.notifyDataSetChanged()
  1485. if UserDefaultsRepository.smallGraphTreatments.value {
  1486. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1487. BGChartFull.data?.notifyDataChanged()
  1488. BGChartFull.notifyDataSetChanged()
  1489. }
  1490. }
  1491. func getChartDataSets(for index: GraphDataIndex) -> (chart: LineChartDataSet?, smallChart: LineChartDataSet?) {
  1492. guard let chart = BGChart.lineData,
  1493. index.rawValue < chart.dataSets.count,
  1494. let smallChartData = BGChartFull.lineData,
  1495. index.rawValue < smallChartData.dataSets.count
  1496. else {
  1497. // print("Warning: Invalid GraphDataIndex \(index.description) or lineData is nil.")
  1498. return (nil, nil)
  1499. }
  1500. let chartDataSet = chart.dataSets[index.rawValue] as? LineChartDataSet
  1501. let smallChartDataSet = smallChartData.dataSets[index.rawValue] as? LineChartDataSet
  1502. return (chartDataSet, smallChartDataSet)
  1503. }
  1504. func addEntryToCharts(entry: ChartDataEntry, chart: LineChartDataSet, smallChart: LineChartDataSet?) {
  1505. chart.addEntry(entry)
  1506. if UserDefaultsRepository.smallGraphTreatments.value, let smallChart = smallChart {
  1507. smallChart.addEntry(entry)
  1508. }
  1509. }
  1510. func updateTempTargetGraph() {
  1511. let dataIndex = GraphDataIndex.tempTarget.rawValue
  1512. guard let chartData = BGChart.lineData,
  1513. chartData.dataSets.count > dataIndex,
  1514. let mainChartDataSet = chartData.dataSets[dataIndex] as? LineChartDataSet
  1515. else {
  1516. print("Error: Could not retrieve temp target datasets.")
  1517. return
  1518. }
  1519. mainChartDataSet.clear()
  1520. var smallChartDataSet: LineChartDataSet?
  1521. if UserDefaultsRepository.smallGraphTreatments.value,
  1522. let smallChartData = BGChartFull.lineData,
  1523. smallChartData.dataSets.count > dataIndex,
  1524. let smallDataSet = smallChartData.dataSets[dataIndex] as? LineChartDataSet
  1525. {
  1526. smallChartDataSet = smallDataSet
  1527. smallChartDataSet?.clear()
  1528. }
  1529. let thisData = tempTargetGraphData
  1530. for tempTarget in thisData {
  1531. let xStart = tempTarget.date
  1532. let xEnd = tempTarget.endDate
  1533. let yCenter = Double(tempTarget.correctionRange[0])
  1534. let yTop = yCenter + 5.0
  1535. let yBottom = yCenter - 5.0
  1536. let entry = TempTargetChartDataEntry(
  1537. xStart: xStart,
  1538. xEnd: xEnd,
  1539. yTop: yTop,
  1540. yBottom: yBottom,
  1541. data: nil
  1542. )
  1543. mainChartDataSet.addEntry(entry)
  1544. if let smallDataSet = smallChartDataSet {
  1545. smallDataSet.addEntry(entry)
  1546. }
  1547. }
  1548. BGChart.data?.notifyDataChanged()
  1549. BGChart.notifyDataSetChanged()
  1550. if let smallDataSet = smallChartDataSet {
  1551. let tempTargetRendererSmall = TempTargetRenderer(
  1552. dataProvider: BGChartFull,
  1553. animator: BGChartFull.chartAnimator,
  1554. viewPortHandler: BGChartFull.viewPortHandler,
  1555. tempTargetDataSetIndex: dataIndex
  1556. )
  1557. BGChartFull.renderer = tempTargetRendererSmall
  1558. BGChartFull.data?.notifyDataChanged()
  1559. BGChartFull.notifyDataSetChanged()
  1560. }
  1561. }
  1562. func wrapText(_ text: String, maxLineLength: Int) -> String {
  1563. return text
  1564. var lines: [String] = []
  1565. var currentLine = ""
  1566. let words = text.components(separatedBy: .whitespacesAndNewlines)
  1567. for word in words {
  1568. if word.count > maxLineLength {
  1569. var wordToProcess = word
  1570. while !wordToProcess.isEmpty {
  1571. let spaceCount = currentLine.isEmpty ? 0 : 1
  1572. let availableSpace = maxLineLength - (currentLine.count + spaceCount)
  1573. if availableSpace <= 0 {
  1574. if !currentLine.isEmpty {
  1575. lines.append(currentLine)
  1576. currentLine = ""
  1577. }
  1578. continue
  1579. }
  1580. let takeCount = min(wordToProcess.count, availableSpace)
  1581. if takeCount <= 0 {
  1582. if !currentLine.isEmpty {
  1583. lines.append(currentLine)
  1584. currentLine = ""
  1585. }
  1586. continue
  1587. }
  1588. let index = wordToProcess.index(wordToProcess.startIndex, offsetBy: takeCount)
  1589. let substring = wordToProcess[..<index]
  1590. if currentLine.isEmpty {
  1591. currentLine = String(substring)
  1592. } else {
  1593. currentLine += " " + substring
  1594. }
  1595. wordToProcess = String(wordToProcess[index...])
  1596. if currentLine.count >= maxLineLength {
  1597. lines.append(currentLine)
  1598. currentLine = ""
  1599. }
  1600. }
  1601. } else {
  1602. let spaceNeeded = currentLine.isEmpty ? 0 : 1
  1603. if currentLine.count + spaceNeeded + word.count > maxLineLength {
  1604. lines.append(currentLine)
  1605. currentLine = word
  1606. } else {
  1607. if currentLine.isEmpty {
  1608. currentLine = word
  1609. } else {
  1610. currentLine += " " + word
  1611. }
  1612. }
  1613. }
  1614. }
  1615. if !currentLine.isEmpty {
  1616. lines.append(currentLine)
  1617. }
  1618. return lines.joined(separator: "\r\n")
  1619. }
  1620. func formatPillText(line1: String, time: TimeInterval, line2: String? = nil) -> String {
  1621. let dateFormatter = DateFormatter()
  1622. if dateTimeUtils.is24Hour() {
  1623. dateFormatter.setLocalizedDateFormatFromTemplate("HH:mm")
  1624. } else {
  1625. dateFormatter.setLocalizedDateFormatFromTemplate("hh:mm")
  1626. }
  1627. let wrappedLine1 = wrapText(line1, maxLineLength: 40)
  1628. let date = Date(timeIntervalSince1970: time)
  1629. let formattedDate = dateFormatter.string(from: date)
  1630. if let line2 = line2 {
  1631. return wrappedLine1 + "\r\n" + line2 + "\r\n" + formattedDate
  1632. } else {
  1633. return wrappedLine1 + "\r\n" + formattedDate
  1634. }
  1635. }
  1636. func updatePredictionGraphGeneric(
  1637. dataIndex: Int,
  1638. predictionData: [ShareGlucoseData],
  1639. chartLabel: String,
  1640. color: UIColor
  1641. ) {
  1642. let mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1643. let smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  1644. mainChart.clear()
  1645. smallChart.clear()
  1646. var colors = [NSUIColor]()
  1647. let maxBGOffset: Float = 20
  1648. for i in 0 ..< predictionData.count {
  1649. let predictionVal = Double(predictionData[i].sgv)
  1650. if Float(predictionVal) > topPredictionBG - maxBGOffset {
  1651. topPredictionBG = Float(predictionVal) + maxBGOffset
  1652. }
  1653. if i == 0 {
  1654. if UserDefaultsRepository.showDots.value {
  1655. colors.append(color.withAlphaComponent(0.0))
  1656. } else {
  1657. colors.append(color.withAlphaComponent(1.0))
  1658. }
  1659. } else {
  1660. colors.append(color)
  1661. }
  1662. let value = ChartDataEntry(
  1663. x: predictionData[i].date,
  1664. y: predictionVal,
  1665. data: formatPillText(
  1666. line1: chartLabel,
  1667. time: predictionData[i].date,
  1668. line2: Localizer.toDisplayUnits(String(predictionVal))
  1669. )
  1670. )
  1671. mainChart.addEntry(value)
  1672. smallChart.addEntry(value)
  1673. }
  1674. smallChart.circleColors.removeAll()
  1675. smallChart.colors.removeAll()
  1676. mainChart.colors.removeAll()
  1677. mainChart.circleColors.removeAll()
  1678. if colors.count > 0 {
  1679. for color in colors {
  1680. mainChart.addColor(color)
  1681. mainChart.circleColors.append(color)
  1682. smallChart.addColor(color)
  1683. smallChart.circleColors.append(color)
  1684. }
  1685. }
  1686. BGChart.rightAxis.axisMaximum = Double(calculateMaxBgGraphValue())
  1687. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  1688. BGChart.data?.notifyDataChanged()
  1689. BGChart.notifyDataSetChanged()
  1690. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  1691. BGChartFull.data?.notifyDataChanged()
  1692. BGChartFull.notifyDataSetChanged()
  1693. }
  1694. }