controllogic: locals as Value, init from declarations, value-aware write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -278,15 +278,19 @@ func (e *Engine) liveGet(ds, name string) float64 {
|
||||
|
||||
// write applies an action.write/lua-set to a target: a bare name updates a
|
||||
// graph-local var; a ds:name target writes to the data source.
|
||||
func (e *Engine) write(cg *compiledGraph, target string, val float64) {
|
||||
func (e *Engine) write(cg *compiledGraph, target string, val Value) {
|
||||
ds, name, ok := parseRef(target)
|
||||
if !ok || math.IsNaN(val) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if ds == "local" {
|
||||
cg.setLocal(name, val)
|
||||
return
|
||||
}
|
||||
f, isNum := val.(float64)
|
||||
if !isNum || math.IsNaN(f) {
|
||||
return
|
||||
}
|
||||
if cg.dryRun {
|
||||
return // simulate: no real data-source write
|
||||
}
|
||||
@@ -301,11 +305,11 @@ func (e *Engine) write(cg *compiledGraph, target string, val float64) {
|
||||
Action: "signal.write",
|
||||
DS: ds,
|
||||
Signal: name,
|
||||
Value: strconv.FormatFloat(val, 'g', -1, 64),
|
||||
Value: strconv.FormatFloat(f, 'g', -1, 64),
|
||||
Detail: "control logic: " + cg.name,
|
||||
Outcome: audit.OutcomeOK,
|
||||
}
|
||||
if err := src.Write(e.root, name, val); err != nil {
|
||||
if err := src.Write(e.root, name, f); err != nil {
|
||||
e.log.Warn("control logic: write failed", "ds", ds, "signal", name, "err", err)
|
||||
ev.Outcome = audit.OutcomeError
|
||||
ev.Error = err.Error()
|
||||
@@ -608,7 +612,8 @@ type compiledGraph struct {
|
||||
prevVal map[string]float64 // last value for change triggers
|
||||
hasVal map[string]bool
|
||||
lastFire map[string]int64 // ns wall clock each trigger last fired
|
||||
locals map[string]float64
|
||||
locals map[string]Value
|
||||
decls map[string]StateVar
|
||||
}
|
||||
|
||||
func compile(g Graph) *compiledGraph {
|
||||
@@ -626,12 +631,21 @@ func compile(g Graph) *compiledGraph {
|
||||
prevVal: map[string]float64{},
|
||||
hasVal: map[string]bool{},
|
||||
lastFire: map[string]int64{},
|
||||
locals: map[string]float64{},
|
||||
locals: map[string]Value{},
|
||||
decls: map[string]StateVar{},
|
||||
fireCh: make(chan string, 16),
|
||||
}
|
||||
for _, n := range g.Nodes {
|
||||
cg.byId[n.ID] = n
|
||||
}
|
||||
for _, sv := range g.StateVars {
|
||||
cg.decls[sv.Name] = sv
|
||||
if sv.Type == "array" {
|
||||
cg.locals[sv.Name] = applySizing(parseInitialArray(sv), sv)
|
||||
} else {
|
||||
cg.locals[sv.Name] = parseScalarInitial(sv)
|
||||
}
|
||||
}
|
||||
for _, w := range g.Wires {
|
||||
port := w.FromPort
|
||||
if port == "" {
|
||||
@@ -682,18 +696,38 @@ func compile(g Graph) *compiledGraph {
|
||||
return cg
|
||||
}
|
||||
|
||||
func (cg *compiledGraph) setLocal(name string, v float64) {
|
||||
func parseScalarInitial(sv StateVar) float64 {
|
||||
s := strings.TrimSpace(sv.Initial)
|
||||
switch s {
|
||||
case "true":
|
||||
return 1
|
||||
case "false":
|
||||
return 0
|
||||
}
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (cg *compiledGraph) setLocal(name string, v Value) {
|
||||
cg.stateMu.Lock()
|
||||
if sv, ok := cg.decls[name]; ok && sv.Type == "array" {
|
||||
if arr, isArr := v.([]Value); isArr {
|
||||
v = applySizing(arr, sv)
|
||||
}
|
||||
}
|
||||
cg.locals[name] = v
|
||||
cg.stateMu.Unlock()
|
||||
}
|
||||
|
||||
func (cg *compiledGraph) getLocal(name string) float64 {
|
||||
func (cg *compiledGraph) getLocal(name string) Value {
|
||||
cg.stateMu.Lock()
|
||||
defer cg.stateMu.Unlock()
|
||||
v, ok := cg.locals[name]
|
||||
if !ok {
|
||||
return 0
|
||||
return 0.0
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -897,7 +931,7 @@ func (cg *compiledGraph) activate(triggerID string) {
|
||||
dt = float64(now-last) / 1e9
|
||||
}
|
||||
|
||||
resolve := func(ds, name string) Value { // shim: was float64 (Task 4 removes)
|
||||
resolve := func(ds, name string) Value {
|
||||
switch ds {
|
||||
case "sys":
|
||||
if name == "dt" {
|
||||
@@ -986,8 +1020,12 @@ func (cg *compiledGraph) run(nodeID string, ctx *runCtx) {
|
||||
cg.follow(node.ID, "done", ctx)
|
||||
|
||||
case "action.write":
|
||||
val := EvalExpr(node.param("expr"), ctx.resolve)
|
||||
cg.emitDebug(node.ID, val, true)
|
||||
val := EvalValue(node.param("expr"), ctx.resolve)
|
||||
f, isNum := val.(float64)
|
||||
if !isNum {
|
||||
f = math.NaN()
|
||||
}
|
||||
cg.emitDebug(node.ID, f, true)
|
||||
cg.engine.write(cg, node.param("target"), val)
|
||||
cg.follow(node.ID, "out", ctx)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user