Graphs.swift 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. //
  2. // Graphs.swift
  3. // LoopFollow
  4. //
  5. // Created by Jon Fawcett on 6/16/20.
  6. // Copyright © 2020 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import Charts
  10. import UIKit
  11. let ScaleXMax:Float = 150.0
  12. extension MainViewController {
  13. func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
  14. if chartView == BGChartFull {
  15. BGChart.moveViewToX(entry.x)
  16. }
  17. if entry.data as? String == "hide"{
  18. BGChart.highlightValue(nil, callDelegate: false)
  19. }
  20. }
  21. func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) {
  22. if chartView == BGChart {
  23. let currentMatrix = chartView.viewPortHandler.touchMatrix
  24. //BGChartFull.viewPortHandler.refresh(newMatrix: currentMatrix, chart: BGChartFull, invalidate: true)
  25. //BGChartFull.highlightValue(x: Double(currentMatrix.tx), y: Double(currentMatrix.ty), dataSetIndex: 0)
  26. }
  27. }
  28. func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
  29. print("Chart Scaled: \(BGChart.scaleX), \(BGChart.scaleY)")
  30. // dont store huge values
  31. var scale: Float = Float(BGChart.scaleX)
  32. if(scale > ScaleXMax ) {
  33. scale = ScaleXMax
  34. }
  35. UserDefaultsRepository.chartScaleX.value = Float(scale)
  36. }
  37. func createGraph(){
  38. self.BGChart.clear()
  39. // Create the BG Graph Data
  40. let entries = bgData
  41. var bgChartEntry = [ChartDataEntry]()
  42. var colors = [NSUIColor]()
  43. var maxBG: Float = UserDefaultsRepository.minBGScale.value
  44. // Setup BG line details
  45. let lineBG = LineChartDataSet(entries:bgChartEntry, label: "")
  46. lineBG.circleRadius = CGFloat(globalVariables.dotBG)
  47. lineBG.circleColors = [NSUIColor.systemGreen]
  48. lineBG.drawCircleHoleEnabled = false
  49. lineBG.axisDependency = YAxis.AxisDependency.right
  50. lineBG.highlightEnabled = true
  51. lineBG.drawValuesEnabled = false
  52. if UserDefaultsRepository.showLines.value {
  53. lineBG.lineWidth = 2
  54. } else {
  55. lineBG.lineWidth = 0
  56. }
  57. if UserDefaultsRepository.showDots.value {
  58. lineBG.drawCirclesEnabled = true
  59. } else {
  60. lineBG.drawCirclesEnabled = false
  61. }
  62. lineBG.setDrawHighlightIndicators(false)
  63. lineBG.valueFont.withSize(50)
  64. // Setup Prediction line details
  65. var predictionChartEntry = [ChartDataEntry]()
  66. let linePrediction = LineChartDataSet(entries:predictionChartEntry, label: "")
  67. linePrediction.circleRadius = CGFloat(globalVariables.dotBG)
  68. linePrediction.circleColors = [NSUIColor.systemPurple]
  69. linePrediction.colors = [NSUIColor.systemPurple]
  70. linePrediction.drawCircleHoleEnabled = false
  71. linePrediction.axisDependency = YAxis.AxisDependency.right
  72. linePrediction.highlightEnabled = true
  73. linePrediction.drawValuesEnabled = false
  74. if UserDefaultsRepository.showLines.value {
  75. linePrediction.lineWidth = 2
  76. } else {
  77. linePrediction.lineWidth = 0
  78. }
  79. if UserDefaultsRepository.showDots.value {
  80. linePrediction.drawCirclesEnabled = true
  81. } else {
  82. linePrediction.drawCirclesEnabled = false
  83. }
  84. linePrediction.setDrawHighlightIndicators(false)
  85. linePrediction.valueFont.withSize(50)
  86. // create Basal graph data
  87. var chartEntry = [ChartDataEntry]()
  88. var maxBasal = UserDefaultsRepository.minBasalScale.value
  89. let lineBasal = LineChartDataSet(entries:chartEntry, label: "")
  90. lineBasal.setDrawHighlightIndicators(false)
  91. lineBasal.setColor(NSUIColor.systemBlue, alpha: 0.5)
  92. lineBasal.lineWidth = 0
  93. lineBasal.drawFilledEnabled = true
  94. lineBasal.fillColor = NSUIColor.systemBlue
  95. lineBasal.fillAlpha = 0.5
  96. lineBasal.drawCirclesEnabled = false
  97. lineBasal.axisDependency = YAxis.AxisDependency.left
  98. lineBasal.highlightEnabled = true
  99. lineBasal.drawValuesEnabled = false
  100. lineBasal.fillFormatter = basalFillFormatter()
  101. // Boluses
  102. var chartEntryBolus = [ChartDataEntry]()
  103. let lineBolus = LineChartDataSet(entries:chartEntryBolus, label: "")
  104. lineBolus.circleRadius = CGFloat(globalVariables.dotBolus)
  105. lineBolus.circleColors = [NSUIColor.systemBlue.withAlphaComponent(0.75)]
  106. lineBolus.drawCircleHoleEnabled = false
  107. lineBolus.setDrawHighlightIndicators(false)
  108. lineBolus.setColor(NSUIColor.systemBlue, alpha: 1.0)
  109. lineBolus.lineWidth = 0
  110. lineBolus.axisDependency = YAxis.AxisDependency.right
  111. lineBolus.valueFormatter = ChartYDataValueFormatter()
  112. lineBolus.valueTextColor = NSUIColor.label
  113. lineBolus.fillFormatter = BolusFillFormatter()
  114. lineBolus.fillColor = NSUIColor.systemBlue
  115. lineBolus.fillAlpha = 0.6
  116. lineBolus.drawCirclesEnabled = true
  117. lineBolus.drawFilledEnabled = false
  118. if UserDefaultsRepository.showValues.value {
  119. lineBolus.drawValuesEnabled = true
  120. lineBolus.highlightEnabled = false
  121. } else {
  122. lineBolus.drawValuesEnabled = false
  123. lineBolus.highlightEnabled = true
  124. }
  125. // Carbs
  126. var chartEntryCarbs = [ChartDataEntry]()
  127. let lineCarbs = LineChartDataSet(entries:chartEntryCarbs, label: "")
  128. lineCarbs.circleRadius = CGFloat(globalVariables.dotCarb)
  129. lineCarbs.circleColors = [NSUIColor.systemOrange.withAlphaComponent(0.75)]
  130. lineCarbs.drawCircleHoleEnabled = false
  131. lineCarbs.setDrawHighlightIndicators(false)
  132. lineCarbs.setColor(NSUIColor.systemBlue, alpha: 1.0)
  133. lineCarbs.lineWidth = 0
  134. lineCarbs.axisDependency = YAxis.AxisDependency.right
  135. lineCarbs.valueFormatter = ChartYDataValueFormatter()
  136. lineCarbs.valueTextColor = NSUIColor.label
  137. lineCarbs.fillFormatter = CarbFillFormatter()
  138. lineCarbs.fillColor = NSUIColor.systemOrange
  139. lineCarbs.fillAlpha = 0.6
  140. lineCarbs.drawCirclesEnabled = true
  141. lineCarbs.drawFilledEnabled = false
  142. if UserDefaultsRepository.showValues.value {
  143. lineCarbs.drawValuesEnabled = true
  144. lineCarbs.highlightEnabled = false
  145. } else {
  146. lineCarbs.drawValuesEnabled = false
  147. lineCarbs.highlightEnabled = true
  148. }
  149. // create Scheduled Basal graph data
  150. var chartBasalScheduledEntry = [ChartDataEntry]()
  151. let lineBasalScheduled = LineChartDataSet(entries:chartBasalScheduledEntry, label: "")
  152. lineBasalScheduled.setDrawHighlightIndicators(false)
  153. lineBasalScheduled.setColor(NSUIColor.systemBlue, alpha: 0.8)
  154. lineBasalScheduled.lineWidth = 2
  155. lineBasalScheduled.drawFilledEnabled = false
  156. lineBasalScheduled.drawCirclesEnabled = false
  157. lineBasalScheduled.axisDependency = YAxis.AxisDependency.left
  158. lineBasalScheduled.highlightEnabled = false
  159. lineBasalScheduled.drawValuesEnabled = false
  160. lineBasalScheduled.lineDashLengths = [10.0, 5.0]
  161. // create Override graph data
  162. var chartOverrideEntry = [ChartDataEntry]()
  163. let lineOverride = LineChartDataSet(entries:chartOverrideEntry, label: "")
  164. lineOverride.setDrawHighlightIndicators(false)
  165. lineOverride.lineWidth = 0
  166. lineOverride.drawFilledEnabled = true
  167. lineOverride.fillFormatter = OverrideFillFormatter()
  168. lineOverride.fillColor = NSUIColor.systemGreen
  169. lineOverride.fillAlpha = 0.6
  170. lineOverride.drawCirclesEnabled = false
  171. lineOverride.axisDependency = YAxis.AxisDependency.right
  172. lineOverride.highlightEnabled = true
  173. lineOverride.drawValuesEnabled = false
  174. // BG Check
  175. var chartEntryBGCheck = [ChartDataEntry]()
  176. let lineBGCheck = LineChartDataSet(entries:chartEntryBGCheck, label: "")
  177. lineBGCheck.circleRadius = CGFloat(globalVariables.dotOther)
  178. lineBGCheck.circleColors = [NSUIColor.systemRed.withAlphaComponent(0.75)]
  179. lineBGCheck.drawCircleHoleEnabled = false
  180. lineBGCheck.setDrawHighlightIndicators(false)
  181. lineBGCheck.setColor(NSUIColor.systemRed, alpha: 1.0)
  182. lineBGCheck.drawCirclesEnabled = true
  183. lineBGCheck.lineWidth = 0
  184. lineBGCheck.highlightEnabled = true
  185. lineBGCheck.axisDependency = YAxis.AxisDependency.right
  186. lineBGCheck.valueFormatter = ChartYDataValueFormatter()
  187. lineBGCheck.drawValuesEnabled = false
  188. // Suspend Pump
  189. var chartEntrySuspend = [ChartDataEntry]()
  190. let lineSuspend = LineChartDataSet(entries:chartEntrySuspend, label: "")
  191. lineSuspend.circleRadius = CGFloat(globalVariables.dotOther)
  192. lineSuspend.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  193. lineSuspend.drawCircleHoleEnabled = false
  194. lineSuspend.setDrawHighlightIndicators(false)
  195. lineSuspend.setColor(NSUIColor.systemGray2, alpha: 1.0)
  196. lineSuspend.drawCirclesEnabled = true
  197. lineSuspend.lineWidth = 0
  198. lineSuspend.highlightEnabled = true
  199. lineSuspend.axisDependency = YAxis.AxisDependency.right
  200. lineSuspend.valueFormatter = ChartYDataValueFormatter()
  201. lineSuspend.drawValuesEnabled = false
  202. // Resume Pump
  203. var chartEntryResume = [ChartDataEntry]()
  204. let lineResume = LineChartDataSet(entries:chartEntryResume, label: "")
  205. lineResume.circleRadius = CGFloat(globalVariables.dotOther)
  206. lineResume.circleColors = [NSUIColor.systemTeal.withAlphaComponent(0.75)]
  207. lineResume.drawCircleHoleEnabled = false
  208. lineResume.setDrawHighlightIndicators(false)
  209. lineResume.setColor(NSUIColor.systemGray4, alpha: 1.0)
  210. lineResume.drawCirclesEnabled = true
  211. lineResume.lineWidth = 0
  212. lineResume.highlightEnabled = true
  213. lineResume.axisDependency = YAxis.AxisDependency.right
  214. lineResume.valueFormatter = ChartYDataValueFormatter()
  215. lineResume.drawValuesEnabled = false
  216. // Sensor Start
  217. var chartEntrySensor = [ChartDataEntry]()
  218. let lineSensor = LineChartDataSet(entries:chartEntrySensor, label: "")
  219. lineSensor.circleRadius = CGFloat(globalVariables.dotOther)
  220. lineSensor.circleColors = [NSUIColor.systemIndigo.withAlphaComponent(0.75)]
  221. lineSensor.drawCircleHoleEnabled = false
  222. lineSensor.setDrawHighlightIndicators(false)
  223. lineSensor.setColor(NSUIColor.systemGray3, alpha: 1.0)
  224. lineSensor.drawCirclesEnabled = true
  225. lineSensor.lineWidth = 0
  226. lineSensor.highlightEnabled = true
  227. lineSensor.axisDependency = YAxis.AxisDependency.right
  228. lineSensor.valueFormatter = ChartYDataValueFormatter()
  229. lineSensor.drawValuesEnabled = false
  230. // Notes
  231. var chartEntryNote = [ChartDataEntry]()
  232. let lineNote = LineChartDataSet(entries:chartEntryNote, label: "")
  233. lineNote.circleRadius = CGFloat(globalVariables.dotOther)
  234. lineNote.circleColors = [NSUIColor.systemGray.withAlphaComponent(0.75)]
  235. lineNote.drawCircleHoleEnabled = false
  236. lineNote.setDrawHighlightIndicators(false)
  237. lineNote.setColor(NSUIColor.systemGray3, alpha: 1.0)
  238. lineNote.drawCirclesEnabled = true
  239. lineNote.lineWidth = 0
  240. lineNote.highlightEnabled = true
  241. lineNote.axisDependency = YAxis.AxisDependency.right
  242. lineNote.valueFormatter = ChartYDataValueFormatter()
  243. lineNote.drawValuesEnabled = false
  244. // Setup the chart data of all lines
  245. let data = LineChartData()
  246. data.addDataSet(lineBG) // Dataset 0
  247. data.addDataSet(linePrediction) // Dataset 1
  248. data.addDataSet(lineBasal) // Dataset 2
  249. data.addDataSet(lineBolus) // Dataset 3
  250. data.addDataSet(lineCarbs) // Dataset 4
  251. data.addDataSet(lineBasalScheduled) // Dataset 5
  252. data.addDataSet(lineOverride) // Dataset 6
  253. data.addDataSet(lineBGCheck) // Dataset 7
  254. data.addDataSet(lineSuspend) // Dataset 8
  255. data.addDataSet(lineResume) // Dataset 9
  256. data.addDataSet(lineSensor) // Dataset 10
  257. data.addDataSet(lineNote) // Dataset 11
  258. data.setValueFont(UIFont.systemFont(ofSize: 12))
  259. // Add marker popups for bolus and carbs
  260. let marker = PillMarker(color: .secondarySystemBackground, font: UIFont.boldSystemFont(ofSize: 14), textColor: .label)
  261. BGChart.marker = marker
  262. // Clear limit lines so they don't add multiples when changing the settings
  263. BGChart.rightAxis.removeAllLimitLines()
  264. //Add lower red line based on low alert value
  265. let ll = ChartLimitLine()
  266. ll.limit = Double(UserDefaultsRepository.lowLine.value)
  267. ll.lineColor = NSUIColor.systemRed.withAlphaComponent(0.5)
  268. BGChart.rightAxis.addLimitLine(ll)
  269. //Add upper yellow line based on low alert value
  270. let ul = ChartLimitLine()
  271. ul.limit = Double(UserDefaultsRepository.highLine.value)
  272. ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
  273. BGChart.rightAxis.addLimitLine(ul)
  274. // Add Now Line
  275. createNowAndDIALines()
  276. startGraphNowTimer()
  277. // Setup the main graph overall details
  278. BGChart.xAxis.valueFormatter = ChartXValueFormatter()
  279. BGChart.xAxis.granularity = 1800
  280. BGChart.xAxis.labelTextColor = NSUIColor.label
  281. BGChart.xAxis.labelPosition = XAxis.LabelPosition.bottom
  282. BGChart.xAxis.drawGridLinesEnabled = false
  283. BGChart.leftAxis.enabled = true
  284. BGChart.leftAxis.labelPosition = YAxis.LabelPosition.insideChart
  285. BGChart.leftAxis.axisMaximum = maxBasal
  286. BGChart.leftAxis.axisMinimum = 0
  287. BGChart.leftAxis.drawGridLinesEnabled = false
  288. BGChart.leftAxis.granularityEnabled = true
  289. BGChart.leftAxis.granularity = 0.5
  290. //BGChart.leftAxis.inverted = true
  291. BGChart.rightAxis.labelTextColor = NSUIColor.label
  292. BGChart.rightAxis.labelPosition = YAxis.LabelPosition.insideChart
  293. BGChart.rightAxis.axisMinimum = 0.0
  294. BGChart.rightAxis.axisMaximum = Double(maxBG)
  295. BGChart.rightAxis.gridLineDashLengths = [5.0, 5.0]
  296. BGChart.rightAxis.drawGridLinesEnabled = false
  297. BGChart.rightAxis.valueFormatter = ChartYMMOLValueFormatter()
  298. BGChart.rightAxis.granularityEnabled = true
  299. BGChart.rightAxis.granularity = 50
  300. BGChart.legend.enabled = false
  301. BGChart.scaleYEnabled = false
  302. BGChart.drawGridBackgroundEnabled = true
  303. BGChart.gridBackgroundColor = NSUIColor.secondarySystemBackground
  304. BGChart.highlightValue(nil, callDelegate: false)
  305. BGChart.data = data
  306. BGChart.setExtraOffsets(left: 10, top: 10, right: 10, bottom: 10)
  307. }
  308. func createNowAndDIALines() {
  309. BGChart.xAxis.removeAllLimitLines()
  310. let ul = ChartLimitLine()
  311. ul.limit = Double(dateTimeUtils.getNowTimeIntervalUTC())
  312. ul.lineColor = NSUIColor.systemGray.withAlphaComponent(0.5)
  313. ul.lineWidth = 1
  314. BGChart.xAxis.addLimitLine(ul)
  315. if UserDefaultsRepository.showDIALines.value {
  316. for i in 1..<7 {
  317. let ul = ChartLimitLine()
  318. ul.limit = Double(dateTimeUtils.getNowTimeIntervalUTC() - Double(i * 60 * 60))
  319. ul.lineColor = NSUIColor.systemGray.withAlphaComponent(0.3)
  320. let dash = 10.0 - Double(i)
  321. let space = 5.0 + Double(i)
  322. ul.lineDashLengths = [CGFloat(dash), CGFloat(space)]
  323. ul.lineWidth = 1
  324. BGChart.xAxis.addLimitLine(ul)
  325. }
  326. }
  327. }
  328. func updateBGGraphSettings() {
  329. let dataIndex = 0
  330. let dataIndexPrediction = 1
  331. let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  332. let linePrediction = BGChart.lineData!.dataSets[dataIndexPrediction] as! LineChartDataSet
  333. if UserDefaultsRepository.showLines.value {
  334. lineBG.lineWidth = 2
  335. linePrediction.lineWidth = 2
  336. } else {
  337. lineBG.lineWidth = 0
  338. linePrediction.lineWidth = 0
  339. }
  340. if UserDefaultsRepository.showDots.value {
  341. lineBG.drawCirclesEnabled = true
  342. linePrediction.drawCirclesEnabled = true
  343. } else {
  344. lineBG.drawCirclesEnabled = false
  345. linePrediction.drawCirclesEnabled = false
  346. }
  347. BGChart.rightAxis.axisMinimum = 0
  348. // Clear limit lines so they don't add multiples when changing the settings
  349. BGChart.rightAxis.removeAllLimitLines()
  350. //Add lower red line based on low alert value
  351. let ll = ChartLimitLine()
  352. ll.limit = Double(UserDefaultsRepository.lowLine.value)
  353. ll.lineColor = NSUIColor.systemRed.withAlphaComponent(0.5)
  354. BGChart.rightAxis.addLimitLine(ll)
  355. //Add upper yellow line based on low alert value
  356. let ul = ChartLimitLine()
  357. ul.limit = Double(UserDefaultsRepository.highLine.value)
  358. ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
  359. BGChart.rightAxis.addLimitLine(ul)
  360. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  361. BGChart.data?.notifyDataChanged()
  362. BGChart.notifyDataSetChanged()
  363. }
  364. func updateBGGraph() {
  365. let dataIndex = 0
  366. let entries = bgData
  367. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  368. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  369. mainChart.clear()
  370. smallChart.clear()
  371. var maxBGOffset: Float = 50
  372. var colors = [NSUIColor]()
  373. for i in 0..<entries.count{
  374. if Float(entries[i].sgv) > topBG - maxBGOffset {
  375. topBG = Float(entries[i].sgv) + maxBGOffset
  376. }
  377. let value = ChartDataEntry(x: Double(entries[i].date), y: Double(entries[i].sgv), data: formatPillText(line1: bgUnits.toDisplayUnits(String(entries[i].sgv)), time: entries[i].date))
  378. mainChart.addEntry(value)
  379. smallChart.addEntry(value)
  380. if Double(entries[i].sgv) >= Double(UserDefaultsRepository.highLine.value) {
  381. colors.append(NSUIColor.systemYellow)
  382. } else if Double(entries[i].sgv) <= Double(UserDefaultsRepository.lowLine.value) {
  383. colors.append(NSUIColor.systemRed)
  384. } else {
  385. colors.append(NSUIColor.systemGreen)
  386. }
  387. }
  388. // Set Colors
  389. let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  390. let lineBGSmall = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  391. lineBG.colors.removeAll()
  392. lineBG.circleColors.removeAll()
  393. lineBGSmall.colors.removeAll()
  394. lineBGSmall.circleColors.removeAll()
  395. if colors.count > 0 {
  396. if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Graph: colors") }
  397. for i in 0..<colors.count{
  398. mainChart.addColor(colors[i])
  399. mainChart.circleColors.append(colors[i])
  400. smallChart.addColor(colors[i])
  401. smallChart.circleColors.append(colors[i])
  402. }
  403. }
  404. BGChart.rightAxis.axisMaximum = Double(topBG)
  405. BGChart.setVisibleXRangeMinimum(600)
  406. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  407. BGChart.data?.notifyDataChanged()
  408. BGChart.notifyDataSetChanged()
  409. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  410. BGChartFull.data?.notifyDataChanged()
  411. BGChartFull.notifyDataSetChanged()
  412. if firstGraphLoad {
  413. var scaleX = CGFloat(UserDefaultsRepository.chartScaleX.value)
  414. print("Scale: \(scaleX)")
  415. if( scaleX > CGFloat(ScaleXMax) ) {
  416. scaleX = CGFloat(ScaleXMax)
  417. UserDefaultsRepository.chartScaleX.value = ScaleXMax
  418. }
  419. BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
  420. firstGraphLoad = false
  421. }
  422. if BGChart.chartXMax > dateTimeUtils.getNowTimeIntervalUTC() {
  423. BGChart.moveViewToAnimated(xValue: dateTimeUtils.getNowTimeIntervalUTC() - (BGChart.visibleXRange * 0.7), yValue: 0.0, axis: .right, duration: 1, easingOption: .easeInBack)
  424. }
  425. }
  426. func updatePredictionGraph() {
  427. let dataIndex = 1
  428. var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  429. var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet
  430. mainChart.clear()
  431. smallChart.clear()
  432. if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Graph: print prediction") }
  433. var colors = [NSUIColor]()
  434. let maxBGOffset: Float = 20
  435. for i in 0..<predictionData.count {
  436. var predictionVal = Double(predictionData[i].sgv)
  437. if Float(predictionVal) > topBG - maxBGOffset {
  438. topBG = Float(predictionVal) + maxBGOffset
  439. }
  440. if i == 0 {
  441. if UserDefaultsRepository.showDots.value {
  442. colors.append(NSUIColor.systemPurple.withAlphaComponent(0.0))
  443. } else {
  444. colors.append(NSUIColor.systemPurple.withAlphaComponent(1.0))
  445. }
  446. } else if predictionVal > 400 {
  447. predictionVal = 400
  448. colors.append(NSUIColor.systemYellow)
  449. } else if predictionVal < 0 {
  450. predictionVal = 0
  451. colors.append(NSUIColor.systemRed)
  452. } else {
  453. colors.append(NSUIColor.systemPurple)
  454. }
  455. let value = ChartDataEntry(x: predictionData[i].date, y: predictionVal, data: formatPillText(line1: bgUnits.toDisplayUnits(String(predictionData[i].sgv)), time: predictionData[i].date))
  456. mainChart.addEntry(value)
  457. smallChart.addEntry(value)
  458. }
  459. smallChart.circleColors.removeAll()
  460. smallChart.colors.removeAll()
  461. mainChart.colors.removeAll()
  462. mainChart.circleColors.removeAll()
  463. if colors.count > 0 {
  464. if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Graph: prediction colors") }
  465. for i in 0..<colors.count{
  466. mainChart.addColor(colors[i])
  467. mainChart.circleColors.append(colors[i])
  468. smallChart.addColor(colors[i])
  469. smallChart.circleColors.append(colors[i])
  470. }
  471. }
  472. BGChart.rightAxis.axisMaximum = Double(topBG)
  473. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  474. BGChart.data?.notifyDataChanged()
  475. BGChart.notifyDataSetChanged()
  476. BGChartFull.data?.dataSets[dataIndex].notifyDataSetChanged()
  477. BGChartFull.data?.notifyDataChanged()
  478. BGChartFull.notifyDataSetChanged()
  479. }
  480. func updateBasalGraph() {
  481. var dataIndex = 2
  482. BGChart.lineData?.dataSets[dataIndex].clear()
  483. var maxBasal = UserDefaultsRepository.minBasalScale.value
  484. for i in 0..<basalData.count{
  485. let value = ChartDataEntry(x: Double(basalData[i].date), y: Double(basalData[i].basalRate), data: formatPillText(line1: String(basalData[i].basalRate), time: basalData[i].date))
  486. BGChart.data?.dataSets[dataIndex].addEntry(value)
  487. if basalData[i].basalRate > maxBasal {
  488. maxBasal = basalData[i].basalRate
  489. }
  490. }
  491. BGChart.leftAxis.axisMaximum = maxBasal
  492. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  493. BGChart.data?.notifyDataChanged()
  494. BGChart.notifyDataSetChanged()
  495. }
  496. func updateBasalScheduledGraph() {
  497. var dataIndex = 5
  498. BGChart.lineData?.dataSets[dataIndex].clear()
  499. for i in 0..<basalScheduleData.count{
  500. let value = ChartDataEntry(x: Double(basalScheduleData[i].date), y: Double(basalScheduleData[i].basalRate))
  501. BGChart.data?.dataSets[dataIndex].addEntry(value)
  502. }
  503. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  504. BGChart.data?.notifyDataChanged()
  505. BGChart.notifyDataSetChanged()
  506. }
  507. func updateBolusGraph() {
  508. var dataIndex = 3
  509. var yTop: Double = 370
  510. var yBottom: Double = 345
  511. BGChart.lineData?.dataSets[dataIndex].clear()
  512. for i in 0..<bolusData.count{
  513. let formatter = NumberFormatter()
  514. formatter.minimumFractionDigits = 0
  515. formatter.maximumFractionDigits = 2
  516. formatter.minimumIntegerDigits = 0
  517. // Check overlapping carbs to shift left if needed
  518. let bolusShift = findNextBolusTime(timeWithin: 240, needle: bolusData[i].date, haystack: bolusData, startingIndex: i)
  519. var dateTimeStamp = bolusData[i].date
  520. if bolusShift {
  521. // Move it half the distance between BG readings
  522. dateTimeStamp = dateTimeStamp - 150
  523. }
  524. // skip if > 24 hours old
  525. if dateTimeStamp < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  526. let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(bolusData[i].sgv), data: formatter.string(from: NSNumber(value: bolusData[i].value)))
  527. BGChart.data?.dataSets[dataIndex].addEntry(dot)
  528. }
  529. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  530. BGChart.data?.notifyDataChanged()
  531. BGChart.notifyDataSetChanged()
  532. }
  533. func updateCarbGraph() {
  534. var dataIndex = 4
  535. BGChart.lineData?.dataSets[dataIndex].clear()
  536. for i in 0..<carbData.count{
  537. let formatter = NumberFormatter()
  538. formatter.minimumFractionDigits = 0
  539. formatter.maximumFractionDigits = 2
  540. formatter.minimumIntegerDigits = 1
  541. var valueString: String = formatter.string(from: NSNumber(value: carbData[i].value))!
  542. if carbData[i].absorptionTime > 0 && UserDefaultsRepository.showAbsorption.value {
  543. let hours = carbData[i].absorptionTime / 60
  544. valueString += " " + String(hours) + "h"
  545. }
  546. // Check overlapping carbs to shift left if needed
  547. let carbShift = findNextCarbTime(timeWithin: 250, needle: carbData[i].date, haystack: carbData, startingIndex: i)
  548. var dateTimeStamp = carbData[i].date
  549. // skip if > 24 hours old
  550. if dateTimeStamp < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  551. if carbShift {
  552. dateTimeStamp = dateTimeStamp - 250
  553. }
  554. let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(carbData[i].sgv), data: valueString)
  555. BGChart.data?.dataSets[dataIndex].addEntry(dot)
  556. }
  557. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  558. BGChart.data?.notifyDataChanged()
  559. BGChart.notifyDataSetChanged()
  560. }
  561. func updateBGCheckGraph() {
  562. var dataIndex = 7
  563. BGChart.lineData?.dataSets[dataIndex].clear()
  564. for i in 0..<bgCheckData.count{
  565. let formatter = NumberFormatter()
  566. formatter.minimumFractionDigits = 0
  567. formatter.maximumFractionDigits = 2
  568. formatter.minimumIntegerDigits = 1
  569. // skip if > 24 hours old
  570. if bgCheckData[i].date < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  571. let value = ChartDataEntry(x: Double(bgCheckData[i].date), y: Double(bgCheckData[i].sgv), data: formatPillText(line1: bgUnits.toDisplayUnits(String(bgCheckData[i].sgv)), time: bgCheckData[i].date))
  572. BGChart.data?.dataSets[dataIndex].addEntry(value)
  573. }
  574. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  575. BGChart.data?.notifyDataChanged()
  576. BGChart.notifyDataSetChanged()
  577. }
  578. func updateSuspendGraph() {
  579. var dataIndex = 8
  580. BGChart.lineData?.dataSets[dataIndex].clear()
  581. let thisData = suspendGraphData
  582. for i in 0..<thisData.count{
  583. // skip if > 24 hours old
  584. if thisData[i].date < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  585. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Suspend Pump", time: thisData[i].date))
  586. BGChart.data?.dataSets[dataIndex].addEntry(value)
  587. }
  588. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  589. BGChart.data?.notifyDataChanged()
  590. BGChart.notifyDataSetChanged()
  591. }
  592. func updateResumeGraph() {
  593. var dataIndex = 9
  594. BGChart.lineData?.dataSets[dataIndex].clear()
  595. let thisData = resumeGraphData
  596. for i in 0..<thisData.count{
  597. // skip if > 24 hours old
  598. if thisData[i].date < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  599. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Resume Pump", time: thisData[i].date))
  600. BGChart.data?.dataSets[dataIndex].addEntry(value)
  601. }
  602. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  603. BGChart.data?.notifyDataChanged()
  604. BGChart.notifyDataSetChanged()
  605. }
  606. func updateSensorStart() {
  607. var dataIndex = 10
  608. BGChart.lineData?.dataSets[dataIndex].clear()
  609. let thisData = sensorStartGraphData
  610. for i in 0..<thisData.count{
  611. // skip if > 24 hours old
  612. if thisData[i].date < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  613. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: "Start Sensor", time: thisData[i].date))
  614. BGChart.data?.dataSets[dataIndex].addEntry(value)
  615. }
  616. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  617. BGChart.data?.notifyDataChanged()
  618. BGChart.notifyDataSetChanged()
  619. }
  620. func updateNotes() {
  621. var dataIndex = 11
  622. BGChart.lineData?.dataSets[dataIndex].clear()
  623. let thisData = noteGraphData
  624. for i in 0..<thisData.count{
  625. // skip if > 24 hours old
  626. if thisData[i].date < dateTimeUtils.getTimeInterval24HoursAgo() { continue }
  627. let value = ChartDataEntry(x: Double(thisData[i].date), y: Double(thisData[i].sgv), data: formatPillText(line1: thisData[i].note, time: thisData[i].date))
  628. BGChart.data?.dataSets[dataIndex].addEntry(value)
  629. }
  630. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  631. BGChart.data?.notifyDataChanged()
  632. BGChart.notifyDataSetChanged()
  633. }
  634. func createSmallBGGraph(){
  635. let entries = bgData
  636. var bgChartEntry = [ChartDataEntry]()
  637. var colors = [NSUIColor]()
  638. let lineBG = LineChartDataSet(entries:bgChartEntry, label: "")
  639. lineBG.drawCirclesEnabled = false
  640. //line2.setDrawHighlightIndicators(false)
  641. lineBG.highlightEnabled = true
  642. lineBG.drawHorizontalHighlightIndicatorEnabled = false
  643. lineBG.drawVerticalHighlightIndicatorEnabled = false
  644. lineBG.highlightColor = NSUIColor.label
  645. lineBG.drawValuesEnabled = false
  646. lineBG.lineWidth = 2
  647. // Setup Prediction line details
  648. var predictionChartEntry = [ChartDataEntry]()
  649. let linePrediction = LineChartDataSet(entries:predictionChartEntry, label: "")
  650. linePrediction.drawCirclesEnabled = false
  651. //line2.setDrawHighlightIndicators(false)
  652. linePrediction.setColor(NSUIColor.systemPurple)
  653. linePrediction.highlightEnabled = true
  654. linePrediction.drawHorizontalHighlightIndicatorEnabled = false
  655. linePrediction.drawVerticalHighlightIndicatorEnabled = false
  656. linePrediction.highlightColor = NSUIColor.label
  657. linePrediction.drawValuesEnabled = false
  658. linePrediction.lineWidth = 2
  659. let data = LineChartData()
  660. data.addDataSet(lineBG)
  661. data.addDataSet(linePrediction)
  662. BGChartFull.highlightPerDragEnabled = true
  663. BGChartFull.leftAxis.enabled = false
  664. BGChartFull.rightAxis.enabled = false
  665. BGChartFull.xAxis.enabled = false
  666. BGChartFull.legend.enabled = false
  667. BGChartFull.scaleYEnabled = false
  668. BGChartFull.scaleXEnabled = false
  669. BGChartFull.drawGridBackgroundEnabled = false
  670. BGChartFull.data = data
  671. }
  672. func updateOverrideGraph() {
  673. var dataIndex = 6
  674. var yTop: Double = Double(topBG - 1)
  675. var yBottom: Double = Double(topBG - 25)
  676. var chart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  677. chart.clear()
  678. let thisData = overrideGraphData
  679. var colors = [NSUIColor]()
  680. for i in 0..<thisData.count{
  681. let thisItem = thisData[i]
  682. let multiplier = thisItem.insulNeedsScaleFactor as! Double * 100.0
  683. //let labelText = String(format: "%.0f%%", multiplier)
  684. var labelText = thisItem.reason + "\r\n"
  685. labelText += String(Int(thisItem.insulNeedsScaleFactor * 100)) + "% "
  686. if thisItem.correctionRange.count == 2 {
  687. labelText += String(thisItem.correctionRange[0]) + "-" + String(thisItem.correctionRange[1])
  688. }
  689. // Start Dot
  690. // Shift dots 30 seconds to create an empty 0 space between consecutive temps
  691. let preStartDot = ChartDataEntry(x: Double(thisItem.date), y: yBottom, data: "hide")
  692. BGChart.data?.dataSets[dataIndex].addEntry(preStartDot)
  693. let value = ChartDataEntry(x: Double(thisItem.date + 1), y: yTop, data: labelText)
  694. BGChart.data?.dataSets[dataIndex].addEntry(value)
  695. if Double(thisItem.insulNeedsScaleFactor) == 1.0 {
  696. colors.append(NSUIColor.systemGray.withAlphaComponent(0.0))
  697. } else if i >= overrideGraphData.count - 2 {
  698. colors.append(NSUIColor.systemGreen)
  699. } else {
  700. colors.append(NSUIColor.systemGray.withAlphaComponent(CGFloat(thisItem.insulNeedsScaleFactor / 2)))
  701. }
  702. // End Dot
  703. let endDot = ChartDataEntry(x: Double(thisItem.endDate - 1), y: yTop, data: labelText)
  704. BGChart.data?.dataSets[dataIndex].addEntry(endDot)
  705. // Post end dot
  706. let postEndDot = ChartDataEntry(x: Double(thisItem.endDate), y: yBottom, data: "hide")
  707. BGChart.data?.dataSets[dataIndex].addEntry(postEndDot)
  708. if Double(thisItem.insulNeedsScaleFactor) == 1.0 {
  709. colors.append(NSUIColor.systemGray.withAlphaComponent(0.0))
  710. } else if i >= overrideGraphData.count - 2 {
  711. colors.append(NSUIColor.systemGreen)
  712. } else {
  713. colors.append(NSUIColor.systemGray.withAlphaComponent(CGFloat(thisItem.insulNeedsScaleFactor / 2)))
  714. }
  715. }
  716. // Set Colors
  717. let line = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
  718. line.colors.removeAll()
  719. line.circleColors.removeAll()
  720. if colors.count > 0 {
  721. for i in 0..<colors.count{
  722. chart.addColor(colors[i])
  723. chart.circleColors.append(colors[i])
  724. }
  725. }
  726. BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
  727. BGChart.data?.notifyDataChanged()
  728. BGChart.notifyDataSetChanged()
  729. }
  730. func formatPillText(line1: String, time: TimeInterval) -> String {
  731. let dateFormatter = DateFormatter()
  732. //let timezoneOffset = TimeZone.current.secondsFromGMT()
  733. //let epochTimezoneOffset = value + Double(timezoneOffset)
  734. if dateTimeUtils.is24Hour() {
  735. dateFormatter.setLocalizedDateFormatFromTemplate("HH:mm")
  736. } else {
  737. dateFormatter.setLocalizedDateFormatFromTemplate("hh:mm")
  738. }
  739. //let date = Date(timeIntervalSince1970: epochTimezoneOffset)
  740. let date = Date(timeIntervalSince1970: time)
  741. let formattedDate = dateFormatter.string(from: date)
  742. return line1 + "\r\n" + formattedDate
  743. }
  744. }