OneNET Studio 简介 产品介绍 快速入门 设备接入与管理 应用开发
应用开发流程 新建项目 添加设备 设备分组 场景联动 应用数据流转 应用API 应用长连接 规则引擎(新)
运维监控 数据可视化 边缘计算 人工智能AI 位置定位 语音通话 工业互联网标识 权限管理 服务协议 更新日志

最佳实践-代理直连设备

1. 修改配置文件

2. 开发业务代码,启动服务

3. 接入设备并登陆

4. 上传设备数据

5. 下发命令及回复响应

6. 登出设备

---

示例代码工程源码(Java):点击下载

工程包括:

  • protocol-adapter-sdk-example-gateway:使用SDK接入私有协议网关子设备;
  • protocol-adapter-sdk-example-with-http:云云对接方案示例,示例使用HTTP作为接收数据的接口;
  • protocol-adapter-sdk-example-with-tcp-protocol-hub:包含TCP协议站模块和适配SDK模块的示例,演示如何将私有协议设备接入到OneNET平台。

以下以protocol-adapter-sdk-example-with-tcp-protocol-hub为例,示例模拟了一台私有协议的风扇设备使用泛协议接入SDK接入OneNET上传数据和下发命令的过程,使用IntelliJ IDEA 2020.1.3 (Ultimate Edition) ,示例中的配置仅供参考,运行时需要修改为您自己的泛协议接入服务的相关配置信息。 示例项目结构如下图所示:

|-- src 
   |-- main 
      |-- java 
         |-- com.github.cm.heclouds.adapter
            |-- adapter
               |-- SampleDeviceDownLinkHandler.java  平台下行数据处理
               |-- SampleDeviceUpLinkHandler.java    设备上行数据处理
            |-- tlv
               |-- Tlv.java                          示例使用的私有协议格式
               |-- TlvDecoder.java                   私有协议解码
               |-- TlvEncoder.java                   私有协议编码
            |-- ProtocolAdapterSDKDemo.java          程序入口类
         |-- resources
            |-- config
               |-- adapter.conf                      SDK配置
               |-- devices.conf                      设备映射配置
               |-- protocolhub-tcp.conf              TCP协议站配置
            |-- logback.xml                          自定义日志配置

|-- pom.xml

本示例定义了一种简单的仅供测试使用的自定义协议格式,为便于测试,协议忽略了SDK对于设备上行数据的响应: 数据格式:TLV,大端模式编码

含义 数据长度 说明
Type 操作类型 2字节(16bit) 1-上线,2-上传数据,3-响应下发命令,9-下线,11-下发命令
Length 数据长度 2字节(16bit) 指定数据payload长度
Value 数据 长度由Length指定 数据payload,JSON类型,详情如下:
  • 设备上线(type=1):
{
  "device_name": "a"
}
  • 设备上传数据(type=2):
{
  "status": 1,        
  "gear": 3,          
  "duration": 20000    
}
字段 含义
status 电风扇状态: 0-关,1-开
gear 电风扇档位:1-低速,2-中速,3-高速
duration 电风扇已开启持续时间: 单位ms
  • 给设备下发命令(type=11):
{
  "id": "1609916964",
  "status": 0,        
  "gear": 1    
}
  • 设备响应下发命令(type=3):
{
  "id": "1609916964",
  "code": 200,
  "msg": "success"
}
字段 含义
id 命令id
code 响应状态:200-成功
msg 响应信息
  • 设备下线(type=9):
{}

1. 修改配置文件

1).新建并修改src/main/resources/config文件夹下的adapter.conf和device.conf文件:
可参考最佳实践-云云对接

2).新建并修改项目src/main/resources/config文件夹下的protocolhub-tcp.conf文件:

3).新建程序入口类ProtocolAdapterSdkDemo.java

2. 开发业务代码,启动SDK服务

1).编写TlvDecoder.java和TlvEncoder.java,实现自定义协议的编解码,详见示例源码。此外,也可以在步骤2中实现对自定义协议的编解码。
2).编写SampleDeviceUpLinkHandler.java和SampleDeviceDownLinkHandler,实现自定义协议设备上下行数据处理接口。

  • SampleDeviceUpLinkHandler.java: 按照自定义协议进行业务操作,业务操作主要使用如下所示的SDK提供的通用API与自动生成的物模型扩展API,其它业务代码可参考示例源码。
    自定义协议设备连接到TCP协议站后,首先应建立与OneNET设备的映射,即在#init(Object, Channel)方法中生成并返回OneNET设备实体Device,这是进行后续业务上行操作的先决条件。
/**
  * 数据上行AdpaterApi
  */ 
private Adapter openApi = new AdapterApi();
  • SampleDeviceDownLinkHandler.java: 对平台下发的响应或者命令进行业务操作,业务操作主要使用如下所示的SDK提供的通用API、自动生成的物模型扩展API、下行消息推送API,其它业务代码可参考示例源码。
/**  
  * 用于推送消息和主动断开连接至TCP协议站的自定义设备连接  
  */ 
private final TcpDeviceDownLinkApi deviceDownLinkApi = new TcpDeviceDownLinkApi();  
/**
  * 物模型设置设备属性监听示例
  * 详见{@link com.github.cm.heclouds.adapter.api.DeviceCommandListener}
  */ 
private final DeviceCommandListener listener = (device, id, version, params) -> {     
    logger.logDevInfo(ConfigUtils.getName(), PLATFORM_DOWN_LINK, device.getProductId(), device.getDeviceName(), "property set command received: " + "id=" + id + ", version=" + version + ", value=" + params);     
    JsonElement status = params.get("status");     
    JsonElement gear = params.get("gear");     
    // 构造给设备下发的命令
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("id", id);
    if (status != null) {
        jsonObject.add("status", status);     
    }     
    if (gear != null) {         
        jsonObject.add("gear", gear);     
    }     
    // type=11表示向设备下发命令     
    Tlv tlv = new Tlv((short) 11, jsonObject);     
    // 推送给设备     
    deviceDownLinkApi.pushToDevice(device, tlv); 
};

3).在ProtocolAdapterSdkDemo.java main方法中配置SDK和TCP协议站,初始化配置并启动服务:

// 初始化日志系统,默认提供文件日志
// 可指定logback日志配置文件相对于resources目录的路径
// 注意,SDK和协议站配置默认统一使用SDK和协议站配置中首先配置的日志系统
// 如以下示例,即使TCP协议站在配置了日志系统,TCP协议站也会使用SDK配置的日志系统
ILogger logger = FileLogger.getInstance("logback.xml");

// 配置SDK
Config sdkConfig = new Config(logger);
// 非必填,默认为文件配置,可选择手动填入泛协议接入服务和平台相关配置
// 如config.connectionHost("${connection_host}")
// .serviceId("${service_id}")
// .instanceName("${instance_name}")
// .instanceKey("${instance_key}")
// 也可通过填入IAdapterConfig接口实现类方式配置泛协议接入服务和平台相关配置,如
sdkConfig.adapterConfig(AdapterFileConfig.getInstance())
        // 配置设备映射关系配置,非必填,默认为文件配置
        .deviceConfig(DeviceFileConfig.getInstance())
        // 配置平台设备的响应及命令下发接口的实现,非必填,默认实现仅打印日志
        .deviceDownLinkHandler(new SampleDeviceDownLinkHandler());
// 初始化SDK配置
sdkConfig.init();


// 配置TCP协议站,此处配置了TLV编解码器
TcpProtocolHubConfig protocolHubConfig = new TcpProtocolHubConfig(logger) {
    @Override
    public void addChannelHandlers(ChannelPipeline pipeline) {
        pipeline.addLast(new TlvDecoder())
                    .addLast(TvlEncoder.INSTANCE);
    }
};
// 配置TCP协议站的配置
protocolHubConfig
        // 同SDK配置,也可使用代码形式配置
        .tcpProtocolHubConfig(TcpFileConfig.getInstance())
        // 配置自定义设备的协议解码及上行业务开发
        .tcpDeviceUpLinkHandler(new SampleDeviceUpLinkHandler());
// 初始化TCP协议站配置
protocolHubConfig.init();

使用IDEA服务成功启动后可看到如下图所示的日志:

或在项目./logs/protocol-adapter-sdk-example-with-tcp-protocol-hub文件夹下可看到日志

同时,可在Studio上观察到泛协议接入服务在线状态:

3. 接入设备并登录

使用TCP工具模拟设备,接入SDK服务,发送16进制字符串00 01 00 13 7B 22 64 65 76 69 63 65 5F 6E 61 6D 65 22 3A 22 61 22 7D报文,此报文为编码后的TLV数据,type=1,表示"device_name"为"a"的设备上线。
规定自定义协议中的"device_name"字段对应设备映射文件中的"originalIdentity",即"device_name"="a"表示OneNET设备

a {
   # 产品ID
   productId = "AAWCVR5JIx4hG9V3"
   # 设备名称
   deviceName = "test_device"
   # 产品或设备key
   key = "xxxxxxxxxxxxxxx"
}

您可使用Adapter提供的通用API上线设备,以下示例代码在SampleDeviceUpLinkHandler类中的#init(Object, Channel)方法中:

Tlv tlv = (Tlv) data;

// type = 1表示设备登陆
if (tlv.getType() != 1) {
    logger.logDevWarn(ConfigUtils.getName(), DEV_UP_LINK, null, null, "not login type, data=" + tlv);
    // 返回null表示丢弃此次消息
    return null;
}

String deviceName = tlv.getValue().get("device_name").getAsString();
if (StringUtil.isNullOrEmpty(deviceName)) {
    logger.logDevWarn(ConfigUtils.getName(), DEV_UP_LINK, null, null, "null or empty originalIdentity, data=" + tlv);
    return null;
}

// 获取设备对应的平台设备信息,用户可自主开发工具用于获取和缓存设备信息的映射
// 平台设备信息实体
Device device = deviceConfig.getDeviceEntity(deviceName);

String productId = device.getProductId();
String devName = device.getDeviceName();
// 鉴权及权限校验
if (!authenticator.checkValid(productId, devName) ||
        !authorizatorPolicy.canWrite(productId, devName)) {
    logger.logDevWarn(ConfigUtils.getName(), DEV_UP_LINK, productId, devName, "device auth failed");
    return null;
}

// 调用AdapterApi进行设备上线操作
CallableFuture<DeviceResult> future = adapterApi.deviceOnline(device);
try {
    DeviceResult deviceResult = future.get(5, TimeUnit.SECONDS);
    // 没有上线成功
    if (!deviceResult.isSuccess()) {
        logger.logDevWarn(ConfigUtils.getName(), DEV_UP_LINK, productId, devName, "device online failed, response: " + deviceResult.getResponse());
        return null;
    }
} catch (ExecutionException | InterruptedException | TimeoutException e) {
    logger.logDevError(ConfigUtils.getName(), DEV_UP_LINK, productId, devName, "device online failed", e);
    return null;
}
return device;

如下图,表示设备成功登录:

同时,在Studio上可观察到接入设备详情及在线状态。

4. 上传设备数据

设备登陆后,使用TCP工具模拟设备,继续发送00 02 00 26 7B 22 73 74 61 74 75 73 22 3A 31 2C 22 67 65 61 72 22 3A 33 2C 22 64 75 72 61 74 69 6F 6E 22 3A 32 30 30 30 30 7D报文,此报文为编码后的TLV数据,type=2,表示设备上传数据:

{
  "status": 1,        
  "gear": 3,          
  "duration": 20000    
}

您可使用Adapter提供的通用API上传设备属性,以下示例代码在SampleDeviceUpLinkHandler类中的#processUpLinkData(Device, Object, Channel)方法中:

int status = tlvValue.get("status").getAsInt();
int gear = tlvValue.get("gear").getAsInt();
int duration = tlvValue.get("duration").getAsInt();
JsonObject statusValue = new JsonObject();
statusValue.addProperty("value", status);
JsonObject gearValue = new JsonObject();
gearValue.addProperty("value", gear);
JsonObject durationValue = new JsonObject();
durationValue.addProperty("value", duration);

JsonObject properties = new JsonObject();
properties.add("status", statusValue);
properties.add("gear", gearValue);
properties.add("duration", durationValue);

adapterApi.uploadProperty(device, new OneJSONRequest(properties));

以上代码最终对应上传到平台的物模型OneJSON格式为:

{
  "id": "1609923196784",
  "version": "1.0",
  "params": {
    "status": {
      "value": 1
    },
    "gear": {
      "value": 3
    },
    "duration": {
      "value": 20000
    }
  }
}

您可参照物模型OneJSON格式和物模型功能点构造自己的OneJSON,也可参考protocol-adapter-sdk-example-code-generator使用自动生成物模型相关代码 生成的扩展API上传单个设备数据。

从日志中可以看出,设备数据上传成功,同时,在Studio上可观察到设备最新上传的数据。

5. 下发命令及回复响应

DownLinkRequestHandler中onSetPropertyRequest(Device, String, String, String, JsonObject)方法提供了处理平台下发设备属性设置业务的接口,SampleDeviceDownLinkHandler中实现了此接口。
SDK提供了DevicePropertySetListener接口用于处理平台下发设备属性设置业务,开发者可选用自己实现此接口,或者实现使用 自动生成物模型相关代码 中生成的设置设备属性监听抽象类。
值得注意的是, 自动生成物模型相关代码 中生成的设置设备属性监听抽象类拆分了下发的设备属性设置命令,如果下发的属性设置同时含有status和gear,则会拆分成两个方法来分别接收两个属性设置,开发者如果想要同时处理status和gear,建议不要使用 自动生成物模型相关代码 中生成的设置设备属性监听抽象类。
1).实现监听接口:

private final DeviceCommandListener listener = (device, id, version, params) -> {
    logger.logDevInfo(ConfigUtils.getName(), PLATFORM_DOWN_LINK, device.getProductId(), device.getDeviceName(), "identifier1 property set command received: " +
            "id=" + id + ", version=" + version + ", value=" + params);
    JsonElement status = params.get("status");
    JsonElement gear = params.get("gear");
    // 构造给设备下发的命令
    // value格式为
    //{
    //  "id": "1609916964",
    //  "status": 0,
    //  "gear": 1,
    //}
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("id", id);
    if (status != null) {
        jsonObject.add("status", status);
    }
    if (gear != null) {
        jsonObject.add("gear", gear);
    }
    // type=11表示向设备下发命令
    Tlv tlv = new Tlv((short) 11, jsonObject);
    // 推送给设备
    deviceDownLinkApi.pushToDevice(device, tlv);
};

2).实现DownLinkRequestHandler,编写收到平台下发设备属性设置的业务逻辑。

@Override
public void onSetPropertyRequest(Device device, String id, String version, JsonObject params) { 
    listener.onCommandReceived(device, id, version, params);     
}

3).调试设备
使用Studio设备调试工具设置设备属性,填入合法属性值,点击发送。

SDK接收到平台下发的设备属性设置请求,会按照代码编写的业务逻辑,将请求发送给设备,设备会收到如下图所示经TLV编码后的消息:

使用TCP工具模拟设备相应命令下发,发送00 03 00 26 7B 22 69 64 22 3A 22 31 37 22 2C 22 63 6F 64 65 22 3A 32 30 30 2C 22 6D 73 67 22 3A 22 73 75 63 63 65 73 73 22 7D报文,此报文为编码后的TLV数据,type=3,表示设备响应回复:

{
    "id": 17,        
    "code": 200,          
    "msg": "success"    
}

您可使用Adapter提供的方法响应命令,如:

String id = tlvValue.get("id").getAsString();
int code = tlvValue.get("code").getAsInt();
String msg = tlvValue.get("msg").getAsString();
// 构造响应回复
Response response = new Response(id, code, msg);
adapterApi.replyPropertySetRequest(device, response);

此时,可在页面上看到平台已成功接收到设备的命令响应。

6. 设备登出

设备发送00 09 00 02 7B 7D报文,此报文为编码后的TLV数据,type=9,表示设备主动下线。调用通用API通知平台接入机主动登出设备:

adapterApi.deviceOffline(device)

接入机返回登出成功响应。

此时在Studio页面中可观察到设备已经离线:

个搜索结果,搜索内容 “

    0 个搜索结果,搜索内容 “