Adds History Graph for channels

This commit is contained in:
Patrick McDonagh
2018-05-29 16:54:16 -05:00
parent a6721d172c
commit 53ddf1b15e
62 changed files with 4637 additions and 2833 deletions

View File

@@ -66,9 +66,16 @@ class ChannelDetailViewController: UIViewController, UITableViewDelegate, UITabl
}
}
@IBAction func writeButtonPressed(_ sender: UIButton) {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openHistoryGraph" {
let destinationVC = segue.destination as! HistoryGraphViewController
destinationVC.channelHistory = channelHistory
destinationVC.user = user
destinationVC.thisChannel = thisChannel
}
}
//MARK: - TABLE METHODS
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

View File

@@ -25,12 +25,23 @@ class DeviceDetailViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
loadData()
self.navigationItem.title = thisDevice?.vanityName
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
SVProgressHUD.show()
firstly {
loadData()
}.done { _ in
self.tableView.reloadData()
}.catch{ error in
print("Error in loadData: \(error)")
}
self.navigationItem.title = thisDevice?.vanityName
self.refreshControl = UIRefreshControl()
refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
// tableView.addSubview(refreshControl!)
firstly {
getChannels(deviceTypeId: thisDevice!.deviceTypeId)
}.then { _ in
@@ -38,11 +49,27 @@ class DeviceDetailViewController: UITableViewController {
}.done { _ in
self.tableView.reloadData()
SVProgressHUD.dismiss()
self.refreshControl?.endRefreshing()
}.catch { error in
print("Error fetching data in DeviceDetailViewController: \(error)")
}
}
@objc func refresh() {
SVProgressHUD.show()
firstly {
getChannels(deviceTypeId: thisDevice!.deviceTypeId)
}.then { _ in
self.getChannelValues(deviceId: self.thisDevice!.id)
}.done { _ in
self.tableView.reloadData()
SVProgressHUD.dismiss()
self.refreshControl?.endRefreshing()
}.catch { error in
print("Error fetching data in DeviceDetailViewController: \(error)")
}
}
@@ -172,10 +199,12 @@ class DeviceDetailViewController: UITableViewController {
}
}
func loadData() {
deviceTypes = realm.objects(DeviceType.self)
thisDevice = realm.objects(Device.self).filter("id == %d", thisDevice!.id).first!
tableView.reloadData()
func loadData() -> Promise<Void> {
return Promise { promise in
deviceTypes = realm.objects(DeviceType.self)
thisDevice = realm.objects(Device.self).filter("id == %d", thisDevice!.id).first!
promise.fulfill(())
}
}

View File

@@ -28,31 +28,60 @@ class DeviceListViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.refreshControl = UIRefreshControl()
// refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
// refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
SVProgressHUD.show()
loadData()
firstly {
getDeviceTypes()
}
.then { _ in
self.getAddresses()
}.then { _ in
self.getGateways()
}.then { _ in
self.getDevices()
self.loadRealmData()
}.then{ _ in
self.loadJSONData()
}.done { _ in
SVProgressHUD.dismiss()
self.tableView.reloadData()
SVProgressHUD.dismiss()
}.catch { error in
print("Error in getting data in DeviceListViewController: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func loadJSONData() -> Promise<Void>{
return Promise { promise in
firstly {
self.getDeviceTypes()
}.then { _ in
self.getAddresses()
}.then { _ in
self.getGateways()
}.then { _ in
self.getDevices()
}.done{ _ in
promise.fulfill(())
}.catch { error in
promise.reject(error)
}
}
}
// @objc func refresh() {
//
// SVProgressHUD.show()
// self.realm.refresh()
// firstly {
// self.loadJSONData()
// }.done { _ in
// self.tableView.reloadData()
// self.refreshControl?.endRefreshing()
// SVProgressHUD.dismiss()
// }.catch { error in
// print("Error in getting data in DeviceListViewController: \(error)")
// }
//
// }
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
@@ -97,6 +126,7 @@ class DeviceListViewController: UITableViewController {
performSegue(withIdentifier: "openDeviceDetailView", sender: self)
}
//MARK: - Data Methods
func getAddresses() ->Promise<Any> {
@@ -284,12 +314,14 @@ class DeviceListViewController: UITableViewController {
//MARK: - Realm Functions
func loadData() {
devices = realm.objects(Device.self)
deviceTypes = realm.objects(DeviceType.self)
gateways = realm.objects(Gateway.self)
addresses = realm.objects(Address.self)
tableView.reloadData()
func loadRealmData() -> Promise<Void> {
return Promise { promise in
devices = realm.objects(Device.self)
deviceTypes = realm.objects(DeviceType.self)
gateways = realm.objects(Gateway.self)
addresses = realm.objects(Address.self)
promise.fulfill(())
}
}
}

View File

@@ -0,0 +1,115 @@
//
// HistoryGraphViewController.swift
// pocloud
//
// Created by Patrick McDonagh on 5/29/18.
// Copyright © 2018 patrickjmcd. All rights reserved.
//
import UIKit
import SwiftChart
class HistoryGraphViewController: UIViewController, ChartDelegate{
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
@IBOutlet weak var lineChart: Chart!
@IBOutlet weak var label: UILabel!
var thisChannel : Channel?
var channelHistory : [ChannelHistoryValue] = [ChannelHistoryValue]()
var user : User?
var dateFormatter = DateFormatter()
let channelDataTypes: [Int : String] = [
0: "Unknown",
1: "Float",
2: "String",
3: "Integer",
4: "Boolean",
5: "DateTime",
6: "Timespan",
7 :"File",
8: "LatLng"
]
override func viewDidLoad() {
super.viewDidLoad()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
lineChart.delegate = self
loadChartData()
}
func loadChartData() {
switch channelDataTypes[(thisChannel?.dataType)!] {
case "Float", "Integer":
print("Float or Integer")
let seriesData : [(x: Int, y: Double)] = channelHistory.map { (chHist) -> (x: Int, y: Double) in
(x: Int(chHist.timestamp!.timeIntervalSince1970), y:Double(chHist.value)!)
}
lineChart.add(ChartSeries(data: seriesData))
setupLineChart()
default:
let alert = UIAlertController(title: "Invalid Type", message: "This channel is of type \(channelDataTypes[(thisChannel?.channelType)!] ?? "ERROR") and cannot be graphed yet.", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .cancel, handler: { _ in
self.navigationController?.popViewController(animated: true)
})
alert.addAction(ok)
present(alert, animated: true)
}
}
func setupLineChart() {
let xLabelMax = lineChart.series.first?.data.first?.x
let xLabelMin = lineChart.series.first?.data.last?.x
let xLabelDoubles = [0.0, 0.25, 0.5, 0.75, 1.0].map { (multiplier) -> Double in
xLabelMin! + (xLabelMax! - xLabelMin!) * multiplier
}
lineChart.xLabels = xLabelDoubles
self.dateFormatter.dateFormat = "HH:mm:ss"
lineChart.xLabelsFormatter = { (i, d) -> String in
self.dateFormatter.string(from: Date(timeIntervalSince1970: d))
}
lineChart.bottomInset = 40
lineChart.series.first?.area = true
}
func didTouchChart(_ chart: Chart, indexes: Array<Int?>, x: Double, left: CGFloat) {
for (seriesIndex, dataIndex) in indexes.enumerated() {
if dataIndex != nil {
let value = chart.valueForSeries(seriesIndex, atIndex: dataIndex)
let valueFormat = self.channelDataTypes[(self.thisChannel?.dataType)!]! == "Integer" ? "%d" : "%.3f"
let valueString = String(format: valueFormat, value!)
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let timestamp = self.dateFormatter.string(from: channelHistory[dataIndex!].timestamp!)
self.label.text = "\(valueString) @ \(timestamp)"
let intendedLeftPosition = left - (0.5 * self.label.frame.width)
if intendedLeftPosition < 8 {
self.leftConstraint.constant = 8
} else if (intendedLeftPosition + self.label.frame.width) > (self.view.frame.width - 8) {
self.leftConstraint.constant = self.view.frame.width - (8 + self.label.frame.width)
} else {
self.leftConstraint.constant = intendedLeftPosition
}
}
}
}
func didFinishTouchingChart(_ chart: Chart) {
label.text = ""
}
func didEndTouchingChart(_ chart: Chart) {
}
}

View File

@@ -262,16 +262,19 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Um-Bu-9sz">
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bkh-Zy-Gmc">
<rect key="frame" x="0.0" y="0.0" width="171.5" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="172" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="172" id="RRS-6S-a7K"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EXp-nl-Xlu">
<rect key="frame" x="171.5" y="0.0" width="171.5" height="43.5"/>
<rect key="frame" x="172" y="0.0" width="171" height="44"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -287,10 +290,9 @@
</stackView>
</subviews>
<constraints>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="5QW-f5-FX9"/>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="leading" secondItem="u2w-Yy-Exm" secondAttribute="leading" constant="16" id="DTN-Mt-MOn"/>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="NKZ-KY-yvA"/>
<constraint firstAttribute="trailing" secondItem="8Um-Bu-9sz" secondAttribute="trailing" constant="16" id="Xho-aE-Uhw"/>
<constraint firstAttribute="bottom" secondItem="8Um-Bu-9sz" secondAttribute="bottom" id="fpI-Sp-LwU"/>
</constraints>
</tableViewCellContentView>
<connections>
@@ -307,6 +309,10 @@
<toolbarItems/>
<navigationItem key="navigationItem" title="Title" id="sUT-g4-ZVO"/>
<nil key="simulatedBottomBarMetrics"/>
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="4fv-Lb-C8k">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
</refreshControl>
<connections>
<segue destination="peV-M2-goO" kind="show" identifier="goToChannelView" id="rTg-Ze-P5c"/>
</connections>
@@ -487,6 +493,13 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mlm-Lp-0uP">
<rect key="frame" x="287" y="300" width="80" height="30"/>
<state key="normal" title="View Graph"/>
<connections>
<segue destination="sJg-Af-FtE" kind="show" identifier="openHistoryGraph" id="WaU-6H-sQq"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@@ -513,7 +526,9 @@
<constraint firstItem="7j2-Cq-PoN" firstAttribute="top" secondItem="qKa-BE-rae" secondAttribute="bottom" constant="4" id="nXo-mX-etp"/>
<constraint firstItem="Q4o-w1-JdP" firstAttribute="leading" secondItem="Eh7-ay-CB3" secondAttribute="leading" constant="16" id="pB7-gv-T5z"/>
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Q4o-w1-JdP" secondAttribute="trailing" constant="16" id="piC-TD-4GF"/>
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Mlm-Lp-0uP" secondAttribute="trailing" constant="8" id="q1R-H2-CcR"/>
<constraint firstItem="Q4o-w1-JdP" firstAttribute="top" secondItem="Eh7-ay-CB3" secondAttribute="top" id="rDC-Kx-1JX"/>
<constraint firstItem="Mlm-Lp-0uP" firstAttribute="top" secondItem="qKa-BE-rae" secondAttribute="bottom" constant="4" id="us2-ib-CDk"/>
<constraint firstItem="Eh7-ay-CB3" firstAttribute="trailing" secondItem="Du3-iX-bbt" secondAttribute="trailing" constant="16" id="w0L-Tf-Bfd"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Eh7-ay-CB3"/>
@@ -541,6 +556,64 @@
</objects>
<point key="canvasLocation" x="3600.8000000000002" y="456.52173913043481"/>
</scene>
<!--History Graph View Controller-->
<scene sceneID="WkV-VI-zTb">
<objects>
<viewController id="sJg-Af-FtE" customClass="HistoryGraphViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="vle-Io-eDj">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t93-GQ-APW" customClass="Chart" customModule="SwiftChart">
<rect key="frame" x="0.0" y="98" width="375" height="569"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="identifier" value="lineChart"/>
<userDefinedRuntimeAttribute type="number" keyPath="lineWidth">
<real key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="labelColor">
<color key="value" red="0.0" green="0.32852089410000002" blue="0.57488495110000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="axesColor">
<color key="value" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="gridColor">
<color key="value" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tap for Value" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GVB-oH-Gbh">
<rect key="frame" x="8" y="64" width="100" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="mH7-T4-RSj"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="t93-GQ-APW" firstAttribute="bottom" secondItem="HhQ-pe-EoY" secondAttribute="bottom" id="4hl-L7-cV4"/>
<constraint firstItem="GVB-oH-Gbh" firstAttribute="top" secondItem="HhQ-pe-EoY" secondAttribute="top" id="JVy-0b-HuT"/>
<constraint firstItem="GVB-oH-Gbh" firstAttribute="leading" secondItem="HhQ-pe-EoY" secondAttribute="leading" constant="8" id="fgy-DC-coW"/>
<constraint firstItem="t93-GQ-APW" firstAttribute="leading" secondItem="HhQ-pe-EoY" secondAttribute="leading" id="gSt-Qz-p3K"/>
<constraint firstItem="t93-GQ-APW" firstAttribute="top" secondItem="GVB-oH-Gbh" secondAttribute="bottom" id="oYi-Rg-r61"/>
<constraint firstItem="t93-GQ-APW" firstAttribute="trailing" secondItem="HhQ-pe-EoY" secondAttribute="trailing" id="z6s-FI-MsI"/>
</constraints>
<viewLayoutGuide key="safeArea" id="HhQ-pe-EoY"/>
</view>
<connections>
<outlet property="label" destination="GVB-oH-Gbh" id="JZo-Wq-9Od"/>
<outlet property="leftConstraint" destination="fgy-DC-coW" id="Icg-I6-cP4"/>
<outlet property="lineChart" destination="t93-GQ-APW" id="HtP-Wq-xnC"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Add-tM-hJL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4352.8000000000002" y="456.52173913043481"/>
</scene>
<!--POCloud Data-->
<scene sceneID="CNC-dV-jZs">
<objects>