GoSungrow/iSolarCloud/api/struct_data.go
2022-04-29 16:36:18 +10:00

798 lines
18 KiB
Go

package api
import (
"GoSungrow/Only"
"GoSungrow/iSolarCloud/api/apiReflect"
"fmt"
"github.com/davecgh/go-spew/spew"
"reflect"
"strconv"
"time"
)
const (
PointTypeInstant = "instant"
PointTypeBoot = "boot"
PointTypeDaily = "daily"
PointTypeMonthly = "monthly"
PointTypeYearly = "yearly"
PointTypeTotal = "total"
)
type DataMap struct {
Entries map[string]DataEntry
Order []string
}
type DataEntry struct {
EndPoint string `json:"endpoint"`
Point *Point `json:"point"`
Date DateTime `json:"date"`
// Id string `json:"id"`
// GroupName string `json:"group_name"`
// Name string `json:"name"`
// Unit string `json:"unit"`
Value string `json:"value"`
ValueFloat float64 `json:"value_float"`
Index int `json:"index"`
}
func NewDataMap() DataMap {
return DataMap{ Entries: make(map[string]DataEntry)}
}
// func (dm *DataMap) Add(point string, entry DataEntry) {
// dm.Entries[point] = entry
// dm.Order = append(dm.Order, point)
// }
func (dm *DataMap) StructToPoints(endpoint string, ref interface{}) {
for range Only.Once {
if endpoint == "" {
endpoint = apiReflect.GetCallerPackage(2)
}
now := NewDateTime(time.Now().Round(5 * time.Minute).Format(DtLayoutZeroSeconds))
vo := reflect.ValueOf(ref)
to := reflect.TypeOf(ref)
// Iterate over all available fields and read the tag value
for i := 0; i < vo.NumField(); i++ {
fieldTo := to.Field(i)
valueTo := vo.Field(i).Interface()
spew.Dump(&fieldTo)
j := fieldTo.Tag.Get("json")
pid := fieldTo.Tag.Get("json")
if (j != "") && (pid == "") {
pid = j
} else if (pid != "") && (j == "") {
j = pid
}
name := fieldTo.Tag.Get("PointName")
if name == "" {
name = PointToName(pid)
}
device := fieldTo.Tag.Get("PointDevice")
if device == "" {
device = "virtual"
}
var ignore bool
if fieldTo.Tag.Get("PointIgnore") != "" {
ignore = true
}
fullName := JoinDevicePoint(device, pid)
unit := fieldTo.Tag.Get("PointUnit")
var uv UnitValue
if unit == "" {
bar := fieldTo.Type.Name()
fmt.Printf("bar:%v\n", bar)
fmt.Println("")
switch bar {
case "int":
uv.Unit = "integer"
uv.ValueInt = valueTo.(int64)
uv.Value = strconv.FormatInt(uv.ValueInt, 10)
case "int32":
uv.Unit = "integer"
uv.ValueInt = valueTo.(int64)
uv.Value = strconv.FormatInt(uv.ValueInt, 10)
case "int64":
uv.Unit = "integer"
uv.ValueInt = valueTo.(int64)
uv.Value = strconv.FormatInt(uv.ValueInt, 10)
case "float32":
uv.Unit = "float"
uv.ValueFloat = float64(valueTo.(float32))
uv.Value = Float64ToString(uv.ValueFloat)
case "float64":
uv.Unit = "float"
uv.ValueFloat = valueTo.(float64)
uv.Value = Float64ToString(uv.ValueFloat)
case "string":
uv.Unit = "string"
uv.Value = valueTo.(string)
case "UnitValue":
fallthrough
case "api.UnitValue":
uv = valueTo.(UnitValue)
uv = uv.UnitValueFix()
default:
ignore = true
}
}
if ignore {
continue
}
// foo := apiReflect.DataStructure {
// Json: j,
// PointDevice: device,
// PointId: pid,
// PointName: name,
// PointUnit: uv.Unit,
// PointType: fieldTo.Tag.Get("PointType"),
// }
// ret[fieldTo.Name] = foo
p := Point {
EndPoint: endpoint,
FullId: fullName,
PsKey: device,
Id: pid,
GroupName: "",
Name: name,
Unit: uv.Unit,
Type: fieldTo.Tag.Get("PointType"),
Valid: true,
}
dm.AddEntry(p, now, uv.Value)
alias := fieldTo.Tag.Get("PointAlias")
if alias != "" {
p.FullId = NameDevicePoint(device, alias)
p.Id = alias
dm.AddEntry(p, now, uv.Value)
}
// spew.Dump(&foo)
// fieldVo := vo.Field(i)
// value := fmt.Sprintf("%v", fieldVo.Interface())
// if value == "" {
// err = errors.New(fmt.Sprintf("option '%s' is empty", fieldTo.Name))
// break
// }
}
}
}
func (dm *DataMap) GetEntry(entry string) DataEntry {
ret := dm.Entries[entry]
return ret
}
func (dm *DataMap) CopyEntry(entry string) *DataEntry {
ret := dm.Entries[entry]
return &ret
// return &DataEntry {
// Date: DateTime{},
// Point: nil,
// Value: "",
// ValueFloat: 0,
// Index: 0,
// }
}
func (dm *DataMap) GetFloatValue(entry string) float64 {
return dm.Entries[entry].ValueFloat
}
func (dm *DataMap) LowerUpper(lower string, upper string) float64 {
l := dm.GetEntry(lower)
u := dm.GetEntry(upper)
if l.ValueFloat > 0 {
return 0 - l.ValueFloat
}
return u.ValueFloat
}
func (dm *DataMap) GetPercent(value string, max string) float64 {
v := dm.GetEntry(value)
m := dm.GetEntry(max)
return GetPercent(v.ValueFloat, m.ValueFloat)
}
func (dm *DataMap) GetValue(refname string) float64 {
v := dm.GetEntry(refname)
return v.ValueFloat
}
// func (dm *DataMap) FromRefAddState(refname string, psId string, point string, name string) {
// v := dm.GetEntry(refname)
// dm.Entries[point] = v.CreateState(psId, point, name)
// dm.Order = append(dm.Order, point)
// }
// func (dm *DataMap) AddVirtualValue(refname string, point string, name string, value float64) {
// v := dm.GetEntry(refname)
// dm.Entries[point] = v.CreateFloat(VirtualPsId, point, name, value)
// dm.Order = append(dm.Order, point)
// }
//
// func (ref *DataMap) AddUnitValue(refname string, point string, name string, value UnitValue) {
// v := ref.GetEntry(refname)
// ref.Entries[point] = v.FromRefAddFloat("virtual", point, name, value.Value)
// ref.Order = append(ref.Order, point)
// }
func (dm *DataMap) Add(point string, de DataEntry) {
for range Only.Once {
// point := de.ValueType.Id
de.Index = len(dm.Entries)
dm.Entries[point] = de
dm.Order = append(dm.Order, point)
if _, ok := Points[point]; ok {
break
}
Points[point] = *de.Point
}
}
func (dm *DataMap) AddEntry(point Point, date DateTime, value string) {
for range Only.Once {
unit := point.Unit // Save unit.
// Match to a previously defined point.
p := GetPoint(point.PsKey, point.Id)
if p == nil {
point = *p
}
if point.PsKey == "" {
point.PsKey = "virtual"
}
if point.Name == "" {
point.Name = PointToName(point.Id)
}
if point.FullId == "" {
point.FullId = JoinDevicePoint(point.PsKey, point.Id)
}
ref := CreateUnitValue(value, unit)
point.Unit = ref.Unit
point.Valid = true
// foo := Point {
// PsKey: psId,
// Id: "total_income",
// Unit: p.TotalIncome.Unit,
// Type: PointTypeTotal,
// }
//
// p := GetPoint(point.PsKey, point.Id)
// if p == nil {
// fmt.Printf("Found point already: %s.%s\n", p.PsKey, p.Id)
// fmt.Println("&point")
// spew.Dump(&point)
// fmt.Println("&p")
// spew.Dump(&p)
// // dm.Add(point.Id, CreateDataEntryUnitValue(date, point.PsKey, point.Id, point.Name, ref))
// // if p.Name == "" {
// // p.Name = PointToName(point.Id)
// // }
// // p = CreatePoint(psId, point, name, value.Unit)
// // break
// }
dm.Add(JoinDevicePoint(point.EndPoint, point.Id), DataEntry {
EndPoint: point.EndPoint,
Point: &point,
Date: date,
Value: ref.Value,
ValueFloat: ref.ValueFloat,
})
}
}
// func (dm *DataMap) AddPointAlias(refPoint Point, point Point) {
// for range Only.Once {
// p := GetPoint(refPoint.PsKey, refPoint.Id)
// if p != nil {
// fmt.Printf("Found point already: %s.%s\n", p.PsKey, p.Id)
// fmt.Println("&point")
// spew.Dump(&point)
// fmt.Println("&p")
// spew.Dump(&p)
// break
// }
//
// if p.PsKey == "" {
// p.PsKey = "virtual"
// }
// if p.Name == "" {
// p.Name = PointToName(point.Id)
// }
// if p.FullId == "" {
// p.FullId = JoinDevicePoint(point.PsKey, point.Id)
// }
// ref := CreateUnitValue(value, p.Unit)
// p.Unit = ref.Unit
// p.Valid = true
//
// dm.Add(point.Id, DataEntry {
// Date: date,
// Point: p,
// Value: ref.Value,
// ValueFloat: ref.ValueFloat,
// })
// }
// }
func (dm *DataMap) FromRefAddAlias2(refname string, psId string, point string, name string) {
de := dm.GetEntry(refname)
dm.Add(point, de.CreateAlias(psId, point, name))
}
func (dm *DataMap) AddEntryFromRef(refPoint Point, point Point, date DateTime, value string) {
for range Only.Once {
p := GetPoint(refPoint.PsKey, refPoint.Id)
if p != nil {
fmt.Printf("Found point already: %s.%s\n", p.PsKey, p.Id)
fmt.Println("&point")
spew.Dump(&point)
fmt.Println("&p")
spew.Dump(&p)
break
}
if point.PsKey == "" {
point.PsKey = "virtual"
}
if point.Name == "" {
point.Name = PointToName(point.Id)
}
if point.FullId == "" {
point.FullId = JoinDevicePoint(point.PsKey, point.Id)
}
ref := CreateUnitValue(value, point.Unit)
point.Unit = ref.Unit
point.Valid = true
dm.Add(point.Id, DataEntry {
Date: date,
Point: p,
Value: ref.Value,
ValueFloat: ref.ValueFloat,
})
}
}
func JoinDevicePoint(device string, point string) string {
var ret string
for range Only.Once {
if device == "" {
device = "virtual"
}
ret = device + "." + point
}
return ret
}
func (dm *DataMap) AddUnitValue(endpoint string, psId string, point string, name string, date DateTime, ref UnitValue) {
for range Only.Once {
if endpoint == "" {
endpoint = apiReflect.GetCallerPackage(2)
}
ref = ref.UnitValueFix()
p := GetPoint(psId, point)
if p == nil {
// No UV found. Create one.
dm.Add(point, CreateDataEntryUnitValue(date, psId, point, name, ref))
break
}
if p.Unit == "" {
p.Unit = ref.Unit
}
if p.Name == "" {
p.Name = name
}
if p.Name == "" {
p.Name = PointToName(point)
}
dm.Add(point, DataEntry {
Date: date,
Point: p,
Value: ref.Value,
ValueFloat: ref.ValueFloat,
})
}
}
func (dm *DataMap) AddFloat(psId string, point string, name string, date DateTime, value float64) {
for range Only.Once {
fvs := Float64ToString(value)
p := GetPoint(psId, point)
if p == nil {
// No UV found. Create one.
dm.Add(point, CreateDataEntryUnitValue(date, psId, point, name, CreateUnitValue(fvs, "float")))
break
}
ref := CreateUnitValue(fvs, p.Unit)
if ref.Unit != p.Unit {
fmt.Printf("OOOPS: Unit mismatch - %f %s != %f %s\n", value, p.Unit, ref.ValueFloat, ref.Unit)
p.Unit = ref.Unit
}
dm.Add(point, DataEntry {
Date: date,
Point: p,
Value: ref.Value,
ValueFloat: ref.ValueFloat,
})
}
de := CreateDataEntryUnitValue(date, psId, point, name, UnitValue {
Unit: "float",
Value: fmt.Sprintf("%f", value),
ValueFloat: 0,
})
dm.Add(point, de)
}
func (dm *DataMap) AddString(psId string, point string, name string, date DateTime, value string) {
dm.Add(point, CreateDataEntryString(date, psId, point, name, value))
}
func (dm *DataMap) AddInt(psId string, point string, name string, date DateTime, value int64) {
de := CreateDataEntryUnitValue(date, psId, point, name, UnitValue {
Unit: "int",
Value: fmt.Sprintf("%d", value),
ValueFloat: float64(value),
})
dm.Add(point, de)
}
// func (dm *DataMap) AddVirtualAliasFromRef(refname string, point string, name string) {
// de := dm.GetEntry(refname)
// dm.Add(point, de.CreateAlias("virtual", point, name))
// }
func (dm *DataMap) FromRefAddAlias(refname string, psId string, point string, name string) {
de := dm.GetEntry(refname)
dm.Add(point, de.CreateAlias(psId, point, name))
}
func (dm *DataMap) FromRefAddState(refname string, psId string, point string, name string) {
v := dm.GetEntry(refname)
dm.Add(point, v.CreateState(psId, point, name))
}
func (dm *DataMap) FromRefAddFloat(refname string, psId string, point string, name string, value float64) {
de := dm.GetEntry(refname)
dm.Add(point, de.CreateFloat(psId, point, name, value))
}
func (dm *DataMap) CopyPoint(refname string, psId string, point string, name string, value float64) {
de := dm.GetEntry(refname)
dm.Add(point, de.CreateFloat(psId, point, name, value))
}
func (de *DataEntry) CreateAlias(psId string, point string, name string) DataEntry {
if name == "" {
name = PointToName(point)
}
de.Point.FullId = NameDevicePoint(psId, point)
de.Point.PsKey = psId
de.Point.Id = point
de.Point.Name = name
de.Point.GroupName = psId
de.Point.Valid = true
de.Index = 0
return *de
}
func (de *DataEntry) CreateFloat(psId string, point string, name string, value float64) DataEntry {
if name == "" {
name = PointToName(point)
}
de2 := de.CreateAlias(psId, point, name)
uv := CreateUnitValue(Float64ToString(value), de2.Point.Unit)
de2.Value = uv.Value
de2.ValueFloat = uv.ValueFloat
return de2
// if name == "" {
// name = PointToName(point)
// }
//
// return DataEntry {
// Date: de.Date,
// Value: Float64ToString(v),
// ValueFloat: v,
// Point: &Point {
// FullId: NameDevicePoint(psId, point),
// GroupName: psId,
// PsKey: psId,
// Id: point,
// Name: name,
// Unit: u,
// Type: "",
// Valid: true,
// },
// Index: 0,
// }
}
func (de *DataEntry) CreateState(psId string, point string, name string) DataEntry {
if name == "" {
name = PointToName(point)
}
de2 := de.CreateAlias(psId, point, name)
de2.Value = fmt.Sprintf("%v", IsActive(de.ValueFloat))
de2.ValueFloat = 0
de2.Point.Unit = "binary"
return de2
// return DataEntry {
// Date: de.Date,
// Value: fmt.Sprintf("%v", IsActive(de.ValueFloat)),
// ValueFloat: 0,
// Point: &Point {
// FullId: NameDevicePoint(psId, point),
// PsKey: psId,
// Id: point,
// GroupName: psId,
// Name: name,
// Unit: "binary",
// Type: "",
// Valid: true,
// },
// Index: 0,
// }
}
func (de *DataEntry) UpdateMeta(date *DateTime, psId string, point string, name string) {
if date != nil {
de.Date = *date
}
if name == "" {
name = PointToName(point)
}
de.Point.FullId = NameDevicePoint(psId, point)
de.Point.PsKey = psId
de.Point.Id = point
de.Point.Name = name
de.Point.GroupName = psId
de.Index = 0
}
func CreateDataEntryActive(date DateTime, psId string, point string, name string, value float64) DataEntry {
p := GetPoint(psId, point)
if p == nil {
if name == "" {
name = PointToName(point)
}
p = CreatePoint(psId, point, name, "state")
}
return DataEntry {
Date: date,
Value: fmt.Sprintf("%v", IsActive(value)),
ValueFloat: 0,
Point: p,
Index: 0,
}
}
func CreateDataEntryString(date DateTime, psId string, point string, name string, value string) DataEntry {
p := GetPoint(psId, point)
if p == nil {
if name == "" {
name = PointToName(point)
}
p = CreatePoint(psId, point, name, "string")
}
return DataEntry {
Date: date,
Value: value,
ValueFloat: 0,
Point: p,
Index: 0,
}
}
func CreateDataEntryUnitValue(date DateTime, psId string, point string, name string, value UnitValue) DataEntry {
value = value.UnitValueFix()
p := GetPoint(psId, point)
if p == nil {
if name == "" {
name = PointToName(point)
}
p = CreatePoint(psId, point, name, value.Unit)
}
return DataEntry {
Date: date,
Value: value.Value,
ValueFloat: value.ValueFloat,
Point: p,
Index: 0,
}
}
func CreatePoint(psId string, point string, name string, unit string) *Point {
if name == "" {
name = PointToName(point)
}
return &Point {
FullId: NameDevicePoint(psId, point),
PsKey: psId,
Id: point,
GroupName: psId,
Name: name,
Unit: unit,
Type: "",
Valid: true,
}
}
func CreateUnitValue(value string, unit string) UnitValue {
ret := UnitValue {
Unit: unit,
Value: value,
}
return ret.UnitValueFix()
}
func (ref *UnitValue) UnitValueFix() UnitValue {
if ref.Unit == "W" {
fvs, err := DivideByThousand(ref.Value)
// fv, err := strconv.ParseFloat(p.Value, 64)
// fv = fv / 1000
if err == nil {
// p.Value = fmt.Sprintf("%.3f", fv)
ref.Value = fvs
ref.Unit = "kW"
}
}
if ref.Unit == "Wh" {
fvs, err := DivideByThousand(ref.Value)
// fv, err := strconv.ParseFloat(p.Value, 64)
// fv = fv / 1000
if err == nil {
// p.Value = fmt.Sprintf("%.3f", fv)
ref.Value = fvs
ref.Unit = "kWh"
}
}
ref.ValueFloat, _ = strconv.ParseFloat(ref.Value, 64)
return *ref
}
func (ref *UnitValue) UnitValueToPoint(psId string, point string, name string) *Point {
uv := ref.UnitValueFix()
// u := ref.Unit
//
// if ref.Unit == "W" {
// fvs, err := DivideByThousand(ref.Value)
// // fv, err := strconv.ParseFloat(p.Value, 64)
// // fv = fv / 1000
// if err == nil {
// // p.Value = fmt.Sprintf("%.3f", fv)
// ref.Value = fvs
// ref.Unit = "kW"
// }
// }
//
// if ref.Unit == "Wh" {
// fvs, err := DivideByThousand(ref.Value)
// // fv, err := strconv.ParseFloat(p.Value, 64)
// // fv = fv / 1000
// if err == nil {
// // p.Value = fmt.Sprintf("%.3f", fv)
// ref.Value = fvs
// ref.Unit = "kWh"
// }
// }
if name == "" {
name = PointToName(point)
}
vt := GetPoint(psId, point)
if !vt.Valid {
vt = &Point {
PsKey: psId,
Id: point,
Name: name,
Unit: uv.Unit,
Type: "PointTypeInstant",
Valid: true,
}
}
return vt
}
func IsActive(value float64) bool {
if (value > 0.01) || (value < -0.01) {
return true
}
return false
}
func GetPercent(value float64, max float64) float64 {
if max == 0 {
return 0
}
return (value / max) * 100
}
// // entries.AddVirtualValue("DailyTotalLoad", "DailyPvEnergyPercent", "daily_pv_energy_percent", "Daily PV Energy Percent", value)
// func NameToRefName(name string) string {
// var ret string // "Daily PV Energy Percent"
// ret = strings.ReplaceAll(name, " ", "_")
// ret = strings.ToLower(ret)
// return ret
// }
//
// func (upper *DataEntry) LowerUpper(lower DataEntry) float64 {
// if lower.ValueFloat > 0 {
// return 0 - lower.ValueFloat
// }
// return upper.ValueFloat
// }
//
// Type string
// Name string
// SubName string
//
// ParentId string
// ParentName string
//
// UniqueId string
// FullName string
// Units string
// ValueName string
// DeviceClass string
//
// Value string