emqx 4
首先要在规则引擎中 配置好 规则 和 资源
资源
主要是选择 资源类型 和 请求 URL (后端接收消息的接口路径)
规则sql
SELECT
topic,clientid,qos,bin2hexstr(payload) as payload,username
FROM
"/transfusion/zhsy/sub/#"
代码片段
// 接收消息的 接口路径 与 资源中配置的 路径一致
@PostMapping("/api/v4/mqtt/publish")
public void getMessage(HttpServletRequest httpServletRequest) throws IOException {
String s = httpServletRequest.getReader().readLine();
httpServletRequest.getReader().close();
Map map = JsonUtils.parseObject(s, Map.class);
log.debug("接收数据的时间",s);
String topic = ObjectUtils.isNotEmpty(map.get("topic")) ? map.get("topic").toString() : "";
String clientid = ObjectUtils.isNotEmpty(map.get("clientid")) ? map.get("clientid").toString() : "";
String event = ObjectUtils.isNotEmpty(map.get("event")) ? map.get("event").toString() : "";
// 判断设备是否离线
if (ObjectUtils.isNotEmpty(clientid) && ObjectUtils.isNotEmpty(event)) {
if (event.equals("client.connected")) {
log.info("设备 上线消息");
// deviceMapper.update(new UpdateWrapper<Device>().lambda().set(Device::getDeviceStatus,).eq(Device::getDeviceSn,clientid));
} else if (event.equals("client.disconnected")) {
log.info("设备 离线消息");
Long existCount = deviceMapper.selectCount(new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, clientid).eq(Device::getIsDelete, 0));
if(existCount > 0){
deviceMapper.update(new UpdateWrapper<Device>().lambda().set(Device::getDeviceStatus, 1).eq(Device::getDeviceSn, clientid).eq(Device::getIsDelete, 0));
}
}
}
// 接收数据
if (ObjectUtils.isNotEmpty(topic) && topic.contains("/transfusion/zhsy/sub")) {
SubscribeMessageBo subscribeMessageBo = new SubscribeMessageBo();
subscribeMessageBo.setUsername(map.get("username").toString());
subscribeMessageBo.setTopic(topic);
subscribeMessageBo.setPayload(map.get("payload").toString().toLowerCase());
subscribeMessageBo.setClientid(map.get("clientid").toString());
//截取 设备 imei
String sn = StringUtils.substringAfter(subscribeMessageBo.getTopic(), "sub/");
// String deviceSn = "";
// if (ObjectUtils.isEmpty(RedisUtils.getCacheObject(sn))) {
// log.info("是否存在设备:{}", deviceSn);
// Device device = deviceMapper.selectOne(new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, sn).eq(Device::getIsDelete, 0));
// Assert.isTrue(ObjectUtils.isNotEmpty(device), "该设备不存在");
// deviceSn = device.getDeviceSn();
// RedisUtils.setCacheObject(deviceSn, deviceSn);
// } else {
// deviceSn = RedisUtils.getCacheObject(sn).toString();
// }
log.info("设备号:{}", sn);
String payload = handlingPayloadToHexStr(subscribeMessageBo.getPayload());
log.info("接收的消息指令:{}", payload);
//保存日志
saveLog(subscribeMessageBo.getPayload(), subscribeMessageBo.getTopic(), 0);
//解析数据
String[] dataArray = StrUtil.split(payload, 2);
int length = dataArray.length;
//帧头
String s1 = dataArray[0] + dataArray[1];
//帧尾
String s2 = dataArray[length - 2] + dataArray[length - 1];
//消息类型 状态 和 报警类型
String s3 = dataArray[2] + dataArray[3];
Device device = new Device();
device.setDeviceSn(sn);
//拼接 16进制 字符
String payloadMessage = dataArray[0] + dataArray[1] + dataArray[2] + dataArray[3] + dataArray[4] + dataArray[5];
// 转化为 byte
byte[] bytes1 = hexStringToByteArray(payloadMessage);
// 校验 sum
byte b = calculateChecksum(bytes1);
// 校验的最终值
String s4 = byteToHexString(b);
log.info("计算的和:{}", s4);
//最终要返回的数据
String value = payloadMessage + "0000" + s4 + dataArray[length - 2] + dataArray[length - 1];
byte[] bytes = HexUtil.decodeHex(value);
log.info("最终返回的数据:{}", bytes);
/**
* 返回响应
*/
//拼装响应数据
HashMap<String, Object> hashMap = new HashMap<>();
String pubTopic = "/transfusion/zhsy/pub/";
hashMap.put("topic", pubTopic + sn);
hashMap.put("payload", Base64.encode(bytes));
hashMap.put("qos", 1);
hashMap.put("clientid", sn);
hashMap.put("encoding", "base64");
hashMap.put("retain", false);
String result = JsonUtils.toJsonString(hashMap);
Map<String, String> headers = new HashMap<>();
headers.put("content-type", "application/json;charset=UTF-8");
headers.put("accept", "application/json");
String user = "admin:public";
String encode = Base64.encode(user.getBytes(StandardCharsets.UTF_8));
headers.put("Authorization", "Basic " + encode);
//查看节点健康状态
//String body = HttpRequest.get("http://hmkj.hmisp.com:9081/status").addHeaders(headers).executeAsync().body();
// 向 emqx 发布 消息
String body = HttpRequest.post("http://hmkj.hmisp.com:9081/api/v4/mqtt/publish").body(result).addHeaders(headers).execute().body();
log.info("响应结果:{}", body);
//保存日志 转换为 16进制字符串
String s5 = handlingPayloadToHexStr(bytes);
saveLog(s5, pubTopic, 1);
log.info("发布的指令:{}", s5);
/**
* 校验数据
*/
if ("3faa".equals(s1) && "5665".equals(s2)) {
if ("736d".equals(s3)) {
//状态指令
if ("00".equals(dataArray[length - 4])) {
//待机指令
device.setDeviceStatus(2);
} else if ("01".equals(dataArray[length - 4])) {
//检测指令
device.setDeviceStatus(4);
} else if ("02".equals(dataArray[length - 4])) {
//无液报警
device.setDeviceStatus(3);
}
} else if ("626c".equals(s3)) {
//电量指令
if ("00".equals(dataArray[length - 4])) {
//低电量
device.setQuantity(1);
} else if ("01".equals(dataArray[length - 4])) {
//中电量
device.setQuantity(2);
} else if ("02".equals(dataArray[length - 4])) {
//高电量
device.setQuantity(3);
}
}
// 修改设备状态
int update = deviceMapper.update(device, new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, sn));
log.info("状态修改是否成功! {}", update);
/**
* 通过 websocket 后台向前台 发送更新的消息
*/
send(sn);
} else {
log.info("该指令格式错误:{}", payload);
}
}
}
@Resource
public WebSocketEndPoint webSocketEndPoint;
/**
* 通过 websocket 后台向前台 发送更新的消息
*/
public void send(String sn){
String userId = deviceMapper.selectUser(sn);
//更新的数据
LargeDeviceVo largeDeviceVo = deviceMapper.selectLarge(sn);
AddUpVo addUpVo = deviceMapper.statisticCount();
Map<String,Object> objects = new HashMap<>();
objects.put("newData",largeDeviceVo);
objects.put("num",addUpVo);
webSocketEndPoint.sendMessageToOne(userId,JsonUtils.toJsonString(objects), null);
}
/*
* v4 版本的 调用 api 的 认证方式
*/
@GetMapping("api/v4/auth")
public void getUserName(){
String msg = "{\"backend\":\"built_in_database\",\"mechanism\":\"password_based\",\"password_hash_algorithm\":{\"name\":\"plain\",\"salt_position\":\"disable\"},\"user_id_type\":\"username\"}";
Map<String, String> headers = new HashMap<>();
headers.put("content-type","application/json;charset=UTF-8");
headers.put("accept","application/json");
String username = "b3172aa5f2e9c4f1";
String password = "734pju9CRaK0omgTjCD38RIQ79CxTs3efYEMvA6OT0gwH";
headers.put("Authorization",Credentials.basic(username,password));
String body = HttpRequest.post("http://192.168.20.53:18083/api/v4/authentication").body(msg).addHeaders(headers).execute().body();
log.info(body);
}
/**
* v4版本 api 设置 账号密码
*/
@GetMapping("api/v4/setUser")
public void setUser() {
Map<String, String> headers = new HashMap<>();
headers.put("content-type", "application/json;charset=UTF-8");
headers.put("accept", "application/json");
String user = "admin:public";
String encode = Base64.encode(user.getBytes(StandardCharsets.UTF_8));
headers.put("Authorization", "Basic " + encode);
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("clientid", "localhost_consumer");
hashMap.put("password", "123456");
String body = HttpRequest.post("http://hmkj.hmisp.com:9081/api/v4/auth_clientid").body(JsonUtils.toJsonString(hashMap)).addHeaders(headers).execute().body();
log.info(body);
}
emqx 5
设计图
规则
连接器
代码片段
/**
* Description:
* 关于 emqx 桥接 webhook 向 java 后端发送消息时需要注意的问题:
*
* @author xcx
* @date 2024/3/14
*/
@RestController
@RequestMapping()
@Slf4j
public class MqttMessqgeController {
@Resource
private DeviceMapper deviceMapper;
@Resource
private LogMapper logMapper;
// v5 版本 接收消息 接口路径
@PostMapping("/api/v5/publish")
public void getMessage(HttpServletRequest httpServletRequest) throws IOException {
String s = httpServletRequest.getReader().readLine();
httpServletRequest.getReader().close();
Map map = JsonUtils.parseObject(s, Map.class);
log.debug("接收数据的时间",s);
String topic = ObjectUtils.isNotEmpty(map.get("topic")) ? map.get("topic").toString() : "";
String clientid = ObjectUtils.isNotEmpty(map.get("clientid")) ? map.get("clientid").toString() : "";
String event = ObjectUtils.isNotEmpty(map.get("event")) ? map.get("event").toString() : "";
// 判断设备是否离线
if (ObjectUtils.isNotEmpty(clientid) && ObjectUtils.isNotEmpty(event)) {
if (event.equals("client.connected")) {
log.info("设备 上线消息");
// deviceMapper.update(new UpdateWrapper<Device>().lambda().set(Device::getDeviceStatus,).eq(Device::getDeviceSn,clientid));
} else if (event.equals("client.disconnected")) {
log.info("设备 离线消息");
Long existCount = deviceMapper.selectCount(new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, clientid).eq(Device::getIsDelete, 0));
if(existCount > 0){
deviceMapper.update(new UpdateWrapper<Device>().lambda().set(Device::getDeviceStatus, 1).eq(Device::getDeviceSn, clientid).eq(Device::getIsDelete, 0));
}
}
}
// 接收数据
if (ObjectUtils.isNotEmpty(topic) && topic.contains("/transfusion/zhsy/sub")) {
SubscribeMessageBo subscribeMessageBo = new SubscribeMessageBo();
subscribeMessageBo.setUsername(map.get("username").toString());
subscribeMessageBo.setTopic(topic);
subscribeMessageBo.setPayload(map.get("payload").toString().toLowerCase());
subscribeMessageBo.setClientid(map.get("clientid").toString());
//截取 设备 imei
String sn = StringUtils.substringAfter(subscribeMessageBo.getTopic(), "sub/");
// String deviceSn = "";
// if (ObjectUtils.isEmpty(RedisUtils.getCacheObject(sn))) {
// log.info("是否存在设备:{}", deviceSn);
// Device device = deviceMapper.selectOne(new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, sn).eq(Device::getIsDelete, 0));
// Assert.isTrue(ObjectUtils.isNotEmpty(device), "该设备不存在");
// deviceSn = device.getDeviceSn();
// RedisUtils.setCacheObject(deviceSn, deviceSn);
// } else {
// deviceSn = RedisUtils.getCacheObject(sn).toString();
// }
log.info("设备号:{}", sn);
String payload = handlingPayloadToHexStr(subscribeMessageBo.getPayload());
log.info("接收的消息指令:{}", payload);
//保存日志
saveLog(subscribeMessageBo.getPayload(), subscribeMessageBo.getTopic(), 0);
//解析数据
String[] dataArray = StrUtil.split(payload, 2);
int length = dataArray.length;
//帧头
String s1 = dataArray[0] + dataArray[1];
//帧尾
String s2 = dataArray[length - 2] + dataArray[length - 1];
//消息类型 状态 和 报警类型
String s3 = dataArray[2] + dataArray[3];
Device device = new Device();
device.setDeviceSn(sn);
//拼接 16进制 字符
String payloadMessage = dataArray[0] + dataArray[1] + dataArray[2] + dataArray[3] + dataArray[4] + dataArray[5];
// 转化为 byte
byte[] bytes1 = hexStringToByteArray(payloadMessage);
// 校验 sum
byte b = calculateChecksum(bytes1);
// 校验的最终值
String s4 = byteToHexString(b);
log.info("计算的和:{}", s4);
//最终要返回的数据
String value = payloadMessage + "0000" + s4 + dataArray[length - 2] + dataArray[length - 1];
byte[] bytes = HexUtil.decodeHex(value);
log.info("最终返回的数据:{}", bytes);
/**
* 返回响应
*/
//拼装响应数据
HashMap<String, Object> hashMap = new HashMap<>();
String pubTopic = "/transfusion/zhsy/pub/";
hashMap.put("topic", pubTopic + sn);
hashMap.put("payload_encoding", "base64");
hashMap.put("qos", 1);
hashMap.put("payload",bytes);
hashMap.put("retain", false);
String result = JsonUtils.toJsonString(hashMap);
Map<String, String> headers = new HashMap<>();
headers.put("content-type", "application/json;charset=UTF-8");
headers.put("accept", "application/json");
// v5版本认证
String username = "437a107f0f534c00";
String password = "yNxDUxn9AgSv2wvujhspmodSYMKWOSJ9BTFWvx7iI9BFvH";
headers.put("Authorization", Credentials.basic(username, password));
//查看节点健康状态
// String body = HttpRequest.get("http://hmkj.hmisp.com:9081/status").addHeaders(headers).executeAsync().body();
String body = HttpRequest.post("http://hmkj.hmisp.com:18083/api/v5/publish").body(result).addHeaders(headers).execute().body();
log.info("响应结果:{}", body);
//保存日志 转换为 16进制字符串
String s5 = handlingPayloadToHexStr(bytes);
saveLog(s5, pubTopic, 1);
log.info("发布的指令:{}", s5);
/**
* 校验数据
*/
if ("3faa".equals(s1) && "5665".equals(s2)) {
if ("736d".equals(s3)) {
//状态指令
if ("00".equals(dataArray[length - 4])) {
//待机指令
device.setDeviceStatus(2);
} else if ("01".equals(dataArray[length - 4])) {
//检测指令
device.setDeviceStatus(4);
} else if ("02".equals(dataArray[length - 4])) {
//无液报警
device.setDeviceStatus(3);
}
} else if ("626c".equals(s3)) {
//电量指令
if ("00".equals(dataArray[length - 4])) {
//低电量
device.setQuantity(1);
} else if ("01".equals(dataArray[length - 4])) {
//中电量
device.setQuantity(2);
} else if ("02".equals(dataArray[length - 4])) {
//高电量
device.setQuantity(3);
}
}
// 修改设备状态
int update = deviceMapper.update(device, new QueryWrapper<Device>().lambda().eq(Device::getDeviceSn, sn));
log.info("状态修改是否成功! {}", update);
/**
* 通过 websocket 后台向前台 发送更新的消息
*/
send(sn);
} else {
log.info("该指令格式错误:{}", payload);
}
}
}
@Resource
public WebSocketEndPoint webSocketEndPoint;
/**
* 通过 websocket 后台向前台 发送更新的消息
*/
public void send(String sn){
String userId = deviceMapper.selectUser(sn);
//更新的数据
LargeDeviceVo largeDeviceVo = deviceMapper.selectLarge(sn);
AddUpVo addUpVo = deviceMapper.statisticCount();
Map<String,Object> objects = new HashMap<>();
objects.put("newData",largeDeviceVo);
objects.put("num",addUpVo);
webSocketEndPoint.sendMessageToOne(userId,JsonUtils.toJsonString(objects), null);
}
}
总结
相比较于 v4版本的,访问接口的 认证方式不一样