278 lines
6.5 KiB
Go
278 lines
6.5 KiB
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/zuadi/tecamino-dbm/utils"
|
|
)
|
|
|
|
type Datapoint struct {
|
|
Datapoints map[string]*Datapoint `json:"-"`
|
|
Path string `json:"path"`
|
|
Value any `json:"value,omitempty"`
|
|
CreateDateTime int64 `json:"createDateTime,omitempty"`
|
|
UpdateDateTime int64 `json:"updateDateTime,omitempty"`
|
|
Type Type `json:"type"`
|
|
ReadWrite Rights `json:"readWrite"`
|
|
Drivers map[string]*Driver `json:"-"`
|
|
Subscribtions Subscribtions `json:"-"`
|
|
}
|
|
|
|
func (d *Datapoint) CreateDatapoint(typ Type, value any, rights Rights, path string) error {
|
|
parts := regexp.MustCompile(`[:]+`).Split(path, -1)
|
|
|
|
current := d
|
|
for i, part := range parts {
|
|
if current.Datapoints == nil {
|
|
current.Datapoints = make(map[string]*Datapoint)
|
|
}
|
|
|
|
if i == len(parts)-1 {
|
|
// Leaf node: create or update datapoint
|
|
if existing, ok := current.Datapoints[part]; ok {
|
|
// Update existing
|
|
existing.Type = typ
|
|
existing.ReadWrite = rights.GetRights()
|
|
existing.Value = typ.ConvertValue(value)
|
|
existing.UpdateDateTime = time.Now().UnixMilli()
|
|
} else {
|
|
// Create new
|
|
current.Datapoints[part] = &Datapoint{
|
|
Path: strings.Join(parts, ":"),
|
|
Type: typ,
|
|
Value: typ.ConvertValue(value),
|
|
ReadWrite: rights.GetRights(),
|
|
CreateDateTime: time.Now().UnixMilli(),
|
|
UpdateDateTime: time.Now().UnixMilli(),
|
|
Subscribtions: InitSubscribtion(),
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Traverse or create intermediate datapoints
|
|
if next, ok := current.Datapoints[part]; ok {
|
|
current = next
|
|
} else {
|
|
newDp := &Datapoint{
|
|
Path: strings.Join(parts[:i+1], ":"),
|
|
Type: NONE,
|
|
ReadWrite: rights.GetRights(),
|
|
CreateDateTime: time.Now().UnixMilli(),
|
|
UpdateDateTime: time.Now().UnixMilli(),
|
|
Subscribtions: InitSubscribtion(),
|
|
}
|
|
current.Datapoints[part] = newDp
|
|
current = newDp
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Datapoint) ImportDatapoint(dp *Datapoint, path string) error {
|
|
parts := regexp.MustCompile(`[:]+`).Split(path, -1)
|
|
|
|
current := d
|
|
for i, part := range parts {
|
|
if current.Datapoints == nil {
|
|
current.Datapoints = make(map[string]*Datapoint)
|
|
}
|
|
|
|
if i == len(parts)-1 {
|
|
// Leaf node: import the datapoint
|
|
if existing, ok := current.Datapoints[part]; ok {
|
|
existing.Type = dp.Type
|
|
existing.Value = current.Type.ConvertValue(dp.Value)
|
|
existing.ReadWrite = dp.ReadWrite.GetRights()
|
|
existing.UpdateDateTime = time.Now().UnixMilli()
|
|
} else {
|
|
dp.Path = strings.Join(parts, ":")
|
|
dp.ReadWrite = dp.ReadWrite.GetRights()
|
|
dp.UpdateDateTime = time.Now().UnixMilli()
|
|
dp.Subscribtions = InitSubscribtion()
|
|
current.Datapoints[part] = dp
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Traverse or create intermediate nodes
|
|
if next, ok := current.Datapoints[part]; ok {
|
|
current = next
|
|
} else {
|
|
newDp := &Datapoint{
|
|
Path: strings.Join(parts[:i+1], ":"),
|
|
Type: NONE,
|
|
ReadWrite: dp.ReadWrite.GetRights(),
|
|
UpdateDateTime: time.Now().UnixMilli(),
|
|
}
|
|
newDp.ReadWrite = newDp.ReadWrite.GetRights()
|
|
current.Datapoints[part] = newDp
|
|
current = newDp
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Datapoint) UpdateDatapointValue(value any, path string) error {
|
|
|
|
paths := regexp.MustCompile(`[:]+`).Split(path, -1)
|
|
|
|
current := d
|
|
for i, part := range paths {
|
|
dp, ok := current.Datapoints[part]
|
|
if !ok {
|
|
return fmt.Errorf("datapoint path not found: %s (at %s)", path, part)
|
|
}
|
|
if i == len(paths)-1 {
|
|
dp.Value = dp.Type.ConvertValue(value)
|
|
dp.UpdateDateTime = time.Now().UnixMilli()
|
|
return nil
|
|
}
|
|
current = dp
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Datapoint) RemoveDatapoint(path string) error {
|
|
parts := regexp.MustCompile(`[:]+`).Split(path, -1)
|
|
|
|
if len(parts) < 1 {
|
|
return fmt.Errorf("invalid path: '%s'", path)
|
|
}
|
|
|
|
current := d
|
|
for i := range len(parts) - 1 {
|
|
next, ok := current.Datapoints[parts[i]]
|
|
if !ok {
|
|
return fmt.Errorf("path not found: '%s'", strings.Join(parts[:i+1], ":"))
|
|
}
|
|
current = next
|
|
}
|
|
|
|
toDelete := parts[len(parts)-1]
|
|
if _, ok := current.Datapoints[toDelete]; ok {
|
|
delete(current.Datapoints, toDelete)
|
|
fmt.Println("Removed datapoint:", path)
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("datapoint '%s' not found", path)
|
|
}
|
|
|
|
func (d *Datapoint) GetAllDatapoints(depth int) (dps []*Datapoint) {
|
|
|
|
var dfs func(dp *Datapoint, currentDepth int)
|
|
dfs = func(dp *Datapoint, currentDepth int) {
|
|
if currentDepth == 0 {
|
|
dps = append(dps, dp)
|
|
}
|
|
|
|
if depth == 1 {
|
|
return
|
|
} else if depth == 0 {
|
|
// Return all descendants
|
|
for _, child := range dp.Datapoints {
|
|
dps = append(dps, child)
|
|
dfs(child, currentDepth+1)
|
|
}
|
|
return
|
|
}
|
|
|
|
if currentDepth == depth-1 {
|
|
return
|
|
}
|
|
|
|
for _, child := range dp.Datapoints {
|
|
dfs(child, currentDepth+1)
|
|
}
|
|
}
|
|
|
|
dfs(d, 0)
|
|
return
|
|
}
|
|
|
|
func (d *Datapoint) QueryDatapoints(depth int, path string) (dps []*Datapoint) {
|
|
parts := strings.Split(path, ":")
|
|
|
|
var dfs func(current *Datapoint, index int)
|
|
dfs = func(current *Datapoint, index int) {
|
|
|
|
if index == len(parts) {
|
|
dps = append(dps, current.GetAllDatapoints(depth)...)
|
|
return
|
|
}
|
|
|
|
pattern := "^" + parts[index] + "$"
|
|
re, err := regexp.Compile(pattern)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for name, dp := range current.Datapoints {
|
|
if re.MatchString(name) {
|
|
dfs(dp, index+1)
|
|
}
|
|
}
|
|
}
|
|
|
|
dfs(d, 0)
|
|
return
|
|
}
|
|
|
|
func (d *Datapoint) AddSubscribtion(id string, sub *Subscribe) {
|
|
if d.Subscribtions == nil {
|
|
return
|
|
}
|
|
|
|
if s, ok := d.Subscribtions[id]; ok {
|
|
s.OnCreate = sub.OnCreate
|
|
s.OnChange = sub.OnChange
|
|
s.OnDelete = sub.OnDelete
|
|
} else {
|
|
d.Subscribtions[id] = &Subscribtion{
|
|
OnCreate: sub.OnCreate,
|
|
OnChange: sub.OnChange,
|
|
OnDelete: sub.OnDelete,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *Datapoint) RemoveSubscribtion(id string) {
|
|
if _, ok := d.Subscribtions[id]; !ok {
|
|
return
|
|
}
|
|
delete(d.Subscribtions, id)
|
|
}
|
|
|
|
func (d *Datapoint) AddDriver(driver, bus string, adr int) {
|
|
if d.Drivers == nil {
|
|
d.Drivers = make(map[string]*Driver)
|
|
}
|
|
|
|
d.Drivers[driver] = &Driver{
|
|
Bus: bus,
|
|
Address: adr,
|
|
}
|
|
}
|
|
|
|
func (d *Datapoint) AddDriverSubscribtion(id string, sub *Subscribe) {
|
|
if s, ok := d.Subscribtions[id]; ok {
|
|
s.OnCreate = sub.OnCreate
|
|
s.OnChange = sub.OnChange
|
|
s.OnDelete = sub.OnDelete
|
|
} else {
|
|
d.Subscribtions[id] = &Subscribtion{
|
|
OnCreate: sub.OnCreate,
|
|
OnChange: sub.OnChange,
|
|
OnDelete: sub.OnDelete,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *Datapoint) GetValueUint64() uint64 {
|
|
return utils.Uint64From(d.Value)
|
|
}
|