Added nodata code
This commit is contained in:
parent
b22e99a072
commit
1185ebd030
64
datapoint.go
64
datapoint.go
@ -21,14 +21,16 @@ const (
|
||||
)
|
||||
|
||||
type Datapoint struct {
|
||||
ID int
|
||||
Group string
|
||||
Name string
|
||||
Datatype DatapointType
|
||||
LastValue time.Time `db:"last_value"`
|
||||
DatapointValueJSON []byte `db:"datapoint_value_json"`
|
||||
LastDatapointValue DatapointValue
|
||||
Found bool
|
||||
ID int
|
||||
Group string
|
||||
Name string
|
||||
Datatype DatapointType
|
||||
LastValue time.Time `db:"last_value"`
|
||||
DatapointValueJSON []byte `db:"datapoint_value_json"`
|
||||
LastDatapointValue DatapointValue
|
||||
Found bool
|
||||
NodataProblemSeconds int `db:"nodata_problem_seconds"`
|
||||
NodataIsProblem bool `db:"nodata_is_problem"`
|
||||
}
|
||||
|
||||
type DatapointValue struct {
|
||||
@ -56,13 +58,13 @@ func (dp DatapointValue) Value() any { // {{{
|
||||
|
||||
return nil
|
||||
} // }}}
|
||||
func (dp DatapointValue) FormattedTime() string {// {{{
|
||||
func (dp DatapointValue) FormattedTime() string { // {{{
|
||||
if dp.ValueDateTime.Valid {
|
||||
return dp.ValueDateTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return "invalid time"
|
||||
}// }}}
|
||||
func (dp Datapoint) Update() (err error) {// {{{
|
||||
} // }}}
|
||||
func (dp Datapoint) Update() (err error) { // {{{
|
||||
name := strings.TrimSpace(dp.Name)
|
||||
if name == "" {
|
||||
err = errors.New("Name can't be empty")
|
||||
@ -71,23 +73,47 @@ func (dp Datapoint) Update() (err error) {// {{{
|
||||
|
||||
if dp.ID == 0 {
|
||||
_, err = service.Db.Conn.Exec(
|
||||
`INSERT INTO datapoint("group", name, datatype) VALUES($1, $2, $3)`,
|
||||
`INSERT INTO datapoint("group", name, datatype) VALUES($1, $2, $3, $4)`,
|
||||
dp.Group,
|
||||
name,
|
||||
dp.Datatype,
|
||||
dp.NodataProblemSeconds,
|
||||
)
|
||||
} else {
|
||||
/* Keep nodata_is_problem as is unless the nodata_problem_seconds is changed.
|
||||
* Otherwise unnecessary nodata problems could be notified when updating unrelated
|
||||
* datapoint properties. */
|
||||
_, err = service.Db.Conn.Exec(
|
||||
`UPDATE datapoint SET "group"=$2, name=$3, datatype=$4 WHERE id=$1`,
|
||||
`
|
||||
UPDATE datapoint
|
||||
SET
|
||||
"group"=$2,
|
||||
name=$3,
|
||||
datatype=$4,
|
||||
nodata_problem_seconds=$5,
|
||||
nodata_is_problem = (
|
||||
CASE
|
||||
WHEN $5 != nodata_problem_seconds THEN false
|
||||
ELSE
|
||||
nodata_is_problem
|
||||
END
|
||||
)
|
||||
WHERE
|
||||
id=$1
|
||||
`,
|
||||
dp.ID,
|
||||
dp.Group,
|
||||
name,
|
||||
dp.Datatype,
|
||||
dp.NodataProblemSeconds,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
}
|
||||
|
||||
return
|
||||
}// }}}
|
||||
} // }}}
|
||||
|
||||
func DatapointAdd[T any](name string, value T) (err error) { // {{{
|
||||
row := service.Db.Conn.QueryRow(`SELECT id, datatype FROM datapoint WHERE name=$1`, name)
|
||||
@ -120,7 +146,7 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
|
||||
return
|
||||
}
|
||||
|
||||
service.Db.Conn.Exec(`UPDATE datapoint SET last_value = NOW() WHERE name=$1`, name)
|
||||
service.Db.Conn.Exec(`UPDATE datapoint SET last_value = NOW(), nodata_is_problem = false WHERE id=$1`, dpID)
|
||||
|
||||
return
|
||||
} // }}}
|
||||
@ -253,14 +279,14 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
|
||||
|
||||
return
|
||||
} // }}}
|
||||
func DatapointDelete(id int) (err error) {// {{{
|
||||
func DatapointDelete(id int) (err error) { // {{{
|
||||
_, err = service.Db.Conn.Exec(`DELETE FROM datapoint WHERE id=$1`, id)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err).WithData(id)
|
||||
}
|
||||
return
|
||||
}// }}}
|
||||
func DatapointValues(id int) (values []DatapointValue, err error) {// {{{
|
||||
} // }}}
|
||||
func DatapointValues(id int) (values []DatapointValue, err error) { // {{{
|
||||
rows, err := service.Db.Conn.Queryx(`SELECT * FROM datapoint_value WHERE datapoint_id=$1 ORDER BY ts DESC LIMIT 500`, id)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err).WithData(id)
|
||||
@ -278,4 +304,4 @@ func DatapointValues(id int) (values []DatapointValue, err error) {// {{{
|
||||
values = append(values, dpv)
|
||||
}
|
||||
return
|
||||
}// }}}
|
||||
} // }}}
|
||||
|
6
main.go
6
main.go
@ -140,6 +140,8 @@ func main() { // {{{
|
||||
service.Register("/configuration", false, false, pageConfiguration)
|
||||
service.Register("/entry/{datapoint}", false, false, entryDatapoint)
|
||||
|
||||
go nodataLoop()
|
||||
|
||||
err = service.Start()
|
||||
if err != nil {
|
||||
logger.Error("webserver", "error", werr.Wrap(err))
|
||||
@ -553,11 +555,15 @@ func pageDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
||||
return
|
||||
}
|
||||
|
||||
var nodataSeconds int
|
||||
nodataSeconds, _ = strconv.Atoi(r.FormValue("nodata_seconds"))
|
||||
|
||||
var dp Datapoint
|
||||
dp.ID = id
|
||||
dp.Group = r.FormValue("group")
|
||||
dp.Name = r.FormValue("name")
|
||||
dp.Datatype = DatapointType(r.FormValue("datatype"))
|
||||
dp.NodataProblemSeconds = nodataSeconds
|
||||
err = dp.Update()
|
||||
if err != nil {
|
||||
httpError(w, werr.Wrap(err).Log())
|
||||
|
75
nodata.go
Normal file
75
nodata.go
Normal file
@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// External
|
||||
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||
|
||||
// Standard
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
// nodataLoop checks if datapoint last_value is larger than the nodata_problem_seconds period and
|
||||
// marks them as problems. They are then notified.
|
||||
func nodataLoop() {
|
||||
var ids []int
|
||||
var err error
|
||||
|
||||
// TODO - should be configurable
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
for {
|
||||
<-ticker.C
|
||||
ids, err = nodataDatapointIDs()
|
||||
if err != nil {
|
||||
err = werr.Wrap(err).Log()
|
||||
logger.Error("nodata", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info("nodata", "problem_ids", ids)
|
||||
}
|
||||
}
|
||||
|
||||
func nodataDatapointIDs() (ids []int, err error) {
|
||||
ids = []int{}
|
||||
|
||||
var rows *sql.Rows
|
||||
rows, err = service.Db.Conn.Query(`
|
||||
UPDATE datapoint
|
||||
SET
|
||||
nodata_is_problem = true
|
||||
FROM (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
datapoint
|
||||
WHERE
|
||||
NOT nodata_is_problem AND
|
||||
extract(EPOCH from (NOW() - last_value))::int > nodata_problem_seconds
|
||||
) AS subquery
|
||||
WHERE
|
||||
datapoint.id = subquery.id
|
||||
RETURNING
|
||||
datapoint.id
|
||||
|
||||
`)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var id int
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(&id); err != nil {
|
||||
err = werr.Wrap(err)
|
||||
return
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
4
sql/00014.sql
Normal file
4
sql/00014.sql
Normal file
@ -0,0 +1,4 @@
|
||||
ALTER TABLE datapoint ADD COLUMN nodata_problem_seconds INT4 NOT NULL DEFAULT 0;
|
||||
ALTER TABLE datapoint ADD COLUMN nodata_is_problem BOOL NOT NULL DEFAULT false;
|
||||
|
||||
CREATE INDEX datapoint_last_value_idx ON public.datapoint ("last_value");
|
@ -25,6 +25,13 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="label">No data<br>problem time<br>(seconds)</div>
|
||||
<div>
|
||||
<input type="text" name="nodata_seconds" value="{{ .Data.Datapoint.NodataProblemSeconds }}">
|
||||
<div class="description">A problem is raised and notified if an entry isn't made within this time.</div>
|
||||
<div class="description">Set to 0 to disable.</div>
|
||||
</div>
|
||||
|
||||
<div></div>
|
||||
<div class="action">
|
||||
{{ if eq .Data.Datapoint.ID 0 }}
|
||||
|
Loading…
Reference in New Issue
Block a user