Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-应用订阅端(北向应用)
之前实现了使用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协议(透传模式)-应用订阅端(北向应用)相关推荐
- Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端(南向设备)
电信的天翼物联网平台CTWing(AIOT)原先是我们俗称的aep,主要用于接入nb-iot设备,当然也可以接入其他的设备,在熟悉AIOT平台后,做后端的我有时候急需终端样品(智能门禁,支付识别终端, ...
- 天翼物联网平台(AIoT)无感迁移能力
近日,天翼物联举办"聚势赋能,匠心服务"天翼物联网平台(AIoT)无感迁移能力线上发布会,推出物联网终端无感迁移能力,以匠心致初心,为实体经济高质量发展注入强劲动力. 天翼物联使能 ...
- 天翼物联网平台(AIoT)量子安全能力发布,为物联网端到端安全加装“量子盾”!
近日,天翼物联举办"安全防护 天翼随行"量子安全能力线上发布会,正式发布天翼物联网平台(AIoT)量子安全能力,为物联网数据安全加装"量子盾",以实际行动践行建 ...
- 一文读懂天翼物联网平台(AIoT)
中国电信天翼智能物联网开放平台(简称"天翼物联网平台AIoT"),是中国电信融合5G.物.云.网.AI等数字要素,打造的泛在智联.全域感知.万物赋智.安全可信.互联互通.虚实共生的 ...
- C#对接天翼物联网平台,一些坑
C#对接天翼物联网平台,一些坑 因为公司要采购一批NB设备,开始着手对接事宜.刚开始也是一头雾水,首先登陆官网,下载各种文档查看.从最开始的申请账号啥的,这些东西应该大家都能搞完.最关键的就是订阅相关 ...
- Springboot毕设项目基于大数据平台的个性化图书推荐系统02tt9java+VUE+Mybatis+Maven+Mysql+sprnig)
Springboot毕设项目基于大数据平台的个性化图书推荐系统02tt9java+VUE+Mybatis+Maven+Mysql+sprnig) 项目运行 环境配置: Jdk1.8 + Tomcat8 ...
- 产品 电信nb接口调用_基于NB-IoT平台数据透传模式的应用接入平台设计方法与流程...
本发明涉及互联网.物联网和软件开发技术领域,具体的说,是一种基于NB-IoT平台数据透传模式的应用接入平台设计方法. 背景技术: NB-IoT支持低功耗设备在广域网的蜂窝数据连接,聚焦于低功耗广覆盖( ...
- 一种有手就行的物联网平台的多终端接入方法
平台:巴法云 接入方法:TCP MQTT HTTP API (一)物联网系统框架的简单架构 这种方法的架构类似于一种抄写 设置于同一个主题下时 巴法云 就像是一块黑板 MCU/FPGA 用户终端则可以 ...
- 嵌入式linux毕业设计,基于嵌入式Linux平台的智能小区终端系统设计与实现
内容简介: 硕士学位论文 基于嵌入式Linux平台的智能小区终端系统设计与实现,正文共69页. [摘要] 智能化小区通过采用智能终端系统,为住户提供了一种更加安全.舒适.方便.快捷和开放的智能化.信息 ...
最新文章
- 转:入侵网站必备-sql server
- linux c glob使用(文件路径模式查找函数)
- linux定时备份mysql_linux定时备份MySQL数据库并删除七天前的备份文件
- 七夕福利 | 拒绝狗粮,给你全部的AI(爱)
- 有关python方面的论文_一篇文章可以带你理解python中的类
- 决策树(Decision Tree,DT)
- Java开发两年:简述jvm内存模型
- Maven 项目中配置私服
- Atitit.研发管理如何避免公司破产倒闭的业务魔咒
- java 模拟 cmd_用JAVA模拟实现CMD命令行
- [spring] 源码简析 如何解决循环依赖?
- 利用VBA操作OutLook批量发送工资条
- bim建模的过程的几个要点
- C语言中side的用法,side的用法总结大全
- 氨氮吹脱法脱与php有关系吗,吹脱法处理氨氮废水
- 65536 65535 65534
- 【雷达仿真 | FMCW TDMA-MIMO毫米波雷达信号处理仿真(可修改为DDMA-MIMO)】
- 不同版本cuda对应的NVIDIA驱动版本
- 教你九招防电脑辐射的方法!
- 用Python判断是否是闰年的三种方法