69 changed files with 504 additions and 622 deletions
@ -1,153 +0,0 @@ |
|||
package com.xr.device_car.config.utils; |
|||
|
|||
|
|||
import com.xr.device_car.modules.analysis.entity.BallheadPT; |
|||
import org.apache.commons.lang.StringEscapeUtils; |
|||
import org.dom4j.Document; |
|||
import org.dom4j.Element; |
|||
import org.dom4j.io.SAXReader; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class AnalysisXml { |
|||
|
|||
|
|||
/** |
|||
* 解析xml[Profiles] 获取 token |
|||
* @param resultStr |
|||
* @return |
|||
*/ |
|||
public static List<String> analysisTokens(String resultStr){ |
|||
// 解析body
|
|||
// 转换返回结果中的特殊字符,返回的结果中会将xml转义,此处需要反转移
|
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element root = document.getRootElement(); |
|||
List<Element> elements = root.element("Body").element("GetProfilesResponse").elements("Profiles"); |
|||
return elements.stream().map(element -> element.attribute("token").getText()).collect(Collectors.toList()); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
/** |
|||
* 解析xml[ProfilesName] 获取 token |
|||
* @param resultStr |
|||
* @return |
|||
*/ |
|||
public static List<String> analysisProfiles(String resultStr){ |
|||
// 解析body
|
|||
// 转换返回结果中的特殊字符,返回的结果中会将xml转义,此处需要反转移
|
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element root = document.getRootElement(); |
|||
List<Element> elements = root.element("Body").element("GetProfilesResponse").elements("Preset"); |
|||
return elements.stream().map(element -> element.element("Name").getText() |
|||
).collect(Collectors.toList()); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
/** |
|||
* 解析xml[MediaUri] 获取 token |
|||
* @param resultStr |
|||
* @return |
|||
*/ |
|||
public static String analysisSnapshotUrl(String resultStr){ |
|||
// 解析body
|
|||
// 转换返回结果中的特殊字符,返回的结果中会将xml转义,此处需要反转移
|
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr).replace("&", "&"); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element rootElement = document.getRootElement(); |
|||
Element element = rootElement.element("Body").element("GetSnapshotUriResponse").element("MediaUri"); |
|||
List<Element> elements=element.elements("Uri"); |
|||
return String.valueOf(elements.get(0).getData()); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 解析xml[MediaUri] 获取 streamUrl |
|||
* @param resultStr |
|||
* @return |
|||
*/ |
|||
public static String analysisStreamUrl(String resultStr){ |
|||
// 解析body
|
|||
// 转换返回结果中的特殊字符,返回的结果中会将xml转义,此处需要反转移
|
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr).replace("&", "&"); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element rootElement = document.getRootElement(); |
|||
Element element = rootElement.element("Body").element("GetStreamUriResponse").element("MediaUri"); |
|||
List<Element> elements=element.elements("Uri"); |
|||
return String.valueOf(elements.get(0).getData()); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static List<BallheadPT> getBallheadPTs(String resultStr){ |
|||
List<BallheadPT> ballheadPTS=new ArrayList<>(); |
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr).replace("&", "&"); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element root = document.getRootElement(); |
|||
List<Element> elements = root.element("Body").element("GetPresetsResponse").elements("Preset"); |
|||
for(Element element:elements){ |
|||
BallheadPT pt=new BallheadPT(); |
|||
String id = element.attribute(0).getValue(); |
|||
pt.setId(id); |
|||
String name = String.valueOf(element.element("Name").getText()); |
|||
pt.setName(name); |
|||
List<Element> ele=element.elements("PTZPosition"); |
|||
pt.setX(String.valueOf(ele.get(0).element("PanTilt").attribute(0).getValue())); |
|||
pt.setY(String.valueOf(ele.get(0).element("PanTilt").attribute(1).getValue())); |
|||
pt.setZ(String.valueOf(ele.get(0).element("Zoom").attribute(0).getValue())); |
|||
ballheadPTS.add(pt); |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return ballheadPTS; |
|||
} |
|||
|
|||
public static BallheadPT getStatus(String resultStr){ |
|||
String xmlStr = StringEscapeUtils.unescapeXml(resultStr).replace("&", "&"); |
|||
SAXReader reader = new SAXReader(); |
|||
try { |
|||
Document document = reader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))); |
|||
Element root = document.getRootElement(); |
|||
Element element = root.element("Body").element("GetStatusResponse").element("PTZStatus").element("Position"); |
|||
String x=String.valueOf(element.element("PanTilt").attribute(0).getValue()); |
|||
String y=String.valueOf(element.element("PanTilt").attribute(1).getValue()); |
|||
String z=String.valueOf(element.element("Zoom").attribute(0).getValue()); |
|||
BallheadPT pt=new BallheadPT(); |
|||
pt.setX(x); |
|||
pt.setY(y); |
|||
pt.setZ(z); |
|||
return pt; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
package com.xr.device_car.config.utils; |
|||
|
|||
import com.xr.device_car.modules.analysis.entity.BallheadPT; |
|||
import com.xr.device_car.modules.analysis.entity.DeviceCamera; |
|||
import com.xr.device_car.modules.analysis.entity.OnvifAuthBean; |
|||
import com.xr.device_car.modules.analysis.entity.OnvifBean; |
|||
import org.springframework.util.CollectionUtils; |
|||
|
|||
import java.awt.image.BufferedImage; |
|||
import java.util.List; |
|||
|
|||
public class HkComUtil { |
|||
|
|||
public static BufferedImage getBole(DeviceCamera device) throws Exception{ //拉取枪机图片
|
|||
OnvifAuthBean onvifBean= new OnvifAuthBean(device.getDeviceIp(),80,device.getAccount(),device.getPassword()); |
|||
String url= OnvifBean.getRequestUrl(onvifBean); |
|||
String auth = OnvifUtils.getAuthorization("digest",onvifBean,"digest/GetProfiles.wsdl", url); |
|||
System.out.println("鉴权:"+auth); |
|||
//获取Token
|
|||
List<String> profileTokens = OnvifUtils.getProfileTokens(onvifBean,auth); |
|||
System.out.println("Token:"+profileTokens); |
|||
onvifBean.setAuth(auth); |
|||
if(!CollectionUtils.isEmpty(profileTokens)){ |
|||
String snapshotUrl=null; |
|||
String token = profileTokens.get(0); |
|||
BallheadPT ballheadPT=OnvifUtils.getPtzStatus(token,onvifBean); |
|||
//如果类型是球机转换指定点位
|
|||
if(device.getDeviceType().equals("3") && OnvifUtils.ptzCamera(token,onvifBean,device.getX(),device.getY(),device.getZ())){ |
|||
Thread.sleep(1000); |
|||
//拉取图片
|
|||
snapshotUrl = OnvifUtils.getSnapshotUrl(token,onvifBean); |
|||
if(snapshotUrl!=null){ |
|||
//转回原来位置
|
|||
OnvifUtils.ptzCamera(token,onvifBean,ballheadPT.getX(),ballheadPT.getY(),ballheadPT.getZ()); |
|||
} |
|||
//如果类型是固定枪机,直接获取图片
|
|||
}else{ |
|||
snapshotUrl = OnvifUtils.getSnapshotUrl(token,onvifBean); |
|||
} |
|||
//返回图片地址
|
|||
return Files.urlByImages(snapshotUrl,device.getAccount(),device.getPassword()); |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
@ -1,141 +0,0 @@ |
|||
package com.xr.device_car.config.utils; |
|||
|
|||
import com.xr.device_car.config.common.RESTClient; |
|||
import com.xr.device_car.modules.analysis.entity.BallheadPT; |
|||
import com.xr.device_car.modules.analysis.entity.DigestBean; |
|||
import com.xr.device_car.modules.analysis.entity.OnvifAuthBean; |
|||
import com.xr.device_car.modules.analysis.entity.OnvifBean; |
|||
import org.springframework.core.io.ClassPathResource; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.util.List; |
|||
|
|||
import static com.xr.device_car.config.common.Const.HEADERS_ONVIF_WWW_AUTHENTICATE; |
|||
|
|||
|
|||
public class OnvifUtils { |
|||
|
|||
|
|||
/** |
|||
* 获取鉴权 |
|||
* @param authType |
|||
* @param requestOnvifBean |
|||
* @param wsdl |
|||
* @param requestUrl |
|||
* @return |
|||
*/ |
|||
public static String getAuthorization(String authType, OnvifBean requestOnvifBean, String wsdl, String requestUrl) { |
|||
//参数
|
|||
// 读取GetProfiles.wsdl
|
|||
String getProfiles = FileUtil.fileReader(new ClassPathResource(wsdl)); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, getProfiles); |
|||
//如果是401的话 获取WWW-Authenticate 重新请求
|
|||
if (HttpResponseBean.isUnAuthorzied(httpResponseBean)) { |
|||
DigestBean digestBean = new DigestBean().getDigestBean(authType,requestOnvifBean, httpResponseBean.getFirstHeader(HEADERS_ONVIF_WWW_AUTHENTICATE).getValue()); |
|||
return digestBean.getToken(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取Token |
|||
* @param requestOnvifBean |
|||
* @param auth |
|||
* @return |
|||
*/ |
|||
public static List<String> getProfileTokens(OnvifBean requestOnvifBean, String auth) { |
|||
//请求url
|
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
//参数
|
|||
// 读取GetProfiles.wsdl
|
|||
String getProfiles = FileUtil.fileReader(new ClassPathResource("digest/GetProfiles.wsdl")); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, getProfiles,auth); |
|||
if(httpResponseBean != null) { |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if (HttpResponseBean.isSuccess(httpResponseBean) && StringUtils.hasLength(resultStr)) { |
|||
return AnalysisXml.analysisTokens(resultStr); |
|||
} |
|||
|
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
/** |
|||
* 获取截图地址 |
|||
* @param profileToken token |
|||
* @param requestOnvifBean |
|||
* @return |
|||
*/ |
|||
public static String getSnapshotUrl(String profileToken, OnvifAuthBean requestOnvifBean) { |
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
//参数
|
|||
// 读取GetProfiles.wsdl
|
|||
String snapshotUrlWsdl = FileUtil.fileReader(new ClassPathResource("digest/GetSnapshotUrl.wsdl")); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, String.format(snapshotUrlWsdl,profileToken),requestOnvifBean.getAuth()); |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if(StringUtils.hasLength(resultStr) && HttpResponseBean.isSuccess(httpResponseBean)) { |
|||
return AnalysisXml.analysisSnapshotUrl(resultStr); |
|||
} |
|||
return null; |
|||
} |
|||
//绝对位置转动球机
|
|||
public static boolean ptzCamera(String profileToken,OnvifAuthBean requestOnvifBean,String x,String y,String z){ |
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
String snapshotUrlWsdl = FileUtil.fileReader(new ClassPathResource("digest/AbsoluteMove.wsdl")); |
|||
String wsdl = String.format(snapshotUrlWsdl,profileToken,x,y,z); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, wsdl,requestOnvifBean.getAuth()); |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if(StringUtils.hasLength(resultStr) && HttpResponseBean.isSuccess(httpResponseBean)) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
//获取球机当前位置
|
|||
public static BallheadPT getPtzStatus(String token,OnvifAuthBean requestOnvifBean){ |
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
String snap = FileUtil.fileReader(new ClassPathResource("digest/GetStatus.wsdl")); |
|||
String wsdl = String.format(snap,token); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, wsdl,requestOnvifBean.getAuth()); |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if(StringUtils.hasLength(resultStr) && HttpResponseBean.isSuccess(httpResponseBean)) { |
|||
return AnalysisXml.getStatus(resultStr); |
|||
} |
|||
return null; |
|||
} |
|||
//获取所有预置点
|
|||
public static List<BallheadPT> getBallHeadPts(String profileToken,OnvifAuthBean requestOnvifBean){ |
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
String snapshotUrlWsdl = FileUtil.fileReader(new ClassPathResource("digest/GetPresets.wsdl")); |
|||
String wsdl = String.format(snapshotUrlWsdl,profileToken); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, wsdl,requestOnvifBean.getAuth()); |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if(StringUtils.hasLength(resultStr) && HttpResponseBean.isSuccess(httpResponseBean)) { |
|||
return AnalysisXml.getBallheadPTs(resultStr); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
//前往指定预置点
|
|||
public static boolean gotoBallHeadPts(String profileToken, OnvifAuthBean requestOnvifBean, BallheadPT ballheadPT){ |
|||
String requestUrl = OnvifBean.getRequestUrl(requestOnvifBean); |
|||
String snapshotUrlWsdl = FileUtil.fileReader(new ClassPathResource("digest/GotoPreset.wsdl")); |
|||
String wsdl = String.format(snapshotUrlWsdl,profileToken,ballheadPT.getId()); |
|||
HttpResponseBean httpResponseBean = RESTClient.getClientConnectionPool() |
|||
.postXML(requestUrl, wsdl,requestOnvifBean.getAuth()); |
|||
String resultStr = httpResponseBean.getBody(); |
|||
if(StringUtils.hasLength(resultStr) && HttpResponseBean.isSuccess(httpResponseBean)) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
package com.xr.device_car.modules.analysis.entity; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class BallheadPT { |
|||
private String id; |
|||
|
|||
private String name; |
|||
|
|||
private String x; |
|||
|
|||
private String y; |
|||
|
|||
private String z; |
|||
|
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<s:Body> |
|||
<tptz:AbsoluteMove> |
|||
<tptz:ProfileToken>%s</tptz:ProfileToken> |
|||
<tptz:Position> |
|||
<tt:PanTilt y="%s" x="%s" space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace" /> |
|||
<tt:Zoom x="%s" space="http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace" /> |
|||
</tptz:Position> |
|||
</tptz:AbsoluteMove> |
|||
</s:Body> |
|||
</s:Envelope> |
|||
@ -1,6 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<soap:Body> |
|||
<tds:GetOnvifInformation /> |
|||
</soap:Body> |
|||
</soap:Envelope> |
|||
@ -1,6 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"> |
|||
<s:Body> |
|||
<tptz:GetConfigurations /> |
|||
</s:Body> |
|||
</s:Envelope> |
|||
@ -1,8 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<s:Body> |
|||
<tptz:GetPresets> |
|||
<tptz:ProfileToken>%s</tptz:ProfileToken> |
|||
</tptz:GetPresets> |
|||
</s:Body> |
|||
</s:Envelope> |
|||
@ -1,6 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<soap:Body> |
|||
<trt:GetProfiles /> |
|||
</soap:Body> |
|||
</soap:Envelope> |
|||
@ -1,8 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<soap:Body> |
|||
<trt:GetSnapshotUri> |
|||
<trt:ProfileToken>%s</trt:ProfileToken> |
|||
</trt:GetSnapshotUri> |
|||
</soap:Body> |
|||
</soap:Envelope> |
|||
@ -1,8 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<s:Body> |
|||
<tptz:GetStatus> |
|||
<tptz:ProfileToken>%s</tptz:ProfileToken> |
|||
</tptz:GetStatus> |
|||
</s:Body> |
|||
</s:Envelope> |
|||
@ -1,25 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<s:Header xmlns:s="http://www.w3.org/2003/05/soap-envelope"> |
|||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> |
|||
<wsse:UsernameToken> |
|||
<wsse:Username>%s</wsse:Username> |
|||
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%s</wsse:Password> |
|||
<wsse:Nonce>%s</wsse:Nonce> |
|||
<wsu:Created>%s</wsu:Created> |
|||
</wsse:UsernameToken> |
|||
</wsse:Security> |
|||
</s:Header> |
|||
<soap:Body> |
|||
<GetStreamUri xmlns="http://www.onvif.org/ver10/media/wsdl"> |
|||
<StreamSetup> |
|||
<!-- Attribute Wild card could not be matched. Generated XML may not be valid. --> |
|||
<Stream xmlns="http://www.onvif.org/ver10/schema">RTP-Unicast</Stream> |
|||
<Transport xmlns="http://www.onvif.org/ver10/schema"> |
|||
<Protocol>%s</Protocol> |
|||
</Transport> |
|||
</StreamSetup> |
|||
<ProfileToken>%s</ProfileToken> |
|||
</GetStreamUri> |
|||
</soap:Body> |
|||
</soap:Envelope> |
|||
@ -1,9 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema"> |
|||
<s:Body> |
|||
<tptz:GotoPreset> |
|||
<tptz:ProfileToken>%s</tptz:ProfileToken> |
|||
<tptz:PresetToken>%s</tptz:PresetToken> |
|||
</tptz:GotoPreset> |
|||
</s:Body> |
|||
</s:Envelope> |
|||
@ -1,19 +1,38 @@ |
|||
package com.xr.device; |
|||
|
|||
import com.xr.device.netty.NettyServer; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; |
|||
import org.springframework.cloud.openfeign.EnableFeignClients; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.scheduling.annotation.EnableAsync; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
|
|||
@EnableScheduling//开启定时任务
|
|||
@SpringBootApplication |
|||
@EnableDiscoveryClient |
|||
@EnableFeignClients |
|||
public class DeviceGatherApplication { |
|||
@EnableAsync//开启异步
|
|||
public class DeviceGatherApplication implements CommandLineRunner { |
|||
|
|||
private final NettyServer nettyServer; |
|||
|
|||
@Autowired |
|||
public DeviceGatherApplication(NettyServer nettyServer) { |
|||
this.nettyServer = nettyServer; |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(DeviceGatherApplication.class, args); |
|||
} |
|||
|
|||
@Async |
|||
@Override |
|||
public void run(String... args) throws Exception { |
|||
nettyServer.start(); |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,60 @@ |
|||
package com.xr.device.netty; |
|||
|
|||
import com.xr.device.websocket.DeviceWebSocketHandler; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.channel.ChannelInboundHandlerAdapter; |
|||
import io.netty.util.ReferenceCountUtil; |
|||
|
|||
public class DeviceHandler extends ChannelInboundHandlerAdapter { |
|||
|
|||
private final HeartbeatService heartbeatService; |
|||
|
|||
private final DeviceWebSocketHandler deviceWebSocketHandler; |
|||
|
|||
public DeviceHandler(HeartbeatService heartbeatService,DeviceWebSocketHandler deviceWebSocketHandler) { |
|||
this.heartbeatService = heartbeatService; |
|||
this.deviceWebSocketHandler = deviceWebSocketHandler; |
|||
} |
|||
|
|||
@Override |
|||
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
|||
heartbeatService.addChannel(ctx.channel()); |
|||
super.channelActive(ctx); |
|||
} |
|||
|
|||
@Override |
|||
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
|||
heartbeatService.removeChannel(ctx.channel()); |
|||
super.channelInactive(ctx); |
|||
} |
|||
|
|||
@Override |
|||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
|||
ByteBuf in = (ByteBuf) msg; |
|||
try { |
|||
String deviceId = ""; // 从消息中提取设备ID
|
|||
String data = ""; // 从消息中提取其他数据
|
|||
|
|||
while (in.isReadable()) { |
|||
deviceId += (char) in.readByte(); |
|||
} |
|||
|
|||
|
|||
|
|||
// 通过WebSocket实时更新状态
|
|||
deviceWebSocketHandler.sendMessageToAll("Device " + deviceId); |
|||
|
|||
System.out.print(data); |
|||
System.out.flush(); |
|||
} finally { |
|||
ReferenceCountUtil.release(msg); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { |
|||
cause.printStackTrace(); |
|||
ctx.close(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package com.xr.device.netty; |
|||
|
|||
import io.netty.channel.Channel; |
|||
import io.netty.channel.ChannelId; |
|||
import io.netty.util.CharsetUtil; |
|||
import io.netty.buffer.Unpooled; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
@Service |
|||
public class HeartbeatService { |
|||
|
|||
private final Map<ChannelId, Channel> channels = new ConcurrentHashMap<>(); |
|||
|
|||
@Scheduled(fixedRate = 5000) |
|||
public void sendHeartbeat() { |
|||
for (Channel channel : channels.values()) { |
|||
if (channel.isOpen()) { |
|||
channel.writeAndFlush(Unpooled.copiedBuffer("Heartbeat", CharsetUtil.UTF_8)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void addChannel(Channel channel) { |
|||
channels.put(channel.id(), channel); |
|||
} |
|||
|
|||
public void removeChannel(Channel channel) { |
|||
channels.remove(channel.id()); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
package com.xr.device.netty; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Configuration |
|||
public class NettyConfig { |
|||
|
|||
@Bean |
|||
public HeartbeatService heartbeatService() { |
|||
return new HeartbeatService(); |
|||
} |
|||
|
|||
@Bean |
|||
public NettyServer nettyServer(HeartbeatService heartbeatService) { |
|||
return new NettyServer(heartbeatService); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
package com.xr.device.netty; |
|||
|
|||
import io.netty.bootstrap.ServerBootstrap; |
|||
import io.netty.channel.ChannelFuture; |
|||
import io.netty.channel.ChannelInitializer; |
|||
import io.netty.channel.ChannelOption; |
|||
import io.netty.channel.EventLoopGroup; |
|||
import io.netty.channel.nio.NioEventLoopGroup; |
|||
import io.netty.channel.socket.SocketChannel; |
|||
import io.netty.channel.socket.nio.NioServerSocketChannel; |
|||
|
|||
public class NettyServer { |
|||
|
|||
private final int port = 8520; |
|||
private final HeartbeatService heartbeatService; |
|||
|
|||
public NettyServer(HeartbeatService heartbeatService) { |
|||
this.heartbeatService = heartbeatService; |
|||
} |
|||
|
|||
public void start() throws InterruptedException { |
|||
EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
|||
EventLoopGroup workerGroup = new NioEventLoopGroup(); |
|||
|
|||
try { |
|||
ServerBootstrap bootstrap = new ServerBootstrap(); |
|||
bootstrap.group(bossGroup, workerGroup) |
|||
.channel(NioServerSocketChannel.class) |
|||
.childHandler(new ChannelInitializer<SocketChannel>() { |
|||
@Override |
|||
protected void initChannel(SocketChannel ch) { |
|||
ch.pipeline().addLast(new DeviceHandler(heartbeatService)); |
|||
} |
|||
}) |
|||
.option(ChannelOption.SO_BACKLOG, 128) |
|||
.childOption(ChannelOption.SO_KEEPALIVE, true); |
|||
|
|||
ChannelFuture f = bootstrap.bind(port).sync(); |
|||
f.channel().closeFuture().sync(); |
|||
} finally { |
|||
workerGroup.shutdownGracefully(); |
|||
bossGroup.shutdownGracefully(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
package com.xr.device.websocket; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.socket.CloseStatus; |
|||
import org.springframework.web.socket.WebSocketSession; |
|||
import org.springframework.web.socket.handler.TextWebSocketHandler; |
|||
import org.springframework.web.socket.TextMessage; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
@Component |
|||
public class DeviceWebSocketHandler extends TextWebSocketHandler { |
|||
|
|||
private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); |
|||
|
|||
@Override |
|||
public void afterConnectionEstablished(WebSocketSession session) throws Exception { |
|||
sessions.put(session.getId(), session); |
|||
} |
|||
|
|||
@Override |
|||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { |
|||
// 处理来自客户端的消息
|
|||
} |
|||
|
|||
@Override |
|||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { |
|||
sessions.remove(session.getId()); |
|||
} |
|||
|
|||
public void sendMessageToAll(String message) throws Exception { |
|||
for (WebSocketSession session : sessions.values()) { |
|||
if (session.isOpen()) { |
|||
session.sendMessage(new TextMessage(message)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void sendMessageToSession(String sessionId, String message) throws Exception { |
|||
WebSocketSession session = sessions.get(sessionId); |
|||
if (session != null && session.isOpen()) { |
|||
session.sendMessage(new TextMessage(message)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package com.xr.device.websocket; |
|||
|
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.socket.config.annotation.EnableWebSocket; |
|||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; |
|||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; |
|||
|
|||
@Configuration |
|||
@EnableWebSocket |
|||
public class WebSocketConfig implements WebSocketConfigurer { |
|||
|
|||
private final DeviceWebSocketHandler deviceWebSocketHandler; |
|||
|
|||
public WebSocketConfig(DeviceWebSocketHandler deviceWebSocketHandler) { |
|||
this.deviceWebSocketHandler = deviceWebSocketHandler; |
|||
} |
|||
|
|||
@Override |
|||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { |
|||
registry.addHandler(deviceWebSocketHandler, "/websocket/device"); |
|||
} |
|||
} |
|||
@ -1,126 +0,0 @@ |
|||
package com.xr.drone.demo; |
|||
|
|||
import java.io.IOException; |
|||
import java.text.SimpleDateFormat; |
|||
|
|||
import com.jnrsmcu.sdk.netdevice.IDataListener; |
|||
import com.jnrsmcu.sdk.netdevice.LoginData; |
|||
import com.jnrsmcu.sdk.netdevice.NodeData; |
|||
import com.jnrsmcu.sdk.netdevice.ParamData; |
|||
import com.jnrsmcu.sdk.netdevice.ParamIdsData; |
|||
import com.jnrsmcu.sdk.netdevice.ParamItem; |
|||
import com.jnrsmcu.sdk.netdevice.RSServer; |
|||
import com.jnrsmcu.sdk.netdevice.RealTimeData; |
|||
import com.jnrsmcu.sdk.netdevice.StoreData; |
|||
import com.jnrsmcu.sdk.netdevice.TelecontrolAck; |
|||
import com.jnrsmcu.sdk.netdevice.TimmingAck; |
|||
import com.jnrsmcu.sdk.netdevice.TransDataAck; |
|||
import com.jnrsmcu.sdk.netdevice.WriteParamAck; |
|||
|
|||
public class Demo { |
|||
|
|||
public static void main(String[] args) throws IOException, |
|||
InterruptedException { |
|||
//RSServer rsServer = RSServer.Initiate(2404);// 初始化
|
|||
RSServer rsServer = RSServer.Initiate(2404,"C:\\Users\\PC\\Desktop\\JavaSDKV2.2.2\\param.dat"); |
|||
rsServer.addDataListener(new IDataListener() {// 添加监听
|
|||
@Override |
|||
public void receiveTimmingAck(TimmingAck data) {// 校时指令应答处理
|
|||
System.out.println("校时应答->设备编号:" + data.getDeviceId() |
|||
+ "\t执行结果:" + data.getStatus()); |
|||
} |
|||
|
|||
@Override |
|||
public void receiveTelecontrolAck(TelecontrolAck data) {// 遥控指令应答处理
|
|||
System.out.println("遥控应答->设备编号:" + data.getDeviceId() |
|||
+ "\t继电器编号:" + data.getRelayId() + "\t执行结果:" |
|||
+ data.getStatus()); |
|||
} |
|||
|
|||
@Override |
|||
public void receiveStoreData(StoreData data) {// 已存储数据接收处理
|
|||
// 遍历节点数据。数据包括网络设备的数据以及各个节点数据。温湿度数据存放在节点数据中
|
|||
for (NodeData nd : data.getNodeList()) { |
|||
SimpleDateFormat sdf = new SimpleDateFormat( |
|||
"yy-MM-dd HH:mm:ss"); |
|||
String str = sdf.format(nd.getRecordTime()); |
|||
System.out.println("存储数据->设备地址:" + data.getDeviceId() |
|||
+ "\t节点:" + nd.getNodeId() + "\t温度:" + nd.getTem() |
|||
+ "\t湿度:" + nd.getHum() + "\t存储时间:" + str); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveRealtimeData(RealTimeData data) {// 实时数据接收处理
|
|||
// 遍历节点数据。数据包括网络设备的数据以及各个节点数据。温湿度数据存放在节点数据中
|
|||
for (NodeData nd : data.getNodeList()) { |
|||
System.out.println("实时数据->设备地址:" + data.getDeviceId() |
|||
+ "\t节点:" + nd.getNodeId() + "\t温度:" + nd.getTem() |
|||
+ "\t湿度:" + nd.getHum() + "\t经度:" + data.getLng() |
|||
+ "\t纬度:" + data.getLat() + "\t坐标类型:" |
|||
+ data.getCoordinateType() + "\t继电器状态:" |
|||
+ data.getRelayStatus()); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveLoginData(LoginData data) {// 登录数据接收处理
|
|||
System.out.println("登录->设备地址:" + data.getDeviceId()); |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveParamIds(ParamIdsData data) { |
|||
String str = "设备参数编号列表->设备编号:" + data.getDeviceId() |
|||
+ "\t参数总数量:" + data.getTotalCount() + "\t本帧参数数量:" |
|||
+ data.getCount() + "\r\n"; |
|||
for (int paramId : data.getPararmIdList())// 遍历设备中参数id编号
|
|||
{ |
|||
str += paramId + ","; |
|||
} |
|||
System.out.println(str); |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveParam(ParamData data) { |
|||
String str = "设备参数->设备编号:" + data.getDeviceId() + "\r\n"; |
|||
|
|||
for (ParamItem pararm : data.getParameterList()) { |
|||
str += "参数编号:" |
|||
+ pararm.getParamId() |
|||
+ "\t参数描述:" |
|||
+ pararm.getDescription() |
|||
+ "\t参数值:" |
|||
+ (pararm.getValueDescription() == null ? pararm |
|||
.getValue() : pararm.getValueDescription() |
|||
.get(pararm.getValue())) + "\r\n"; |
|||
} |
|||
System.out.println(str); |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveWriteParamAck(WriteParamAck data) { |
|||
String str = "下载设备参数->设备编号:" + data.getDeviceId() + "\t参数数量:" |
|||
+ data.getCount() + "\t" |
|||
+ (data.isSuccess() ? "下载成功" : "下载失败"); |
|||
System.out.println(str); |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void receiveTransDataAck(TransDataAck data) { |
|||
String str = "数据透传->设备编号:" + data.getDeviceId() + "\t响应结果:" |
|||
+ data.getData() + "\r\n字节数:" + data.getTransDataLen(); |
|||
System.out.println(str); |
|||
|
|||
} |
|||
}); |
|||
rsServer.start(); |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
package com.xr.drone.ntp; |
|||
|
|||
import org.apache.commons.net.ntp.NTPUDPClient; |
|||
import org.apache.commons.net.ntp.TimeInfo; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.net.DatagramPacket; |
|||
import java.net.DatagramSocket; |
|||
import java.net.InetAddress; |
|||
import java.nio.ByteBuffer; |
|||
import java.time.Instant; |
|||
import java.time.ZoneOffset; |
|||
import java.time.ZonedDateTime; |
|||
import java.util.Arrays; |
|||
|
|||
@Service |
|||
public class NtpService implements Runnable { |
|||
|
|||
private static final int NTP_PORT = 1123; |
|||
private static final long NTP_TIMESTAMP_OFFSET = 2208988800L; |
|||
|
|||
public void start() { |
|||
new Thread(this).start(); |
|||
} |
|||
|
|||
@Override |
|||
public void run() { |
|||
try (DatagramSocket socket = new DatagramSocket(NTP_PORT)) { |
|||
byte[] buffer = new byte[48]; |
|||
while (true) { |
|||
DatagramPacket requestPacket = new DatagramPacket(buffer, buffer.length); |
|||
socket.receive(requestPacket); |
|||
|
|||
long currentTime = System.currentTimeMillis() / 1000 + NTP_TIMESTAMP_OFFSET; |
|||
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(); |
|||
} |
|||
} |
|||
|
|||
|
|||
public static void main(String[] args) { |
|||
String serverAddress = "192.168.1.83"; // 替换为你的NTP服务器IP
|
|||
int port = 1123; // 使用端口1123
|
|||
|
|||
try { |
|||
NTPUDPClient client = new NTPUDPClient(); |
|||
client.setDefaultTimeout(10000); |
|||
client.open(); |
|||
|
|||
InetAddress hostAddr = InetAddress.getByName(serverAddress); |
|||
TimeInfo info = client.getTime(hostAddr, port); |
|||
|
|||
// 处理时间信息
|
|||
long returnTime = info.getReturnTime(); |
|||
long receiveTime = info.getMessage().getReceiveTimeStamp().getTime(); |
|||
long transmitTime = info.getMessage().getTransmitTimeStamp().getTime(); |
|||
|
|||
System.out.println("NTP Server Time: " + receiveTime); |
|||
System.out.println("Local Return Time: " + returnTime); |
|||
System.out.println("Transmit Time: " + transmitTime); |
|||
|
|||
client.close(); |
|||
} catch (Exception e) { |
|||
System.err.println("Error: " + e.getMessage()); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue