13 changed files with 532 additions and 6 deletions
@ -0,0 +1,116 @@ |
|||||
|
package com.xr.device.client; |
||||
|
|
||||
|
import com.xr.device.common.utils.StaticPropUtil; |
||||
|
import com.xr.device.model.service.impl.MqttServiceImpl; |
||||
|
import org.eclipse.paho.client.mqttv3.*; |
||||
|
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Component |
||||
|
public class MqttClient implements Runnable{ |
||||
|
|
||||
|
private static org.eclipse.paho.client.mqttv3.MqttClient client; |
||||
|
private static MqttConnectOptions options; |
||||
|
// 开始连接
|
||||
|
public void start() { |
||||
|
new Thread(this).start(); |
||||
|
} |
||||
|
public void run() { |
||||
|
try { |
||||
|
if(client!=null){ |
||||
|
return; |
||||
|
} |
||||
|
// host为主机名,clientid即连接MQTT的客户端ID,一般以唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
|
||||
|
client = new org.eclipse.paho.client.mqttv3.MqttClient(StaticPropUtil.host, StaticPropUtil.clientId, new MemoryPersistence()); |
||||
|
// MQTT的连接设置
|
||||
|
options = new MqttConnectOptions(); |
||||
|
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,设置为true表示每次连接到服务器都以新的身份连接
|
||||
|
options.setCleanSession(false); |
||||
|
// 设置连接的用户名
|
||||
|
options.setUserName(StaticPropUtil.clentName); |
||||
|
// 设置连接的密码
|
||||
|
options.setPassword(StaticPropUtil.clentPassword.toCharArray()); |
||||
|
// 设置超时时间 单位为秒
|
||||
|
options.setConnectionTimeout(StaticPropUtil.timeOut); |
||||
|
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
|
||||
|
options.setKeepAliveInterval(StaticPropUtil.interval); |
||||
|
// 设置断开后重新连接
|
||||
|
//options.setAutomaticReconnect(true);
|
||||
|
// 设置回调
|
||||
|
client.setCallback(new MqttServiceImpl()); |
||||
|
MqttTopic topic = client.getTopic(StaticPropUtil.TOPIC1); |
||||
|
// setWill方法,如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息 遗嘱
|
||||
|
options.setWill(topic, "close".getBytes(), 1, true); |
||||
|
client.connect(options); |
||||
|
// 订阅消息
|
||||
|
subList(StaticPropUtil.TOPIC1,2); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 关闭MQTT连接 |
||||
|
*/ |
||||
|
public void close() throws MqttException { |
||||
|
client.close(); |
||||
|
client.disconnect(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向某个主题发布消息 默认qos:1 |
||||
|
* |
||||
|
* @param topic:发布的主题 |
||||
|
* @param msg:发布的消息 |
||||
|
*/ |
||||
|
public void pub(String topic, String msg) throws MqttException { |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
//mqttMessage.setQos(2);
|
||||
|
mqttMessage.setPayload(msg.getBytes()); |
||||
|
MqttTopic mqttTopic = client.getTopic(topic); |
||||
|
MqttDeliveryToken token = mqttTopic.publish(mqttMessage); |
||||
|
token.waitForCompletion(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向某个主题发布消息 |
||||
|
* |
||||
|
* @param topic: 发布的主题 |
||||
|
* @param msg: 发布的消息 |
||||
|
* @param qos: 消息质量 Qos:0、1、2 |
||||
|
*/ |
||||
|
public void pub(String topic, String msg, int qos) throws MqttException { |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setQos(qos); |
||||
|
mqttMessage.setPayload(msg.getBytes()); |
||||
|
MqttTopic mqttTopic = client.getTopic(topic); |
||||
|
MqttDeliveryToken token = mqttTopic.publish(mqttMessage); |
||||
|
token.waitForCompletion(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订阅某一个主题 ,此方法默认的的Qos等级为:1 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
*/ |
||||
|
public void sub(String topic) throws MqttException { |
||||
|
client.subscribe(topic); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订阅某一个主题,可携带Qos |
||||
|
* |
||||
|
* @param topic 所要订阅的主题 |
||||
|
* @param qos 消息质量:0、1、2 |
||||
|
*/ |
||||
|
public void subList(String topic, int qos) throws MqttException { |
||||
|
List<String> list= Arrays.asList(topic.trim().split(",")); |
||||
|
for(String str:list){ |
||||
|
client.subscribe(str, qos); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
package com.xr.device.client; |
||||
|
|
||||
|
import com.xr.device.common.utils.StaticPropUtil; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.net.DatagramPacket; |
||||
|
import java.net.DatagramSocket; |
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.util.Arrays; |
||||
|
|
||||
|
@Service |
||||
|
public class NtpService implements Runnable { |
||||
|
|
||||
|
|
||||
|
public void start() { |
||||
|
new Thread(this).start(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
try (DatagramSocket socket = new DatagramSocket(StaticPropUtil.ntpPort)) { |
||||
|
byte[] buffer = new byte[48]; |
||||
|
while (true) { |
||||
|
DatagramPacket requestPacket = new DatagramPacket(buffer, buffer.length); |
||||
|
socket.receive(requestPacket); |
||||
|
|
||||
|
long currentTime = Long.parseLong(System.currentTimeMillis() / 1000 + StaticPropUtil.timestampOffset); |
||||
|
ByteBuffer responseBuffer = ByteBuffer.allocate(48); |
||||
|
Arrays.fill(responseBuffer.array(), (byte) 0); |
||||
|
|
||||
|
responseBuffer.put(0, (byte) 0x24); // LI, Version, Mode
|
||||
|
responseBuffer.put(1, (byte) 1); // Stratum
|
||||
|
responseBuffer.put(2, (byte) 0); // Poll
|
||||
|
responseBuffer.put(3, (byte) -20); // Precision
|
||||
|
|
||||
|
responseBuffer.putInt(4, 0); // Root Delay
|
||||
|
responseBuffer.putInt(8, 0); // Root Dispersion
|
||||
|
responseBuffer.putInt(12, 0x4C4F434C); // Reference Identifier ("LOCL")
|
||||
|
|
||||
|
responseBuffer.putLong(16, currentTime << 32); // Reference Timestamp
|
||||
|
responseBuffer.putLong(24, ByteBuffer.wrap(requestPacket.getData(), 40, 8).getLong()); // Originate Timestamp
|
||||
|
responseBuffer.putLong(32, currentTime << 32); // Receive Timestamp
|
||||
|
responseBuffer.putLong(40, currentTime << 32); // Transmit Timestamp
|
||||
|
|
||||
|
DatagramPacket responsePacket = new DatagramPacket( |
||||
|
responseBuffer.array(), |
||||
|
responseBuffer.array().length, |
||||
|
requestPacket.getAddress(), |
||||
|
requestPacket.getPort() |
||||
|
); |
||||
|
socket.send(responsePacket); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
package com.xr.device.common.task; |
||||
|
|
||||
|
import com.xr.device.client.MqttClient; |
||||
|
import com.xr.device.client.NtpService; |
||||
|
import com.xr.device.netty.NettyServer; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.scheduling.annotation.Async; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
public class ApplicationTaskTool { |
||||
|
|
||||
|
private final NettyServer nettyServer; |
||||
|
|
||||
|
private final NtpService ntpService; |
||||
|
|
||||
|
private final MqttClient mqttClient; |
||||
|
|
||||
|
@Async("fixedTaskExecutor") |
||||
|
public void startNettyServer() throws Exception { |
||||
|
nettyServer.start(); |
||||
|
} |
||||
|
|
||||
|
@Async("fixedTaskExecutor") |
||||
|
public void startNtpService() throws Exception { |
||||
|
ntpService.start(); |
||||
|
} |
||||
|
|
||||
|
@Async("fixedTaskExecutor") |
||||
|
public void startMqttClient() throws Exception { |
||||
|
mqttClient.start(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.xr.device.common.thread; |
||||
|
|
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||
|
|
||||
|
import java.util.concurrent.Executor; |
||||
|
|
||||
|
@Configuration |
||||
|
public class ThreadPoolConfig { |
||||
|
|
||||
|
@Bean(name = "fixedTaskExecutor") |
||||
|
public Executor taskExecutor() { |
||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
||||
|
int poolSize = 10; // 固定线程池大小
|
||||
|
executor.setCorePoolSize(poolSize); |
||||
|
executor.setMaxPoolSize(poolSize); |
||||
|
executor.setQueueCapacity(100); |
||||
|
executor.setThreadNamePrefix("FixedAsync-"); |
||||
|
executor.initialize(); |
||||
|
return executor; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
package com.xr.device.common.utils; |
||||
|
|
||||
|
import com.xr.device.model.service.impl.MqttServiceImpl; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.*; |
||||
|
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import javax.annotation.PostConstruct; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
public class MqttClientUtil { |
||||
|
|
||||
|
|
||||
|
private MqttClient mqttClient; |
||||
|
private MqttConnectOptions mqttConnectOptions; |
||||
|
|
||||
|
@PostConstruct |
||||
|
private void init(){ |
||||
|
connect(StaticPropUtil.host, StaticPropUtil.clientId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 链接mqtt |
||||
|
* @param host |
||||
|
* @param clientId |
||||
|
*/ |
||||
|
private void connect(String host,String clientId){ |
||||
|
try{ |
||||
|
mqttClient = new MqttClient(host,clientId,new MemoryPersistence()); |
||||
|
mqttClient.setCallback(new MqttServiceImpl()); |
||||
|
mqttConnectOptions = getMqttConnectOptions(); |
||||
|
mqttClient.connect(mqttConnectOptions); |
||||
|
}catch (Exception e){ |
||||
|
log.error("mqtt服务链接异常!"); |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置链接对象信息 |
||||
|
* setCleanSession true 断开链接即清楚会话 false 保留链接信息 离线还会继续发消息 |
||||
|
* @return |
||||
|
*/ |
||||
|
private MqttConnectOptions getMqttConnectOptions(){ |
||||
|
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); |
||||
|
mqttConnectOptions.setUserName(StaticPropUtil.clentName); |
||||
|
mqttConnectOptions.setPassword(StaticPropUtil.clentPassword.toCharArray()); |
||||
|
mqttConnectOptions.setServerURIs(new String[]{StaticPropUtil.host}); |
||||
|
mqttConnectOptions.setKeepAliveInterval(StaticPropUtil.interval); |
||||
|
mqttConnectOptions.setConnectionTimeout(StaticPropUtil.timeOut); |
||||
|
//mqttConnectOptions.setAutomaticReconnect(true);
|
||||
|
mqttConnectOptions.setCleanSession(false); |
||||
|
return mqttConnectOptions; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
*mqtt链接状态 |
||||
|
* @return |
||||
|
*/ |
||||
|
private boolean isConnect(){ |
||||
|
if(Objects.isNull(this.mqttClient)){ |
||||
|
return false; |
||||
|
} |
||||
|
return mqttClient.isConnected(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置重连 |
||||
|
* @throws Exception |
||||
|
*/ |
||||
|
private void reConnect() throws Exception{ |
||||
|
if(Objects.nonNull(this.mqttClient)){ |
||||
|
log.info("mqtt 服务已重新链接..."); |
||||
|
this.mqttClient.connect(this.mqttConnectOptions); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 断开链接 |
||||
|
* @throws Exception |
||||
|
*/ |
||||
|
private void closeConnect() throws Exception{ |
||||
|
if(Objects.nonNull(this.mqttClient)){ |
||||
|
log.info("mqtt 服务已断开链接..."); |
||||
|
this.mqttClient.disconnect(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发布消息 |
||||
|
* @param topic |
||||
|
* @param message |
||||
|
* @param qos |
||||
|
* @throws Exception |
||||
|
*/ |
||||
|
public void sendMessage(String topic,String message,int qos) throws Exception { |
||||
|
if(Objects.nonNull(this.mqttClient) && this.mqttClient.isConnected()){ |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setPayload(message.getBytes()); |
||||
|
mqttMessage.setQos(qos); |
||||
|
|
||||
|
MqttTopic mqttTopic = mqttClient.getTopic(topic); |
||||
|
|
||||
|
if(Objects.nonNull(mqttTopic)){ |
||||
|
try{ |
||||
|
MqttDeliveryToken publish = mqttTopic.publish(mqttMessage); |
||||
|
if(publish.isComplete()){ |
||||
|
log.info("消息发送成功---->{}",message); |
||||
|
} |
||||
|
}catch(Exception e){ |
||||
|
log.error("消息发送异常",e); |
||||
|
} |
||||
|
} |
||||
|
}else{ |
||||
|
reConnect(); |
||||
|
sendMessage(topic,message,qos); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.xr.device.model.entity; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class Heartbeat { |
||||
|
|
||||
|
private String battery_level; |
||||
|
|
||||
|
private String created; |
||||
|
|
||||
|
private String id; |
||||
|
|
||||
|
private String ip_address; |
||||
|
|
||||
|
private String mac_address; |
||||
|
|
||||
|
private String name; |
||||
|
|
||||
|
private String serial_number; |
||||
|
|
||||
|
private String timestamp; |
||||
|
|
||||
|
private String updated; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
package com.xr.device.model.service.impl; |
||||
|
|
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.xr.device.model.entity.Heartbeat; |
||||
|
import com.xr.onvifhk.entity.DeviceInfo; |
||||
|
import com.xr.onvifhk.util.HkComUtil; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
|
||||
|
public class MqttServiceImpl implements MqttCallback { |
||||
|
@Override |
||||
|
public void connectionLost(Throwable throwable) { |
||||
|
System.out.println("connectionLost---------连接断开,可以做重连"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void messageArrived(String topic, MqttMessage message) throws Exception { |
||||
|
System.out.println("接收消息主题 : " + topic); |
||||
|
System.out.println("接收消息Qos : " + message.getQos()); |
||||
|
if(topic.equals("camera/heartbeat")){ |
||||
|
//收到设备心跳后,调取onvif抓取照片
|
||||
|
String res = new String(message.getPayload()); |
||||
|
if(!res.equals("close")){ |
||||
|
Heartbeat mqttTop = JSONObject.parseObject(res, Heartbeat.class); |
||||
|
DeviceInfo info = new DeviceInfo(); |
||||
|
info.setPort(1002); |
||||
|
info.setIp(mqttTop.getIp_address()); |
||||
|
info.setPassword("1234"); |
||||
|
info.setAccount("admin"); |
||||
|
String url = HkComUtil.getBole(info,null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deliveryComplete(IMqttDeliveryToken token) { |
||||
|
System.out.println("deliveryComplete---------" + token.isComplete()); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue