Better interactive graph support.
This commit is contained in:
parent
16b4bd53f4
commit
50664ca965
32
main.go
32
main.go
@ -138,6 +138,7 @@ func main() { // {{{
|
|||||||
service.Register("/datapoint/update/{id}", false, false, actionDatapointUpdate)
|
service.Register("/datapoint/update/{id}", false, false, actionDatapointUpdate)
|
||||||
service.Register("/datapoint/delete/{id}", false, false, actionDatapointDelete)
|
service.Register("/datapoint/delete/{id}", false, false, actionDatapointDelete)
|
||||||
service.Register("/datapoint/values/{id}", false, false, pageDatapointValues)
|
service.Register("/datapoint/values/{id}", false, false, pageDatapointValues)
|
||||||
|
service.Register("/datapoint/json/{id}", false, false, actionDatapointJson)
|
||||||
|
|
||||||
service.Register("/triggers", false, false, pageTriggers)
|
service.Register("/triggers", false, false, pageTriggers)
|
||||||
service.Register("/trigger/create/{sectionID}/{name}", false, false, actionTriggerCreate)
|
service.Register("/trigger/create/{sectionID}/{name}", false, false, actionTriggerCreate)
|
||||||
@ -750,6 +751,37 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
page.Render(w, r)
|
page.Render(w, r)
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
func actionDatapointJson(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
||||||
|
idStr := r.PathValue("id")
|
||||||
|
id, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStr := r.URL.Query().Get("f")
|
||||||
|
from, err := time.ParseInLocation("2006-01-02 15:04:05", fromStr[0:min(19, len(fromStr))], smonConfig.Timezone())
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toStr := r.URL.Query().Get("t")
|
||||||
|
to, err := time.ParseInLocation("2006-01-02 15:04:05", toStr[0:min(19, len(toStr))], smonConfig.Timezone())
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values, err := DatapointValues(id, from, to)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
j, _ := json.Marshal(values)
|
||||||
|
w.Write(j)
|
||||||
|
} // }}}
|
||||||
|
|
||||||
func pageTriggers(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
func pageTriggers(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
||||||
areas, err := TriggersRetrieve()
|
areas, err := TriggersRetrieve()
|
||||||
|
1
sql/00021.sql
Normal file
1
sql/00021.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE INDEX datapoint_value_ts_idx ON public.datapoint_value (ts);
|
73
static/js/datapoint_values.js
Normal file
73
static/js/datapoint_values.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
function offsetTime(seconds) {
|
||||||
|
const el = document.getElementById('offset-time')
|
||||||
|
el.value = seconds
|
||||||
|
el.form.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Graph {
|
||||||
|
constructor(datapointID, initialData) {
|
||||||
|
this.dataset = new Dataset(datapointID, initialData)
|
||||||
|
|
||||||
|
this.createGraph()
|
||||||
|
}
|
||||||
|
|
||||||
|
async createGraph() {
|
||||||
|
this.graphValues = document.getElementById('graph-values');
|
||||||
|
|
||||||
|
const values = [{
|
||||||
|
x: this.dataset.xValues(),
|
||||||
|
y: this.dataset.yValues(),
|
||||||
|
}]
|
||||||
|
|
||||||
|
this.layout = {
|
||||||
|
margin: {
|
||||||
|
t: 24,
|
||||||
|
r: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Plotly.react(this.graphValues, values, this.layout);
|
||||||
|
this.graphValues.on('plotly_relayout', attr => this.relayoutHandler(attr))
|
||||||
|
}
|
||||||
|
|
||||||
|
async relayoutHandler(attr) {
|
||||||
|
if (!attr.hasOwnProperty('xaxis.range[0]') || !attr.hasOwnProperty('xaxis.range[1]'))
|
||||||
|
return
|
||||||
|
|
||||||
|
this.dataset.extend(attr['xaxis.range[0]'], attr['xaxis.range[1]'])
|
||||||
|
.then(() => {
|
||||||
|
const values = [{
|
||||||
|
x: this.dataset.xValues(),
|
||||||
|
y: this.dataset.yValues(),
|
||||||
|
}]
|
||||||
|
Plotly.react(this.graphValues, values, this.layout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dataset {
|
||||||
|
constructor(id, initialData) {
|
||||||
|
this.datapointID = id
|
||||||
|
this.values = {}
|
||||||
|
initialData.forEach(v=>this.values[v.ID] = v)
|
||||||
|
}
|
||||||
|
|
||||||
|
xValues() {
|
||||||
|
return Object.keys(this.values).map(dpID => this.values[dpID].Ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
yValues() {
|
||||||
|
return Object.keys(this.values).map(dpID => this.values[dpID].ValueInt.Int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
async extend(from, to) {
|
||||||
|
return fetch(`/datapoint/json/${this.datapointID}?f=${from}&t=${to}`)
|
||||||
|
.then(data => data.json())
|
||||||
|
.then(datapointValues => {
|
||||||
|
datapointValues.forEach(dp=>{
|
||||||
|
this.values[dp.ID] = dp
|
||||||
|
})
|
||||||
|
document.getElementById('num-values').innerText = Object.keys(this.values).length
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
{{ block "page_label" . }}{{end}}
|
{{ block "page_label" . }}{{end}}
|
||||||
|
|
||||||
<form action="/datapoint/values/{{ .Data.Datapoint.ID }}" method="get">
|
<form action="/datapoint/values/{{ .Data.Datapoint.ID }}" method="get">
|
||||||
|
|
||||||
{{ if eq .Data.Datapoint.Datatype "INT" }}
|
{{ if eq .Data.Datapoint.Datatype "INT" }}
|
||||||
<div>
|
<div>
|
||||||
<input name="display" value="graph" type="radio" id="display-graph" {{ if $graph }} checked {{ end}}> <label for="display-graph">Graph</label>
|
<input name="display" value="graph" type="radio" id="display-graph" {{ if $graph }} checked {{ end}}> <label for="display-graph">Graph</label>
|
||||||
@ -17,6 +18,7 @@
|
|||||||
<input name="f" type="datetime-local" step="1" value="{{ .Data.TimeFrom }}">
|
<input name="f" type="datetime-local" step="1" value="{{ .Data.TimeFrom }}">
|
||||||
<input name="t" type="datetime-local" step="1" value="{{ .Data.TimeTo }}">
|
<input name="t" type="datetime-local" step="1" value="{{ .Data.TimeTo }}">
|
||||||
<button>OK</button>
|
<button>OK</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
@ -24,22 +26,17 @@
|
|||||||
<div class="graph">
|
<div class="graph">
|
||||||
<div id="graph-values"></div>
|
<div id="graph-values"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<b>Number of values:</b>
|
||||||
|
<span id="num-values">{{ len .Data.Values }}</span>
|
||||||
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
const graphValues = document.getElementById('graph-values');
|
new Graph(
|
||||||
let data = {{ .Data }}
|
{{ .Data.Datapoint.ID }},
|
||||||
let x = data.Values.map(d => d.Ts)
|
{{ .Data.Values }},
|
||||||
let y = data.Values.map(d => d.ValueInt.Int64)
|
)
|
||||||
Plotly.newPlot(
|
|
||||||
graphValues,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
},
|
|
||||||
], {
|
|
||||||
margin: { t: 0 },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div id="values">
|
<div id="values">
|
||||||
@ -48,7 +45,7 @@
|
|||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
{{ range .Data.Values }}
|
{{ range .Data.Values }}
|
||||||
<div class="value">{{ format_time .Ts }}</div>
|
<div class="value">{{ format_time .Ts }}</div>
|
||||||
<div class="value">{{ format_time .Value }}</div>
|
<div class="value">{{ .Value }}</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
Loading…
Reference in New Issue
Block a user