Commit d6c0a972 authored by 何金镒's avatar 何金镒

111

parents
Pipeline #588 failed with stages
# mcp sse
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ikonke</groupId>
<artifactId>konke-ai-alibaba-mcp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>konke-ai-alibaba-mcp</name>
<description>konke-ai-alibaba-mcp</description>
<properties>
<java.version>17</java.version>
<hutool.version>5.7.10</hutool.version>
<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
<mysql.version>8.0.32</mysql.version>
<!-- Spring AI -->
<spring-ai.version>1.0.0</spring-ai.version>
<!-- Spring Boot -->
<spring-boot.version>3.4.5</spring-boot.version>
<!-- DashScope Java SDK -->
<dashscope.version>2.20.0</dashscope.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- sse -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.ikonke.konkeaialibabamcp;
import com.ikonke.konkeaialibabamcp.aitools.DeviceTools;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class KonkeAiAlibabaMcpApplication {
public static void main(String[] args) {
SpringApplication.run(KonkeAiAlibabaMcpApplication.class, args);
}
@Bean
public ToolCallbackProvider tools(DeviceTools deviceTools) {
return MethodToolCallbackProvider.builder().toolObjects(deviceTools).build();
}
}
package com.ikonke.konkeaialibabamcp.aitools;
import com.ikonke.konkeaialibabamcp.entity.mongodb.DeviceStatus;
import com.ikonke.konkeaialibabamcp.service.mongdbservice.DeviceStatusService;
import com.ikonke.konkeaialibabamcp.utils.KonkeIotUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class DeviceTools {
private final DeviceStatusService deviceStatusUtil;
private final KonkeIotUtils konkeIotServer;
public DeviceTools( DeviceStatusService deviceStatusUtil, KonkeIotUtils konkeIotServer){
this.deviceStatusUtil = deviceStatusUtil;
this.konkeIotServer = konkeIotServer;
}
@Tool(description = "根据主机号和设备ID查询设备状态信息.")
public DeviceStatus findDeviceState(@ToolParam(description = "主机号") String ccuName,
@ToolParam(description = "设备ID") Integer devId) {
log.info("DeviceTools:findDeviceState....ccuName:{},devId:{}",ccuName,devId);
return deviceStatusUtil.findUserByUserId(ccuName, devId);
}
@Tool(description = "根据主机号和设备ID控制打开设备")
public String optDeviceOpen(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String operateId){
log.info("DeviceTools:optDeviceOpen....ccuName:{},devId:{},operateId:{}",ccuName,devId,operateId);
return konkeIotServer.optDeviceOpen(ccuName, devId, operateId);
}
@Tool(description = "根据主机号和设备ID控制关闭设备")
public String optDeviceClosed(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String operateId){
log.info("DeviceTools:optDeviceClosed....ccuName:{},devId:{},operateId:{}",ccuName,devId,operateId);
return konkeIotServer.optDeviceClosed(ccuName, devId, operateId);
}
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document(collection = "t_devices")
public class Device {
@Id
private String id;
private Integer devId;
private Integer realType;
private String macLast4;
private Integer subType;
private Integer type;
private String ccuName;
private String name;
private String key;
private Integer roomId;
private String mac;
private Long lastUpdateTime;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Map;
/**
* 设备状态
*/
@Data
@Document(collection = "t_device_status")
public class DeviceStatus {
@Id
private String id;
private Integer devId;
private Integer realType;
private String ccuName;
private String key;
private Long lastUpdateTime;
private Map<Object, Object> status;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import cn.hutool.json.JSONArray;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document(collection = "t_device_TSL")
public class DeviceTSL {
@Id
private String id;
private JSONArray actions;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Field;
@Data
public class Floor {
@Field("id")
private Integer id;
private String name;
private Integer pos;
private Object icon;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Data
@Document(collection = "t_region")
public class Region {
@Id
private String id;
private List<Room> rooms;
private List<Floor> floors;
private String ccuName;
private String key;
private Long lastUpdateTime;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Field;
@Data
public class Room {
@Field("id")
private Integer id;
private Integer floorId;
private String name;
private String pos;
private String icon;
}
package com.ikonke.konkeaialibabamcp.entity.mongodb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document(collection = "t_ai_user")
public class User {
@Id
private String id;
private String userId;
private String phone;
private String assistantId;
private String threadId;
}
package com.ikonke.konkeaialibabamcp.service.mongdbservice;
import cn.hutool.json.JSONUtil;
import com.ikonke.konkeaialibabamcp.entity.mongodb.Device;
import com.ikonke.konkeaialibabamcp.entity.mongodb.DeviceStatus;
import com.ikonke.konkeaialibabamcp.entity.mongodb.Region;
import com.ikonke.konkeaialibabamcp.entity.mongodb.Room;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
public class DeviceStatusService {
private final MongoTemplate mongoTemplate;
public DeviceStatusService(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
/**
* 查询设备状态
*/
public DeviceStatus findUserByUserId(@ToolParam(description = "主机号") String ccuName,
@ToolParam(description = "设备ID") Integer devId) {
log.info("DeviceStatusUtil:findUserByUserId....ccuName:{},devId:{}",ccuName,devId);
return mongoTemplate.findOne(Query.query(
Criteria.where("ccuName").is(ccuName)
.and("devId").is(devId)
), DeviceStatus.class);
}
/**
* 根据主机、房间名、设备名查询设备状态
*/
public DeviceStatus findByName(String ccuName,String roomName,String deviceName) {
log.info("主机:{},查询房间:{} ,下的设备: {} ,状态",ccuName,roomName,deviceName);
// 第一步:根据房间名称查找 Region 获取 ccuName
Query regionQuery = new Query(Criteria.where("ccuName").is(ccuName));
List<Region> region = mongoTemplate.find(regionQuery, Region.class);
log.info("region:{}", JSONUtil.toJsonStr(region));
if (region.size() != 1) {
log.error("主机:{} 没有找到匹配的房间",ccuName);
return null; // 没有找到匹配的房间
}
Integer roomId = null;
List<Room> rooms = region.get(0).getRooms();
for (Room room : rooms){
if (room.getName().contains(roomName)){
roomId = room.getId();
}
}
if(roomId==null){
log.error("主机:{} 没有找到匹配的房间:{}",ccuName,roomName);
return null;
}
// 查询房间下的设备
Integer devId = null;
Query regionQuery2 = new Query(Criteria.where("ccuName").is(ccuName)
.and("roomId").is(roomId)
.and("name").regex(".*" + deviceName + ".*", "i"));
List<Device> device = mongoTemplate.find(regionQuery2, Device.class);
if (device.size() != 1) {
log.error("主机:{} 下的设备:{}有{}个",ccuName,deviceName,device.size());
return null;
}
devId = device.get(0).getDevId();
if(devId==null){
log.error("主机:{} 下的设备:{}的devId为空",ccuName,deviceName);
return null;
}
//查询设备状态
return mongoTemplate.findOne(Query.query(
Criteria.where("ccuName").is(ccuName)
.and("devId").is(devId)
), DeviceStatus.class);
}
}
package com.ikonke.konkeaialibabamcp.utils;
import cn.hutool.core.exceptions.StatefulException;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ikonke.konkeaialibabamcp.entity.mongodb.DeviceStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Map;
@Slf4j
@Component
public class KonkeIotUtils {
@Value("${konke.deviceCloud.baseUrl}")
private String baseUrl;
@Value("${konke.deviceCloud.appId}")
private String appId;
@Value("${konke.deviceCloud.appKey}")
private String appKey;
private final MongoTemplate mongoTemplate;
public KonkeIotUtils( MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public final static Map<String, String> actionMap = Map.of(
"3", "SwitchOpt"
);
/**
* 判断设备是否在线
*/
public boolean getDeviceOnline(String ccuName, String devId){
DeviceStatus one = mongoTemplate.findOne(Query.query(
Criteria.where("ccuName").is(ccuName)
.and("devId").is(devId)
), DeviceStatus.class);
return one != null && one.getStatus().containsKey("online") && "1".equals(one.getStatus().get("online").toString());
}
public String optDeviceOpen(String ccuName, String devId,String operateId){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
String action = null;
switch (operateId) {
case "1":
case "3":
case "4":
case "5":
case "7":
case "9":
case "10":
case "11":
case "12":
case "13":
case "14":
case "15":
case "17":
case "18":
case "201":
case "492":
case "493":
case "501":
case "502":
case "503":
case "504":
case "505":
case "506":
case "507":
case "508":
case "509":
case "510":
case "511":
case "513":
case "514":
case "515":
case "516":
case "517":
case "518":
case "519":
case "701":
action = "SwitchOpt";
break;
case "1001":
action = "MotorOpt";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("SwitchOpt".equals(action)){
opt.set("on", true);
}else if("MotorOpt".equals(action)){
opt.set("opt", "OPEN");
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
public String optDeviceClosed(String ccuName, String devId,String operateId){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
String action = null;
switch (operateId) {
case "1":
case "3":
case "4":
case "5":
case "7":
case "9":
case "10":
case "11":
case "12":
case "13":
case "14":
case "15":
case "17":
case "18":
case "201":
case "492":
case "493":
case "501":
case "502":
case "503":
case "504":
case "505":
case "506":
case "507":
case "508":
case "509":
case "510":
case "511":
case "513":
case "514":
case "515":
case "516":
case "517":
case "518":
case "519":
case "701":
action = "SwitchOpt";
break;
case "1001":
action = "MotorOpt";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("SwitchOpt".equals(action)){
opt.set("on", false);
}else if("MotorOpt".equals(action)){
opt.set("opt", "CLOSE");
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
// 如何控制设备
/**
* 1、查找设备是否存在
* 2、根据设备ID查询设备状态(判断是否已操作符合,比如已经开灯了)???
* 3、设备支持哪些操作?
* 4、根据设备ID操作设备
*/
public String optAIDevice(String ccuName, String devId, String action, String actionArg){
log.info("optAIDevice....ccuName:{},devId:{},action:{},actionArg:{}",ccuName,devId,action, JSONUtil.parseObj(actionArg));
JSONObject opt = new JSONObject();
opt.set("action", action);
opt.set("actionArg", JSONUtil.parseObj(actionArg));
return optDevice(ccuName, devId, opt.toString());
}
/**
* {"action":"SwitchOpt","actionArg":{"on":true}}
*/
public String optDevice(String ccuName, String devId, String requestMessage){
String url = baseUrl + "/1.0/app/ccu/" + ccuName + "/dev/" + devId + "/opt";
log.info("KonkeIotServer optDevice url {}, param {}, appId {}, appKey {}", url, requestMessage, appId, appKey);
String result2 = HttpRequest.post(url)
.header("appId", appId)
.header("appKey", appKey)
.body(requestMessage)
.execute().body();
log.info("KonkeIotServer optDevice result {}", result2);
return result2;
}
}
server:
port: 9998
spring:
main:
banner-mode: off
data:
mongodb:
uri: mongodb://user002:konke2025@172.17.13.1:27017,172.17.13.2:27017,172.17.13.3:27017/konke_iot?authSource=konke_iot&authMechanism=SCRAM-SHA-1
ai:
mcp:
server:
name: konke-ai-alibaba-mcp-server
version: 0.0.1
type: ASYNC # Recommended for reactive applications
# ?? sse ????????? /sse
# ???????? ip:port/sse/mcp
sse-endpoint: /sse
sse-message-endpoint: /mcp
capabilities:
tool: true
resource: true
prompt: true
completion: true
konke:
deviceCloud:
appId: 10001304
appKey: 0c350d82-aa95-46e1-91b8-b8d508b5226a
baseUrl: http://172.17.12.6:10000
package com.ikonke.konkeaialibabamcp;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class KonkeAiAlibabaMcpApplicationTests {
@Test
void contextLoads() {
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment