Added configuration page
Former-commit-id: 8ab1873678b8e4a56332da785cd6f943648f6afd
This commit is contained in:
parent
b88f260ebc
commit
de1706d67a
7 changed files with 176 additions and 106 deletions
26
src/se/koc/hal/PCConfigureHttpPage.java
Normal file
26
src/se/koc/hal/PCConfigureHttpPage.java
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package se.koc.hal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import zutil.io.file.FileUtil;
|
||||||
|
import zutil.net.http.HttpHeaderParser;
|
||||||
|
import zutil.net.http.HttpPage;
|
||||||
|
import zutil.net.http.HttpPrintStream;
|
||||||
|
import zutil.parser.Templator;
|
||||||
|
|
||||||
|
public class PCConfigureHttpPage implements HttpPage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void respond(HttpPrintStream out, HttpHeaderParser client_info,
|
||||||
|
Map<String, Object> session, Map<String, String> cookie,
|
||||||
|
Map<String, String> request) throws IOException {
|
||||||
|
|
||||||
|
Templator tmpl = new Templator(FileUtil.find("web-resource/configure.html"));
|
||||||
|
|
||||||
|
out.print(tmpl.compile());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -15,23 +15,32 @@ import zutil.net.http.HttpPage;
|
||||||
import zutil.net.http.HttpPrintStream;
|
import zutil.net.http.HttpPrintStream;
|
||||||
import zutil.parser.Templator;
|
import zutil.parser.Templator;
|
||||||
|
|
||||||
public class PowerChallengeHttpPage implements HttpPage {
|
public class PCOverviewHttpPage implements HttpPage {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void respond(HttpPrintStream out, HttpHeaderParser client_info, Map<String, Object> session, Map<String, String> cookie, Map<String, String> request) throws IOException {
|
public void respond(HttpPrintStream out, HttpHeaderParser client_info, Map<String, Object> session, Map<String, String> cookie, Map<String, String> request) throws IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ArrayList<PowerData> minDataList = PowerChallenge.db.exec(
|
ArrayList<PowerData> minDataList = PowerChallenge.db.exec(
|
||||||
"SELECT * FROM sensor_data_aggr "
|
"SELECT user.username as username, sensor_data_aggr.timestamp_start as timestamp, sensor_data_aggr.data as data "
|
||||||
+ "WHERE sensor_id == 1 AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.FIVE_MINUTES_IN_MS-1),
|
+ "FROM sensor_data_aggr, user, sensor "
|
||||||
|
+ "WHERE sensor.id = sensor_data_aggr.sensor_id "
|
||||||
|
+ "AND user.id = sensor.user_id "
|
||||||
|
+ "AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.FIVE_MINUTES_IN_MS-1),
|
||||||
new SQLPowerDataBuilder());
|
new SQLPowerDataBuilder());
|
||||||
ArrayList<PowerData> hourDataList = PowerChallenge.db.exec(
|
ArrayList<PowerData> hourDataList = PowerChallenge.db.exec(
|
||||||
"SELECT * FROM sensor_data_aggr "
|
"SELECT user.username as username, sensor_data_aggr.timestamp_start as timestamp, sensor_data_aggr.data as data "
|
||||||
+ "WHERE sensor_id == 1 AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.HOUR_IN_MS-1),
|
+ "FROM sensor_data_aggr, user, sensor "
|
||||||
|
+ "WHERE sensor.id = sensor_data_aggr.sensor_id "
|
||||||
|
+ "AND user.id = sensor.user_id "
|
||||||
|
+ "AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.HOUR_IN_MS-1),
|
||||||
new SQLPowerDataBuilder());
|
new SQLPowerDataBuilder());
|
||||||
ArrayList<PowerData> dayDataList = PowerChallenge.db.exec(
|
ArrayList<PowerData> dayDataList = PowerChallenge.db.exec(
|
||||||
"SELECT * FROM sensor_data_aggr "
|
"SELECT user.username as username, sensor_data_aggr.timestamp_start as timestamp, sensor_data_aggr.data as data "
|
||||||
+ "WHERE sensor_id == 1 AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.DAY_IN_MS-1),
|
+ "FROM sensor_data_aggr, user, sensor "
|
||||||
|
+ "WHERE sensor.id = sensor_data_aggr.sensor_id "
|
||||||
|
+ "AND user.id = sensor.user_id "
|
||||||
|
+ "AND timestamp_end-timestamp_start == " + (DataAggregatorDaemon.DAY_IN_MS-1),
|
||||||
new SQLPowerDataBuilder());
|
new SQLPowerDataBuilder());
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,7 +48,7 @@ public class PowerChallengeHttpPage implements HttpPage {
|
||||||
tmpl.set("minData", minDataList);
|
tmpl.set("minData", minDataList);
|
||||||
tmpl.set("hourData", hourDataList);
|
tmpl.set("hourData", hourDataList);
|
||||||
tmpl.set("dayData", dayDataList);
|
tmpl.set("dayData", dayDataList);
|
||||||
tmpl.set("username", "Ziver");
|
tmpl.set("username", new String[]{"Ziver", "Daniel"});
|
||||||
|
|
||||||
out.print(tmpl.compile());
|
out.print(tmpl.compile());
|
||||||
|
|
||||||
|
|
@ -51,9 +60,11 @@ public class PowerChallengeHttpPage implements HttpPage {
|
||||||
public static class PowerData{
|
public static class PowerData{
|
||||||
long timestamp;
|
long timestamp;
|
||||||
int data;
|
int data;
|
||||||
public PowerData(long time, int data) {
|
String username;
|
||||||
|
public PowerData(long time, int data, String uname) {
|
||||||
this.timestamp = time;
|
this.timestamp = time;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.username = uname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +73,7 @@ public class PowerChallengeHttpPage implements HttpPage {
|
||||||
public ArrayList<PowerData> handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
public ArrayList<PowerData> handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||||
ArrayList<PowerData> list = new ArrayList<>();
|
ArrayList<PowerData> list = new ArrayList<>();
|
||||||
while(result.next()){
|
while(result.next()){
|
||||||
list.add(new PowerData(result.getLong("timestamp_start"), result.getInt("data")));
|
list.add(new PowerData(result.getLong("timestamp"), result.getInt("data"), result.getString("username")));
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,8 @@ public class PowerChallenge {
|
||||||
|
|
||||||
HttpServer http = new HttpServer(8080);
|
HttpServer http = new HttpServer(8080);
|
||||||
http.setDefaultPage(new HttpFilePage(FileUtil.find("web-resource/")));
|
http.setDefaultPage(new HttpFilePage(FileUtil.find("web-resource/")));
|
||||||
http.setPage("/", new PowerChallengeHttpPage());
|
http.setPage("/", new PCOverviewHttpPage());
|
||||||
|
http.setPage("/configure", new PCConfigureHttpPage());
|
||||||
http.start();
|
http.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ package se.koc.hal.deamon;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
@ -33,41 +35,61 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run(){
|
||||||
|
try {
|
||||||
|
List<Integer> sensorIdList = PowerChallenge.db.exec("SELECT id FROM sensor", new SQLResultHandler<List<Integer>>(){
|
||||||
|
@Override
|
||||||
|
public List<Integer> handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||||
|
ArrayList<Integer> list = new ArrayList<>();
|
||||||
|
while(result.next()){
|
||||||
|
list.add(result.getInt("id"));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(int id : sensorIdList){
|
||||||
|
logger.fine("Aggregating sensor_id: " + id);
|
||||||
|
aggregateSensor(id);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void aggregateSensor(int sensorId) {
|
||||||
DBConnection db = PowerChallenge.db;
|
DBConnection db = PowerChallenge.db;
|
||||||
try {
|
try {
|
||||||
Long maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr", new SimpleSQLHandler<Long>());
|
Long maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr WHERE sensor_id == "+sensorId, new SimpleSQLHandler<Long>());
|
||||||
if(maxDBTimestamp == null)
|
if(maxDBTimestamp == null)
|
||||||
maxDBTimestamp = 0l;
|
maxDBTimestamp = 0l;
|
||||||
// 5 minute aggregation
|
// 5 minute aggregation
|
||||||
long minPeriodTimestamp = getTimestampMinutePeriodStart(5, System.currentTimeMillis());
|
long minPeriodTimestamp = getTimestampMinutePeriodStart(5, System.currentTimeMillis());
|
||||||
logger.fine("Calculating 5 min periods... (from:"+ maxDBTimestamp +", to:"+ minPeriodTimestamp +")");
|
logger.fine("Calculating 5 min periods... (from:"+ maxDBTimestamp +", to:"+ minPeriodTimestamp +")");
|
||||||
db.exec("SELECT * FROM sensor_data_raw "
|
db.exec("SELECT * FROM sensor_data_raw "
|
||||||
+ "WHERE timestamp > " + maxDBTimestamp + " AND timestamp < " + minPeriodTimestamp
|
+ "WHERE sensor_id == "+sensorId+" AND timestamp > " + maxDBTimestamp + " AND timestamp < " + minPeriodTimestamp
|
||||||
+ " ORDER BY timestamp ASC",
|
+ " ORDER BY timestamp ASC",
|
||||||
new FiveMinuteAggregator());
|
new FiveMinuteAggregator());
|
||||||
|
|
||||||
// hour aggregation
|
// hour aggregation
|
||||||
maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr WHERE timestamp_end-timestamp_start == " + (HOUR_IN_MS-1), new SimpleSQLHandler<Long>());
|
maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr WHERE sensor_id == "+sensorId+" AND timestamp_end-timestamp_start == " + (HOUR_IN_MS-1), new SimpleSQLHandler<Long>());
|
||||||
if(maxDBTimestamp == null)
|
if(maxDBTimestamp == null)
|
||||||
maxDBTimestamp = 0l;
|
maxDBTimestamp = 0l;
|
||||||
long hourPeriodTimestamp = getTimestampMinutePeriodStart(60, System.currentTimeMillis()-HOUR_AGGREGATION_OFFSET);
|
long hourPeriodTimestamp = getTimestampMinutePeriodStart(60, System.currentTimeMillis()-HOUR_AGGREGATION_OFFSET);
|
||||||
logger.fine("Calculating hour periods... (from:"+ maxDBTimestamp +", to:"+ hourPeriodTimestamp +")");
|
logger.fine("Calculating hour periods... (from:"+ maxDBTimestamp +", to:"+ hourPeriodTimestamp +")");
|
||||||
db.exec("SELECT * FROM sensor_data_aggr "
|
db.exec("SELECT * FROM sensor_data_aggr "
|
||||||
+ "WHERE " + maxDBTimestamp + " < timestamp_start AND timestamp_start < " + hourPeriodTimestamp + " AND timestamp_end-timestamp_start == " + (FIVE_MINUTES_IN_MS-1)
|
+ "WHERE sensor_id == "+sensorId+" AND " + maxDBTimestamp + " < timestamp_start AND timestamp_start < " + hourPeriodTimestamp + " AND timestamp_end-timestamp_start == " + (FIVE_MINUTES_IN_MS-1)
|
||||||
+" ORDER BY timestamp_start ASC",
|
+" ORDER BY timestamp_start ASC",
|
||||||
new HourAggregator());
|
new HourAggregator());
|
||||||
|
|
||||||
// day aggregation
|
// day aggregation
|
||||||
maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr WHERE timestamp_end-timestamp_start == " + (DAY_IN_MS-1), new SimpleSQLHandler<Long>());
|
maxDBTimestamp = db.exec("SELECT MAX(timestamp_end) FROM sensor_data_aggr WHERE sensor_id == "+sensorId+" AND timestamp_end-timestamp_start == " + (DAY_IN_MS-1), new SimpleSQLHandler<Long>());
|
||||||
if(maxDBTimestamp == null)
|
if(maxDBTimestamp == null)
|
||||||
maxDBTimestamp = 0l;
|
maxDBTimestamp = 0l;
|
||||||
long dayPeriodTimestamp = getTimestampHourPeriodStart(24, System.currentTimeMillis()-DAY_AGGREGATION_OFFSET);
|
long dayPeriodTimestamp = getTimestampHourPeriodStart(24, System.currentTimeMillis()-DAY_AGGREGATION_OFFSET);
|
||||||
logger.fine("Calculating day periods... (from:"+ maxDBTimestamp +", to:"+ dayPeriodTimestamp +")");
|
logger.fine("Calculating day periods... (from:"+ maxDBTimestamp +", to:"+ dayPeriodTimestamp +")");
|
||||||
db.exec("SELECT * FROM sensor_data_aggr "
|
db.exec("SELECT * FROM sensor_data_aggr "
|
||||||
+ "WHERE " + maxDBTimestamp + " < timestamp_start AND timestamp_start < " + dayPeriodTimestamp + " AND timestamp_end-timestamp_start == " + (HOUR_IN_MS-1)
|
+ "WHERE sensor_id == "+sensorId+" AND " + maxDBTimestamp + " < timestamp_start AND timestamp_start < " + dayPeriodTimestamp + " AND timestamp_end-timestamp_start == " + (HOUR_IN_MS-1)
|
||||||
+" ORDER BY timestamp_start ASC",
|
+" ORDER BY timestamp_start ASC",
|
||||||
new DayAggregator());
|
new DayAggregator());
|
||||||
|
|
||||||
|
|
|
||||||
54
web-resource/configure.html
Normal file
54
web-resource/configure.html
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="">
|
||||||
|
|
||||||
|
<title>Power;Challenge</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="css/main.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
|
||||||
|
<script src="js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<a class="navbar-brand" href="#">Power;Challenge</a>
|
||||||
|
</div>
|
||||||
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li><a href="#">Ziver</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-md-2 sidebar">
|
||||||
|
<ul class="nav nav-sidebar">
|
||||||
|
<li><a href="/">Overview</a></li>
|
||||||
|
<li><a href="#">Heat Map</a></li>
|
||||||
|
<li><a href="#">Statistics</a></li>
|
||||||
|
<li class="active"><a href="configure">Configuration <span class="sr-only">(current)</span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||||
|
<h1 class="page-header">Configuration</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -90,7 +90,7 @@ body {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.placeholders {
|
.placeholders {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 200px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.placeholders h4 {
|
.placeholders h4 {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
|
||||||
<title>Dashboard Template for Bootstrap</title>
|
<title>Power;Challenge</title>
|
||||||
|
|
||||||
<!-- Bootstrap core CSS -->
|
<!-- Bootstrap core CSS -->
|
||||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
@ -19,55 +19,36 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
Morris.Line({
|
chartData("min-power-chart",
|
||||||
element: 'min-power-chart',
|
[{{#minData}}
|
||||||
data: [
|
{ y: {{.timestamp}}, {{.username}}: {{.data}}/1000 },
|
||||||
{{#minData}}
|
{{/minData}}]
|
||||||
{ y: {{.timestamp}}, a: {{.data}} },
|
);
|
||||||
{{/minData}}
|
chartData("hour-power-chart",
|
||||||
],
|
[{{#hourData}}
|
||||||
xkey: 'y',
|
{ y: {{.timestamp}}, {{.username}}: {{.data}}/1000 },
|
||||||
ykeys: ['a'],
|
{{/hourData}}]
|
||||||
labels: [
|
);
|
||||||
'{{username}}'
|
chartData("day-power-chart",
|
||||||
],
|
[{{#dayData}}
|
||||||
continuousLine: false,
|
{ y: {{.timestamp}}, {{.username}}: {{.data}}/1000 },
|
||||||
pointSize: 0,
|
{{/dayData}}]
|
||||||
postUnits: 'Wh'
|
);
|
||||||
});
|
|
||||||
Morris.Line({
|
|
||||||
element: 'hour-power-chart',
|
|
||||||
data: [
|
|
||||||
{{#hourData}}
|
|
||||||
{ y: {{.timestamp}}, a: {{.data}} },
|
|
||||||
{{/hourData}}
|
|
||||||
],
|
|
||||||
xkey: 'y',
|
|
||||||
ykeys: ['a'],
|
|
||||||
labels: [
|
|
||||||
'{{username}}'
|
|
||||||
],
|
|
||||||
continuousLine: false,
|
|
||||||
pointSize: 0,
|
|
||||||
postUnits: 'Wh'
|
|
||||||
});
|
|
||||||
Morris.Line({
|
|
||||||
element: 'day-power-chart',
|
|
||||||
data: [
|
|
||||||
{{#dayData}}
|
|
||||||
{ y: {{.timestamp}}, a: {{.data}} },
|
|
||||||
{{/dayData}}
|
|
||||||
],
|
|
||||||
xkey: 'y',
|
|
||||||
ykeys: ['a'],
|
|
||||||
labels: [
|
|
||||||
'{{username}}'
|
|
||||||
],
|
|
||||||
continuousLine: false,
|
|
||||||
pointSize: 0,
|
|
||||||
postUnits: 'Wh'
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
var userArray = [ {{#username}} '{{.}}', {{/username}} ];
|
||||||
|
function chartData(elementId, data){
|
||||||
|
Morris.Line({
|
||||||
|
element: elementId,
|
||||||
|
data: data,
|
||||||
|
xkey: 'y',
|
||||||
|
ykeys: userArray,
|
||||||
|
labels: userArray,
|
||||||
|
continuousLine: false,
|
||||||
|
pointSize: 0,
|
||||||
|
postUnits: 'kWh',
|
||||||
|
resize: true
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -91,53 +72,28 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-md-2 sidebar">
|
<div class="col-sm-3 col-md-2 sidebar">
|
||||||
<ul class="nav nav-sidebar">
|
<ul class="nav nav-sidebar">
|
||||||
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
|
<li class="active"><a href="/">Overview <span class="sr-only">(current)</span></a></li>
|
||||||
<li><a href="#">Reports</a></li>
|
<li><a href="#">Heat Map</a></li>
|
||||||
<li><a href="#">Analytics</a></li>
|
<li><a href="#">Statistics</a></li>
|
||||||
<li><a href="#">Export</a></li>
|
<li><a href="configure">Configuration</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||||
<h1 class="page-header">Dashboard</h1>
|
<h1 class="page-header">Overview</h1>
|
||||||
|
|
||||||
<div class="row placeholders">
|
<div class="row placeholders">
|
||||||
<H2>Last 24 hours</H2>
|
<H1>Last 24 hours</H1>
|
||||||
<div id="min-power-chart"></div>
|
<div id="min-power-chart" style="height:450px;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row placeholders">
|
<div class="row placeholders">
|
||||||
<H2>Previous two days</H2>
|
<H1>Previous two days</H1>
|
||||||
<div id="hour-power-chart"></div>
|
<div id="hour-power-chart" style="height:450px;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row placeholders">
|
<div class="row placeholders">
|
||||||
<H2>Long term</H2>
|
<H1>Long term</H1>
|
||||||
<div id="day-power-chart"></div>
|
<div id="day-power-chart" style="height:450px;"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="sub-header">Section title</h2>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>Header</th>
|
|
||||||
<th>Header</th>
|
|
||||||
<th>Header</th>
|
|
||||||
<th>Header</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>1,001</td>
|
|
||||||
<td>Lorem</td>
|
|
||||||
<td>ipsum</td>
|
|
||||||
<td>dolor</td>
|
|
||||||
<td>sit</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue