diff --git a/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/test/TeTestController.java b/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/test/TeTestController.java
index 87e6946..f60dfe6 100644
--- a/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/test/TeTestController.java
+++ b/cmvr-iot-admin/src/main/java/com/cmvr/web/controller/test/TeTestController.java
@@ -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;
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/下一曲.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/下一曲.png
new file mode 100644
index 0000000..538645c
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/下一曲.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/充放电.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/充放电.png
new file mode 100644
index 0000000..12b8d17
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/充放电.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/地图.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/地图.png
new file mode 100644
index 0000000..6f05ef8
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/地图.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航去公司.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航去公司.png
new file mode 100644
index 0000000..9ecb817
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航去公司.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航回家.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航回家.png
new file mode 100644
index 0000000..25f19b2
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/导航回家.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅.png
new file mode 100644
index 0000000..509b391
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅通风.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅通风.png
new file mode 100644
index 0000000..58fdb7d
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/座椅通风.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/打开空调.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/打开空调.png
new file mode 100644
index 0000000..4549124
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/打开空调.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/播放音乐.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/播放音乐.png
new file mode 100644
index 0000000..ab62475
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/播放音乐.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/显示.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/显示.png
new file mode 100644
index 0000000..9545285
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/显示.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/灯光.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/灯光.png
new file mode 100644
index 0000000..d3d4bf3
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/灯光.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调.png
new file mode 100644
index 0000000..78492de
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调首页.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调首页.png
new file mode 100644
index 0000000..f7763ce
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/空调首页.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/设置.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/设置.png
new file mode 100644
index 0000000..77a97c2
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/设置.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/车辆控制.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/车辆控制.png
new file mode 100644
index 0000000..ab0c8d6
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/车辆控制.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/辅助驾驶.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/辅助驾驶.png
new file mode 100644
index 0000000..6fb77a0
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/辅助驾驶.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/运动模式.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/运动模式.png
new file mode 100644
index 0000000..0b504d6
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/运动模式.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/连接.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/连接.png
new file mode 100644
index 0000000..7d3f813
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/连接.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/门窗锁.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/门窗锁.png
new file mode 100644
index 0000000..004e3fd
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/门窗锁.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/首页.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/首页.png
new file mode 100644
index 0000000..e145d92
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/首页.png differ
diff --git a/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/驾驶偏好.png b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/驾驶偏好.png
new file mode 100644
index 0000000..14638e7
Binary files /dev/null and b/cmvr-iot-admin/src/main/resources/icon/xiaomi/su7/驾驶偏好.png differ
diff --git a/cmvr-iot-api/cmvr-iot-llm/pom.xml b/cmvr-iot-api/cmvr-iot-llm/pom.xml
index ed2a52d..67871a4 100644
--- a/cmvr-iot-api/cmvr-iot-llm/pom.xml
+++ b/cmvr-iot-api/cmvr-iot-llm/pom.xml
@@ -19,6 +19,11 @@
cmvr-iot-common
+
+ com.cmvr
+ cmvr-iot-system
+
+
com.volcengine
volc-sdk-java
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/LLMTouchService.java b/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/LLMTouchService.java
index 033f64f..f318a7b 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/LLMTouchService.java
+++ b/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/LLMTouchService.java
@@ -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);
}
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/LLMTouchServiceImpl.java b/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/LLMTouchServiceImpl.java
index 1e66d5a..4002c1e 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/LLMTouchServiceImpl.java
+++ b/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/LLMTouchServiceImpl.java
@@ -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 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));
}
-// getCoordinates()
- return "";
+
+ // 下一步就是 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());
+ }
+
+ // 获取下一步点击坐标
+ 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 headers = new HashMap<>();
+ headers.put("Apikey", "d1ebtc5u6s5pkko77dkg");
+
+ Map 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 entry : nodes.entrySet()) {
+ JSONObject node = (JSONObject) entry.getValue();
+ if ("end".equalsIgnoreCase(node.getString("nodeType"))) {
+ return node;
+ }
+ }
+ return null;
}
}
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarIcon.java b/cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarIcon.java
similarity index 97%
rename from cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarIcon.java
rename to cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarIcon.java
index d224e83..5d0690f 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarIcon.java
+++ b/cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarIcon.java
@@ -1,4 +1,4 @@
-package com.cmvr.llm.model;
+package com.cmvr.system.domain.vo;
import lombok.Data;
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarUi.java b/cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarUi.java
similarity index 96%
rename from cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarUi.java
rename to cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarUi.java
index 74206cf..e1564ea 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/model/CarUi.java
+++ b/cmvr-iot-system/src/main/java/com/cmvr/system/domain/vo/CarUi.java
@@ -1,4 +1,4 @@
-package com.cmvr.llm.model;
+package com.cmvr.system.domain.vo;
import lombok.Data;
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/CarPathFinderService.java b/cmvr-iot-system/src/main/java/com/cmvr/system/service/CarPathFinderService.java
similarity index 91%
rename from cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/CarPathFinderService.java
rename to cmvr-iot-system/src/main/java/com/cmvr/system/service/CarPathFinderService.java
index 935d937..8ff689f 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/CarPathFinderService.java
+++ b/cmvr-iot-system/src/main/java/com/cmvr/system/service/CarPathFinderService.java
@@ -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;
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/CarPathFinderServiceImpl.java b/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/CarPathFinderServiceImpl.java
similarity index 55%
rename from cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/CarPathFinderServiceImpl.java
rename to cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/CarPathFinderServiceImpl.java
index 1e16d4e..0ed1272 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/service/impl/CarPathFinderServiceImpl.java
+++ b/cmvr-iot-system/src/main/java/com/cmvr/system/service/impl/CarPathFinderServiceImpl.java
@@ -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 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);
}
diff --git a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/util/CarPathFinderUtil.java b/cmvr-iot-system/src/main/java/com/cmvr/system/util/CarPathFinderUtil.java
similarity index 96%
rename from cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/util/CarPathFinderUtil.java
rename to cmvr-iot-system/src/main/java/com/cmvr/system/util/CarPathFinderUtil.java
index f50041d..f00d439 100644
--- a/cmvr-iot-api/cmvr-iot-llm/src/main/java/com/cmvr/llm/util/CarPathFinderUtil.java
+++ b/cmvr-iot-system/src/main/java/com/cmvr/system/util/CarPathFinderUtil.java
@@ -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;
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/enums/ActionEnum.java b/cmvr-iot-test/src/main/java/com/cmvr/test/enums/ActionEnum.java
index ec15b5f..3edec86 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/enums/ActionEnum.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/enums/ActionEnum.java
@@ -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", "延迟节点"),
// 系统行为
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/enums/NodeTypeEnum.java b/cmvr-iot-test/src/main/java/com/cmvr/test/enums/NodeTypeEnum.java
index d6a71ff..5d550b8 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/enums/NodeTypeEnum.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/enums/NodeTypeEnum.java
@@ -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;
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowGraph.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowGraph.java
index d2276c6..ce4dfa3 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowGraph.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowGraph.java
@@ -45,6 +45,8 @@ public class FlowGraph {
*/
private List 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;
+ }
}
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowModelBuilder.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowModelBuilder.java
index 688cd65..669b84e 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowModelBuilder.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/builder/FlowModelBuilder.java
@@ -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:
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowLoopNodeHandler.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowLoopNodeHandler.java
index 21c5ccd..5bb9fcf 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowLoopNodeHandler.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowLoopNodeHandler.java
@@ -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);
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowStopLoopNodeHandler.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowStopLoopNodeHandler.java
new file mode 100644
index 0000000..a0fedfc
--- /dev/null
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowStopLoopNodeHandler.java
@@ -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();
+ }
+}
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowSubEndNodeHandler.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowSubEndNodeHandler.java
new file mode 100644
index 0000000..c0ccd2b
--- /dev/null
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/dispatcher/FlowSubEndNodeHandler.java
@@ -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());
+ }
+}
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowItemExecutor.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowItemExecutor.java
index 9c4427d..2fd4838 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowItemExecutor.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowItemExecutor.java
@@ -76,7 +76,7 @@ public class FlowItemExecutor {
List 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) {
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskScheduler.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskScheduler.java
index 60daadf..32fe23c 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskScheduler.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/FlowTaskScheduler.java
@@ -58,7 +58,7 @@ public class FlowTaskScheduler {
int loopIteration) {
String instId = context.getInstId();
- FlowNodeWrapper startNode = graph.getInDegreeZeroNode();
+ FlowNodeWrapper startNode = graph.findSubStartNodeId();
List nextNodes = graph.getNextNodes(startNode.getNodeId());
if (nextNodes.isEmpty()) {
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java
index 2c67670..4e7539e 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/edge/EdgeCameraOperateService.java
@@ -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();
diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTouchOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTouchOperateService.java
index ffcb4c4..e1c6019 100644
--- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTouchOperateService.java
+++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTouchOperateService.java
@@ -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);
}
@@ -78,12 +73,4 @@ public class LLMTouchOperateService implements LLMOperateService {
}
return TaskNodeExecuteResult.success(output);
}
-}
-
-/**
- * String uiName = llmTouchService.bgeVl(image);
- * List path = carPathFinderService.findPath("厂商", "车型", uiName, targetFeature);
- *
- * String coordinates = llmTouchService.getCoordinates("", "", "");
- *
- */
\ No newline at end of file
+}
\ No newline at end of file