chore: 客流计数器对接-数据解析、自定义返回DTO

This commit is contained in:
lzh
2025-12-25 15:16:53 +08:00
parent 4066d65da2
commit 9d6b53c4f6
3 changed files with 176 additions and 1 deletions

View File

@@ -0,0 +1,131 @@
package com.viewshanghai.module.iot.gateway.codec.people;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.viewshanghai.framework.common.util.json.JsonUtils;
import com.viewshanghai.module.iot.core.enums.IotDeviceMessageMethodEnum;
import com.viewshanghai.module.iot.core.mq.message.IotDeviceMessage;
import com.viewshanghai.module.iot.gateway.codec.IotDeviceMessageCodec;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* 客流计数器设备 编解码器
* <p>
* 适配设备上传的特定 JSON 格式
*/
@Slf4j
@Component
public class IotPeopleCounterCodec implements IotDeviceMessageCodec {
/**
* 编解码器类型,需要在设备管理中配置为该值
*/
public static final String TYPE = "PEOPLE_COUNTER";
@Override
public String type() {
return TYPE;
}
@Override
public IotDeviceMessage decode(byte[] bytes) {
// 1. 预处理:转换字符串并修复可能的中文引号问题
String jsonStr = new String(bytes, StandardCharsets.UTF_8);
jsonStr = jsonStr.replace("", "\"").replace("", "\""); // 修复中文双引号
// 2. 解析 JSON
PeopleCounterPayload payload;
try {
payload = JsonUtils.parseObject(jsonStr, PeopleCounterPayload.class);
} catch (Exception e) {
log.error("[decode][解析设备数据失败: {}]", jsonStr, e);
throw new IllegalArgumentException("JSON 格式错误");
}
Assert.notNull(payload, "消息内容不能为空");
// 3. 构建属性参数 Map (params)
Map<String, Object> params = MapUtil.newHashMap();
// 3.1 基础属性
params.put("rssi", payload.getRssi());
params.put("model", payload.getModel());
params.put("version", payload.getVersion());
params.put("pair_status", StrUtil.trim(payload.getPair())); // 去除可能的空格
// 3.2 业务数据 (取 data 列表中时间最新的一条)
LocalDateTime reportTime = LocalDateTime.now();
if (CollUtil.isNotEmpty(payload.getData())) {
// 尝试按时间排序取最新,或者直接取最后一条(视设备协议而定,这里假设时间字符串可排序)
PeopleCounterData latestData = payload.getData().stream()
.max(Comparator.comparing(PeopleCounterData::getTime))
.orElse(payload.getData().get(payload.getData().size() - 1));
params.put("people_in", latestData.getIn());
params.put("people_out", latestData.getOut());
params.put("battery_rx", latestData.getRxBat());
params.put("battery_tx", latestData.getTxBat());
// 解析上报时间(改为使用本地当前时间)
// if (StrUtil.isNotBlank(latestData.getTime())) {
// try {
// // 格式20250501093000 -> yyyyMMddHHmmss
// reportTime = LocalDateTimeUtil.parse(latestData.getTime(), DatePattern.PURE_DATETIME_PATTERN);
// } catch (Exception e) {
// log.warn("[decode][时间格式解析失败: {}]", latestData.getTime());
// }
// }
}
// 4. 构建 IotDeviceMessage
return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), params)
.setReportTime(LocalDateTime.now());
}
@Override
public byte[] encode(IotDeviceMessage message) {
// 如果需要下发指令给设备,可以在这里实现编码逻辑
// 目前需求主要是上报解码,暂不支持下行或透传原样
return new byte[0];
}
/**
* 设备上报的数据结构
*/
@Data
public static class PeopleCounterPayload {
private String uuid;
private String model;
private String version;
private String pair;
private Integer num;
// 处理 JSON 中的大写或特殊命名,这里假设 JsonUtils 使用 Jackson
@JsonProperty("RSSI")
private Integer rssi;
private List<PeopleCounterData> data;
}
@Data
public static class PeopleCounterData {
private String time;
private Integer in;
private Integer out;
private Integer rxBat;
private Integer txBat;
}
}

View File

@@ -0,0 +1,44 @@
package com.viewshanghai.module.iot.gateway.codec.people.dto;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 客流计数器设备上报响应
*
* @author 芋道源码
*/
@Data
public class PeopleCounterUploadResp {
private Integer statusCode; // 0 正常 1 未绑定平台 2 解析 json 出错
private String time; // yyyyMMddHHmmss
private String open; // 营业开始时间
private String close; // 营业结束时间
private Integer saveCycle; // 存储周期
private Integer upCycle; // 上报周期
private String license; // 0 正常 1 证书到期
private String direction; // twoWay / onlyIn / onlyOut / exchange
private String upgradeUrl; // 升级地址(无升级可为空)
/**
* 构建默认的客流计数器响应数据
*
* @return 响应对象
*/
public static PeopleCounterUploadResp createDefault() {
PeopleCounterUploadResp resp = new PeopleCounterUploadResp();
resp.setStatusCode(0);
resp.setTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
resp.setOpen("0800");
resp.setClose("2300");
resp.setSaveCycle(1);
resp.setUpCycle(1);
resp.setLicense("0");
resp.setDirection("twoWay");
resp.setUpgradeUrl("http://op.foorir.com/upgrade/2023120.bin");
return resp;
}
}

View File

@@ -34,7 +34,7 @@ viewshanghai:
gateway:
# 设备 RPC 配置
rpc:
url: ${VIEWSHANGHAI_IOT_GATEWAY_RPC_URL:http://124.221.55.225:48080} # 主程序 API 地址
url: ${VIEWSHANGHAI_IOT_GATEWAY_RPC_URL:http://127.0.0.1:48080} # 主程序 API 地址
connect-timeout: 30s
read-timeout: 30s
# 设备 Token 配置