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