介绍:
- 本次Java后端和硬件进行交互,采用的是UDP协议,JSON传输,POST请求,springboot,mysql(用的是alibaba.fastjson的包)
- 主要流程是: 由于当前设备含有心跳包,所以先去配置好接收心跳的接口,配置好之后再让云端调用JAVA后台的接口获取存入云端数据库的数据,使用网络请求工具类HttpClientUtil.doPost() 填入本地另一套程序的路径和将拿到的数据封装成JSON进行请求, 另一套程序需要写一个方法,配置好路径,之后需要创建UDP客户端Socket,接收需要向设备传输的数据,配置好设备硬件的IP地址和端口号,默认写死门禁硬件的端口号,防止空指针,设备发生心跳后,更新端口数组,并将解析后的UTF-8(心跳)信息传送给云端接收。
No nonsense
云端代码(只含逻辑层)
参考如下:
public R<Map<String,Integer>> unlockDoors(JSONObject jsonObject) {
if(null == jsonObject.getString("deviceId")){
return R.fail("门禁设备表ID不能为空");
}
if(null == jsonObject.getString("xqId")){
return R.fail("小区ID不能为空");
}
if(null == jsonObject.getString("ubicellJyh")){
return R.fail("系统家园号不能为空");
}
//门禁设备表
AcDevlist dev = acDevlistDao.selectById(jsonObject.getString("deviceId"));
if(null == dev){
return R.fail("系统未找到对应用户");
}
TUserbase base = tUserbaseDao.getUserByJyh(Long.parseLong(jsonObject.getString("xqId")),jsonObject.getString("ubicellJyh"));
if(null == base){
return R.fail("系统未找到对应用户");
}
XqInfo info = xqInfoDao.selectById(Long.parseLong(jsonObject.getString("xqId")));
if(null == info){
return R.fail("系统没找到小区信息");
}
AcHouse house =acHouseDao.selectById(dev.getHouseId());
if(null == house){
return R.fail("系统没找到设备对应的房间");
}
// 发送的数据
JSONObject json = new JSONObject();
json.put("communityId", info.getCommunityCode());
json.put("deviceCode", dev.getDevSn());
json.put("cmd","unlock");
json.put("phone",base.getPhone());
json.put("roomNumber",house.getHouseNum());
json.put("unitId","0011");
json.put("id",info.getId());
String strJson = json.toString();
System.err.println("strJson---------------:"+strJson);
AcDoorOperLog acDoorOperLog = new AcDoorOperLog();
acDoorOperLog.setXqId(Long.valueOf(jsonObject.getString("xqId")));//小区ID
acDoorOperLog.setDoorId(dev.getId());//设备ID
acDoorOperLog.setOperResult(0);//开门状态,0失败,1成功
acDoorOperLog.setDevName(dev.getName());//设备名称
acDoorOperLog.setUsername(base.getUbicellJyh());//系统家园号
acDoorOperLog.setNickname(base.getNickname());//用户昵称
acDoorOperLog.setPhone(base.getPhone());//用户手机号
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
acDoorOperLog.setGmtCreate( sdf.parse(sdf.format(new Date())));
} catch (ParseException e) {
e.printStackTrace();
}
//新增一条开门日志
acDoorOperLogDao.insert(acDoorOperLog);
insertId = acDoorOperLog.getId();
try { HttpClientUtil.doPost("本地另一套程序的路径接口地址",strJson);
//休眠3秒
Thread.sleep(3000);
if (udpControl.getReceiveControlParameters() != null){
Map<String, Integer> map = new HashMap<>();
if (udpControl.getReceiveControlParameters().equals("0")){
//修改开门日志->改为开门状态(1)
AcDoorOperLog acDoorOperLog2 = new AcDoorOperLog();
acDoorOperLog2.setId(insertId);
acDoorOperLog2.setOperResult(1);
acDoorOperLogDao.updateById(acDoorOperLog2);
map.put("开门成功",0);
return R.success(map);
}
if (udpControl.getReceiveControlParameters().equals("1") || udpControl.getReceiveControlParameters().equals("2")){
map.put("开门失败",1);
return R.success(map);
}
}else{
System.err.println("检查对象参数");
return R.fail("返回的参数为null");
}
} catch (Exception e) {
e.printStackTrace();
}
return R.success(new HashMap<>());
}
另一套程序的逻辑层
参考如下:
private void AccessControlUDP(@RequestBody JSONObject jsonObject) {
try {
// 创建UDP客户端Socket
DatagramSocket socket = new DatagramSocket();
// 发送数据
JSONObject data = new JSONObject();
data.put("communityId",jsonObject.getString("communityId"));
data.put("deviceCode", jsonObject.getString("deviceCode"));
data.put("cmd", jsonObject.getString("cmd"));
data.put("phone", jsonObject.getString("phone"));
data.put("roomNumber", jsonObject.getString("roomNumber"));
data.put("unitId", jsonObject.getString("unitId"));
data.put("id", jsonObject.getString("id"));
String message = data.toString();
System.err.println("message:"+message);
// 设备硬件的IP地址和端口号
InetAddress address = InetAddress.getByName("设备IP");
Map<String,Integer> udpPortMap = RLFUDPServer.udpPortMap.getMaps();
if (udpPortMap == null || udpPortMap.isEmpty()) {
int ports = "随便写死个端口";// 门禁硬件的端口号为空默认写死(*) 防止空指针
byte[] sendData = message.getBytes();
DatagramPacket packet = new DatagramPacket(sendData, sendData.length, address, ports);
socket.send(packet);
socket.close(); //关闭Socket
}else {//发生心跳后,更新端口数组
if (jsonObject.getString("deviceCode").equals(udpPortMap.keySet().iterator().next())){
String key = udpPortMap.keySet().iterator().next();
Integer value = udpPortMap.get(key);
byte[] sendData = message.getBytes();
DatagramPacket packet = new DatagramPacket(sendData, sendData.length, address, value);
socket.send(packet);
socket.close(); //关闭Socket
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
程序获取心跳
@WebListener
//@Component
public class RLFUDPServer implements ServletContextListener {
//记录日志
public static Logger logger = Logger.getLogger(RLFUDPServer.class.getName());
//最大udp数据大小
public static final int MAX_UDP_DATA_SIZE = ();
//监听端口
public static final int UDP_PORT = ();
//包
public static DatagramPacket packet = ();
//套接字
public static DatagramSocket socket = ();
public static UdpPortMap udpPortMap;
static {
udpPortMap = new UdpPortMap();
}
//这是一个事件类,用于通知web应用程序servlet上下文的更改。( ServletContextEvent )
//在contextInitialized()方法中,通过ServletContext对象注册一个Servlet,用于接收心跳请求。
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
logger.info("-------> 启动线程,监听UDP数据,端口为:"+UDP_PORT);
// 启动一个线程,监听UDP数据报
new Thread(new UDPProcess(UDP_PORT)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class UDPProcess implements Runnable {
public UDPProcess(final int port) throws SocketException {
//创建服务器端DatagramSocket,指定端口
socket = new DatagramSocket(port);
}
@Override
public void run() {
// TODO Auto-generated method stub
logger.info("-------> 创建数据报文,用于接收客户端发送的数据");
//创建数据报文,用于接收客户端发送的数据
while (true) {
byte[] buffer = new byte[MAX_UDP_DATA_SIZE];
packet = new DatagramPacket(buffer, buffer.length);
try {
logger.info("-------> 门禁心跳将在50秒后发送");
logger.info("-------> 等稍后....");
logger.info("-------> 此方法在接收到数据之前,将会一直阻塞");
logger.info("-------> 等待中接收....");
socket.receive(packet);
new Thread(new Process(packet)).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static class Process implements Runnable {
public Process(DatagramPacket packet) throws UnsupportedEncodingException {
// TODO Auto-generated constructor stub
//返回数据缓冲区。接收到的数据或要发送的数据从缓冲区中的偏移量开始,并运行长度为long。
// 接收到的UDP信息,然后解码
byte[] buffer = packet.getData();
String srt2 = new String(buffer, "UTF-8").trim();
logger.info("-------> 解析后的UTF-8(心跳)信息为:" + srt2);
//使用第三方的Gson库, 将UTF-8编码的字符串, 转换为(google.gson.JsonObject)对象进行传递
Gson gson = new Gson();
JsonObject jsonObjects = gson.fromJson(srt2, JsonObject.class);
HttpClientUtil.doPost("解析后的UTF-8(心跳)信息向云端传送(云端接收心跳信息的接口路径)",jsonObjects);
int port = packet.getPort();
String code = jsonObjects.get("deviceCode").getAsString();
Map<String,Integer> map = new HashMap<>();
map.put(code,port);
udpPortMap.setMaps(map);
}
}
}
@Override //在contextDestroyed()方法中,取消注册的Servlet。
public void contextDestroyed(ServletContextEvent sce) { logger.info("========UDPListener摧毁========="); }
}
云端接收心跳逻辑层
public void HeartbeatProcessingService(JSONObject jsonObject) {
if (jsonObject.getString("cmd").equals("unlockack")){
udpControl = new UDPControl();
udpControl.setReceiveControlParameters(jsonObject.getString("type"));
} else if (jsonObject.getString("cmd").equals("heart")){
try{
//设备编码, 小区ID 不能回空
if (jsonObject.getString("deviceCode") != null && jsonObject.getString("communityId") != null){
System.err.println("进入心跳判断");
//进入心跳判断
List<AcDevlist> list = acDevlistDao.equipmentList(jsonObject.getString("deviceCode"),Long.valueOf(jsonObject.getString("communityId")));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (list.isEmpty()){
log.info("当前设备没有录入");
}else{
for (AcDevlist acDevlist : list){
int sums = acDevlistHeartDao.acAevListIdNum(acDevlist.getId());
if (sums >= 1){
log.info("在心跳表中已存在");
acDevlistHeartDao.acdevlistByXqCode(acDevlist.getId(),sdf.parse(sdf.format(new Date())));
}else{
log.info("在心跳表中不存在");
AcDevlistHeart acDevlistHeart = new AcDevlistHeart();
acDevlistHeart.setAcDevlistId(acDevlist.getId());
acDevlistHeart.setHeartbeatTime(sdf.parse(sdf.format(new Date())));
acDevlistHeartDao.insert(acDevlistHeart);
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
emmm