Graphs.swift 81 KB

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