Graphs.swift 80 KB

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