001/**
002 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.gnu.org/licenses/lgpl-3.0.txt
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package dev.tinyflow.core.chain;
017
018import dev.tinyflow.core.util.JsConditionUtil;
019import dev.tinyflow.core.util.StringUtil;
020import org.slf4j.Logger;
021
022import java.io.Serializable;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027public abstract class Node implements Serializable {
028    private static final Logger log = org.slf4j.LoggerFactory.getLogger(Node.class);
029
030    protected String id;
031    protected String parentId;
032    protected String name;
033    protected String description;
034
035//    protected List<Edge> inwardEdges;
036//    protected List<Edge> outwardEdges;
037
038    protected NodeCondition condition;
039    protected NodeValidator validator;
040
041    // 循环执行相关属性
042    protected boolean loopEnable = false;           // 是否启用循环执行
043    protected long loopIntervalMs = 3000;            // 循环间隔时间(毫秒)
044    protected NodeCondition loopBreakCondition;      // 跳出循环的条件
045    protected int maxLoopCount = 0;                  // 0 表示不限制循环次数
046
047    protected boolean retryEnable = false;
048    protected boolean resetRetryCountAfterNormal = false;
049    protected int maxRetryCount = 0;
050    protected long retryIntervalMs = 3000;
051
052    // 算力消耗定义,积分消耗
053    protected String computeCostExpr;
054
055    public String getId() {
056        return id;
057    }
058
059    public void setId(String id) {
060        this.id = id;
061    }
062
063    public String getParentId() {
064        return parentId;
065    }
066
067    public void setParentId(String parentId) {
068        this.parentId = parentId;
069    }
070
071    public String getName() {
072        return name;
073    }
074
075    public void setName(String name) {
076        this.name = name;
077    }
078
079    public String getDescription() {
080        return description;
081    }
082
083    public void setDescription(String description) {
084        this.description = description;
085    }
086
087//    public List<Edge> getInwardEdges() {
088//        return inwardEdges;
089//    }
090//
091//    public void setInwardEdges(List<Edge> inwardEdges) {
092//        this.inwardEdges = inwardEdges;
093//    }
094//
095//    public List<Edge> getOutwardEdges() {
096////        return outwardEdges;
097//    }
098//
099//    public void setOutwardEdges(List<Edge> outwardEdges) {
100//        this.outwardEdges = outwardEdges;
101//    }
102
103    public NodeCondition getCondition() {
104        return condition;
105    }
106
107    public void setCondition(NodeCondition condition) {
108        this.condition = condition;
109    }
110
111    public NodeValidator getValidator() {
112        return validator;
113    }
114
115    public void setValidator(NodeValidator validator) {
116        this.validator = validator;
117    }
118
119//    protected void addOutwardEdge(Edge edge) {
120//        if (this.outwardEdges == null) {
121//            this.outwardEdges = new ArrayList<>();
122//        }
123//        this.outwardEdges.add(edge);
124//    }
125//
126//    protected void addInwardEdge(Edge edge) {
127//        if (this.inwardEdges == null) {
128//            this.inwardEdges = new ArrayList<>();
129//        }
130//        this.inwardEdges.add(edge);
131//    }
132
133    public boolean isLoopEnable() {
134        return loopEnable;
135    }
136
137    public void setLoopEnable(boolean loopEnable) {
138        this.loopEnable = loopEnable;
139    }
140
141    public long getLoopIntervalMs() {
142        return loopIntervalMs;
143    }
144
145    public void setLoopIntervalMs(long loopIntervalMs) {
146        this.loopIntervalMs = loopIntervalMs;
147    }
148
149    public NodeCondition getLoopBreakCondition() {
150        return loopBreakCondition;
151    }
152
153    public void setLoopBreakCondition(NodeCondition loopBreakCondition) {
154        this.loopBreakCondition = loopBreakCondition;
155    }
156
157    public int getMaxLoopCount() {
158        return maxLoopCount;
159    }
160
161    public void setMaxLoopCount(int maxLoopCount) {
162        this.maxLoopCount = maxLoopCount;
163    }
164
165    public List<Parameter> getParameters() {
166        return null;
167    }
168
169    public boolean isRetryEnable() {
170        return retryEnable;
171    }
172
173    public void setRetryEnable(boolean retryEnable) {
174        this.retryEnable = retryEnable;
175    }
176
177    public boolean isResetRetryCountAfterNormal() {
178        return resetRetryCountAfterNormal;
179    }
180
181    public void setResetRetryCountAfterNormal(boolean resetRetryCountAfterNormal) {
182        this.resetRetryCountAfterNormal = resetRetryCountAfterNormal;
183    }
184
185    public int getMaxRetryCount() {
186        return maxRetryCount;
187    }
188
189    public void setMaxRetryCount(int maxRetryCount) {
190        this.maxRetryCount = maxRetryCount;
191    }
192
193    public long getRetryIntervalMs() {
194        return retryIntervalMs;
195    }
196
197    public void setRetryIntervalMs(long retryIntervalMs) {
198        this.retryIntervalMs = retryIntervalMs;
199    }
200
201    public String getComputeCostExpr() {
202        return computeCostExpr;
203    }
204
205    public void setComputeCostExpr(String computeCostExpr) {
206        if (computeCostExpr != null) {
207            computeCostExpr = computeCostExpr.trim();
208        }
209        this.computeCostExpr = computeCostExpr;
210    }
211
212    public NodeValidResult validate() throws Exception {
213        return validator != null ? validator.validate(this) : NodeValidResult.ok();
214    }
215
216
217    public abstract Map<String, Object> execute(Chain chain);
218
219    public long calculateComputeCost(Chain chain, Map<String, Object> executeResult) {
220
221        if (StringUtil.noText(computeCostExpr)) {
222            return 0;
223        }
224
225        if (computeCostExpr.startsWith("{{") && computeCostExpr.endsWith("}}")) {
226            String expr = computeCostExpr.substring(2, computeCostExpr.length() - 2);
227            return doCalculateComputeCost(expr, chain, executeResult);
228        } else {
229            try {
230                return Long.parseLong(computeCostExpr);
231            } catch (NumberFormatException e) {
232                log.error(e.toString(), e);
233            }
234            return 0;
235        }
236    }
237
238    protected long doCalculateComputeCost(String expr, Chain chain, Map<String, Object> result) {
239//        Map<String, Object> parameterValues = chain.getState().getParameterValuesOnly(this, this.getParameters(), null);
240        Map<String, Object> parameterValues = chain.getState().resolveParameters(this, this.getParameters(), null,true);
241        Map<String, Object> newMap = new HashMap<>(result);
242        newMap.putAll(parameterValues);
243        return JsConditionUtil.evalLong(expr, chain, newMap);
244    }
245}