feat(edge): 添加机械臂关节空间运动功能和QUIC连接机器人标识支持

- 新增ARM_MOVE_TO_J动作类型支持关节空间运动
- 在QUIC协议中添加robotId字段用于机器人识别
- 扩展ArmService添加ExecuteJsonCommand RPC方法
- 更新设备列表获取逻辑使用新API接口
- 优化协议注释文档和设备类型定义
- 重构机械臂操作服务实现关节空间运动功能
This commit is contained in:
lixiaolong 2026-08-04 15:40:35 +08:00
parent c8b2c39d74
commit 7278d2c0f0
20 changed files with 13896 additions and 223 deletions

4
.gitignore vendored
View File

@ -45,3 +45,7 @@ nbdist/
!*/build/*.java
!*/build/*.html
!*/build/*.xml
# Local verification tests
/cmvr-iot-test/src/test/java/com/cmvr/test/flow/runtime/operator/edge/EdgeAgvOperateServiceTest.java
/cmvr-iot-test/src/test/java/com/cmvr/test/flow/runtime/operator/edge/EdgeArmOperateServiceTest.java

View File

@ -1,6 +1,7 @@
package com.cmvr.edge.client.service.impl;
import cmvr.api.Common;
import cmvr.api.ArmServiceGrpc;
import cmvr.api.SystemCommand;
import cmvr.api.SystemServiceGrpc;
import com.alibaba.fastjson2.JSON;
@ -63,8 +64,8 @@ public class EdgeSystemServiceImpl implements EdgeSystemService {
.setHeader(EdgeCommonUtil.buildRequest(request.getDeviceId()))
.setRequestJson(request.getRequestJson() == null ? "" : request.getRequestJson())
.build();
SystemServiceGrpc.SystemServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
request.getTerminalId(), SystemServiceGrpc.SystemServiceBlockingStub.class);
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
request.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
return stub.executeJsonCommand(grpcRequest);
}
@ -105,47 +106,69 @@ public class EdgeSystemServiceImpl implements EdgeSystemService {
}
@Override
public List<DeDeviceRegistration> deviceList(String terminalId) {
try {
//调用接口获取返回值对象
SystemCommand.GetSystemStatusCommand.Feedback response=getSystemStatus(terminalId);
SystemServiceGrpc.SystemServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
terminalId, SystemServiceGrpc.SystemServiceBlockingStub.class);
SystemCommand.GetDeviceListCommand.Feedback response = stub.getDeviceList(
SystemCommand.GetDeviceListCommand.Request.newBuilder().build());
// 获取设备列表
List<SystemCommand.DeviceList> deviceList = response.getDeviceListList();
// 如果设备列表不为空则处理设备信息
if (!deviceList.isEmpty()) {
// 创建Device对象列表用于存储转换后的设备信息
List<DeDeviceRegistration> devices = new ArrayList<>();
// 遍历设备列表将每个设备信息转换为Device对象
for (SystemCommand.DeviceList device : deviceList) {
DeDeviceRegistration d=new DeDeviceRegistration();
// 设置设备ID
d.setDeviceCode(device.getDeviceId());
// 设置设备类型将枚举类型转换为字符串
d.setDeviceModel(device.getDeviceType().name());
// 设置设备的终端ID
d.setIdDeDeviceTerminalConfig(terminalId);
devices.add(d);
List<SystemCommand.SystemDeviceInfo> deviceList = response.getDeviceListList();
List<DeDeviceRegistration> devices = new ArrayList<>(deviceList.size());
for (SystemCommand.SystemDeviceInfo device : deviceList) {
DeDeviceRegistration registration = new DeDeviceRegistration();
registration.setDeviceCode(device.getDeviceId());
registration.setDeviceModel(toDeviceModel(device));
registration.setIdDeDeviceTerminalConfig(terminalId);
devices.add(registration);
}
// 记录获取到的设备数量
logger.info("获取到设备列表,数量:{}", devices.size());
logger.info("获取到设备列表,数量:{},设备管理器:{} {}",
devices.size(), response.getManagerName(), response.getManagerVersion());
return devices;
} catch (Exception e) {
logger.error("调用设备列表接口异常", e);
throw new GlobalException("获取设备列表失败: " + e.getMessage());
}
}
// 如果响应为空或设备列表为空记录错误信息
logger.error("获取系统状态失败: {}", response != null ?
(response.getHeader() != null ? response.getHeader().getErrorMessage() : "响应头为空") : "响应为空");
return null;
}catch (Exception e){
// 捕获并记录调用过程中的异常
logger.error("调用系统状态接口异常", e);
return null;
private String toDeviceModel(SystemCommand.SystemDeviceInfo device) {
switch (device.getDeviceType()) {
case SYSTEM_DEVICE_TYPE_AGV:
return "AGV";
case SYSTEM_DEVICE_TYPE_ARM:
return "ARM";
case SYSTEM_DEVICE_TYPE_BATTERY:
return "Battery";
case SYSTEM_DEVICE_TYPE_BIO_HEAD:
return "BioHead";
case SYSTEM_DEVICE_TYPE_CAMERA:
return "Camera";
case SYSTEM_DEVICE_TYPE_CAN_BUS:
return "CanBus";
case SYSTEM_DEVICE_TYPE_DEX_HAND:
return "DexHand";
case SYSTEM_DEVICE_TYPE_GRIPPER:
return "Gripper";
case SYSTEM_DEVICE_TYPE_MICROPHONE:
return "Microphone";
case SYSTEM_DEVICE_TYPE_MOTOR:
return "Motor";
case SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM:
return "MotorSystem";
case SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER:
return "MujocoViewer";
case SYSTEM_DEVICE_TYPE_MUJOCO_WORLD:
return "MujocoWorld";
case SYSTEM_DEVICE_TYPE_ROBOT:
return "Robot";
case SYSTEM_DEVICE_TYPE_SPEAKER:
return "Speaker";
case SYSTEM_DEVICE_TYPE_UNSPECIFIED:
case UNRECOGNIZED:
default:
return device.getTypeName().isEmpty() ? "Unknown" : device.getTypeName();
}
}

View File

@ -449,6 +449,37 @@ public final class ArmServiceGrpc {
return getComputeForwardKinematicsMethod;
}
private static volatile io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "ExecuteJsonCommand",
requestType = cmvr.api.Common.JsonDeviceCommand.Request.class,
responseType = cmvr.api.Common.JsonDeviceCommand.Feedback.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod() {
io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request, cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod;
if ((getExecuteJsonCommandMethod = ArmServiceGrpc.getExecuteJsonCommandMethod) == null) {
synchronized (ArmServiceGrpc.class) {
if ((getExecuteJsonCommandMethod = ArmServiceGrpc.getExecuteJsonCommandMethod) == null) {
ArmServiceGrpc.getExecuteJsonCommandMethod = getExecuteJsonCommandMethod =
io.grpc.MethodDescriptor.<cmvr.api.Common.JsonDeviceCommand.Request, cmvr.api.Common.JsonDeviceCommand.Feedback>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "ExecuteJsonCommand"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.Common.JsonDeviceCommand.Request.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.Common.JsonDeviceCommand.Feedback.getDefaultInstance()))
.setSchemaDescriptor(new ArmServiceMethodDescriptorSupplier("ExecuteJsonCommand"))
.build();
}
}
}
return getExecuteJsonCommandMethod;
}
/**
* Creates a new async stub that supports all call types for the service
*/
@ -595,6 +626,16 @@ public final class ArmServiceGrpc {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getComputeForwardKinematicsMethod(), responseObserver);
}
/**
* <pre>
* Vendor-specific arm extension currently used for AUBO cabinet IO.
* </pre>
*/
public void executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getExecuteJsonCommandMethod(), responseObserver);
}
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
@ -695,6 +736,13 @@ public final class ArmServiceGrpc {
cmvr.api.ArmCommand.ComputeForwardKinematics.Request,
cmvr.api.ArmCommand.ComputeForwardKinematics.Response>(
this, METHODID_COMPUTE_FORWARD_KINEMATICS)))
.addMethod(
getExecuteJsonCommandMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
new MethodHandlers<
cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback>(
this, METHODID_EXECUTE_JSON_COMMAND)))
.build();
}
}
@ -824,6 +872,17 @@ public final class ArmServiceGrpc {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getComputeForwardKinematicsMethod(), getCallOptions()), request, responseObserver);
}
/**
* <pre>
* Vendor-specific arm extension currently used for AUBO cabinet IO.
* </pre>
*/
public void executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback> responseObserver) {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getExecuteJsonCommandMethod(), getCallOptions()), request, responseObserver);
}
}
/**
@ -937,6 +996,16 @@ public final class ArmServiceGrpc {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getComputeForwardKinematicsMethod(), getCallOptions(), request);
}
/**
* <pre>
* Vendor-specific arm extension currently used for AUBO cabinet IO.
* </pre>
*/
public cmvr.api.Common.JsonDeviceCommand.Feedback executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getExecuteJsonCommandMethod(), getCallOptions(), request);
}
}
/**
@ -1064,6 +1133,17 @@ public final class ArmServiceGrpc {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getComputeForwardKinematicsMethod(), getCallOptions()), request);
}
/**
* <pre>
* Vendor-specific arm extension currently used for AUBO cabinet IO.
* </pre>
*/
public com.google.common.util.concurrent.ListenableFuture<cmvr.api.Common.JsonDeviceCommand.Feedback> executeJsonCommand(
cmvr.api.Common.JsonDeviceCommand.Request request) {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getExecuteJsonCommandMethod(), getCallOptions()), request);
}
}
private static final int METHODID_TORQUE_OFF = 0;
@ -1080,6 +1160,7 @@ public final class ArmServiceGrpc {
private static final int METHODID_CALIBRATE_ZERO_Q = 11;
private static final int METHODID_GET_POSE_MATRIX = 12;
private static final int METHODID_COMPUTE_FORWARD_KINEMATICS = 13;
private static final int METHODID_EXECUTE_JSON_COMMAND = 14;
private static final class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
@ -1154,6 +1235,10 @@ public final class ArmServiceGrpc {
serviceImpl.computeForwardKinematics((cmvr.api.ArmCommand.ComputeForwardKinematics.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.ArmCommand.ComputeForwardKinematics.Response>) responseObserver);
break;
case METHODID_EXECUTE_JSON_COMMAND:
serviceImpl.executeJsonCommand((cmvr.api.Common.JsonDeviceCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback>) responseObserver);
break;
default:
throw new AssertionError();
}
@ -1229,6 +1314,7 @@ public final class ArmServiceGrpc {
.addMethod(getCalibrateZeroQMethod())
.addMethod(getGetPoseMatrixMethod())
.addMethod(getComputeForwardKinematicsMethod())
.addMethod(getExecuteJsonCommandMethod())
.build();
}
}

View File

@ -25,7 +25,7 @@ public final class ArmServiceOuterClass {
java.lang.String[] descriptorData = {
"\n\032cmvr/api/arm_service.proto\022\010cmvr.api\032\025" +
"cmvr/api/common.proto\032\032cmvr/api/arm_comm" +
"and.proto2\246\010\n\nArmService\022N\n\ttorqueOff\022\037." +
"and.proto2\207\t\n\nArmService\022N\n\ttorqueOff\022\037." +
"cmvr.api.CommandHeader.Request\032 .cmvr.ap" +
"i.CommandHeader.Feedback\022M\n\010torqueOn\022\037.c" +
"mvr.api.CommandHeader.Request\032 .cmvr.api" +
@ -51,8 +51,10 @@ public final class ArmServiceOuterClass {
"est\032 .cmvr.api.GetPoseMatrix.Response\022s\n" +
"\030computeForwardKinematics\022*.cmvr.api.Com" +
"puteForwardKinematics.Request\032+.cmvr.api" +
".ComputeForwardKinematics.Responseb\006prot" +
"o3"
".ComputeForwardKinematics.Response\022_\n\022Ex" +
"ecuteJsonCommand\022#.cmvr.api.JsonDeviceCo" +
"mmand.Request\032$.cmvr.api.JsonDeviceComma" +
"nd.Feedbackb\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,

View File

@ -10484,7 +10484,7 @@ public final class MotorCommand {
/**
* <pre>
* The PLC/driver watchdog is authoritative. This service watchdog prevents a
* The device/driver watchdog is authoritative. This service watchdog prevents a
* stalled gRPC client from retaining control indefinitely.
* </pre>
*
@ -10563,7 +10563,7 @@ public final class MotorCommand {
private int watchdogTimeoutMs_;
/**
* <pre>
* The PLC/driver watchdog is authoritative. This service watchdog prevents a
* The device/driver watchdog is authoritative. This service watchdog prevents a
* stalled gRPC client from retaining control indefinitely.
* </pre>
*
@ -11049,7 +11049,7 @@ public final class MotorCommand {
private int watchdogTimeoutMs_ ;
/**
* <pre>
* The PLC/driver watchdog is authoritative. This service watchdog prevents a
* The device/driver watchdog is authoritative. This service watchdog prevents a
* stalled gRPC client from retaining control indefinitely.
* </pre>
*
@ -11062,7 +11062,7 @@ public final class MotorCommand {
}
/**
* <pre>
* The PLC/driver watchdog is authoritative. This service watchdog prevents a
* The device/driver watchdog is authoritative. This service watchdog prevents a
* stalled gRPC client from retaining control indefinitely.
* </pre>
*
@ -11078,7 +11078,7 @@ public final class MotorCommand {
}
/**
* <pre>
* The PLC/driver watchdog is authoritative. This service watchdog prevents a
* The device/driver watchdog is authoritative. This service watchdog prevents a
* stalled gRPC client from retaining control indefinitely.
* </pre>
*

View File

@ -77,6 +77,37 @@ public final class SystemServiceGrpc {
return getGetSystemStatusMethod;
}
private static volatile io.grpc.MethodDescriptor<cmvr.api.SystemCommand.GetDeviceListCommand.Request,
cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> getGetDeviceListMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "GetDeviceList",
requestType = cmvr.api.SystemCommand.GetDeviceListCommand.Request.class,
responseType = cmvr.api.SystemCommand.GetDeviceListCommand.Feedback.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<cmvr.api.SystemCommand.GetDeviceListCommand.Request,
cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> getGetDeviceListMethod() {
io.grpc.MethodDescriptor<cmvr.api.SystemCommand.GetDeviceListCommand.Request, cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> getGetDeviceListMethod;
if ((getGetDeviceListMethod = SystemServiceGrpc.getGetDeviceListMethod) == null) {
synchronized (SystemServiceGrpc.class) {
if ((getGetDeviceListMethod = SystemServiceGrpc.getGetDeviceListMethod) == null) {
SystemServiceGrpc.getGetDeviceListMethod = getGetDeviceListMethod =
io.grpc.MethodDescriptor.<cmvr.api.SystemCommand.GetDeviceListCommand.Request, cmvr.api.SystemCommand.GetDeviceListCommand.Feedback>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetDeviceList"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.SystemCommand.GetDeviceListCommand.Request.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.SystemCommand.GetDeviceListCommand.Feedback.getDefaultInstance()))
.setSchemaDescriptor(new SystemServiceMethodDescriptorSupplier("GetDeviceList"))
.build();
}
}
}
return getGetDeviceListMethod;
}
private static volatile io.grpc.MethodDescriptor<cmvr.api.SystemCommand.UpdateParamsCommand.Request,
cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> getUpdateParamsMethod;
@ -108,37 +139,6 @@ public final class SystemServiceGrpc {
return getUpdateParamsMethod;
}
private static volatile io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "ExecuteJsonCommand",
requestType = cmvr.api.Common.JsonDeviceCommand.Request.class,
responseType = cmvr.api.Common.JsonDeviceCommand.Feedback.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod() {
io.grpc.MethodDescriptor<cmvr.api.Common.JsonDeviceCommand.Request, cmvr.api.Common.JsonDeviceCommand.Feedback> getExecuteJsonCommandMethod;
if ((getExecuteJsonCommandMethod = SystemServiceGrpc.getExecuteJsonCommandMethod) == null) {
synchronized (SystemServiceGrpc.class) {
if ((getExecuteJsonCommandMethod = SystemServiceGrpc.getExecuteJsonCommandMethod) == null) {
SystemServiceGrpc.getExecuteJsonCommandMethod = getExecuteJsonCommandMethod =
io.grpc.MethodDescriptor.<cmvr.api.Common.JsonDeviceCommand.Request, cmvr.api.Common.JsonDeviceCommand.Feedback>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "ExecuteJsonCommand"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.Common.JsonDeviceCommand.Request.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
cmvr.api.Common.JsonDeviceCommand.Feedback.getDefaultInstance()))
.setSchemaDescriptor(new SystemServiceMethodDescriptorSupplier("ExecuteJsonCommand"))
.build();
}
}
}
return getExecuteJsonCommandMethod;
}
private static volatile io.grpc.MethodDescriptor<cmvr.api.SystemCommand.StopAllCommand.Request,
cmvr.api.SystemCommand.StopAllCommand.Feedback> getStopAllMethod;
@ -234,16 +234,16 @@ public final class SystemServiceGrpc {
/**
*/
public void updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getUpdateParamsMethod(), responseObserver);
public void getDeviceList(cmvr.api.SystemCommand.GetDeviceListCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetDeviceListMethod(), responseObserver);
}
/**
*/
public void executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getExecuteJsonCommandMethod(), responseObserver);
public void updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getUpdateParamsMethod(), responseObserver);
}
/**
@ -269,6 +269,13 @@ public final class SystemServiceGrpc {
cmvr.api.SystemCommand.GetSystemStatusCommand.Request,
cmvr.api.SystemCommand.GetSystemStatusCommand.Feedback>(
this, METHODID_GET_SYSTEM_STATUS)))
.addMethod(
getGetDeviceListMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
new MethodHandlers<
cmvr.api.SystemCommand.GetDeviceListCommand.Request,
cmvr.api.SystemCommand.GetDeviceListCommand.Feedback>(
this, METHODID_GET_DEVICE_LIST)))
.addMethod(
getUpdateParamsMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
@ -276,13 +283,6 @@ public final class SystemServiceGrpc {
cmvr.api.SystemCommand.UpdateParamsCommand.Request,
cmvr.api.SystemCommand.UpdateParamsCommand.Feedback>(
this, METHODID_UPDATE_PARAMS)))
.addMethod(
getExecuteJsonCommandMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
new MethodHandlers<
cmvr.api.Common.JsonDeviceCommand.Request,
cmvr.api.Common.JsonDeviceCommand.Feedback>(
this, METHODID_EXECUTE_JSON_COMMAND)))
.addMethod(
getStopAllMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
@ -326,18 +326,18 @@ public final class SystemServiceGrpc {
/**
*/
public void updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> responseObserver) {
public void getDeviceList(cmvr.api.SystemCommand.GetDeviceListCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> responseObserver) {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getUpdateParamsMethod(), getCallOptions()), request, responseObserver);
getChannel().newCall(getGetDeviceListMethod(), getCallOptions()), request, responseObserver);
}
/**
*/
public void executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback> responseObserver) {
public void updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request,
io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> responseObserver) {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getExecuteJsonCommandMethod(), getCallOptions()), request, responseObserver);
getChannel().newCall(getUpdateParamsMethod(), getCallOptions()), request, responseObserver);
}
/**
@ -379,16 +379,16 @@ public final class SystemServiceGrpc {
/**
*/
public cmvr.api.SystemCommand.UpdateParamsCommand.Feedback updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request) {
public cmvr.api.SystemCommand.GetDeviceListCommand.Feedback getDeviceList(cmvr.api.SystemCommand.GetDeviceListCommand.Request request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getUpdateParamsMethod(), getCallOptions(), request);
getChannel(), getGetDeviceListMethod(), getCallOptions(), request);
}
/**
*/
public cmvr.api.Common.JsonDeviceCommand.Feedback executeJsonCommand(cmvr.api.Common.JsonDeviceCommand.Request request) {
public cmvr.api.SystemCommand.UpdateParamsCommand.Feedback updateParams(cmvr.api.SystemCommand.UpdateParamsCommand.Request request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getExecuteJsonCommandMethod(), getCallOptions(), request);
getChannel(), getUpdateParamsMethod(), getCallOptions(), request);
}
/**
@ -431,18 +431,18 @@ public final class SystemServiceGrpc {
/**
*/
public com.google.common.util.concurrent.ListenableFuture<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> updateParams(
cmvr.api.SystemCommand.UpdateParamsCommand.Request request) {
public com.google.common.util.concurrent.ListenableFuture<cmvr.api.SystemCommand.GetDeviceListCommand.Feedback> getDeviceList(
cmvr.api.SystemCommand.GetDeviceListCommand.Request request) {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getUpdateParamsMethod(), getCallOptions()), request);
getChannel().newCall(getGetDeviceListMethod(), getCallOptions()), request);
}
/**
*/
public com.google.common.util.concurrent.ListenableFuture<cmvr.api.Common.JsonDeviceCommand.Feedback> executeJsonCommand(
cmvr.api.Common.JsonDeviceCommand.Request request) {
public com.google.common.util.concurrent.ListenableFuture<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback> updateParams(
cmvr.api.SystemCommand.UpdateParamsCommand.Request request) {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getExecuteJsonCommandMethod(), getCallOptions()), request);
getChannel().newCall(getUpdateParamsMethod(), getCallOptions()), request);
}
/**
@ -456,8 +456,8 @@ public final class SystemServiceGrpc {
private static final int METHODID_GET_SYSTEM_INFO = 0;
private static final int METHODID_GET_SYSTEM_STATUS = 1;
private static final int METHODID_UPDATE_PARAMS = 2;
private static final int METHODID_EXECUTE_JSON_COMMAND = 3;
private static final int METHODID_GET_DEVICE_LIST = 2;
private static final int METHODID_UPDATE_PARAMS = 3;
private static final int METHODID_STOP_ALL = 4;
private static final class MethodHandlers<Req, Resp> implements
@ -485,14 +485,14 @@ public final class SystemServiceGrpc {
serviceImpl.getSystemStatus((cmvr.api.SystemCommand.GetSystemStatusCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.GetSystemStatusCommand.Feedback>) responseObserver);
break;
case METHODID_GET_DEVICE_LIST:
serviceImpl.getDeviceList((cmvr.api.SystemCommand.GetDeviceListCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.GetDeviceListCommand.Feedback>) responseObserver);
break;
case METHODID_UPDATE_PARAMS:
serviceImpl.updateParams((cmvr.api.SystemCommand.UpdateParamsCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.UpdateParamsCommand.Feedback>) responseObserver);
break;
case METHODID_EXECUTE_JSON_COMMAND:
serviceImpl.executeJsonCommand((cmvr.api.Common.JsonDeviceCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.Common.JsonDeviceCommand.Feedback>) responseObserver);
break;
case METHODID_STOP_ALL:
serviceImpl.stopAll((cmvr.api.SystemCommand.StopAllCommand.Request) request,
(io.grpc.stub.StreamObserver<cmvr.api.SystemCommand.StopAllCommand.Feedback>) responseObserver);
@ -560,8 +560,8 @@ public final class SystemServiceGrpc {
.setSchemaDescriptor(new SystemServiceFileDescriptorSupplier())
.addMethod(getGetSystemInfoMethod())
.addMethod(getGetSystemStatusMethod())
.addMethod(getGetDeviceListMethod())
.addMethod(getUpdateParamsMethod())
.addMethod(getExecuteJsonCommandMethod())
.addMethod(getStopAllMethod())
.build();
}

View File

@ -24,29 +24,26 @@ public final class SystemServiceOuterClass {
static {
java.lang.String[] descriptorData = {
"\n\035cmvr/api/system_service.proto\022\010cmvr.ap" +
"i\032\025cmvr/api/common.proto\032\035cmvr/api/syste" +
"m_command.proto2\363\003\n\rSystemService\022b\n\rGet" +
"SystemInfo\022&.cmvr.api.GetSystemInfoComma" +
"nd.Request\032\'.cmvr.api.GetSystemInfoComma" +
"nd.Feedback\"\000\022h\n\017GetSystemStatus\022(.cmvr." +
"api.GetSystemStatusCommand.Request\032).cmv" +
"r.api.GetSystemStatusCommand.Feedback\"\000\022" +
"_\n\014UpdateParams\022%.cmvr.api.UpdateParamsC" +
"ommand.Request\032&.cmvr.api.UpdateParamsCo" +
"mmand.Feedback\"\000\022a\n\022ExecuteJsonCommand\022#" +
".cmvr.api.JsonDeviceCommand.Request\032$.cm" +
"vr.api.JsonDeviceCommand.Feedback\"\000\022P\n\007S" +
"topAll\022 .cmvr.api.StopAllCommand.Request" +
"\032!.cmvr.api.StopAllCommand.Feedback\"\000b\006p" +
"roto3"
"i\032\035cmvr/api/system_command.proto2\364\003\n\rSys" +
"temService\022b\n\rGetSystemInfo\022&.cmvr.api.G" +
"etSystemInfoCommand.Request\032\'.cmvr.api.G" +
"etSystemInfoCommand.Feedback\"\000\022h\n\017GetSys" +
"temStatus\022(.cmvr.api.GetSystemStatusComm" +
"and.Request\032).cmvr.api.GetSystemStatusCo" +
"mmand.Feedback\"\000\022b\n\rGetDeviceList\022&.cmvr" +
".api.GetDeviceListCommand.Request\032\'.cmvr" +
".api.GetDeviceListCommand.Feedback\"\000\022_\n\014" +
"UpdateParams\022%.cmvr.api.UpdateParamsComm" +
"and.Request\032&.cmvr.api.UpdateParamsComma" +
"nd.Feedback\"\000\022P\n\007StopAll\022 .cmvr.api.Stop" +
"AllCommand.Request\032!.cmvr.api.StopAllCom" +
"mand.Feedback\"\000b\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
cmvr.api.Common.getDescriptor(),
cmvr.api.SystemCommand.getDescriptor(),
});
cmvr.api.Common.getDescriptor();
cmvr.api.SystemCommand.getDescriptor();
}

View File

@ -20,4 +20,7 @@ service ArmService {
rpc calibrateZeroQ(CalibrateZeroQ.Request) returns (CalibrateZeroQ.Response);
rpc getPoseMatrix(GetPoseMatrix.Request) returns (GetPoseMatrix.Response);
rpc computeForwardKinematics(ComputeForwardKinematics.Request) returns (ComputeForwardKinematics.Response);
// Vendor-specific arm extension currently used for AUBO cabinet IO.
rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback);
}

View File

@ -101,7 +101,7 @@ message SetMotorEnabledRequest {
message CyclicStreamOpen {
MotorTarget target = 1;
// The PLC/driver watchdog is authoritative. This service watchdog prevents a
// The device/driver watchdog is authoritative. This service watchdog prevents a
// stalled gRPC client from retaining control indefinitely.
uint32 watchdog_timeout_ms = 2;
}

View File

@ -21,6 +21,77 @@ message DeviceList {
DeviceType device_type = 2;
}
// Stable device categories used by GetDeviceList. This intentionally does not
// reuse the legacy DeviceType enum above: its zero value is AGV and it does not
// cover all DeviceManager categories.
enum SystemDeviceType {
SYSTEM_DEVICE_TYPE_UNSPECIFIED = 0;
SYSTEM_DEVICE_TYPE_AGV = 1;
SYSTEM_DEVICE_TYPE_ARM = 2;
SYSTEM_DEVICE_TYPE_BATTERY = 3;
SYSTEM_DEVICE_TYPE_BIO_HEAD = 4;
SYSTEM_DEVICE_TYPE_CAMERA = 5;
SYSTEM_DEVICE_TYPE_CAN_BUS = 6;
SYSTEM_DEVICE_TYPE_DEX_HAND = 7;
SYSTEM_DEVICE_TYPE_GRIPPER = 8;
SYSTEM_DEVICE_TYPE_MICROPHONE = 9;
SYSTEM_DEVICE_TYPE_MOTOR = 10;
SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM = 11;
SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER = 12;
SYSTEM_DEVICE_TYPE_MUJOCO_WORLD = 13;
SYSTEM_DEVICE_TYPE_ROBOT = 14;
SYSTEM_DEVICE_TYPE_SPEAKER = 15;
}
enum SystemDeviceState {
SYSTEM_DEVICE_STATE_UNSPECIFIED = 0;
SYSTEM_DEVICE_STATE_DISABLED = 1;
SYSTEM_DEVICE_STATE_INITIALIZING = 2;
SYSTEM_DEVICE_STATE_REGISTERED = 3;
SYSTEM_DEVICE_STATE_READY = 4;
SYSTEM_DEVICE_STATE_RUNNING = 5;
SYSTEM_DEVICE_STATE_STOPPED = 6;
SYSTEM_DEVICE_STATE_ERROR = 7;
}
enum SystemDeviceHealth {
SYSTEM_DEVICE_HEALTH_UNSPECIFIED = 0;
SYSTEM_DEVICE_HEALTH_HEALTHY = 1;
SYSTEM_DEVICE_HEALTH_DEGRADED = 2;
SYSTEM_DEVICE_HEALTH_FAULT = 3;
}
message SystemDeviceInfo {
string device_id = 1;
SystemDeviceType device_type = 2;
// Concrete backend name for display and diagnostics only. Consumers must
// use device_type, rather than this free-form string, for decisions.
string type_name = 3;
// GetDeviceList currently publishes only enabled entries. Keep this field
// explicit so each row remains self-describing and future-compatible.
bool enabled = 4;
SystemDeviceState manager_state = 5;
SystemDeviceHealth health = 6;
bool has_error = 7;
string error_message = 8;
uint64 status_updated_at_unix_ms = 9;
}
message GetDeviceListCommand {
message Request {}
message Feedback {
CommandHeader.Feedback header = 1;
string manager_name = 2;
string manager_version = 3;
string manager_description = 4;
repeated SystemDeviceInfo device_list = 5;
uint64 sampled_at_unix_ms = 6;
}
}
message GetSystemInfoCommand {
message Request {}

View File

@ -1,6 +1,5 @@
syntax = "proto3";
import "cmvr/api/common.proto";
import "cmvr/api/system_command.proto";
package cmvr.api;
@ -9,9 +8,9 @@ package cmvr.api;
service SystemService {
rpc GetSystemInfo(GetSystemInfoCommand.Request) returns (GetSystemInfoCommand.Feedback) {}
rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {}
rpc GetDeviceList(GetDeviceListCommand.Request) returns (GetDeviceListCommand.Feedback) {}
rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {}
rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback) {}
rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {}
}

View File

@ -2,11 +2,10 @@ syntax = "proto3";
package cmvr.quic_edge.v1;
option java_package = "com.cmvr.quic.edge.v1";
option java_multiple_files = true;
option java_outer_classname = "QuicEdgeProtocol";
// cmvr-es提交428ee328中的QUIC edge v1协议保持线兼容
// Every application message on the edge-opened reliable bidirectional stream
// is carried by this envelope. message_sequence is strictly increasing per
// sender (gaps are allowed) and is independent from the heartbeat sequence used
// for liveness acknowledgement.
message EdgeControlEnvelope {
uint32 protocol_version = 1;
uint64 message_sequence = 2;
@ -16,9 +15,11 @@ message EdgeControlEnvelope {
NodeRegisterResponse node_register_response = 11;
NodeHeartbeat node_heartbeat = 12;
NodeHeartbeatAck node_heartbeat_ack = 13;
MediaSessionOpen media_session_open = 20;
MediaTrackDescriptor media_track_descriptor = 21;
MediaSessionClose media_session_close = 22;
ProtocolError protocol_error = 30;
}
}
@ -29,18 +30,24 @@ message NetworkInterfaceAddress {
ADDRESS_FAMILY_IPV4 = 1;
ADDRESS_FAMILY_IPV6 = 2;
}
string interface_name = 1;
string ip_address = 2;
AddressFamily family = 3;
bool loopback = 4;
}
// The existing cmvr-es gRPC server remains the robot-control endpoint. The
// edge advertises its current reachable address through the QUIC control plane.
message GrpcEndpoint {
string host = 1;
uint32 port = 2;
bool tls = 3;
}
// Stable protocol-level categories for devices managed by cmvr-es. These
// values intentionally do not reuse the configuration or gRPC API enums:
// their zero values and supported categories have different semantics.
enum DeviceKind {
DEVICE_KIND_UNSPECIFIED = 0;
DEVICE_KIND_AGV = 1;
@ -58,6 +65,8 @@ enum DeviceKind {
DEVICE_KIND_SPEAKER = 13;
}
// DeviceManager's view of a configured entry. REGISTERED means that the
// manager owns a device record but has no more specific lifecycle signal.
enum ManagedDeviceState {
MANAGED_DEVICE_STATE_UNSPECIFIED = 0;
MANAGED_DEVICE_STATE_DISABLED = 1;
@ -69,6 +78,8 @@ enum ManagedDeviceState {
MANAGED_DEVICE_STATE_ERROR = 7;
}
// Health is independent of lifecycle. UNSPECIFIED means that no trustworthy
// health observation is available and must never be interpreted as healthy.
enum DeviceHealthStatus {
DEVICE_HEALTH_STATUS_UNSPECIFIED = 0;
DEVICE_HEALTH_STATUS_HEALTHY = 1;
@ -79,12 +90,24 @@ enum DeviceHealthStatus {
message ManagedDeviceStatus {
string device_id = 1;
DeviceKind kind = 2;
// Concrete implementation name when a device object exists; otherwise a
// category label. It is for display/diagnostics only. Consumers use kind,
// rather than this free-form string, for machine decisions.
string type_name = 3;
bool enabled = 4;
ManagedDeviceState manager_state = 5;
DeviceHealthStatus health = 6;
// false means that no error is currently confirmed. It does not turn
// DEVICE_HEALTH_STATUS_UNSPECIFIED into a healthy observation.
bool has_error = 7;
string error_message = 8;
// Time at which DeviceManager last changed the lifecycle/error record.
// DeviceManagerSnapshot.sampled_at_unix_ms is the freshness timestamp for
// the health observation carried by this heartbeat.
uint64 status_updated_at_unix_ms = 9;
}
@ -92,6 +115,9 @@ message DeviceManagerSnapshot {
string manager_name = 1;
string manager_version = 2;
string manager_description = 3;
// Current cmvr-es senders include only enabled devices. The enabled field in
// each row and DISABLED enum value remain part of v1 for wire compatibility.
repeated ManagedDeviceStatus devices = 4;
uint64 sampled_at_unix_ms = 5;
}
@ -102,8 +128,11 @@ message NodeDescriptor {
string software_version = 3;
repeated NetworkInterfaceAddress local_interfaces = 4;
GrpcEndpoint grpc_endpoint = 5;
string robot_id = 6;
}
// This must be the first application message sent after each QUIC connection
// is established. A reconnect always creates a new registration session.
message NodeRegisterRequest {
NodeDescriptor node = 1;
uint64 sent_at_unix_ms = 2;
@ -113,10 +142,17 @@ message NodeRegisterResponse {
bool accepted = 1;
string session_id = 2;
string message = 3;
// Zero tells the edge to keep its locally configured interval.
uint32 heartbeat_interval_ms = 4;
// Derived by the receiver from the authenticated QUIC peer address. It is
// not copied from a client-supplied local interface.
string observed_source_ip = 5;
}
// A heartbeat carries a fresh network snapshot so address changes are reported
// without opening a second protocol or connection.
message NodeHeartbeat {
string node_id = 1;
string boot_id = 2;
@ -127,6 +163,7 @@ message NodeHeartbeat {
repeated NetworkInterfaceAddress local_interfaces = 7;
GrpcEndpoint grpc_endpoint = 8;
DeviceManagerSnapshot device_manager = 9;
string robot_id = 10;
}
message NodeHeartbeatAck {
@ -141,6 +178,9 @@ message NodeHeartbeatAck {
message MediaSessionOpen {
string node_id = 1;
uint64 session_epoch = 2;
// Binds the media epoch to the accepted node registration on this QUIC
// connection. Media must not start before this session is assigned.
string session_id = 3;
}
@ -156,14 +196,25 @@ message MediaTrackDescriptor {
string device_id = 3;
string codec = 4;
uint64 codec_generation = 5;
// The exact MediaSourceHub track and the 32-bit token repeated in every
// DATAGRAM header. The full generation remains on the reliable stream.
string source_track_id = 6;
uint32 codec_generation_token = 7;
string payload_format = 8;
// Video fields. They are zero for audio tracks.
uint32 width = 10;
uint32 height = 11;
uint32 frames_per_second = 12;
// Audio fields. They are zero for video tracks.
uint32 sample_rate = 20;
uint32 channels = 21;
// Decoder initialization bytes, for example AVCC/HVCC or AudioSpecificConfig.
// Existing cmvr-es sources may leave this empty when configuration NAL units
// are carried in-band.
bytes codec_config = 30;
}

View File

@ -35,6 +35,7 @@ message NodeSnapshot {
DeviceManagerSnapshot device_manager = 12;
uint64 media_session_epoch = 13;
repeated MediaTrack tracks = 14;
string robot_id = 15;
}
message GrpcEndpoint {

View File

@ -1,15 +1,18 @@
package com.cmvr.quic.gateway.core;
import com.cmvr.quic.edge.v1.DeviceManagerSnapshot;
import com.cmvr.quic.edge.v1.EdgeControlEnvelope;
import com.cmvr.quic.edge.v1.GrpcEndpoint;
import com.cmvr.quic.edge.v1.ManagedDeviceStatus;
import com.cmvr.quic.edge.v1.MediaSessionOpen;
import com.cmvr.quic.edge.v1.MediaTrackDescriptor;
import com.cmvr.quic.edge.v1.NetworkInterfaceAddress;
import com.cmvr.quic.edge.v1.NodeDescriptor;
import com.cmvr.quic.edge.v1.NodeHeartbeat;
import com.cmvr.quic.edge.v1.NodeRegisterRequest;
import cmvr.quic_edge.v1.QuicEdge.DeviceManagerSnapshot;
import cmvr.quic_edge.v1.QuicEdge.EdgeControlEnvelope;
import cmvr.quic_edge.v1.QuicEdge.GrpcEndpoint;
import cmvr.quic_edge.v1.QuicEdge.ManagedDeviceStatus;
import cmvr.quic_edge.v1.QuicEdge.MediaSessionOpen;
import cmvr.quic_edge.v1.QuicEdge.MediaTrackDescriptor;
import cmvr.quic_edge.v1.QuicEdge.NetworkInterfaceAddress;
import cmvr.quic_edge.v1.QuicEdge.NodeDescriptor;
import cmvr.quic_edge.v1.QuicEdge.NodeHeartbeat;
import cmvr.quic_edge.v1.QuicEdge.NodeHeartbeatAck;
import cmvr.quic_edge.v1.QuicEdge.NodeRegisterRequest;
import cmvr.quic_edge.v1.QuicEdge.NodeRegisterResponse;
import cmvr.quic_edge.v1.QuicEdge.ProtocolError;
import com.cmvr.quic.gateway.config.GatewayConfig;
import com.cmvr.quic.gateway.protocol.DatagramHeader;
import com.cmvr.quic.gateway.protocol.MediaReassembler;
@ -61,6 +64,7 @@ public final class ConnectionSession {
private String bootId = "";
private String sessionId = "";
private String softwareVersion = "";
private String robotId = "";
private long registeredAtMs;
private long lastHeartbeatAtMs;
private long heartbeatSequence;
@ -142,6 +146,7 @@ public final class ConnectionSession {
registered = true;
nodeId = node.getNodeId();
bootId = node.getBootId();
robotId = node.getRobotId();
sessionId = UUID.randomUUID().toString();
softwareVersion = node.getSoftwareVersion();
localInterfaces = node.getLocalInterfacesList();
@ -152,7 +157,7 @@ public final class ConnectionSession {
EdgeControlEnvelope response = EdgeControlEnvelope.newBuilder()
.setProtocolVersion(PROTOCOL_VERSION)
.setMessageSequence(nextOutboundSequence())
.setNodeRegisterResponse(com.cmvr.quic.edge.v1.NodeRegisterResponse.newBuilder()
.setNodeRegisterResponse(NodeRegisterResponse.newBuilder()
.setAccepted(true)
.setSessionId(sessionId)
.setMessage("accepted by cmvr Java QUIC gateway")
@ -160,9 +165,9 @@ public final class ConnectionSession {
.setObservedSourceIp(observedSourceIp))
.build();
send(response);
log.info("QUIC终端注册成功nodeId={}bootId={}sessionId={}remoteIp={}"
log.info("QUIC终端注册成功nodeId={}robotId={}bootId={}sessionId={}remoteIp={}"
+ "grpcAdvertised={}:{}grpcEffective={}:{}",
nodeId, bootId, sessionId, observedSourceIp,
nodeId, robotId, bootId, sessionId, observedSourceIp,
grpcEndpoint.getHost(), grpcEndpoint.getPort(),
observedSourceIp, grpcEndpoint.getPort());
}
@ -179,6 +184,9 @@ public final class ConnectionSession {
heartbeatSequence = heartbeat.getSequence();
lastHeartbeatAtMs = System.currentTimeMillis();
softwareVersion = heartbeat.getSoftwareVersion();
if (!heartbeat.getRobotId().isEmpty()) {
robotId = heartbeat.getRobotId();
}
localInterfaces = heartbeat.getLocalInterfacesList();
grpcEndpoint = heartbeat.getGrpcEndpoint();
if (heartbeat.hasDeviceManager()) {
@ -191,7 +199,7 @@ public final class ConnectionSession {
send(EdgeControlEnvelope.newBuilder()
.setProtocolVersion(PROTOCOL_VERSION)
.setMessageSequence(nextOutboundSequence())
.setNodeHeartbeatAck(com.cmvr.quic.edge.v1.NodeHeartbeatAck.newBuilder()
.setNodeHeartbeatAck(NodeHeartbeatAck.newBuilder()
.setAccepted(true)
.setAcknowledgedSequence(heartbeat.getSequence())
.setMessage("ok")
@ -207,10 +215,10 @@ public final class ConnectionSession {
&& heartbeatMessagesReceived % HEARTBEAT_LOG_INTERVAL != 0) {
return;
}
log.info("收到QUIC终端心跳nodeId={}sessionId={}count={}sequence={}"
log.info("收到QUIC终端心跳nodeId={}robotId={}sessionId={}count={}sequence={}"
+ "sentAtUnixMs={}softwareVersion={}interfaces={}"
+ "grpcAdvertised={}:{}grpcEffective={}:{}devices={}",
nodeId, sessionId, heartbeatMessagesReceived, heartbeat.getSequence(),
nodeId, robotId, sessionId, heartbeatMessagesReceived, heartbeat.getSequence(),
heartbeat.getSentAtUnixMs(), softwareVersion, localInterfaces.size(),
grpcEndpoint.getHost(), grpcEndpoint.getPort(), observedSourceIp,
grpcEndpoint.getPort(), deviceManager.getDevicesCount());
@ -365,7 +373,7 @@ public final class ConnectionSession {
EdgeControlEnvelope response = EdgeControlEnvelope.newBuilder()
.setProtocolVersion(PROTOCOL_VERSION)
.setMessageSequence(nextOutboundSequence())
.setProtocolError(com.cmvr.quic.edge.v1.ProtocolError.newBuilder()
.setProtocolError(ProtocolError.newBuilder()
.setCode(PROTOCOL_ERROR_CODE)
.setMessage(message)
.setRelatedMessageSequence(relatedSequence)
@ -380,6 +388,7 @@ public final class ConnectionSession {
.setBootId(bootId)
.setSessionId(sessionId)
.setSoftwareVersion(softwareVersion)
.setRobotId(robotId)
.setOnline(online)
.setRegisteredAtUnixMs(registeredAtMs)
.setLastHeartbeatAtUnixMs(lastHeartbeatAtMs)

View File

@ -1,6 +1,6 @@
package com.cmvr.quic.gateway.server;
import com.cmvr.quic.edge.v1.EdgeControlEnvelope;
import cmvr.quic_edge.v1.QuicEdge.EdgeControlEnvelope;
import com.cmvr.quic.gateway.core.ConnectionSession;
import com.cmvr.quic.gateway.protocol.ControlFrameDecoder;
import com.google.protobuf.InvalidProtocolBufferException;

View File

@ -61,6 +61,7 @@ public enum ActionEnum {
TOUCH("EDGE", "TOUCH", "触控"),
// 末端运动
ARM_MOVE_TO_POINT("EDGE", "ARM_MOVE_TO_POINT", "运动到指定点"),
ARM_MOVE_TO_J("EDGE", "ARM_MOVE_TO_J", "关节空间运动"),
// --------------- 头部 ---------------
BIO_HEAD_SPEAK_START("EDGE", "BIO_HEAD_SPEAK_START", "开始说话"),

View File

@ -1,5 +1,7 @@
package com.cmvr.test.flow.runtime.operator.edge;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.EdgeCommonVO;
@ -91,10 +93,70 @@ public class EdgeArmOperateService implements EdgeOperateService {
);
log.info("机械臂末端运动任务下发成功");
} else if (action == ActionEnum.ARM_MOVE_TO_J) {
executeMoveJ(edgeCommonVO, inputParams);
} else {
throw new GlobalException("不支持的机械臂操作类型: " + action);
}
return TaskNodeExecuteResult.success();
}
private void executeMoveJ(EdgeCommonVO edgeCommonVO, JSONObject inputParams) {
double[] target = parseDoubleArray(inputParams.get("target"), "target", true);
double[] jointVelocityLimits = parseDoubleArray(
inputParams.get("jointVelocityLimits"), "jointVelocityLimits", false);
log.info("执行机械臂关节空间运动设备ID: {},目标关节数: {}",
edgeCommonVO.getDeviceId(), target.length);
edgeArmService.moveJ(
edgeCommonVO,
target,
inputParams.getDouble("velocity"),
inputParams.getDouble("acceleration"),
inputParams.getDouble("blendRadius"),
jointVelocityLimits,
inputParams.getBoolean("asynchronous")
);
log.info("机械臂关节空间运动任务下发成功设备ID: {}", edgeCommonVO.getDeviceId());
}
private double[] parseDoubleArray(Object value, String parameterName, boolean required) {
if (value == null || value instanceof String && ((String) value).trim().isEmpty()) {
if (required) {
throw new GlobalException("参数" + parameterName + "不能为空");
}
return null;
}
try {
JSONArray array;
if (value instanceof String) {
String text = ((String) value).trim();
array = JSON.parseArray(text.startsWith("[") ? text : "[" + text + "]");
} else {
array = JSON.parseArray(JSON.toJSONString(value));
}
if (array == null || array.isEmpty()) {
if (required) {
throw new GlobalException("参数" + parameterName + "不能为空");
}
return null;
}
double[] result = new double[array.size()];
for (int index = 0; index < array.size(); index++) {
Double number = array.getDouble(index);
if (number == null) {
throw new GlobalException("参数" + parameterName + "的第" + (index + 1) + "项不是有效数字");
}
result[index] = number;
}
return result;
} catch (GlobalException exception) {
throw exception;
} catch (RuntimeException exception) {
throw new GlobalException("参数" + parameterName + "必须是数字数组,例如[0,0,0,0,0,0]");
}
}
}

View File

@ -14,13 +14,13 @@ import com.cmvr.edge.client.service.EdgeHlcService;
import com.cmvr.edge.client.service.EdgeMicrophoneService;
import com.cmvr.edge.client.service.EdgeSpeakerService;
import com.cmvr.edge.client.service.EdgeAgvService;
import com.cmvr.edge.client.service.EdgeArmService;
import com.cmvr.device.service.InspectionAlertListenService;
import com.cmvr.llm.service.LLMAiAgentPlatformService;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.operator.edge.EdgeManualInspectionOperateService;
import com.cmvr.test.flow.runtime.operator.edge.EdgeDeviceCommandOperateService;
import com.cmvr.test.flow.runtime.operator.edge.EdgeArmOperateService;
import com.cmvr.test.flow.runtime.operator.llm.InspectionMeterRecognizeOperateService;
import com.cmvr.test.model.vo.FlowActionRequestVO;
import lombok.RequiredArgsConstructor;
@ -39,7 +39,7 @@ public class FlowActionExecutorService {
private final LLMAiAgentPlatformService llmAiAgentPlatformService;
private final EdgeAgvService edgeAgvService;
private final InspectionAlertListenService inspectionAlertListenService;
private final EdgeArmService edgeArmService;
private final EdgeArmOperateService edgeArmOperateService;
private final EdgeManualInspectionOperateService edgeManualInspectionOperateService;
private final EdgeDeviceCommandOperateService edgeDeviceCommandOperateService;
private final InspectionMeterRecognizeOperateService inspectionMeterRecognizeOperateService;
@ -117,7 +117,14 @@ public class FlowActionExecutorService {
// ==== 机械臂 ====
case ARM_MOVE_TO_POINT:
return executeArmMoveToPoint(edgeCommonVO, payload);
case ARM_MOVE_TO_J: {
TaskNodeExecuteMessage message = buildSingleNodeMessage(action, payload);
message.setTerminalId(terminalId);
edgeArmOperateService.execute(message);
return action == ActionEnum.ARM_MOVE_TO_J
? "机械臂关节空间运动任务下发成功"
: "机械臂末端运动任务下发成功";
}
// ==== 语料 ====
case VI_PLAY_CORPUS:
@ -185,43 +192,6 @@ public class FlowActionExecutorService {
}
}
private String executeArmMoveToPoint(EdgeCommonVO edgeCommonVO, JSONObject payload) {
Double x = payload.getDouble("x");
Double y = payload.getDouble("y");
Double z = payload.getDouble("z");
Double rx = payload.getDouble("rx");
Double ry = payload.getDouble("ry");
Double rz = payload.getDouble("rz");
String frame = payload.getString("frame");
Double velocity = payload.getDouble("velocity");
Double acceleration = payload.getDouble("acceleration");
Double blendRadius = payload.getDouble("blendRadius");
if (x == null || y == null || z == null) {
throw new GlobalException("X、Y、Z坐标不能为空");
}
if (rx == null || ry == null || rz == null) {
throw new GlobalException("RX、RY、RZ旋转角度不能为空");
}
// 单节点执行和正式工作流保持一致先开启力矩再下发笛卡尔直线运动
edgeArmService.torqueOn(edgeCommonVO);
edgeArmService.moveL(
edgeCommonVO,
x,
y,
z,
rx,
ry,
rz,
frame,
velocity,
acceleration,
blendRadius
);
return "机械臂末端运动任务下发成功";
}
private String executeInspectionAlertAction(ActionEnum action, JSONObject payload) {
String terminalId = payload.getString("terminalId");
java.util.List<String> types = resolveInspectionAlertEventTypes(payload);