feat: 触控交互实现、触控交互节点
fix: 分支节点在循环体内部的问题解决
@ -3,8 +3,8 @@ package com.cmvr.web.controller.test;
|
||||
|
||||
import com.cmvr.common.core.controller.BaseController;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.llm.service.CarPathFinderService;
|
||||
import com.cmvr.system.domain.vo.CarIcon;
|
||||
import com.cmvr.system.service.CarPathFinderService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/下一曲.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/充放电.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/地图.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航去公司.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航回家.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅通风.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/打开空调.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/播放音乐.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/显示.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/灯光.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调首页.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/设置.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/车辆控制.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/辅助驾驶.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/运动模式.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/连接.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/门窗锁.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/首页.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/驾驶偏好.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
@ -19,6 +19,11 @@
|
||||
<artifactId>cmvr-iot-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.volcengine</groupId>
|
||||
<artifactId>volc-sdk-java</artifactId>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.cmvr.llm.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface LLMTouchService {
|
||||
@ -14,5 +16,5 @@ public interface LLMTouchService {
|
||||
*/
|
||||
public String getCoordinates(String iconImage, String iconDesc, String inputImage);
|
||||
|
||||
public String touchCoordinates(File image,String targetUi,String manufacturer, String vehType);
|
||||
public JSONObject touchCoordinates(File image, String targetUi, String manufacturer, String vehType);
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
package com.cmvr.llm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.common.utils.http.CallAPIUtil;
|
||||
import com.cmvr.llm.config.APIProperties;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.llm.service.CarPathFinderService;
|
||||
import com.cmvr.llm.service.LLMTouchService;
|
||||
import com.cmvr.system.domain.vo.CarIcon;
|
||||
import com.cmvr.system.service.CarPathFinderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -25,7 +27,9 @@ public class LLMTouchServiceImpl implements LLMTouchService {
|
||||
|
||||
@Override
|
||||
public String bgeVl(File image) {
|
||||
return CallAPIUtil.doPostFile(apiProperties.getBgeVl(), null, "image", image, null);
|
||||
String response = CallAPIUtil.doPostFile(apiProperties.getBgeVl(), null, "image", image, null);
|
||||
JSONObject result = JSON.parseObject(response);
|
||||
return result.getString("uiName");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,24 +60,151 @@ public class LLMTouchServiceImpl implements LLMTouchService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String touchCoordinates(File image,String targetUi, String manufacturer, String vehType) {
|
||||
public JSONObject touchCoordinates(File image, String targetUi, String manufacturer, String vehType) {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("isFinish", false);
|
||||
|
||||
// 当前界面识别
|
||||
String currentUi = bgeVl(image);
|
||||
if (currentUi != null && currentUi.equals(targetUi)) {
|
||||
return null;
|
||||
if (StrUtil.isEmpty(currentUi)) {
|
||||
throw new GlobalException("未获取到当前位置");
|
||||
}
|
||||
if (currentUi.equals("设置驾驶偏好")) {
|
||||
currentUi = "驾驶偏好";
|
||||
}
|
||||
|
||||
// 已在目标界面:直接返回完成
|
||||
if (StrUtil.equals(currentUi, targetUi)) {
|
||||
result.put("isFinish", true);
|
||||
result.put("coordinates", "");
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算从当前到目标的路径
|
||||
List<CarIcon> path = carPathFinderService.findPath(manufacturer, vehType, currentUi, targetUi);
|
||||
String nextUi;
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
CarIcon carIcon = path.get(i);
|
||||
if (carIcon.getIconName().equals(currentUi)) {
|
||||
nextUi = path.get(i + 1).getIconName();
|
||||
if (CollUtil.isEmpty(path)) {
|
||||
throw new GlobalException(StrUtil.format("未找到从 [{}] 到 [{}] 的路径", currentUi, targetUi));
|
||||
}
|
||||
|
||||
// 下一步就是 path.get(0)
|
||||
CarIcon nextStepIcon = path.get(0);
|
||||
|
||||
// 分别上传当前截图和图标图片获得可访问 URL
|
||||
String imageUrl;
|
||||
String iconImageUrl;
|
||||
String iconName = nextStepIcon.getIconName();
|
||||
try {
|
||||
// 上传截图
|
||||
// byte[] imageBytes = FileUtil.readBytes(image);
|
||||
// imageUrl = LargeModelFileUploadUtil.uploadFile(imageBytes);
|
||||
//
|
||||
// // 拼接图标路径,例如 icons/honda/sedan/空调控制.png
|
||||
// String iconPath = StrUtil.format("icon/{}/{}/{}.png", manufacturer, vehType, iconName);
|
||||
//
|
||||
// // 从 resources 加载图标文件
|
||||
// InputStream iconStream = this.getClass().getClassLoader().getResourceAsStream(iconPath);
|
||||
// if (iconStream == null) {
|
||||
// throw new GlobalException("未找到图标资源文件:" + iconPath);
|
||||
// }
|
||||
//
|
||||
// // 上传图标
|
||||
// byte[] iconBytes = IoUtil.readBytes(iconStream);
|
||||
// iconImageUrl = LargeModelFileUploadUtil.uploadFile(iconBytes);
|
||||
imageUrl = "https://aiagentplatform.cmft.com/api/proxy/down?Action=Download&Version=2022-01-01&Path=upload/full/5d/a3/07244360c69636d5c146e5470d8d5ddf12ceed2643ffd892b7b7a5c4aaeb&IsAnonymous=true";
|
||||
iconImageUrl =
|
||||
iconName.equals("驾驶偏好") ?
|
||||
"https://aiagentplatform.cmft.com/api/proxy/down?Action=Download&Version=2022-01-01&Path=upload/full/a4/1b/6a7319709844c0f483a1d7adcfdbbff478ff5e34bed9ecff5d8fd407f3ac&IsAnonymous=true"
|
||||
: "https://aiagentplatform.cmft.com/api/proxy/down?Action=Download&Version=2022-01-01&Path=upload%2Ffull%2Ff1%2Fb9%2F8fb5f96f0299ccef60511eb8f7cfdcec890e563bc8e00854e63484086953&IsAnonymous=true";
|
||||
} catch (Exception e) {
|
||||
throw new GlobalException("上传图标或截图失败:" + e.getMessage());
|
||||
}
|
||||
// getCoordinates()
|
||||
return "";
|
||||
|
||||
// 获取下一步点击坐标
|
||||
String coordinates;
|
||||
try {
|
||||
coordinates = getCoordinates(iconImageUrl, nextStepIcon.getIconDesc(), imageUrl);
|
||||
} catch (Exception e) {
|
||||
throw new GlobalException("获取点击坐标失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
// 7) 返回
|
||||
result.put("isFinish", false);
|
||||
result.put("coordinates", coordinates);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String queryResultByRunId(String processId) {
|
||||
return "";
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Apikey", "d1ebtc5u6s5pkko77dkg");
|
||||
|
||||
Map<String, String> body = new HashMap<>();
|
||||
body.put("AppKey", "d1ebtc5u6s5pkko77dkg");
|
||||
body.put("AppID", "d1ebtabnjkflk4gmhikg");
|
||||
body.put("RunID", processId);
|
||||
body.put("UserID", "18888888888");
|
||||
|
||||
|
||||
String responseBody;
|
||||
JSONObject endNode;
|
||||
int failCount = 0;
|
||||
int maxFail = 40;
|
||||
|
||||
while (true) {
|
||||
responseBody = CallAPIUtil.doPostJson(apiProperties.getQueryResultUrl(), headers, body);
|
||||
JSONObject jsonObject = JSON.parseObject(responseBody);
|
||||
|
||||
String status = jsonObject.getString("status");
|
||||
if ("success".equals(status)) {
|
||||
JSONObject nodes = jsonObject.getJSONObject("nodes");
|
||||
if (nodes == null) {
|
||||
throw new GlobalException("响应中 nodes 为空:" + responseBody);
|
||||
}
|
||||
|
||||
endNode = findEndNode(nodes);
|
||||
if (endNode != null) {
|
||||
break;
|
||||
} else {
|
||||
throw new GlobalException("未找到 nodeType=end 的节点:" + nodes);
|
||||
}
|
||||
} else if ("failed".equals(status)) {
|
||||
// 如果失败
|
||||
throw new GlobalException(responseBody);
|
||||
} else {
|
||||
// 处理其他状态码
|
||||
failCount++;
|
||||
if (failCount > maxFail) {
|
||||
throw new GlobalException("达到最大重试次数,API状态仍未成功:" + status);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new GlobalException("轮询被中断");
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 output 内容并清洗
|
||||
JSONObject output = endNode.getJSONObject("output");
|
||||
String res = output.getString("content");
|
||||
if (StrUtil.isEmpty(res)) {
|
||||
throw new GlobalException("未找到坐标内容");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 nodes 中提取 nodeType=end 的节点
|
||||
*/
|
||||
private JSONObject findEndNode(JSONObject nodes) {
|
||||
for (Map.Entry<String, Object> entry : nodes.entrySet()) {
|
||||
JSONObject node = (JSONObject) entry.getValue();
|
||||
if ("end".equalsIgnoreCase(node.getString("nodeType"))) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.cmvr.llm.model;
|
||||
package com.cmvr.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.cmvr.llm.model;
|
||||
package com.cmvr.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.llm.service;
|
||||
package com.cmvr.system.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.system.domain.vo.CarIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
package com.cmvr.llm.service.impl;
|
||||
package com.cmvr.system.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.cmvr.common.core.redis.RedisCache;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.llm.model.CarUi;
|
||||
import com.cmvr.llm.service.CarPathFinderService;
|
||||
import com.cmvr.llm.util.CarPathFinderUtil;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.system.domain.SysVehOperConfig;
|
||||
import com.cmvr.system.domain.vo.CarIcon;
|
||||
import com.cmvr.system.domain.vo.CarUi;
|
||||
import com.cmvr.system.service.CarPathFinderService;
|
||||
import com.cmvr.system.service.ISysVehOperConfigService;
|
||||
import com.cmvr.system.util.CarPathFinderUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -23,20 +25,22 @@ import java.util.Map;
|
||||
@RequiredArgsConstructor
|
||||
public class CarPathFinderServiceImpl implements CarPathFinderService {
|
||||
|
||||
private final RedisCache redisCache;
|
||||
private final ISysVehOperConfigService vehOperConfigService;
|
||||
|
||||
/**
|
||||
* 从 Redis 获取 JSON 配置,查找路径
|
||||
* 获取 JSON 配置,查找路径
|
||||
*/
|
||||
@Override
|
||||
public List<CarIcon> findPath(String manufacturer, String vehType, String startUi, String targetFeature) {
|
||||
String redisKey = StrUtil.format("{}:{}:{}", "veh_oper_config", manufacturer, vehType);
|
||||
String configStr = redisCache.getCacheObject(redisKey);
|
||||
SysVehOperConfig sysVehOperConfig = vehOperConfigService.selectSysVehOperConfig(manufacturer, vehType);
|
||||
if (ObjUtil.isEmpty(sysVehOperConfig)) {
|
||||
throw new GlobalException("未找到对应的车机配置");
|
||||
}
|
||||
String configStr = sysVehOperConfig.getJsonData();
|
||||
if (StrUtil.isEmpty(configStr)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject configObj = JSON.parseObject(configStr);
|
||||
JSONArray jsonData = configObj.getJSONArray("jsonData");
|
||||
JSONArray jsonData = JSON.parseArray(configStr);
|
||||
return findPath(jsonData, startUi, targetFeature);
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package com.cmvr.llm.util;
|
||||
package com.cmvr.system.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.llm.model.CarUi;
|
||||
import com.cmvr.system.domain.vo.CarIcon;
|
||||
import com.cmvr.system.domain.vo.CarUi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -19,9 +19,9 @@ public enum ActionEnum {
|
||||
SUB_START("NONE", "SUB_START", "循环开始(子流程开始)"),
|
||||
END("NONE", "END", "结束"),
|
||||
START_LOOP("NONE", "START_LOOP", "开始循环"),
|
||||
STOP_LOOP("NONE", "START_LOOP", "结束循环"),
|
||||
STOP_LOOP("NONE", "STOP_LOOP", "结束循环"),
|
||||
BRANCH("NONE", "BRANCH", "分支"),
|
||||
SUB_END("NONE", "SUB_STOP", "子流程结束"),
|
||||
SUB_END("NONE", "SUB_END", "子流程结束"),
|
||||
SLEEP("NONE", "SLEEP", "延迟节点"),
|
||||
|
||||
// 系统行为
|
||||
|
||||
@ -15,9 +15,10 @@ public enum NodeTypeEnum {
|
||||
FUNCTION("function"),
|
||||
BRANCH("branch"),
|
||||
LOOP("loop"),
|
||||
STOP_LOOP("stopLoop"),
|
||||
END("end"),
|
||||
SLEEP("sleep"),
|
||||
// SUB_END("sub_end"),
|
||||
SUB_END("subEnd"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
|
||||
@ -45,6 +45,8 @@ public class FlowGraph {
|
||||
*/
|
||||
private List<FlowParamDef> startInputParamsDefs = new ArrayList<>();
|
||||
|
||||
// 是否跳出循环
|
||||
private volatile boolean stopLoop = false;
|
||||
|
||||
/**
|
||||
* 是否为子图
|
||||
@ -92,6 +94,22 @@ public class FlowGraph {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找子图起始节点(SUB_START 类型)
|
||||
*/
|
||||
public FlowNodeWrapper findSubStartNodeId() {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return nodeMap.values().stream()
|
||||
.filter(n -> n.getNodeType() == NodeTypeEnum.SUB_START)
|
||||
// .map(FlowNodeWrapper::getNodeId)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("未找到开始节点"));
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子图中起始节点(入度为0)
|
||||
*/
|
||||
@ -268,4 +286,11 @@ public class FlowGraph {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否跳出循环(用于 STOP_LOOP 控制节点)
|
||||
*/
|
||||
public boolean isBreakLoop() {
|
||||
return stopLoop;
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,10 +286,14 @@ public class FlowModelBuilder {
|
||||
return NodeTypeEnum.SUB_START;
|
||||
case "end":
|
||||
return NodeTypeEnum.END;
|
||||
case "subend":
|
||||
return NodeTypeEnum.SUB_END;
|
||||
case "branch":
|
||||
return NodeTypeEnum.BRANCH;
|
||||
case "loop":
|
||||
return NodeTypeEnum.LOOP;
|
||||
case "stoploop":
|
||||
return NodeTypeEnum.STOP_LOOP;
|
||||
case "sleep":
|
||||
return NodeTypeEnum.SLEEP;
|
||||
default:
|
||||
|
||||
@ -22,6 +22,7 @@ public class FlowLoopNodeHandler implements FlowNodeTypeHandler {
|
||||
|
||||
@Override
|
||||
public TaskNodeExecuteResult handle(TaskNodeExecuteMessage message) {
|
||||
String instId = message.getInstId();
|
||||
FlowGraph graph = message.getGraph();
|
||||
// 当前从第几次循环开始
|
||||
int loopNum = message.getLoopNum() < 0 ? 1 : message.getLoopNum();
|
||||
@ -40,8 +41,13 @@ public class FlowLoopNodeHandler implements FlowNodeTypeHandler {
|
||||
|
||||
// 循环执行子图
|
||||
for (int i = loopNum; i <= loopCount; i++) {
|
||||
// 如果子图设置了跳出标志,则结束循环
|
||||
if (subGraph.isBreakLoop()) {
|
||||
log.info("检测到子图设置跳出标志,提前结束循环:instId={}, nodeId={}", instId, nodeId);
|
||||
break;
|
||||
}
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
String instId = message.getInstId();
|
||||
|
||||
taskThreadRegistry.registerLatch(instId, nodeId, latch);
|
||||
FlowItemExecutor flowItemExecutor = SpringUtils.getBean(FlowItemExecutor.class);
|
||||
flowItemExecutor.executeSubGraph(subGraph, message, latch::countDown, i);
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package com.cmvr.test.flow.runtime.dispatcher;
|
||||
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component("STOP_LOOP")
|
||||
@RequiredArgsConstructor
|
||||
public class FlowStopLoopNodeHandler implements FlowNodeTypeHandler {
|
||||
|
||||
@Override
|
||||
public TaskNodeExecuteResult handle(TaskNodeExecuteMessage message) {
|
||||
message.getGraph().setStopLoop(true);
|
||||
log.info("STOP_LOOP 节点执行,设置跳出标志:instId={}, nodeId={}", message.getInstId(), message.getNodeId());
|
||||
|
||||
return TaskNodeExecuteResult.success();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.cmvr.test.flow.runtime.dispatcher;
|
||||
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
|
||||
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component("SUB_END")
|
||||
@RequiredArgsConstructor
|
||||
public class FlowSubEndNodeHandler implements FlowNodeTypeHandler {
|
||||
|
||||
@Override
|
||||
public TaskNodeExecuteResult handle(TaskNodeExecuteMessage message) {
|
||||
return TaskNodeExecuteResult.success(message.getInputParams());
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ public class FlowItemExecutor {
|
||||
List<FlowParamDef> startInputDefs = subGraph.getStartInputParamsDefs();
|
||||
|
||||
// 子图的 start 节点
|
||||
FlowNodeWrapper subStartNode = subGraph.getInDegreeZeroNode();
|
||||
FlowNodeWrapper subStartNode = subGraph.findSubStartNodeId();
|
||||
String subStartNodeId = subStartNode.getNodeId();
|
||||
|
||||
NodeExecutor executor = node -> executeNode(
|
||||
@ -131,7 +131,7 @@ public class FlowItemExecutor {
|
||||
}
|
||||
|
||||
// END 节点 or 子图结束
|
||||
if (node.getNodeType() == NodeTypeEnum.END || graph.isSubGraphEndNode(nodeId)) {
|
||||
if (node.getNodeType() == NodeTypeEnum.END || node.getNodeType() == NodeTypeEnum.SUB_END) {
|
||||
try {
|
||||
onFinished.run();
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -58,7 +58,7 @@ public class FlowTaskScheduler {
|
||||
int loopIteration) {
|
||||
|
||||
String instId = context.getInstId();
|
||||
FlowNodeWrapper startNode = graph.getInDegreeZeroNode();
|
||||
FlowNodeWrapper startNode = graph.findSubStartNodeId();
|
||||
List<String> nextNodes = graph.getNextNodes(startNode.getNodeId());
|
||||
|
||||
if (nextNodes.isEmpty()) {
|
||||
|
||||
@ -55,8 +55,15 @@ public class EdgeCameraOperateService implements EdgeOperateService {
|
||||
}
|
||||
|
||||
case CAMERA_GETRGBIMAGE: {
|
||||
String imageUrl;
|
||||
// String imageUrl = StrUtil.format("id为 [{}] 的相机拍照了", deviceId);
|
||||
String imageUrl = "http://10.148.20.35:9000/cmvr-iot/FILE/20250820/1755670270208.jpg";
|
||||
if (message.getLoopNum() <= 1) {
|
||||
// 车辆控制调节
|
||||
imageUrl = "http://192.168.1.100:9000/cmvr-iot/FILE/20250821/1755761816340.jpg";
|
||||
} else {
|
||||
// 驾驶偏好
|
||||
imageUrl = "http://192.168.1.100:9000/cmvr-iot/FILE/20250822/1755849654731.jpg";
|
||||
}
|
||||
// String imageUrl = edgeCameraService.getRGBImage(terminalId, deviceId);
|
||||
|
||||
JSONArray imageUrls = new JSONArray();
|
||||
|
||||
@ -6,8 +6,6 @@ import com.alibaba.fastjson2.JSONObject;
|
||||
import com.cmvr.common.config.properties.MinioProperties;
|
||||
import com.cmvr.common.core.minio.MinioService;
|
||||
import com.cmvr.common.exception.GlobalException;
|
||||
import com.cmvr.llm.model.CarIcon;
|
||||
import com.cmvr.llm.service.CarPathFinderService;
|
||||
import com.cmvr.llm.service.LLMTouchService;
|
||||
import com.cmvr.test.enums.ActionEnum;
|
||||
import com.cmvr.test.flow.builder.FlowNodeWrapper;
|
||||
@ -19,7 +17,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -29,7 +26,6 @@ public class LLMTouchOperateService implements LLMOperateService {
|
||||
private final TaskInstHolder taskInstHolder;
|
||||
private final MinioService minioService;
|
||||
private final MinioProperties minioProps;
|
||||
private final CarPathFinderService carPathFinderService;
|
||||
private final LLMTouchService llmTouchService;
|
||||
|
||||
@Override
|
||||
@ -46,9 +42,9 @@ public class LLMTouchOperateService implements LLMOperateService {
|
||||
JSONObject output = new JSONObject();
|
||||
switch (action) {
|
||||
case TOUCH_COORDINATES: {
|
||||
// 必须获取前面的图片 和当前节点的指令
|
||||
String words = inputParams.getString("words");
|
||||
String targetFeature = inputParams.getString("targetFeature");
|
||||
String manufacturer = inputParams.getString("manufacturer");
|
||||
String vehType = inputParams.getString("vehType");
|
||||
// 图片节点的id
|
||||
FlowNodeWrapper imageNode = message.getGraph()
|
||||
.getNodeMap()
|
||||
@ -65,8 +61,7 @@ public class LLMTouchOperateService implements LLMOperateService {
|
||||
String objectName = StrUtil.removePrefix(url, StrUtil.subBefore(url, prefix, true) + prefix);
|
||||
try {
|
||||
File image = minioService.getFile(bucketName, objectName);
|
||||
// llmTouchService.touchCoordinates();
|
||||
output.put("coordinates", "");
|
||||
output = llmTouchService.touchCoordinates(image, targetFeature, manufacturer, vehType);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -79,11 +74,3 @@ public class LLMTouchOperateService implements LLMOperateService {
|
||||
return TaskNodeExecuteResult.success(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String uiName = llmTouchService.bgeVl(image);
|
||||
* List<CarIcon> path = carPathFinderService.findPath("厂商", "车型", uiName, targetFeature);
|
||||
*
|
||||
* String coordinates = llmTouchService.getCoordinates("", "", "");
|
||||
*
|
||||
*/
|
||||