一、问题场景
物联设备比如NB设备通过NB协议将数据传到电信平台后,我们的应用服务如何从电信平台获取可用的上报数据。以下通过电信开发者平台提供的SDK来简单演示下整个过程。
二、使用电信 SDK进行开发
电信AIOT物联平台提供了两种方式获取平台数据,一种是HTTP方式,一种事SDK方式。推荐使用SDK方式,因为HTTP方式调用时可能会遇到一些不通或不稳定的情况。
电信平台提供了多种SDK,本示例使用JAVA SDK开发。电信平台提供了在线API调试,在开发程序前,可以先通过在线API来验证一下请求和响应的演示过程。参见我另一篇文章介绍物联网:从电信物联开发平台AIoT获取物联设备上报数据示例
下列调用设备数据查询接口getDeviceStatusHisInPage为例。
1.下载电信sdk的jar包
点击下载地址,从电信开发者平台下载两个jar包为,ag-sdk-biz-53266.tar.gz-20240517.102115-SNAPSHOT.jar和ctg-ag-sdk-core-2.8.0-20230508.100604-1.jar。
2.引入jar依赖包
<dependency>
<groupId>com.ctg.ag.sdk.biz</groupId>
<artifactId>sdkpackage</artifactId>
<scope>system</scope>
<version>1.0</version>
<systemPath>${project.basedir}/lib/ag-sdk-biz-53266.tar.gz-20240517.102115-SNAPSHOT.jar</systemPath>
</dependency>
<dependency>
<groupId>com.ctg.ag.sdk.core</groupId>
<artifactId>sdkcore</artifactId>
<scope>system</scope>
<version>1.0</version>
<systemPath>${project.basedir}/lib/ctg-ag-sdk-core-2.8.0-20230508.100604-1.jar</systemPath>
</dependency>
程序实现方法源码如下:
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ctg.ag.sdk.biz.AepDeviceStatusClient;
import com.ctg.ag.sdk.biz.aep_device_status.GetDeviceStatusHisInPageRequest;
import com.ctg.ag.sdk.biz.aep_device_status.GetDeviceStatusHisInPageResponse;
public ResultMsg ctGetDeviceStatusHisInPage(@RequestBody HashMap<String, Object> map) {
String estr = "电信物联平台接口-用于测试物联上报数据 (ctGetDeviceStatusHisInPage)====:";
ResultMsg rMsg = new ResultMsg(); //此类是返回对象类,你可以根据自己的需求来定义返回类型
JSONObject jb;
try {
logger.info(estr + map.toString());
String secret = "a2ze0b9su3";//密钥,到控制台->应用管理打开应用可以找到此值
String appkey = "t9U2ykdRIO5";//appKey,到控制台->应用管理打开应用可以找到此值
AepDeviceStatusClient client = AepDeviceStatusClient.newClient()
.appKey(appkey).appSecret(secret)
.build();
//创建对应方法请求对象,不同的请求方法,对应不同的类名
GetDeviceStatusHisInPageRequest request = new GetDeviceStatusHisInPageRequest();
// 将入参转为json 字符串格式
String bodyString=Comm.hashMapToJsonStr(map);
request.setBody(bodyString.getBytes()); //具体格式见前面请求body说明
//向电信平台发送请求
GetDeviceStatusHisInPageResponse response= client.getDeviceStatusHisInPage(request);
//返回响应信息
if(response.getStatusCode()==200){
//字符串转为json对象
jb=Comm.strToJson(new String(response.getBody(),"UTF-8"));
//将结果中的设备状态列表中转为数组
JSONArray array=(JSONArray) jb.get("deviceStatusList");
if(array==null){
rMsg.Clear();
rMsg.setResultMsg(jb.toString());
return rMsg;
}
String data,state;
//遍历数组,逐条解码,将对应设备上报数据中的点表(字节)转换为可用数据.(下列为该示例设备的点表字符串按厂家给的位数规则进行分拆转换)
for (int i = 0; i < array.size(); i++) {
jb=(JSONObject)array.get(i);
//base64位转为16进制
data=Comm.base64toHex(Comm.getString(jb.get("APPdata")));
jb.put("APPdata",data);
jb.put("imei",data.substring(4,20));
jb.put("sim",data.substring(50,70));
jb.put("curRead",Comm.hexToInt(data.substring(70,78))*0.01);//16进制转位10进制
jb.put("cdateTime",data.substring(78,90));
state=Comm.getStrAddPrefix(Comm.hexToBinary(data.substring(94,96)),"0",8);//16进制转2进制
jb.put("powerState",state.substring(2,4));//1x 电池正常 0x 电池欠电
jb.put("valveState",state.substring(4,6));//00 无阀 01 阀门关闭 10 阀门开启 11 阀门不到位
}
rMsg.setSuccess();
rMsg.setList(Comm.toList(array));
}
logger.info(rMsg.toString());
client.shutdown();
return rMsg;
} catch (Exception e) {
map.clear();
map.put("oper_module", estr);
map.put("oper_content", estr + e.getMessage());
logExceptServ.add(map);
rMsg.setResultMsg(estr + e.getMessage());
return rMsg;
}
}
以下是上述代码中使用到的方法,主要是在解析过程中使用到字节转换,你可以根据不同设备厂家提供的对应点表说明文档,依实际情况来按位数解析。本示例的设备是远传水表。
/**
* HashMap转json字符串
*/
public static String hashMapToJsonStr(HashMap<String, Object> map) {
JSONObject jObject = new JSONObject();
for (Map.Entry<String, Object> item : map.entrySet()) {
jObject.put(item.getKey(), item.getValue());
}
return jObject.toString();
}
/**
* 将Json字符串转换为Json
* @param json
* @return
*/
public static JSONObject strToJson(String json) {
// 将json字符串转换成jsonObject
return JSONObject.parseObject(json);
}
// base64转16进制
public static final String base64toHex(String str) {
byte[] data= Base64.getDecoder().decode(str);
final StringBuffer sb = new StringBuffer(data.length * 2);
for (int i = 0; i < data.length; i++) {
sb.append(DIGITS[(data[i] >>> 4) & 0x0F]);
sb.append(DIGITS[data[i] & 0x0F]);
}
return sb.toString();
}
private static final char[] DIGITS
= {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
//十六进制转十进制
public static int hexToInt(String hex) {
return Integer.parseInt(hex,16);
}
/**
* 字符串前补指定字符,指定总长度
* @return java.lang.String
* @Param [str 原字符串, c 前缀字符, allLen 总长度]
* @Author quan
* @Date 2020/12/29
*/
public static String getStrAddPrefix(String str, String c, int allLen) {
while (str.length() < allLen) {
str = c + str;
}
return str;
}
//十六进制转二进制
public static String hexToBinary(String hex) {
int hexint = Integer.parseInt(hex, 16);
String binary = Integer.toBinaryString(hexint);
return binary;
}
/**
* 将对象类(或json格式的字符串)解析成List对象
* @param object json格式:[{point_ids: "49,", road_ids: "32", officer_ids: "8f08537b00d74fa2b99120f3a2a1fd9a"}]
* @return List<Object></>
*
* @author qiang
*/
public static List<Object> toList(Object object) {
List<Object> list = new ArrayList<>();
if(object == null || object.equals(""))
return null;
// 将json字符串转换成jsonObject
JSONArray jsonArray = JSONArray.parseArray(object.toString());
// 此时需要加个判断
if (jsonArray.isEmpty()) {
System.out.println("jsonArray 为空");
} else {
list.addAll(jsonArray);
}
return list;
}
3.运行程序并调用接口后输出结果
补充说明
在引用电信SDK的两个依赖包后,本地开发环境运行正常,但若出现打包后在生产环境运行出错,提示电信jar中的某方法丢失,是因为该本地引用包没有打包到进去的原因。可以参见我这篇文章来解决:在IDEA引入本地jar包的方法并解决打包scope为system时发布无法打包进lib的方案。