之前实现了使用Springboot+Netty基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端(南向设备),模拟设备发送的数据给到了AIOT平台,那么在第三方应用也需要去订阅AIOT平台的数据,以及对设备进行下发指令(消息),订阅设备消息可以使用http,对整个产品进行设备消息订阅。

订阅方地址可以先用接口来接收json字符串,他的格式是json的(接收设备订阅的数据后,必须要有返回,否则AIOT平台会认为没有推送到位,还会推送直到触发禁制)

package boot.ctwing.tcp.app.controller;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;/***   蚂蚁舞*/
@Controller
@RequestMapping("/receive")
public class CtWingReceiveController {private final Logger log =  LoggerFactory.getLogger(this.getClass());@PostMapping(value = "/tcp-transfer/data", produces = "application/json;charset=UTF-8")@ResponseBodypublic Map<String, Object> receiveTcpTransferController(@RequestBody String str) {log.info(str);Map<String, Object> mapResult = new HashMap<>();mapResult.put("state", true);return mapResult;}}

如果说不创建应用开发,只需要设备推送上来的数据,那么此刻就可以去启动模拟的设备,尝试发消息,北向应用也启动,这样的话就能订阅到从模拟设备发送过来的数据,下下行数据,需要先在应用开发-应用管理里面创建应用得到APP_KEY和APP_SECRET。

在这里我们能看到AIOT的接口文档和在线调试还有sdk下载,在这里我只需要下发指令的操作,那么就去找下发指令的文档信息,在线调试是最快调通下发的,我这里已经把它转换成程序了。

新建Springboot的maven项目,pom.xml文件导入依赖包(用到了swagger来测试下发数据)

    <modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>boot.ctwing.tcp.app</groupId><artifactId>boot-example-ctwing-tcp-app-2.0.5</artifactId><version>0.0.1-SNAPSHOT</version><name>boot-example-ctwing-tcp-app-2.0.5</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.20</version><scope>compile</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.6</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>com.ctg.ag</groupId><artifactId>ctg-ag-sdk-core</artifactId><version>2.7.0-20221208.015855-5</version><scope>system</scope><systemPath>${project.basedir}/doc/lib/ctg-ag-sdk-core-2.7.0-20221208.015855-5.jar</systemPath></dependency><dependency><groupId>com.ctg.ag</groupId><artifactId>ag-sdk-biz-108549.tar.gz</artifactId><version>20221221.143437-SNAPSHOT</version><scope>system</scope><systemPath>${project.basedir}/doc/lib/ag-sdk-biz-108549.tar.gz-20221221.143437-SNAPSHOT.jar</systemPath></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>boot.ctwing.tcp.app.BootCtWingTcpApp</mainClass><includeSystemScope>true</includeSystemScope><!--外部进行打包--></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>

Springboot启动类,Netty启动

package boot.ctwing.tcp.app;import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/***  蚂蚁舞*/
@SpringBootApplication
public class BootCtWingTcpApp implements CommandLineRunner
{public static void main( String[] args ) throws Exception {SpringApplication.run(BootCtWingTcpApp.class, args);System.out.println( "Hello World!" );}@Overridepublic void run(String... args) throws Exception {}
}
SwaggerConfig配置
package boot.ctwing.tcp.app.config;import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;/***  蚂蚁舞*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {@Beanpublic Docket createRestApi(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).paths(Predicates.not(PathSelectors.regex("/error.*"))).paths(PathSelectors.regex("/.*")).build().apiInfo(apiInfo());}private ApiInfo apiInfo(){return new ApiInfoBuilder().title("天翼物联网CtWing北向接收订阅数据端").description("接收订阅数据端需要的下行测试接口").version("0.01").build();}/*** http://localhost:8178/doc.html  地址和端口根据实际项目查看*/}
CtWingConstant
package boot.ctwing.tcp.app.config;/***  蚂蚁舞*/
public class CtWingConstant {//  产品Idpublic static final int PRODUCT_ID = 15506850;//  应用开发-应用管理-具体应用的App Keypublic static final String APP_KEY = "XXXXXXX";//  应用开发-应用管理-具体应用的App Secretpublic static final String APP_SECRET = "XXXXXXXX";//  产品信息里的Master-APIkeypublic static final String MASTER_KEY = "XXXXXXXXXXXXXXXXXXXX";//  数据订阅通知public static final String dataReport = "dataReport";//  设备上线下线订阅通知public static final String deviceOnlineOfflineReport = "deviceOnlineOfflineReport";//  指令下发结果订阅public static final String commandResponse = "commandResponse";}
CtWingReceiveController
package boot.ctwing.tcp.app.controller;import boot.ctwing.tcp.app.config.CtWingConstant;
import boot.ctwing.tcp.app.utils.CtWingUtils;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import java.util.Base64;
import java.util.HashMap;
import java.util.Map;/***   蚂蚁舞*/
@Controller
@RequestMapping("/receive")
public class CtWingReceiveController {private final Logger log =  LoggerFactory.getLogger(this.getClass());@PostMapping(value = "/tcp-transfer/data", produces = "application/json;charset=UTF-8")@ResponseBodypublic Map<String, Object> receiveTcpTransferController(@RequestBody String str) {log.info(str);JSONObject jsonObject = JSONObject.parseObject(str);String messageType = jsonObject.getString("messageType");switch (messageType){  // 这里只是例举了三种case CtWingConstant.dataReport:String APPdata = jsonObject.getJSONObject("payload").getString("APPdata");byte[] decoded2 = Base64.getDecoder().decode(APPdata);String hex2 = CtWingUtils.bytesToHexStr(decoded2);System.out.println(CtWingUtils.hexStrToStr(hex2));break;case CtWingConstant.deviceOnlineOfflineReport:String eventType = jsonObject.getString("eventType");System.out.println(eventType);break;case CtWingConstant.commandResponse:String taskId = jsonObject.getString("taskId");//JSONObject jsonResult = jsonObject.getJSONObject("result");System.out.println(taskId);break;default:break;}Map<String, Object> mapResult = new HashMap<>();mapResult.put("state", true);return mapResult;}}
CtWingDownCmdController
package boot.ctwing.tcp.app.controller;import boot.ctwing.tcp.app.config.CtWingConstant;
import boot.ctwing.tcp.app.utils.CtWingDownSendUtils_1;
import boot.ctwing.tcp.app.utils.CtWingDownSendUtils_2;
import boot.ctwing.tcp.app.utils.CtWingDownSendUtils_3;
import com.alibaba.fastjson.JSONObject;
import com.ctg.ag.sdk.biz.aep_device_command.CreateCommandResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;/***   蚂蚁舞 下行指令*/
@Controller
@RequestMapping("/down")
public class CtWingDownCmdController {private final Logger log =  LoggerFactory.getLogger(this.getClass());@PostMapping(value = "/cmd_send_1")@ResponseBodypublic String cmd_send_1(@RequestParam(name="imei",required = true) String imei, @RequestParam(name="payload",required = true) String payload) throws Exception {JSONObject jsonContent = new JSONObject();jsonContent.put("dataType", 1);  // TCP和LWM2M协议透传的content内容, payload:指令内容,数据格式为十六进制时需要填十六进制字符串,jsonContent.put("payload",payload);   //  dataType:数据类型:1字符串,2十六进制JSONObject jsonObject = new JSONObject();jsonObject.put("content", jsonContent);   //  指令内容,必填,格式为JsonjsonObject.put("deviceId", CtWingConstant.PRODUCT_ID+imei);  // 设备ID,(当指令级别为设备级时必填,为设备组级时则不填)jsonObject.put("operator", "myw"); //  操作者,必填jsonObject.put("productId", CtWingConstant.PRODUCT_ID);  //  产品ID,必填jsonObject.put("ttl", 0);   //  设备指令缓存时长,选填。单位:秒,取值范围:0-864000,不携带则默认值:7200 如不需缓存请填0jsonObject.put("level", 1);    //  指令级别,1或2为设备级别,3为设备组级别,选填。不填默认设备级CreateCommandResponse response = CtWingDownSendUtils_1.sendApi(jsonObject.toString());log.info(response.toString());return response.toString();}@PostMapping(value = "/cmd_send_2")@ResponseBodypublic String cmd_send_2(@RequestParam(name="imei",required = true) String imei, @RequestParam(name="payload",required = true) String payload) throws Exception {JSONObject jsonContent = new JSONObject();jsonContent.put("dataType", 1);  // TCP和LWM2M协议透传的content内容, payload:指令内容,数据格式为十六进制时需要填十六进制字符串,jsonContent.put("payload",payload);    //  dataType:数据类型:1字符串,2十六进制JSONObject jsonObject = new JSONObject();jsonObject.put("content", jsonContent);   //  指令内容,必填,格式为JsonjsonObject.put("deviceId", CtWingConstant.PRODUCT_ID+imei);  // 设备ID,(当指令级别为设备级时必填,为设备组级时则不填)jsonObject.put("operator", "myw"); //  操作者,必填jsonObject.put("productId", CtWingConstant.PRODUCT_ID);  //  产品ID,必填jsonObject.put("ttl", 0);   //  设备指令缓存时长,选填。单位:秒,取值范围:0-864000,不携带则默认值:7200 如不需缓存请填0jsonObject.put("level", 1);    //  指令级别,1或2为设备级别,3为设备组级别,选填。不填默认设备级String str = CtWingDownSendUtils_2.httpPostExample(jsonObject.toString());log.info(str);return str;}@PostMapping(value = "/cmd_send_3")@ResponseBodypublic String cmd_send_3(@RequestParam(name="imei",required = true) String imei, @RequestParam(name="payload",required = true) String payload) throws Exception {JSONObject jsonContent = new JSONObject();jsonContent.put("dataType", 1);  // TCP和LWM2M协议透传的content内容, payload:指令内容,数据格式为十六进制时需要填十六进制字符串,jsonContent.put("payload",payload);    //  dataType:数据类型:1字符串,2十六进制JSONObject jsonObject = new JSONObject();jsonObject.put("content", jsonContent);   //  指令内容,必填,格式为JsonjsonObject.put("deviceId", CtWingConstant.PRODUCT_ID+imei);  // 设备ID,(当指令级别为设备级时必填,为设备组级时则不填)jsonObject.put("operator", "myw"); //  操作者,必填jsonObject.put("productId", CtWingConstant.PRODUCT_ID);  //  产品ID,必填jsonObject.put("ttl", 0);   //  设备指令缓存时长,选填。单位:秒,取值范围:0-864000,不携带则默认值:7200 如不需缓存请填0jsonObject.put("level", 1);    //  指令级别,1或2为设备级别,3为设备组级别,选填。不填默认设备级String strResult = CtWingDownSendUtils_3.send_post(jsonObject.toString());log.info(strResult);return strResult;}@PostMapping(value = "/cmd_send_test")@ResponseBodypublic String cmd_send_test() throws Exception {JSONObject jsonContent = new JSONObject();jsonContent.put("dataType", 1);  // TCP和LWM2M协议透传的content内容, payload:指令内容,数据格式为十六进制时需要填十六进制字符串,jsonContent.put("payload","hello"); //   dataType:数据类型:1字符串,2十六进制JSONObject jsonObject = new JSONObject();jsonObject.put("content", jsonContent);   //  指令内容,必填,格式为JsonjsonObject.put("deviceId", CtWingConstant.PRODUCT_ID+"869401041201815"); //   设备ID,(当指令级别为设备级时必填,为设备组级时则不填)jsonObject.put("operator", "myw"); //  操作者,必填jsonObject.put("productId", CtWingConstant.PRODUCT_ID);  //  产品ID,必填jsonObject.put("ttl", 0);   //  设备指令缓存时长,选填。单位:秒,取值范围:0-864000,不携带则默认值:7200 如不需缓存请填0jsonObject.put("level", 1);    //  指令级别,1或2为设备级别,3为设备组级别,选填。不填默认设备级//        CreateCommandResponse response = CtWingDownSendUtils_1.sendApi(jsonObject.toString());
//      log.info(response.toString());
//      return response.toString();
//
//      String str = CtWingDownSendUtils_2.httpPostExample(jsonObject.toString());
//      log.info(str);
//      return str;String strResult = CtWingDownSendUtils_3.send_post(jsonObject.toString());log.info(strResult);return strResult;}}

CtWingDownSendUtils_1

package boot.ctwing.tcp.app.utils;import boot.ctwing.tcp.app.config.CtWingConstant;
import com.ctg.ag.sdk.biz.AepDeviceCommandClient;
import com.ctg.ag.sdk.biz.aep_device_command.CreateCommandRequest;
import com.ctg.ag.sdk.biz.aep_device_command.CreateCommandResponse;/***  蚂蚁舞*/
public class CtWingDownSendUtils_1 {/***  ctwing提供的sdk方式下发指令,方便快速,在后台创建应用后可获取sdk*/public static CreateCommandResponse sendApi(String jsonStr) throws Exception {AepDeviceCommandClient client = AepDeviceCommandClient.newClient().appKey(CtWingConstant.APP_KEY).appSecret(CtWingConstant.APP_SECRET).build();CreateCommandRequest request = new CreateCommandRequest();// set your request params hererequest.setParamMasterKey(CtWingConstant.MASTER_KEY);   // single valuerequest.setBody(jsonStr.getBytes()); //具体格式CreateCommandResponse commandResponse = client.CreateCommand(request);client.shutdown();return commandResponse;}}
CtWingDownSendUtils_2
package boot.ctwing.tcp.app.utils;import boot.ctwing.tcp.app.config.CtWingConstant;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;public class CtWingDownSendUtils_2 {//  官方提供的examplepublic static String httpPostExample(String bodyString) throws Exception {String secret = CtWingConstant.APP_SECRET;//密钥,到控制台->应用管理打开应用可以找到此值String application = CtWingConstant.APP_KEY;//appKey,到控制台->应用管理打开应用可以找到此值String version = "20190712225145";//api版本,到文档中心->使能平台API文档打开要调用的api可以找到版本值String MasterKey = CtWingConstant.MASTER_KEY;//MasterKey,在产品中心打开对应的产品查看此值// 下面以增加设备的API为例【具体信息请以使能平台的API文档为准】。//请求BODY,到文档中心->使能平台API文档打开要调用的api中,在“请求BODY”中查看//String bodyString = "{\"deviceName\":\"testDevice\",\"deviceSn\":\"\",\"imei\":123456789012345,\"operator\":\"admin\",\"productId\":\"9392\"}";CloseableHttpClient httpClient = null;HttpResponse response = null;httpClient = HttpClientBuilder.create().build();long offset = getTimeOffset();// 获取时间偏移量,方法见前面// 构造请求的URL,具体参考文档中心->使能平台API文档中的请求地址和访问路径URIBuilder uriBuilder = new URIBuilder();uriBuilder.setScheme("https");uriBuilder.setHost("ag-api.ctwing.cn/aep_device_command"); //请求地址uriBuilder.setPath("/command"); //访问路径,可以在API文档中对应API中找到此访问路径// 在请求的URL中添加参数,具体参考文档中心->API文档中请求参数说明// (如果有MasterKey,将MasterKey加到head中,不加在此处)//uriBuilder.addParameter("productId", "9392");//如果没有其他参数,此行不要HttpPost httpPost = new HttpPost(uriBuilder.build());//构造post请求long timestamp = System.currentTimeMillis() + offset;// 获取时间戳Date date = new Date(timestamp);SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));String dataString = dateFormat.format(date);// 生成格式化的日期字符串// head中添加公共参数httpPost.addHeader("MasterKey", MasterKey);// MasterKey加在此处head中httpPost.addHeader("application", application);httpPost.addHeader("timestamp", "" + timestamp);httpPost.addHeader("version", version);httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");httpPost.addHeader("Date", dataString);// 下列注释的head暂时未用到// httpPost.addHeader("sdk", "GIT: a4fb7fca");// httpPost.addHeader("Accept", "gzip,deflate");// httpPost.addHeader("User-Agent", "Telecom API Gateway Java SDK");// 构造签名需要的参数,如果参数中有MasterKey,则添加来参与签名计算,// 其他参数根据实际API从URL中获取,如有其他参数,写法参考get示例Map<String, String> param = new HashMap<String, String>();param.put("MasterKey", MasterKey);// 添加签名httpPost.addHeader("signature", sign(param, timestamp, application, secret, bodyString.getBytes()));//请求添加body部分httpPost.setEntity(new StringEntity(bodyString,"utf-8"));try {// 发送请求response = httpClient.execute(httpPost);// 从response获取响应结果String result = new String(EntityUtils.toByteArray(response.getEntity()));httpClient.close();return result;} catch (IOException e) {e.printStackTrace();}return null;}/*** @param param    api 配置参数表* @param timestamp UNIX格式时间戳* @param application appKey,到应用管理打开应用可以找到此值* @param secret 密钥,到应用管理打开应用可以找到此值* @param body 请求body数据,如果是GET请求,此值写null* @return 签名数据*/public static String sign(Map<String, String> param, long timestamp, String application, String secret, byte[] body) throws Exception {// 连接系统参数StringBuffer sb = new StringBuffer();sb.append("application").append(":").append(application).append("\n");sb.append("timestamp").append(":").append(timestamp).append("\n");// 连接请求参数if (param != null) {TreeSet<String> keys = new TreeSet<String>(param.keySet());for (String s : keys) {String val = param.get(s);sb.append(s).append(":").append(val == null ? "" : val).append("\n");}}//body数据写入需要签名的字符流中ByteArrayOutputStream baos = new ByteArrayOutputStream();baos.write(sb.toString().getBytes("utf-8"));if (body != null && body.length > 0) {baos.write(body);baos.write("\n".getBytes("utf-8"));}// 得到需要签名的字符串String string = baos.toString("utf-8");System.out.println("Sign string: " + string);// hmac-sha1编码byte[] bytes = null;SecretKey secretKey = new SecretKeySpec(secret.getBytes("utf-8"), "HmacSha1");Mac mac = Mac.getInstance(secretKey.getAlgorithm());mac.init(secretKey);bytes = mac.doFinal(string.getBytes("utf-8"));// base64编码// 得到需要提交的signature签名数据return new String(Base64.encodeBase64(bytes));}public static long getTimeOffset() {long offset = 0;HttpResponse response = null;//构造httpGet请求CloseableHttpClient httpClient = HttpClientBuilder.create().build();HttpGet httpTimeGet = new HttpGet("https://ag-api.ctwing.cn/echo");try {long start = System.currentTimeMillis();response = httpClient.execute(httpTimeGet);long end = System.currentTimeMillis();//时间戳在返回的响应的head的x-ag-timestamp中Header[] headers = response.getHeaders("x-ag-timestamp");if (headers.length > 0) {long serviceTime = Long.parseLong(headers[0].getValue());offset = serviceTime - (start + end) / 2L;}httpClient.close();} catch (IOException e) {e.printStackTrace();}return offset;}}
CtWingDownSendUtils_3
package boot.ctwing.tcp.app.utils;import boot.ctwing.tcp.app.config.CtWingConstant;
import cn.hutool.http.HttpRequest;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;/***  蚂蚁舞*/
public class CtWingDownSendUtils_3 {//  使用hutool requestprivate static final String url_time = "https://ag-api.ctwing.cn/echo";private static final String url_cmd = "http://ag-api.ctwing.cn/aep_device_command/command";public static String send_post(String data) throws Exception {long offset = getTimeOffset();// 获取时间偏移量long timestamp = System.currentTimeMillis() + offset;// 获取时间戳Date date = new Date(timestamp);SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));String dataString = dateFormat.format(date);// 生成格式化的日期字符串Map<String, String> param = new HashMap<>();param.put("MasterKey", CtWingConstant.MASTER_KEY);String signature = sign(param, timestamp, CtWingConstant.APP_KEY, "if8hEBlPIs", data.getBytes());//System.out.println(signature);Map<String, String> headers = new HashMap<>();headers.put("Content-Type", "application/json; charset=UTF-8");headers.put("application", CtWingConstant.APP_KEY);headers.put("MasterKey", CtWingConstant.MASTER_KEY);// MasterKey加在此处head中headers.put("timestamp",timestamp+"");headers.put("signature", signature);headers.put("Date", dataString);headers.put("version", "20190712225145"); //版本  需要根据实际来确定return HttpRequest.post(url_cmd).headerMap(headers, true).body(data).timeout(6000).execute().body();}public static String sign(Map<String, String> param, long timestamp, String application, String secret, byte[] body) throws Exception {// 连接系统参数StringBuffer sb = new StringBuffer();sb.append("application").append(":").append(application).append("\n");sb.append("timestamp").append(":").append(timestamp).append("\n");// 连接请求参数if (param != null) {TreeSet<String> keys = new TreeSet<String>(param.keySet());for (String s : keys) {String val = param.get(s);sb.append(s).append(":").append(val == null ? "" : val).append("\n");}}//body数据写入需要签名的字符流中ByteArrayOutputStream baos = new ByteArrayOutputStream();baos.write(sb.toString().getBytes(StandardCharsets.UTF_8));if (body != null && body.length > 0) {baos.write(body);baos.write("\n".getBytes(StandardCharsets.UTF_8));}// 得到需要签名的字符串String string = baos.toString("utf-8");// hmac-sha1编码byte[] bytes = null;SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSha1");Mac mac = Mac.getInstance(secretKey.getAlgorithm());mac.init(secretKey);bytes = mac.doFinal(string.getBytes(StandardCharsets.UTF_8));// base64编码// 得到需要提交的signature签名数据return new String(Base64.encodeBase64(bytes));}public static long getTimeOffset() {long offset = 0;long start = System.currentTimeMillis();cn.hutool.http.HttpResponse res = HttpRequest.get(url_time).execute();long end = System.currentTimeMillis();//时间戳在返回的响应的head的x-ag-timestamp中String headers = res.header("x-ag-timestamp");if (headers.length() > 0) {long serviceTime = Long.parseLong(headers);offset = serviceTime - (start + end) / 2L;}return offset;}}
CtWingUtils
package boot.ctwing.tcp.app.utils;import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/***  蚂蚁舞*/
public class CtWingUtils {private static final Logger log =  LoggerFactory.getLogger(CtWingUtils.class);private static final char[] HEXES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};public static String bytesToHexStr(byte[] bytes) {if (bytes == null || bytes.length == 0) {return null;}StringBuilder hex = new StringBuilder(bytes.length * 2);for (byte b : bytes) {hex.append(HEXES[(b >> 4) & 0x0F]);hex.append(HEXES[b & 0x0F]);}return hex.toString().toUpperCase();}public static byte[] hexStrToBytes(String hex) {if (hex == null || hex.length() == 0) {return null;}char[] hexChars = hex.toCharArray();byte[] bytes = new byte[hexChars.length / 2];   // 如果 hex 中的字符不是偶数个, 则忽略最后一个for (int i = 0; i < bytes.length; i++) {bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);}return bytes;}public static String strToHexStr(String str) {StringBuilder sb = new StringBuilder();byte[] bs = str.getBytes();int bit;for (int i = 0; i < bs.length; i++) {bit = (bs[i] & 0x0f0) >> 4;sb.append(HEXES[bit]);bit = bs[i] & 0x0f;sb.append(HEXES[bit]);}return sb.toString().trim();}public static String hexStrToStr(String hexStr) {//能被16整除,肯定可以被2整除byte[] array = new byte[hexStr.length() / 2];try {for (int i = 0; i < array.length; i++) {array[i] = (byte) (0xff & Integer.parseInt(hexStr.substring(i * 2, i * 2 + 2), 16));}hexStr = new String(array, StandardCharsets.UTF_8);} catch (Exception e) {e.printStackTrace();return "";}return hexStr;}}

代码目录结构

├─boot-example-ctwing-tcp-app-2.0.5
│  │  pom.xml
│  │
│  ├─doc
│  │  │  108549_sdk.tar.gz
│  │  │  Readme.md
│  │  │
│  │  ├─demo
│  │  │      AepCommandModbusDemo.java
│  │  │      AepDeviceCommandDemo.java
│  │  │      AepDeviceCommandLwmProfileDemo.java
│  │  │      AepDeviceControlDemo.java
│  │  │      AepDeviceEventDemo.java
│  │  │      AepDeviceGroupManagementDemo.java
│  │  │      AepDeviceManagementDemo.java
│  │  │      AepDeviceModelDemo.java
│  │  │      AepDeviceStatusDemo.java
│  │  │      AepEdgeGatewayDemo.java
│  │  │      AepFirmwareManagementDemo.java
│  │  │      AepModbusDeviceManagementDemo.java
│  │  │      AepMqSubDemo.java
│  │  │      AepNbDeviceManagementDemo.java
│  │  │      AepProductManagementDemo.java
│  │  │      AepPublicProductManagementDemo.java
│  │  │      AepRuleEngineDemo.java
│  │  │      AepSoftwareManagementDemo.java
│  │  │      AepSoftwareUpgradeManagementDemo.java
│  │  │      AepStandardManagementDemo.java
│  │  │      AepSubscribeNorthDemo.java
│  │  │      AepUpgradeManagementDemo.java
│  │  │      TenantAppStatisticsDemo.java
│  │  │      TenantDeviceStatisticsDemo.java
│  │  │      UsrDemo.java
│  │  │
│  │  ├─doc
│  │  │      ApiDocument_AEPNB设备管理_aep_nb_device_management.md
│  │  │      ApiDocument_AEP事件上报_aep_device_event.md
│  │  │      ApiDocument_AEP产品管理_aep_product_management.md
│  │  │      ApiDocument_AEP公共产品管理_aep_public_product_management.md
│  │  │      ApiDocument_AEP分组管理_aep_device_group_management.md
│  │  │      ApiDocument_AEP升级包管理_aep_software_management.md
│  │  │      ApiDocument_AEP固件管理_aep_firmware_management.md
│  │  │      ApiDocument_AEP固件远程升级管理_aep_upgrade_management.md
│  │  │      ApiDocument_AEP指令下发_aep_device_command.md
│  │  │      ApiDocument_AEP指令下发_lwm2m有profile_aep_device_command_lwm_profile.md
│  │  │      ApiDocument_AEP指令下发_modbus_aep_command_modbus.md
│  │  │      ApiDocument_AEP数据查询_aep_device_status.md
│  │  │      ApiDocument_AEP标准物模型管理_aep_standard_management.md
│  │  │      ApiDocument_AEP物模型管理_aep_device_model.md
│  │  │      ApiDocument_AEP规则引擎_aep_rule_engine.md
│  │  │      ApiDocument_AEP订阅管理_aep_subscribe_north.md
│  │  │      ApiDocument_AEP设备管理_aep_device_management.md
│  │  │      ApiDocument_AEP设备管理_modbus_aep_modbus_device_management.md
│  │  │      ApiDocument_AEP软件升级管理_aep_software_upgrade_management.md
│  │  │      ApiDocument_AEP边缘网关接入_aep_edge_gateway.md
│  │  │      ApiDocument_AEP远程控制_aep_device_control.md
│  │  │      ApiDocument_MQ订阅推送管理_aep_mq_sub.md
│  │  │      ApiDocument_User_usr.md
│  │  │      ApiDocument_感知终端数据分析_tenant_device_statistics.md
│  │  │      ApiDocument_物联网应用数据分析_tenant_app_statistics.md
│  │  │
│  │  ├─lib
│  │  │      ag-sdk-biz-108549.tar.gz-20221221.143437-SNAPSHOT.jar
│  │  │      ag-sdk-biz-108549.tar.gz-20221221.143437-SNAPSHOT.pom.xml
│  │  │      ctg-ag-sdk-core-2.7.0-20221208.015855-5.jar
│  │  │      ctg-ag-sdk-core-2.7.0-20221208.015855-5.pom.xml
│  │  │
│  │  └─src
│  │          ag-sdk-biz-108549.tar.gz-20221221.143437-source.jar
│  │
│  └─src
│      ├─main
│      │  ├─java
│      │  │  └─boot
│      │  │      └─ctwing
│      │  │          └─tcp
│      │  │              └─app
│      │  │                  │  BootCtWingTcpApp.java
│      │  │                  │
│      │  │                  ├─config
│      │  │                  │      CtWingConstant.java
│      │  │                  │      SwaggerConfig.java
│      │  │                  │
│      │  │                  ├─controller
│      │  │                  │      CtWingDownCmdController.java
│      │  │                  │      CtWingReceiveController.java
│      │  │                  │
│      │  │                  └─utils
│      │  │                          CtWingDownSendUtils_1.java
│      │  │                          CtWingDownSendUtils_2.java
│      │  │                          CtWingDownSendUtils_3.java
│      │  │                          CtWingUtils.java
│      │  │
│      │  └─resources
│      │          application.properties
│      │          logback-spring.xml
│      │
│      └─test
│          └─java
│              └─boot
│                  └─ctwing
│                      └─tcp
│                          └─app
│                                  BootCtWingTcpAppTest.java
│                                  Test.java
│       

订阅需要部署在云服务器上或者其他方式,启动项目后,在启动模拟设备端的应用,操作上行下行交互,可以看到很不错的结果

在这里我用了三种方式视线指令的下发

第一种 AIOT提供的SDK 超好用

第二种 AIOT在官方文档提供的Demo 代码较多,方便理解,也可用

第三种 使用Hutool工具的请求来实现,实际也是参考AIOT官方提供demo实现的

再看看AIOT平台的下发指令日志

订阅数据的json数据格式,这里记录一下

上下线消息通知

{"timestamp":1671885117746,"tenantId":"XXXXXX","protocol":"tcp","productId":"15506850","messageType":"deviceOnlineOfflineReport","ipv4Address":"39.144.17.238","iccid":"undefined","eventType":1,"deviceId":"15506850869401041201815"}{"timestamp":1671886478951,"tenantId":"XXXXXX","protocol":"tcp","productId":"15506850","messageType":"deviceOnlineOfflineReport","eventType":0,"deviceId":"15506850869401041201815"}

数据上报通知

{"upPacketSN":-1,"upDataSN":-1,"topic":"ad","timestamp":1671885539011,"tenantId":"XXXXXX","serviceId":"","protocol":"tcp","productId":"15506850","payload":{"APPdata":"6JqC6JqB6Iie"},"messageType":"dataReport","deviceType":"","deviceId":"15506850869401041201815","assocAssetId":"","IMSI":"","IMEI":""}{"upPacketSN":-1,"upDataSN":-1,"topic":"ad","timestamp":1671885567941,"tenantId":"XXXXXX","serviceId":"","protocol":"tcp","productId":"15506850","payload":{"APPdata":"bXl5aHR3MTIzNDU0NjU0Njc0ZXF3amRjcW93ZWljcW93aXhjbmRjeA=="},"messageType":"dataReport","deviceType":"","deviceId":"15506850869401041201815","assocAssetId":"","IMSI":"","IMEI":""}

指令下发后通知

{"timestamp":1671932803885,"tenantId":"XXXXXX","taskId":2,"result":{"resultDetail":"","resultCode":"SENT"},"protocol":"tcp","productId":"15506850","messageType":"commandResponse","deviceId":"15506850869401041201815"}

Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-应用订阅端(北向应用)相关推荐

  1. Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端(南向设备)

    电信的天翼物联网平台CTWing(AIOT)原先是我们俗称的aep,主要用于接入nb-iot设备,当然也可以接入其他的设备,在熟悉AIOT平台后,做后端的我有时候急需终端样品(智能门禁,支付识别终端, ...

  2. 天翼物联网平台(AIoT)无感迁移能力

    近日,天翼物联举办"聚势赋能,匠心服务"天翼物联网平台(AIoT)无感迁移能力线上发布会,推出物联网终端无感迁移能力,以匠心致初心,为实体经济高质量发展注入强劲动力. 天翼物联使能 ...

  3. 天翼物联网平台(AIoT)量子安全能力发布,为物联网端到端安全加装“量子盾”!

    近日,天翼物联举办"安全防护 天翼随行"量子安全能力线上发布会,正式发布天翼物联网平台(AIoT)量子安全能力,为物联网数据安全加装"量子盾",以实际行动践行建 ...

  4. 一文读懂天翼物联网平台(AIoT)

    中国电信天翼智能物联网开放平台(简称"天翼物联网平台AIoT"),是中国电信融合5G.物.云.网.AI等数字要素,打造的泛在智联.全域感知.万物赋智.安全可信.互联互通.虚实共生的 ...

  5. C#对接天翼物联网平台,一些坑

    C#对接天翼物联网平台,一些坑 因为公司要采购一批NB设备,开始着手对接事宜.刚开始也是一头雾水,首先登陆官网,下载各种文档查看.从最开始的申请账号啥的,这些东西应该大家都能搞完.最关键的就是订阅相关 ...

  6. Springboot毕设项目基于大数据平台的个性化图书推荐系统02tt9java+VUE+Mybatis+Maven+Mysql+sprnig)

    Springboot毕设项目基于大数据平台的个性化图书推荐系统02tt9java+VUE+Mybatis+Maven+Mysql+sprnig) 项目运行 环境配置: Jdk1.8 + Tomcat8 ...

  7. 产品 电信nb接口调用_基于NB-IoT平台数据透传模式的应用接入平台设计方法与流程...

    本发明涉及互联网.物联网和软件开发技术领域,具体的说,是一种基于NB-IoT平台数据透传模式的应用接入平台设计方法. 背景技术: NB-IoT支持低功耗设备在广域网的蜂窝数据连接,聚焦于低功耗广覆盖( ...

  8. 一种有手就行的物联网平台的多终端接入方法

    平台:巴法云 接入方法:TCP MQTT HTTP API (一)物联网系统框架的简单架构 这种方法的架构类似于一种抄写 设置于同一个主题下时 巴法云 就像是一块黑板 MCU/FPGA 用户终端则可以 ...

  9. 嵌入式linux毕业设计,基于嵌入式Linux平台的智能小区终端系统设计与实现

    内容简介: 硕士学位论文 基于嵌入式Linux平台的智能小区终端系统设计与实现,正文共69页. [摘要] 智能化小区通过采用智能终端系统,为住户提供了一种更加安全.舒适.方便.快捷和开放的智能化.信息 ...

最新文章

  1. 转:入侵网站必备-sql server
  2. linux c glob使用(文件路径模式查找函数)
  3. linux定时备份mysql_linux定时备份MySQL数据库并删除七天前的备份文件
  4. 七夕福利 | 拒绝狗粮,给你全部的AI(爱)
  5. 有关python方面的论文_一篇文章可以带你理解python中的类
  6. 决策树(Decision Tree,DT)
  7. Java开发两年:简述jvm内存模型
  8. Maven 项目中配置私服
  9. Atitit.研发管理如何避免公司破产倒闭的业务魔咒
  10. java 模拟 cmd_用JAVA模拟实现CMD命令行
  11. [spring] 源码简析 如何解决循环依赖?
  12. 利用VBA操作OutLook批量发送工资条
  13. bim建模的过程的几个要点
  14. C语言中side的用法,side的用法总结大全
  15. 氨氮吹脱法脱与php有关系吗,吹脱法处理氨氮废水
  16. 65536 65535 65534
  17. 【雷达仿真 | FMCW TDMA-MIMO毫米波雷达信号处理仿真(可修改为DDMA-MIMO)】
  18. 不同版本cuda对应的NVIDIA驱动版本
  19. 教你九招防电脑辐射的方法!
  20. 用Python判断是否是闰年的三种方法

热门文章

  1. 天涯明月刀自动演奏器,弹琴脚本,易语言开发,实现键盘映射
  2. 美团开放配送平台,争食即时配送业务
  3. python入门经典例题
  4. 现今计算机CPU,计算机cpu是什么
  5. 大数据专业前景怎么样?
  6. Registration system(翻译 day 3)
  7. 8小时删!付费买的资源,请低调使用~
  8. 2000套像素RPG角色四向行走图
  9. JavaScript时钟(指针式样的)
  10. 完整版软件测试工程师培训视频教程下载